Software Testing

Now that all the modules are done, and strung together in main, let’s do some testing. Specifically, let’s look at the timing.

For this testing, each module turns an LED on (active low) when it starts running code, and off when it finishes. The LED is left alone if the code does not run. By doing this, we can see how long it takes for a module to run, as well as how frequently it runs.

Load regulator, desired current update duration
Load regulator, offset current adjustment duration
Load regulator, desired current update period

The load regulator adjusts the desired current, and then adjusts the offset current. The desired current takes 132 us to run, while the offset takes 196 us to run. The desired current is updated at about 900 Hz. The code ideally updates at 1 kHz, but 900 Hz should be frequent enough for our purposes, since this load is designed for inputs that change slowly. The current offset is adjusted at about 60 Hz, so that’s fine.

Updating the desired current and adjusting the offset both probably suffer from the same slowdown: waiting for the hardware. For example, in order to update the desired current, the ADC must be read, which involves setting up and then waiting for a 16 bit SPI transaction. Likewise, in order to adjust the offset, the current monitor ADC must be read, which again requires the code to wait for the I2C transaction to begin and end. The way to address this issue is to do the reading in two steps: first, set up the read. Then, exit the code and do something else. During this time, the read will complete. Finally, when the load regulator module is run again, it can just act on the received data. This eliminates the need for the code to wait for the data, speeding up execution in exchange for increased complexity.

UI module, updating LCD screen
LCD screen update, zoomed in

The user-interface takes about 85 ms to update a screen, and the screen is updated every second, assuming no user input. It’s so slow for two reasons. Firstly, the entire screen isn’t updated in one go. In order to avoid blocking the load regulator module, which has the highest priority, the user interface module transmits one character to the screen, and then exits. Since the screen is 4×20 characters, the module must be run about 80 times per screen. The second reason the screen update is slow is because the screen is updated through I2C at 100 kHz. In order to save GPIO, I am using an I2C interface, which is notoriously slow; additionally, I can’t go 400 kHz like I can with the current monitor ADC due to limitations of the IC on the LCD module. This means that each character update, which must be done 80 times per screen, is painfully slow.

LCD update, set up
LCD update, single character

Let’s take a closer look at the 85 ms. There’s actually two different durations: the set up and the character update. The set up is where the code determines what the current screen is, what the user input does (if there were any), and what characters should be transmitted to the LCD. All combined, this process takes 2.25 ms to run.

Next is updating the screen, one character at a time. The time it takes to change one character on the screen is 0.81 ms, and this is done 80 times. Ideally, the code would take 80*0.81+2.25 = 67.05 ms to run, but this is assuming no interruptions occur.

Speaking of interruptions, you’ll see that the time between each character update varies; some pauses are longer than others. These pauses are when the user interface module stops updating the screen so that other modules can execute; how long it takes for the code to get back to the user interface determines the length of the pause. Let’s see how the load regulator and user interface modules affect each other:

LCD update and load regulator

CH1 is the LED for the user interface, and CH2 is the LED for the load regulator. As you can see, the two modules impact each other’s performance. When the LCD isn’t being updated, the load regulator is being sampled at a higher rate. Conversely, because the load regulator is being read in-between character updates, the screen isn’t being updated as quickly as it could be. Let’s take a closer look at how the two modules interleave their activity:

Load regulator, away from LCD update

As mentioned previously, when the LCD isn’t being updated, the load regulator runs at about 900 Hz.

Load regulator, during single character update

When the user interface module is updating characters on the LCD, the load regulator is cut down to about 500 Hz, which is a big hit. In the example above, one character is updated to the LCD. Then, the load regulator wasn’t set to run yet, so the code updates the next character. This causes the next load regulator run to be delayed, impacting performance.

Load regulator, during LCD set up

Even worse is when the user interface setup occurs. Since the setup takes longer than two character updates, the load regulator frequency is reduced even further, to around 300 Hz.

It is ultimately a good thing that the two modules interleave their activity; if one module hogged the CPU, then the system performance would suffer much worse than what we’re seeing here. Additionally, since the variable load is designed to work with constant or slowly changing input power, the intermittent reduction in how frequently the load regulator module is run is fine by me. Lastly, as bad as it seems, the user interface is only run once a second, which is very infrequent, or when the user provides input, which means the user is probably browsing the menu, and the variable load’s performance during that time probably won’t be critical, since the user is most likely about to change the variable load’s mode or target value.

If you can’t stand the user interface interfering with the execution of the load regulator module, then I recommend using interrupts to run the load regulator, rather than a timer like I did. I used timers because it makes the coding and debugging simpler. For my purposes the current behavior is acceptable.

Temperature regulator duration

The temperature regulator takes 496 us to run. This is a shockingly long time for such as simple code. All it’s doing is reading an ADC, then updating the duty cycle! As previously mentioned with the load regulator, the slowdown is most likely starting and then waiting for the ADC to complete its conversion, which for the ATMEGA32U4 can take a while, since the ADC isn’t very fast. Like before, the way to address this would be to execute other code while waiting for the ADC conversion to complete, reducing the slowdown significantly. However, since this module only runs twice a second, it has very little impact on the system performance.

Debugger duration

The debugger takes 147 us to run, and runs once a second. It’s a lot faster than I expected. Since the debugger uses the string library, and uses functions like strcat and strcpy, I was worried the debugger would take milliseconds to run. A nice surprise!

Funny story, actually. The current code only sets up the string to transmit, and then lets an interrupt transmit the string one character at a time. That means the software doesn’t have to wait for the transmission to complete, reducing the duration of the debugger module. The original code I wrote for the debugger didn’t do that, and the code actually had to wait for the whole string to finish transmitting before moving on. Since the baudrate is slow (compared to I2C or SPI, for example), the debugger actually took hundreds of milliseconds to complete! Shows you how much time-saving interrupts are capable of.

Overall, I’m very happy with the timing of the system. The most important timing for this project is the load regulator, and though it’s disappointing that it’s not at 1 kHz, and it can drop down to around 300 Hz, I doubt that it’ll matter in the end. The difference between 900 Hz and 1 kHz is negligible, and the dips in timing won’t matter since they happen so briefly and infrequently. Since the code seems finished, let’s move on to the assembly!

Leave a comment

Design a site like this with WordPress.com
Get started