STM32 SPI Library for APA102 RGB LED

STM32 SPI Library for APA102 RGB LED

Please read Liability Disclaimer and License Agreement CAREFULLY

This library is adapted for a APA102 LED strip with 30 LEDs.

Read this article to have a better understanding Ambient light with APA102 and MQTT

APA102.h

 

#ifndef __APA102_H
#define __APA102_H
#ifdef __cplusplus
 extern "C" {
#endif

	#include <stdint.h>
	#define LED_INIT					0xE0
	#define LED_GLOBAL				0x1F	
	#define LED_COUNT					143										//Number of LEDs in strip
	#define FirstThird				48										//LED end position of first third of the strip
	#define SecondThird				95										//LED end position of second third of the strip
	#define FRAME_SIZE				4*LED_COUNT						//Number of LEDs in strip
	#define START_FRAME_SIZE	4											//0x00, 0x00, 0x00, 0x00
	#define END_FRAME_SIZE		(LED_COUNT + 15)/16		//
	#define END_POSITION			(START_FRAME_SIZE + FRAME_SIZE)
	#define BUFFER_SIZE				(END_POSITION + END_FRAME_SIZE)
	#define TIME_OUT					1000
	#define PI								3.14159265358979323846
	// Define a macro for the maximum of two values
	#define MAX(a, b) ((a) > (b) ? (a) : (b))
	#define MIN(a, b) (((a) < (b)) ? (a) : (b))
	// Because we have 4 bytes for start frame we need to offset
	#define getPosition(pos)	(4*(pos + 1))

	extern volatile uint8_t effectDone;
	void APA_init(void);
	void APA_setColor(uint16_t led, uint8_t red, uint8_t green, uint8_t blue);
	void APA_setColorBright(uint16_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t bright);
	void APA_setAllColor(uint8_t red, uint8_t green, uint8_t blue);
	void APA_setRGB(uint16_t led, uint32_t rgb, uint8_t bright);
	void APA_setAllRGB(uint32_t rgb, uint8_t bright);
	void APA_setllumination(uint8_t intensity);
	void APA_setLedIllumination(uint16_t led, uint8_t intensity);
	void APA_setAllBrightness(uint8_t intensity);
	void APA_setLedOff(uint16_t led);
	void APA_setLedOn(uint16_t led);
	void APA_update(uint8_t crtLED_Strip);
	void APA_AllOff();
	//Light effects functions
	void FluidRainbowCycle(uint16_t duration_ms);
	void SnowEffect(uint16_t duration_ms);
	void PulseEffect(uint16_t duration_ms);
	void FlashingEffect(uint16_t duration_ms);
	void FadingEffect(uint16_t duration_ms);
	void ColorWipe(uint16_t duration_ms);
	void TheaterChase(uint8_t red, uint8_t green, uint8_t blue, uint16_t duration_ms);
	void BreathEffect(uint16_t duration_ms);
	void WaveEffect(uint16_t duration_ms);
#ifdef __cplusplus
}
#endif
#endif /*__ APA102_H */

 

The APA102.c

The SPI is using only the clock and data lines

#include "APA102.h"
#include "spi.h"
#include "usart.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
static uint8_t SpiSendFrame[BUFFER_SIZE];
volatile uint8_t effectDone = 1;
uint8_t prevEffect = 0;
// Set up the buffer for the strip
void APA_init(void) {
	uint16_t position;
	// Set the value for brightness
	memset(SpiSendFrame, LED_INIT, BUFFER_SIZE);
	// Add the start frame
	memset(SpiSendFrame, 0x00, START_FRAME_SIZE);
	// Add the end frame
	memset(&SpiSendFrame[END_POSITION], 0xFF, END_FRAME_SIZE);
}

//Set RGB color of a specified LED
void APA_setColor(uint16_t led, uint8_t red, uint8_t green, uint8_t blue){
	uint16_t pos = 1 + getPosition(led);
	SpiSendFrame[pos] = blue;
	SpiSendFrame[pos + 1] = green;
	SpiSendFrame[pos + 2] = red;
}

//Set RGB color and Brightness of a specified LED
void APA_setColorBright(uint16_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t bright){
	uint16_t pos = getPosition(led);
	SpiSendFrame[pos] = LED_INIT | bright;
	SpiSendFrame[pos + 1] = blue;
	SpiSendFrame[pos + 2] = green;
	SpiSendFrame[pos + 3] = red;
}

