Close

[FW] The butchered code

A project log for Hardware based RGBW Lamp controller

Or how to write firmware the weird way to submit a project in the 1kB Challenge.

enricoEnrico 01/04/2017 at 22:120 Comments

The initial, ideal version of the firmware was used on other projects, like this. It was implementing every step of the color generation, from the autowhite adjustment, to the main color selection, the PWM generation, the power monitor, the serial commands, the encoder management and so on. Everything in 8098 kB of program memory, and 368 bytes of RAM.

Cutting out almost 90% of the code was an overwhelming challenge. The main constraint was my idea to stick with the RGB project for the 1kB Challenge, instead focusing on something else. And for pure coincidence I had also bought a Lattice FPGA. Then the works has just started: what could be moved away from the software and that can be represented in raw logic? How can the information be passed between the microcontroller and the FPGA?

The result is an hated spaghetti code, with no safety features from the ADC for the power monitoring, no encoder management and so on. The only thing was to rely as much as possible on the MCU hardware, for the UART, PWM and.... no, there are no pins available and there is no way to implement any protocol in bit banged way.

And now I realized that the only thing that keep a soul in the MCU is the data management from the user and the overall control. Moreover, also the data management from UART is kept inside the MCU, since it was implementing all the characters filtering and the numeric conversions. In this way, the user can still manage the double functionality, which is from hexadecimal commands from serial or using 3 buttons (instead of a rotary encoder).

All the functions were optimized accordingto the ASCII tables that allow huge data compression. A snippet of the original character conversion function:

static unsigned char lamp_c2h(unsigned char msb, unsigned char lsb){
	if ((msb < 'a' && msb > '9') || msb < '0' || msb > 'f' ||
	(lsb < 'a' && lsb > '9') || lsb < '0' || lsb > 'f'){
		// printf("Not HEX value. msb: %c, lsb: %c\n", msb, lsb);
		return 0;
	}
	if (msb >= 'a' && msb <= 'f'){
		msb = msb - 'a' + 10;
		} else {
		msb = msb - '0';
	}
	if (lsb >= 'a' && lsb <= 'f'){
		lsb = lsb - 'a' + 10;
		} else {
		lsb = lsb - '0';
	}
	return (lsb+(msb*16));
}

became this:

static unsigned char lamp_c2h(unsigned char msb){
    uint8_t tmp = 0;
    tmp = msb;
    msb=msb&0x0F ;
    if (tmp>'9')
    msb+=9;
    return (msb);
}
And I also learned how a shift instruction could take a lot of space, when you start noticing every single byte eaten up by your code.

The double edge weapon of the hardware relief

In this way a small SPI master control is written, in order to send the correct data to the FPGA. Again, you can feel how much work you move away from the MCU's processor (and so from the memory occupation) when you have a dedicated HW that does the job.

And I am not talking about the FPGA, but I am referring on how just the SPI hardware of the Atmega was of huge help in memory reduction, try to think in implement this in firmware, how much more space have been taken off? This is obvious, but not always appreciated.

A byte assignment in the FW, and a lot of stuff moving in the hardware. That is fun...or, no, not when you have to deal with designing another piece of hardware that shall receive all that beauty from the SPI of the Atmega. And now I am talking about the FPGA.

The final memory status

Or the final countdown to 1024 bytes. At the end, I discovered that I was trained to freeing up the RAM. To me, was way more tricky to find out how to save on byte from flash rather than from RAM.

This meas that for coincidence I have freed the 92% of the RAM. While my efforts were in freeing up the program memory, which now is reduced by the 88%.

As a result, this is my memory implementation, from the build output of the Atmel Studio 7:

Program Memory Usage : 954 bytes 2,9 % Full
Data Memory Usage : 29 bytes 1,4 % Full

Discussions