An Even Newer, Even More Improved V2 Power Supply: Part 2
I had to get someone from work to remove the USB interface ASIC I had installed upside down. Oh well, stuff happens.
Once installed rightside up, the USB interface worked like a champ.
I went crazy writing software for the local processor. I based in on some software I wrote a few years ago as an experiment in writing C++ code for small embedded processors.
The general approach is that all the hardware is controlled through C++ objects. For example, if I want to drive the status LED on the SheevaPower 2.0 PCB, I instantiate an LED object to control that LED:
// Create a Led object to drive the SheevaPower status LED
statusLed = new Led(PSTR("status"), Led::CURRENT_SOURCE, Led::PORT_D, _BV(PD2));
The arguments to the constructor tell the object to drive the LED in current source fashion, meaning the the LED is wired such that driving a '1' on the output port will source current through the LED making it light up. The final two parameters explain which port the LED is attached to (port D), and which GPIO bit on that port (bit 2) is wired to the LED.
Here is the definition of the Led constructor:
: Command(PSTR("led"), _name, PSTR("state=<off|on|toggle|?> [, ...]"))
The 'PSTR("status")' is telling the new LED object what its name is: "status". The PSTR() thing is a annoying thing that the AVR processor needs to explain to the C compiler that the string should be stored in the Flash memory instruction space, not the RAM.
One cool thing about the hardware-controlling objects in the system is that they are all required to implement a really simple command parser. Above, you can see the Led constructer call the Command constructor as it gets created. Each object knows what it is (a LED in this example), so they all inherit a tiny parser that knows how to set and read LED states. The name ("status") is used to identify which LED you might want to control, if the system had more than one.
One cool part about this approach is that anything that is based on the Command object will automatically register themselves with the main command parser as they get instantiated. Basically, the Command object creates a linked list of all the objects that inherit from Command.
The Command object itself implements the basic command parser. It's job is to figure out which object is being referred to on the command line, and then passing control of the parser to that object which finishes up the job.
In addition, all command objects are required to provide a "help" string to their constructor. As a result, the built-in "help" command simply scans the list of command objects and displays all the help strings:
beep duration-in-mSec@freq-in-HZ [, ...]
id [<FW|PCB>[=?]] [, ...]
led:status state=<off|on|toggle|?> [, ...]
From the help string list above, you can see how the following commands would be interpreted by the Led command parser:
led:status state=on (would turn the LED on)
led:status state=off (would turn the LED off)
led:status state=off state=toggle (would turn the LED off, then toggle it on)
led:status state=? (would tell you if the LED was on (1) or off (0))
So why does a power supply implement a command parser? The whole point is that it gives the SheevaPlug the means to talk to the power supply, ask it questions about the various voltage levels, and even control things, even if it is just an LED.
When I get some time, I will add commands to allow the SheevaPlug to display its own messages on the LCD display. That would be kind of handy, given that the Sheevaplug is headless.
Next up: Installed and working!
It will be worth it in the end.