//Set RGB color all LEDs
void APA_setAllColor(uint8_t red, uint8_t green, uint8_t blue){
	for (uint16_t led = 0; led < LED_COUNT; led++)	{
		APA_setColor(led, red, green, blue);
	}
}

//Set RGB color of a specified LED in 24 bit format
void APA_setRGB(uint16_t led, uint32_t rgb, uint8_t bright) {
	uint16_t pos = getPosition(led);
	SpiSendFrame[pos] = LED_INIT | bright;
	SpiSendFrame[pos + 3] = (uint8_t)(rgb);
	SpiSendFrame[pos + 2] = (uint8_t)(rgb >> 8);
	SpiSendFrame[pos + 1] = (uint8_t)(rgb >> 16);
}
//Set RGB color of all LEDs in 24 bit format
void APA_setAllRGB(uint32_t rgb, uint8_t bright){
	for (uint16_t led = 0; led < LED_COUNT; led++)	{
		APA_setRGB(led, rgb, bright);
	}
}

//Set brightness of a specified LED
void APA_setLedBrightness(uint16_t led, uint8_t bright) {
	SpiSendFrame[getPosition(led)] = LED_INIT | bright;
}

//Set brightness for all LEDs
void APA_setAllBrightness(uint8_t intensity) {
	for (uint8_t led = 0; led < LED_COUNT; led++)	{
		APA_setLedBrightness(led, intensity);
	}
}

//Turn On a specified LED
void APA_setLedOff(uint16_t led) {
	SpiSendFrame[getPosition(led)] =  LED_INIT;
}

//Turn Off a specified LED
void APA_setLedOn(uint16_t led) {
	SpiSendFrame[getPosition(led)] =  LED_GLOBAL;
}

//Turns Off both strips
void APA_AllOff(){
	APA_setAllColor(0, 0, 0);
	APA_update(0);
	APA_update(1);
}

//Send the buffer data to LED strip on SPI1 or 2
void APA_update(uint8_t crtLED_Strip) {
	SPI_HandleTypeDef crtSPI;
	if(crtLED_Strip) {
		crtSPI = hspi1;
	} else {
		crtSPI = hspi2;
	}
	// send spi frame with all led values
	HAL_SPI_Transmit(&crtSPI, (uint8_t *)&SpiSendFrame, BUFFER_SIZE, TIME_OUT);
}

//Check if we got new data from MQTT
static uint8_t checkForUpdates(){
	if(prevEffect != frame_MQTT[Effect]){
		prevEffect = frame_MQTT[Effect];
		APA_setAllBrightness(frame_MQTT[Intensity]);
	}
	if (ESP_Ready) {
		APA_setAllBrightness(frame_MQTT[Intensity]);
	}
	if(!frame_MQTT[Power]) {
		effectDone = 1;
		return 1;
	}
	return 0;
}

//Get a color from color wheel
static uint8_t* ColorWheel(uint8_t WheelPos) {
    static uint8_t c[3];
    WheelPos = 255 - WheelPos;
    if (WheelPos < 85) {
        c[0] = 255 - WheelPos * 3;
        c[1] = 0;
        c[2] = WheelPos * 3;
    }
    else if (WheelPos < 170) {
        WheelPos -= 85;
        c[0] = 0;
        c[1] = WheelPos * 3;
        c[2] = 255 - WheelPos * 3;
    } else {
        WheelPos -= 170;
        c[0] = WheelPos * 3;
        c[1] = 255 - WheelPos * 3;
        c[2] = 0;
    }
    return c;
}


void FluidRainbowCycle(uint16_t duration_ms) {
	uint16_t steps = duration_ms / 10; // Assuming a step every 10 ms
	uint8_t segment_size = LED_COUNT / 7;
	for (uint16_t step = 0; step < steps; ++step) {
		if (checkForUpdates()) {
			return;
		}
		// Iterate over each segment
		for (uint8_t segment = 0; segment < 7; ++segment) {
			// Calculate the color for the current segment
			uint8_t gradient = (step * 256 / steps + segment * 30) % 256;
			uint8_t *color = ColorWheel(gradient);
			// Shift the segment along the strip
			for (uint8_t i = 0; i < segment_size; ++i) {
				uint16_t index = segment * segment_size + i;
				// Calculate the shifted index
				uint16_t shiftedIndex = (index + step) % LED_COUNT;
				APA_setColor(shiftedIndex, color[0], color[1], color[2]);
			}
			// Update both segments
			APA_update(0);
			APA_update(1);
		}
		// Introduce a slight delay to control the speed of the effect
		HAL_Delay(10);
	}
	effectDone = 1;
}

