In Log #2, I explained how I found the source code I hoped would work with my hoverboard, and how I prepared for developing new code.
The first step was to build and deploy one of the code repo's I had found..
I decided on the Gaucho LawmMower hack, as it seemed to interface to more external devices, and it included more additional support files (like schematics and images).
I forked and cloned the repo using Github Desktop. It took a but of tweaking to get Keil uVision 5 to accept the project as cloned, but once I discovered how to overcome one build problem, it looked like it was ready to flash. I didn't record the specific build error that was being thrown (something about a missing map or file ??), but the solution was to change the project setting below.
The overall project structure is really easy to work with. There were matching .C and .H files, and the number of total files was reasonable. Since it took me a while to get the hang of what the files were doing, here's a quick overview of the ones that interested me the most.
main.c Obviously the main program, but it mainly forms the background error checking function. After sounding the startup tomes, it enters an endless loop checking for error conditions, such as low voltage, high current and inactivity timeout, at which point it sounds an alarm, sets the status LEDS and powers down both boards.
setup.c This module contains the initialization code for all of the hardware interfaces: Timers, GPIO, Interrupts, Watchdog, ADC, PWM & Comm Ports. If you want to see how the code interfaces with the hardware, this is where you need to look.
it.c This module contains the running code for Interrupts and timers (which are based on those interrupts). Most of the code in this module is time critical, so there are no loops of heavy processing. Functions typically set some flags and maybe call a small function, and then return.
bldc.c This module is where all the cool Brushless DC motor control logic exists. The main bldc control function CalculateBLDC() is called periodically. The code says every 62.5 uS, but on the HOVER-1 Ultra, is appears to run twice as often as this, at 31.25 uS. I don't know if this is a configuration error, or perhaps the processor has a faster Crystal, but it's a bonus. The Gaucho code has several motion command functions that I didn't end up using, but these sit on top of the normal PWM power control. The main function of CalculateBLDC() seems to be to determine which drive phases should be active, and what PWM power level is required. The HOVER-1 wheel has 15 phases per revolution, and 6 Steps per phase (my wording). This means that for each revolution of the wheel, you are able to discern 90 transitions (Steps).
commsXxxxxx.c There are several module which have the same file name structure: comms followed by the name of the communications function. Each of these files deals with one of the various serial communications ports. This could be for inter-board communication, diagnostics, Bluetooth or any other purpose. Each instance has a very similar structure. There is a function called updateXxxxInput() which is called based on some interrupt (UART Rx or Timer) which signals that a single input event must be processed. There is also a function called checkXxxxInput() who's job it is to process a completed input buffer and take the appropriate action. I found it very easy to add my own communications using this structure.
It's worth noting that in both of the two code sets that I found, there was the assumption of a master/slave relationship between the two controller boards. The "Master" would determine that some action needed to be taken and it would take that action locally, and then tell the slave to do the same thing. This was done over the MasterSlave comms interface.
In order for single set of source code to be used to generate the binary image for both controllers, there were two compiler #defines created (called MASTER and SLAVE) which were used for conditional compilation. In several files, there are sequences like the following:
// Variables which will be written by slave frame bool slaveBoardMovementByStepCompleted=FALSE; // Variables which will be send to master FlagStatus upperLEDMaster = RESET; FlagStatus lowerLEDMaster = RESET; FlagStatus mosfetOutMaster = RESET; FlagStatus beepsBackwardsMaster = RESET;
Since I just wanted to prove that I could build an deploy the code to one controller, I #defined MASTER and used that generated binary throughout my initial testing.
Later on, when I got around to developing my own code, I wanted both controller boards to be running the same code, so one of the first things I did was to eliminate the MASTER and SLAVE defines, and delete all the slave code. But that's a later story.