11/03/2020 at 19:40 •
While testing the temperature measurement, I noticed that the values were rather unstable. I quickly noticed this was caused by an unstable supply voltage. In the STM32 the ADC is referenced to VDDA, which is more or less VCC with filtering. I probably should have added more filtering / larger caps, but first I tried to address that issue in software.
Compensate against VREF
The STM32 have an internal 1.2V reference. This reference can be measured by the ADC and the result can be used used to compensate other ADC values for varying operating voltages. The ST_VREFINT_CAL value is a constant for the ADC readout of the internal reference at VDDA=3.3V. The lower the operating voltage, the higher the reference reading will get.
vin = ((3300ULL * ST_VREFINT_CAL) * <ADC_IN>) / <ADC_REF> / 4096;
This compensation pretty much solved the noise issue, but from time to time a few outlier caused the controller to look a bit unstable.
Since the signal is now pretty stable over all, we only have to address a few outliers. This is done by calculating the difference of the average difference to the average of the last 16 readings.
If err is below a threshold, the value is accepted and used for the controller. If the values is rejected, the controller re-uses the last reading. This is no problem, since the controller cycles are rather short, typically 10ms. The new value is added to the window in any case, so a sudden jump in the values will not stop the controller form working.
What causes the noise / how to avoid it?
The two methods eliminate the noise issue, but it would be better to figure out, why we have noise in the first place. I've been measuring the VCC with an oscilloscope and can confirm, there is noise on the VCC line. Its not just minor noise, there are some larger drops in the supply voltage. My suspect is the display, but since I soldered it onto the board, there is no easy way to test without it. I tried to add some caps across its voltage pins, but it didn't improve much. So for now I leave it like that since its working and I'm kind of happy with the software solution.
10/26/2020 at 14:40 •
16KB flash should be enough for everybody...
... at least that's what I though when selecting the controller for this project. After all, controlling a soldering iron is not a very complex task, so I just got the smallest STM32F0 available. Then I started to write the code and figured out, I was gravely mistaken. The whole project with the features I wanted would have never fit into that controller, so I hade to find ways to save flash.
HAL is huge
Initially, I wanted to build this with the STM32Cube HAL. I've never used it before, but since it's kind of standard I wanted to give it a try. But once I set up the project, I realized, this is a bad idea! Even a very simple test program already takes 10k, so that's not gonna work.
libopencm3 to the rescue
In all my other STM32 projects, I always used the libopencm3. That's a free HAL for Cortex-M ARMs. It does not come with the nice graphical pin and clock assignment thingy, so it requires a bit more data-sheet diving. And I think it makes a slightly worse job at abstracting the hardware, so the code is a bit more target specific. But its WAY smaller, so that's why I used libopencm3 for this project as well.
Fonts aren't small either
Since I'm using a graphic display, I needed fonts! And since I don't want all text in the same font size, I need more than one of those. I used bitmap fonts, which makes fonts with large letters quite big. And so when I ran out of flash again and decided to do something about the font size.
Strip unused characters
I realized, that the biggest font (16x26) is exclusively used for the temperature display in the main screen. But there only the numbers and the 'C' is needed. So the first reduction was by creating a copy of that font that only contains 11 symbols instead of a full char set.
Compress the font
Even with the reduced char set in the large font, the flash was still to small, so I decided with experimenting with ways to compress font. For large fonts, this is usually done by using vector fonts. But for smaller fonts, this gets ugly results, so that's not really a good option. Instead, I was looking for ways to compress the bitmap font. Since after compression, the sizes of the single bitmaps won't be the same, an offset table is needed so the symbols can be found again. This further increases the size of the compressed data so an efficient compression is needed to reach the break-even.
At first I experimented with different forms of run-length encoding, but the resulting compression ratio was disappointing. The following example shows a very simple run length encoding, where only blocks of '0' are encoded. Blocks of '1' or mixed blocks are stored raw. This shows that even with data that is very compression friendly and an optimal setting for the block length, the compression ration isn't very good. For small data sizes, the size of the headers just eats the space savings, so the data needs to be very compressible to even reach the break-even.
Code: 0xxxx -> xxxx bits of 0 1xxx -> xxx bits of uncompressed data Original: 00000000110000000000 20 bit Encoded: 0100010101101010 16 bit ^8x0 ^2x* **^ 10x0
Geometric compression / Bounding boxes
I decided to try an encoding scheme, where the break-even is very early. When looking at the font, I figured that most chars do not fill the whole bitmap. So I only stored the used area of the char and have a small header containing the position and size of that area. These headers are tiny and there is only one header for each char. For a 11x18 font the header is just 18 bit (x/w each 4 bit + y/h each 5 bit). So the break-even is reached as soon as we can avoid storing a single column.
Header: xxxxwwwwyyyyyhhhhh 18 bit x: Offset (x) w: Width of the stored region y: Offset (y) h: Height of the stored region
Below is the summary of the compression efficiency for the used 11x18 font
11x18 Font 95 Chars Raw size: 11x18 = 198 bits -> 25 bytes (rounded) per char => Total: 2375 bytes Compressed: Offset table: 11 bits per entry, 95 entries -> 131 bytes Data size: 1364 => Total: 1495 bytes ===> 37% size saved by compression