// Adjust the divisor in the if (rand() % 10 == 0) statement to control the density of the snowflakes.
// A smaller divisor will result in a denser snowfall.
void SnowEffect(uint16_t duration_ms) {
	uint16_t pixel;
	APA_setAllColor(255, 255, 255);
  APA_setAllBrightness(0);
  pixel = rand() % 144;
	APA_setLedBrightness(pixel, 31);
	APA_update(0);
	HAL_Delay(20);
	APA_setLedBrightness(pixel, 0);
	APA_update(0);
	HAL_Delay(20);
	
  pixel = rand() % 144;
	APA_setLedBrightness(pixel, 31);
	APA_update(1);
	HAL_Delay(20);
	APA_setLedBrightness(pixel, 0);
	APA_update(1);
  HAL_Delay(duration_ms);
	effectDone = 1;
}

void PulseEffect(uint16_t duration_ms) {
	uint16_t steps = duration_ms / 10; // Assuming a step every 10 ms
	uint8_t maxBrightness = 255;
	for (uint16_t step = 0; step < steps; ++step) {
		if (checkForUpdates()) {
			return;
		}
		// Calculate brightness based on a sine function to create a pulsating effect
		uint8_t brightness = maxBrightness * sin(step * PI / steps);
		// Set color for the left side of the strip
		APA_setAllColor((brightness * frame_MQTT[LeftRed]) / maxBrightness, (brightness * frame_MQTT[LeftGreen]) / maxBrightness, (brightness * frame_MQTT[LeftBlue]) / maxBrightness);
		APA_update(0);
		// Set color for the right side of the strip
		APA_setAllColor((brightness * frame_MQTT[RightRed]) / maxBrightness, (brightness * frame_MQTT[RightGreen]) / maxBrightness, (brightness * frame_MQTT[RightBlue]) / maxBrightness);
		APA_update(1);
		HAL_Delay(10);
	}
	effectDone = 1;
}

void FlashingEffect(uint16_t duration_ms) {
	uint16_t steps = duration_ms / 10; // Assuming a step every 10 ms
	for (uint16_t step = 0; step < steps && frame_MQTT[Power]; ++step) {
		if(checkForUpdates()) {
			return;
		}
		if (step % 2 == 0) {
			APA_setAllColor(frame_MQTT[LeftRed], frame_MQTT[LeftGreen], frame_MQTT[LeftBlue]);
		} else {
			APA_setAllColor(0, 0, 0);
		}
		APA_update(0);
		if (step % 2 == 0) {
			APA_setAllColor(frame_MQTT[RightRed], frame_MQTT[RightGreen], frame_MQTT[RightBlue]);
		} else {
			APA_setAllColor(0, 0, 0);
		}
		APA_update(1);
		HAL_Delay(10);
	}
	effectDone = 1;
}

// Function to create a fading effect
void FadingEffect(uint16_t duration_ms) {
	uint16_t steps = duration_ms / 25; // Assuming a step every 10 ms
	for (uint16_t step = 0; step < steps && frame_MQTT[Power]; ++step) {
		if (checkForUpdates()) {
			return;
		}
		// Calculate the current color based on the step
		uint8_t gradient = (step * 256 / steps) % 256;
		uint8_t *color = ColorWheel(gradient);
		// Set the calculated color with fading effect for each LED in the strip
		for (uint16_t i = 0; i < LED_COUNT; ++i) {
			// Calculate the fading effect based on the current step
			uint8_t fadingFactor = (steps - step) * 31 / steps;
			// Apply fading to each channel
			uint8_t fadedRed = (color[0] * fadingFactor) / 31;
			uint8_t fadedGreen = (color[1] * fadingFactor) / 31;
			uint8_t fadedBlue = (color[2] * fadingFactor) / 31;
			APA_setColor(i, fadedRed, fadedGreen, fadedBlue);
		}
		// Update both segments
		APA_update(0);
		APA_update(1);
		HAL_Delay(25);
	}
	effectDone = 1;
}

