Michael J. McCaffrey

Tagline and stuff.

STM32 & UART Example

Applying HTML Color Codes to an RGB LED


  One of my assignments during my embedded systems class was to somehow control an RGB LED by sending UART messages to a processor. I thought it would be neat to send HTML color codes to the processor and have it change the color of the LED accordingly. When coding a website, the colors are defined by 6-bit hexadecimal numbers. The 2 most significant bits correspond to red, the next two to green, and the last two to blue. You can create any color with combinations of these three values. Below is a demo video which shows the final result of this project.


Setup

  My instance of this project is based on the STM32F4-Discovery development board, but it could be done similarly on any STM32 or ported to any processor. In the code snippet to the right, I have #includes for the necessary hardware peripherals as well as some supporting #includes that allow for things like tolower() and printf(). After the #includes, I use some #defines to give the output pins some easy to remember names.


Variables and Functions

// create initialization structures
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

// define RGB color type
typedef struct {
	uint8_t R, G, B;
} ColorTypeDef;

ColorTypeDef ColorStructure; // create color type instance
uint32_t color = 0x000000; // storage for color value

// function prototypes
void AssignColor(ColorTypeDef * ColorStructure, uint32_t TargetColor);
void ColorPWM(ColorTypeDef * ColorStructure);
void InitializePeriph();
uint8_t ascii_to_hex(uint8_t ascii);

  Next we can define some variables and function prototypes. The STM32 libraries use structures to initialize its peripherals, these are defined as handy initialization typedefs as seen in the code to the left. Following that is a new typedef structure that keeps track of each color's value. Lastly we add some prototypes for the necessary supporting functions. These functions will be explained in detail below.


Converting ASCII to Hexadecimal

  The UART messages that the processor will be receiving will be in ASCII format, meaning that a character like "0" actually has a value of 48, an "e" is 101, etc. Since HTML color codes are simply numbers in hexadecimal format, we need to convert all of the possible incoming messages that we intend to receive. The function ascii_to_hex() handles this with a simple switch statement and returns the correct value.


Assigning Values to R, G, and B

/* AssignColor() takes a html color code and parses it into
 * Red, Green and Blue values
 */
void AssignColor(ColorTypeDef * ColorStructure, uint32_t TargetColor){

	ColorStructure->R = (TargetColor&0x00FF0000)/0x10000;
	ColorStructure->G = (TargetColor&0x0000FF00)/ 0x100;
	ColorStructure->B = (TargetColor&0x000000FF)/0x1;
}

  The AssignColor() function splits the hexadecimal color value into R, G and B values by simply applying a mask and dividing. For example, if provided with #A8E577, AssignColor() will AND it with FF0000, resulting in A80000. We can then obtain a 2-bit hex value by simply dividing that number by 65536 (or in hex, 10000). This produces the R value of A8. Similarly, we can AND the original color value A8E577 with 00FF00 and divide by 256 (100 in hex) to get G. Finally, assign B = (A8E577 & 0000FF)/1 = 0x77. The ColorStructure variable is now updated and ready to be applied to the LED.


UART Interrupt Service Routine

  When a UART message is received by the processor, an interrupt is fired which calls its interrupt service routine. If you're unfamiliar with interrupts, they basically allow us to tell a processor to stop what it's doing when certain conditions are met and to respond to those conditions in a certain way. Here, a UART Rx interrupt sends the received character to the ascii_to_hex() fuction. A variable, ISRCount, keeps a tally of all of the interrupts that have happened so far. By using the modulo operator with the ISRCount, we can keep track of where each hex number should fall in the html color code. Each hex number can be shifted and added to the total accordingly, resulting in a complete color code after 6 interrupts.


Applying the R, G, and B values to the LED.

    /* ColorPWM applies the RGB values to PWM pulse widths and apply
 * the appropriate signals to the GPIO
 */
void ColorPWM(ColorTypeDef * ColorStructure){

	uint16_t rLimit = (ColorStructure->R + ColorStructure->G + ColorStructure->B);
	uint16_t gLimit = (ColorStructure->G + ColorStructure->B);
	uint8_t bLimit = (ColorStructure->B);

	uint8_t r, g, b;

	for (uint16_t scan = 0; scan <= 0x0300; scan++){
		if (scan < rLimit && scan >= gLimit && scan >= bLimit){
			g = 0;
			b = 0;
			r = 1;
		}
		else if (scan < gLimit && scan >= bLimit){
			b = 0;
			r = 0;
			g = 1;
		}
		else if (scan < bLimit){
			r = 0;
			g = 0;
			b = 1;
		}
		else if (rLimit == 0){
			r = 0;
			g = 0;
			b = 0;
		}
		else {
			r = 0;
			g = 0;
			b = 0;
		}

		GPIO_Write(GPIOD, (RedLED*r) | (GreenLED*g) | (BlueLED*b));
	}
}

  To apply the RGB values to the LED, we can simply use a sort of pulse width modulation. The method used in this example employs a FOR loop that counts from 0 to 765, which allows for 255 loops each for R, G, and B. Each color is assigned a limit according to its hexadecimal value which provides a range of loops during which it will be active. The FOR loop scans through the 0-765 range and updates the GPIO outputs each time around. Let's look at #AE008C as an example. The previously described functions will have populated the ColorStructure with R = 0xAE, G = 0x00, and B = 0x8C; The red LED's upper limit is assigned as 0xAE + 0x00 + 0x8C = 314, the total of R, G, and B. Green's upper limit is set to the sum of G and B, so 0x00 + 0x8C = 140. Blue's upper limit is always equal to the blue value itself.

 The FOR loop begins scanning numbers from 0 to 765. As it counts, it checks each number and where it fits between the R, G, and B limits. If the current value of scan is below the red limit, but greater than or equal to the blue and green limits, then the loop must be in the active red range and the red LED is written high. If scan is below the green limit but greater than or equal to the blue limit, green is turned on. Similarly, If scan is less than the blue limit, the blue LED is activated. Since the red limit is the total of the ColorStructures R, G, and B values, if it is zero we know that all values are zero and all colors should be off. This function is called repeatedly in the main() function's while(1) loop and only stops to respond to UART interrupts.


Final Thoughts

  It is good to understand that the maximum duty cycle any color will have with this approach is 1/3. I find that despite this, it can provide vibrant light from the LED and as a bonus, the colors are fairly accurate. While writing this guide, I thought up a few ways to significantly improve on this project. I hope that if you followed along with me here, you will try to make your design even better! Thank you for reading!

Back to Top