Log #3 Exploring Firmware

A project log for Hoverboards for Assistive Devices

Want to motorize a wheeled assistive device? What better than a hacked hoverboard with brushless motors, electronics and power.

Phil MalonePhil Malone 04/16/2020 at 17:490 Comments

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:

#ifdef MASTER
   #define USART_MASTERSLAVE_TX_BYTES 10  // Transmit byte count      
   #define USART_MASTERSLAVE_RX_BYTES 5   // Receive byte count 
    // Variables which will be written by slave frame
    bool slaveBoardMovementByStepCompleted=FALSE;

#ifdef SLAVE
   #define USART_MASTERSLAVE_TX_BYTES 5   // Transmit byte count      
   #define USART_MASTERSLAVE_RX_BYTES 10  // Receive byte count 
    // 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.