// Function to create a color wipe effect with changing brightness
void ColorWipe(uint16_t duration_ms) {
	const uint16_t steps = duration_ms / 10;
	for (uint16_t step = 0; step < steps; ++step) {
		if (checkForUpdates()) {
			return;
		}
		// Calculate the color based on a smooth gradient for the rainbow effect
		uint8_t gradient = (step * 256 / steps) % 256;
		uint8_t *color = ColorWheel(gradient);
		// Set the color for each LED in the strip
		for (uint16_t i = 0; i < LED_COUNT; ++i) {
			// Calculate the brightness based on a sine wave
			float brightness = fabs(sin((float)step / (steps / 2) * PI));
			// Scale brightness to a range from 0 to 31
			uint8_t scaledBrightness = (uint8_t)(brightness * 31);
			// Set the color and brightness for each LED
			APA_setColorBright(i, color[0], color[1], color[2], scaledBrightness);
		}
		// Update the LED strip for both segments
		APA_update(0);
		APA_update(1);
		// Introduce a slight delay to control the speed of the effect
		HAL_Delay(25);
	}
	effectDone = 1;
}

void TheaterChase(uint8_t red, uint8_t green, uint8_t blue, uint16_t duration_ms) {
	uint16_t steps = duration_ms / (10 * 3); // Adjusted the total steps for a smoother effect
	uint8_t gapSize = 3;
	for (uint16_t j = 0; j < 10; ++j) {
		for (uint16_t q = 0; q < gapSize; ++q) {
			if (checkForUpdates()) {
				return;
			}
			// Set color for every third LED in a smooth gradient
			for (uint16_t i = 0; i < LED_COUNT; i += 3 + gapSize) {
				// Calculate the intermediate color for the wipe effect
				uint8_t stepRed = (red * (i + q + 1)) / LED_COUNT;
				uint8_t stepGreen = (green * (i + q + 1)) / LED_COUNT;
				uint8_t stepBlue = (blue * (i + q + 1)) / LED_COUNT;
				APA_setColor(i + q, stepRed, stepGreen, stepBlue);
			}
			// Update both segments
			APA_update(0);
			APA_update(1);
			HAL_Delay(steps);
			// Turn off every third LED
			for (uint16_t i = 0; i < LED_COUNT; i += 3 + gapSize) {
				APA_setColor(i + q, 0, 0, 0);
			}
		}
	}
	effectDone = 1;
}

// Function to create a breathing effect on an APA102 LED strip
void BreathEffect(uint16_t duration_ms) {
	const uint16_t steps = duration_ms / 10;
	const uint16_t halfSteps = steps / 2;
	for (uint16_t step = 0; step < steps; ++step) {
		if (checkForUpdates()) {
			return;
		}
		// Calculate the brightness based on a sine wave
		float brightness = sin((float)step / halfSteps * PI);
		// Iterate over each LED in the strip
		for (uint16_t i = 0; i < LED_COUNT; ++i) {
			// Calculate the distance from the center
			float distanceFromCenter = abs(LED_COUNT / 2 - i);
			// Scale brightness based on the distance from the center
			uint8_t scaledBrightness = (uint8_t)(brightness * (1 - distanceFromCenter / (LED_COUNT / 2)) * 31);
			// Set the color and brightness for each LED
			APA_setColorBright(i, frame_MQTT[LeftRed], frame_MQTT[LeftGreen], frame_MQTT[LeftBlue], scaledBrightness);
		}
		// Update the LED strip for both segments
		APA_update(0);
		APA_update(1);

		// Introduce a slight delay to control the speed of the effect
		HAL_Delay(30);
	}
	effectDone = 1;
}

// Function to create a wave effect on an APA102 LED strip
void WaveEffect(uint16_t duration_ms) {
	const uint16_t steps = duration_ms / 10;
	const uint8_t segment_size = LED_COUNT / 2;
	for (uint16_t step = 0; step < steps; ++step) {
		if (checkForUpdates()) {
			return;
		}
		// Calculate the position of the wave peak based on the step
		float position = sin((float)step / steps * 2 * PI) * segment_size + segment_size;
		// Iterate over each LED in the strip
		for (uint16_t i = 0; i < LED_COUNT; ++i) {
			// Calculate the distance from the wave position
			float distanceFromWave = fabs(position - i);
			// Scale brightness based on the distance from the wave
			uint8_t brightness = (uint8_t)(31 * (1 - distanceFromWave / segment_size));
			// Set the color and brightness for each LED
			APA_setColorBright(i, frame_MQTT[LeftRed], frame_MQTT[LeftGreen], frame_MQTT[LeftBlue], brightness);
		}
		// Update the LED strip for both segments
		APA_update(0);
		APA_update(1);
		// Introduce a slight delay to control the speed of the effect
		HAL_Delay(10);
	}
	effectDone = 1;
}

Comments powered by CComment