FreeBASIC continues to impress me with its modernity. I already found out that it’s possible to implement C-style pointers in FreeBASIC — and it turns out that you can have C++ – style constructor functionality, as well (code that runs automatically when an object is instantiated.)
For our “object,” we’ll use a custom FreeBASIC type :
type node
x as double
y as double
visited as ubyte
end type
It would be nice if we could assign default properties to these values automatically when they are allocated. Looking through the documentation, it seems provision is made for this:
type node
x as double = 0
y as double = 0
visited as ubyte = 0
end type
This works, of course. But what if we wanted random values — would that work?
type node
x as double = rnd*100
y as double = rnd*100
visited as ubyte = 0
end type
It turns out that this works, even when allocating an entire array of the type:
dim as node lotsOfNodes(1000) 'This will allocate the memory AND assign default values!
There was only one question, at this point. How far could we go? Was it possible to call a function and therefore run arbitrary code? Yes!
'Demonstration of "constructor"-type initialization
'Copyleft 2022, M. Eric Carr / Paleotechnologist.Net
'(CC:BY-NC-SA)
const MAXNODES = 20
declare function myFunction(x as double) as double
type node
x as double = myFunction(100)
y as double = myFunction(100)
visited as integer = 0
end type
dim as node f(MAXNODES) 'Nodes are initialized as they are created
dim as integer n
for n=0 to MAXNODES-1
print f(n).x,f(n).y,f(n).visited 'View the contents to check initialization
next n
sleep
function myFunction(x as double) as double
static callCount as ulongint
callCount=callCount+1
return rnd*x
end function
Maybe they’re still not true “objects” — but they pass the duck test (with the possible exception of not having similar destructor functionality.) At any rate, it’s fun to see a language that’s ultimately still a descendant of 1964 Dartmouth BASIC capable of things like running arbitrary code implicitly on object creation.
One common task for an electronic device is to read the position of a rotary input. This could be to determine the position of part of a mechanical assembly, or simply to allow the user to select from various options using a scroll wheel.
Rotary encoders generally come in two varieties — absolute and relative. Absolute encoders, as you might expect, convey information about their absolute position. Even if the system has been powered off, these encoders will tell the system exactly where it is (plus or minus one position count.)
Quadrature encoders, on the other hand, do not remember any information about their position. They simply allow the microcontroller (or other circuit) to determine how far the encoder has turned while the microcontroller is monitoring it. This is perfect for jog dials used by the user, where absolute dial position doesn’t matter. Also, they’re easier to manufacture and work with — just two signal wires will do (plus ground and power.)
Quadrature encoders are so named because they supply two phases of information, in quadrature. That is, these two phases transition 90 degrees apart in phase. One cycle of quadrature output (usually corresponding to one “click” of a dial) comprises four distinct phases. Turning it in one direction, the output might be
LL –> LH –> HH –> HL –> LL.
Turned the other way, this encoder would then output
LL –> HL –> HH –> LH –> LL.
Only one line changes state at a time (the criterion for a Gray code) — and by monitoring both lines, the direction of travel can be determined. (If the old state were HH, for example, the next state would have to be either LH or HL — one for clockwise and the other for counterclockwise. The state it changed into would tell you which direction it turned.)
As long as the microcontroller checks both lines faster than the maximum rate at which they can change, no position skip is possible. (Skipping may not be a huge issue on a jog wheel if it only happens once in a while — but skipping steps would make a positional encoder lose track of where it was, possibly causing a mechanical crash.)
Here is Arduino C code (tested on ESP32) to read two pins: ROTARYDAT and ROTARYCLK (defined elsewhere) to get the quadrature state of a rotary input, and update global integer variables called encHalfPhases and encTheta. (Using encHalfPhases directly resulted in the control being too sensitive to use reliably, so encTheta divides it by two.)
void IRAM_ATTR ISR() {
//Interrupt handler for quadrature encoder on pins ROTARYDAT and ROTARYCLK.
//Check the quadrature state, update theta, and GTFO...
//CC:BY-NC-SA M. Eric Carr / Paleotechnologist.Net
//Grab the new state (TODO: use raw GPIO reads to do this atomically and faster)
uint8_t newState;
newState=0x00;
if(digitalRead(ROTARYDAT)){newState|=0x01;}
if(digitalRead(ROTARYCLK)){newState|=0x02;}
//Update enctheta based on pin state changes
if((newState ^ encState) & 0x02){
//Clock changed. Increment or decrement count based on if clk and data match
if((newState & 0x01)^((newState & 0x02)>>1)){
encHalfPhases++;}
else{
encHalfPhases--;}
}
//Update the encoder state
encState=newState;
//Update theta
encTheta=(encHalfPhases>>1); //Divide by two. Shift may be faster than div??
}
The code is written to be run as an interrupt service routine (ISR), so it should execute and return very quickly, with no loops or delays. Attach it to interrupt-on-change-capable pins as follows:
Through the magic of interrupts, when either pin changes state, the ISR_quadrature routine is called automatically, the saved state is updated, theta incremented, and control is handed back to the main program. All of this should take a few microseconds on most MCUs.
The IRAM_ATTR requests that the compiler place this routine in SRAM instead of Flash memory, for speed of execution (interrupt handlers should be very fast, because you want it to finish before another important interrupt event comes along.)
If implemented as an ISR tied to interrupt-on-change, this routine should work reliably to track quadrature changes. Use it to implement jog controls or to see how far your robot has traveled etc.
A subtle note: Although it may be tempting to use CLK and DT to clock directional data in to a D flip-flop, this setup can glitch if CLK is toggled without changing DT. The “Clock” and “Data” names are misnomers; thinking of them as Phase A and Phase B is safer.
Full-color (okay, 16- or 18-bit color) LCD and LED displays have been available as breadboardable, microcontroller-friendly modules for several years now. Recently, though, round displays have been appearing in hobbyist-friendly form. These can be thought of as rectangular displays that simply don’t implement pixels that aren’t in the circle radius.
GC9A01 display modules use an SPI bus interface, which is slightly more involved to set up than the ubiquitous I2C (or “Wire”) interface. There’s a good reason for this — at 240×240 resolution (really 120px radius), that’s some 45,000 pixels per frame, or north of 800,000 pixels. If you want to do any kind of video or animation, you need several frames per second. SPI is generally used for higher data rate applications, since it can run at 8Mb or more.
I followed the instructions in this video to get the display set up using a standard ESP32 dev board module. The Arduino GFX Graphics Library referenced in the video can drive the GC9A01 via the usual set of APIs for drawing pixels, lines, circles, etc. Do note that these are 3.3V parts — it’s best to drive them with something modern and fast like an ESP32, which will probably be a 3v3 part itself. An Arduino Uno or PIC16F84A or 8051 could work with these, though — just very slowly. Maybe I’ll get the DrACo/Z80 to run one (via a voltage divider, at least).
They’d make great aircraft instrument replica displays, if only they were a little larger…
I’m used to discovering cool new technology when browsing sites like DigiKey. But occasionally, you’ll come across device descriptions that at first look like oxymorons.
I recently needed to order a few 32kB SRAM chips for a course I’m teaching, involving building the latest incarnation of the DrACo/Z80 computer. DigiKey has always been a reliable source for SRAM, and ever since I learned that pretty much every manufacturer out there uses compatible pinouts and protocols, life has been easy. (There are benefits to working with tech with forty-year-old traditions and standards!)
While ordering the SRAM, I came across an interesting filter field — Volatile or Non-Volatile. This is interesting because SRAM, being transistor-based, is inherently volatile: when you remove the power, it stops remembering whatever you stored in it. (SRAM uses six-transistor memory cells, and when you remove the power, the delicate balancing act that keeps it in a zero or one state goes away. When powered up again, it will basically pick 0 or 1 at random.)
Filtering by Non-Volatile and selecting a likely-looking example, it became apparent what I was looking at. Someone using SRAM had at some point asked an engineer to make a version that wouldn’t lose its contents when powered off. Having looked at the problem, an engineering solution was arrived at — a button-cell battery and a small power management circuit was unceremoniously grafted on top of a memory chip. When external power is available, the system works as usual. When power to the Z80 is cut, it keeps the SRAM memory powered but inactive, with a backup time of up to eleven years, per the datasheet. It looks weird, but it works nicely.
The form factor is still DIP28 with the same (or compatible) pinout — but due to the addition of the backup battery, it’s in a 600-mil (0.6″) wide format instead of 300-mil. The 32kB SRAM chips we’ve used over the years have come in both formats — and both work well on a breadboard. However, the original prototype uses a 300-mil socket. Re-wire-wrapping it would be a boring, tedious weekend project with a real risk of breaking things.
The most efficient solution (the one which would have it working by the next lecture and which didn’t involve FedExing exotic adapter PC boards overnight) was to hack together an adapter out of stripboard. It actually turned out better than I was expecting.
This almost solved the problem. The row connectors I used for the 300-mil pins were too big to fit in the DrACo’s wire-wrapped socket. Fortunately, a pair of DIP14 sockets were just the right size to fit in the socket and accept the new contraption.
It’s weird, having 32kB of memory space that won’t go away. Now I need to figure out what to do with it. For now, I’m running a short program to zero it all out.
What uses do people have for so many thousands of bytes of memory, anyway…??