Skip to content

Commit

Permalink
HSB rainbow and final cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
ErniW committed Jan 2, 2023
1 parent c2b03bd commit d39b952
Show file tree
Hide file tree
Showing 8 changed files with 501 additions and 666 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Neopixels bare metal for STM32
**Bare-metal implementation of WS2812 LED strip for STM32F446RE with PWM and DMA (Direct Memory Access).** During research I found many similar materials about working with WS2812 LED but all of them were for STM32 HAL without explaining the bare-metal concepts (some tutorials have in the title "bare-metal" but eventually misleads potential readers). Doing it from scratch was quite challenging. I guess I'm the first one to publish such thing without unnecessary complexity.
**Bare-metal implementation of WS2812 LED strip for STM32F446RE with PWM and DMA (Direct Memory Access).** During research I found many similar materials about working with WS2812 LED but all of them were for STM32 HAL without explaining the bare-metal concepts (some tutorials have in the title "bare-metal" but eventually misleads potential readers). Doing it from scratch was quite challenging. I guess I'm the first one to publish such thing without unnecessary complexity. It's in progress so I will appreciate any feedback.

## Timing constrains from documentation:

Expand Down Expand Up @@ -79,15 +79,14 @@ I've tested the code with 8 LED strip and 1 meter, 74 LED strip. STM32 operates
- *Does it make sense to define interrupts for both timer and DMA?*
- *How to not store DMA buffer as uint32_t to reduce its size (no, changing sizes in DMA won't work).*
- *Trying to debug PWM signals without an oscilloscope is not possible.*
- *Fix the HSB convertion algorithm. I tested it in Visual Studio with C++ but there is an issue with conversion using doubles. The saturation and brightness must be set to 100.*

## List of structs:
- `Led`: stores the rgb values.
- `LedStrip`: stores the pointer to led array, its size and strip state.

## List of functions:
- `timer_init`: Configure the timer
- `dma_init`: Configure the DMA.
- `timer_init()`: Configure the timer
- `dma_init()`: Configure the DMA.
- `setRGB(r, g, b)`: Set pixel color as RGB.
- `setHSB(h, s, b)`: Set pixel color as HSB and convert it to RGB. It's slower than setting directly as RGB. HSB is in range of 360, 100, 100.
- `createStrip(led_array, size)`: Create the strip struct.
Expand Down
Binary file modified build/Neopixels.bin
Binary file not shown.
Binary file modified build/Neopixels.elf
Binary file not shown.
1,010 changes: 470 additions & 540 deletions build/Neopixels.hex

Large diffs are not rendered by default.

14 changes: 6 additions & 8 deletions includes/neopixels.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#pragma once
#include "../STM32F446RE/stm32f4xx.h"

#define TIMER_PRESCALER 2
#define WS2812_FREQUENCY_800KHZ_TICKS 56
#define LOGIC_0_TICKS 16
#define LOGIC_1_TICKS 32
#define WS2812_RESET_TICKS 2250
#define TIMER_PRESCALER 2
#define WS2812_FREQ_800KHZ_TICKS 56
#define LOGIC_0_TICKS 16
#define LOGIC_1_TICKS 32
#define WS2812_RESET_TICKS 2250

typedef struct {
uint8_t r;
Expand Down Expand Up @@ -36,6 +36,4 @@ void clearStrip(LedStrip *strip);

Led setHSB(int h, uint8_t s, uint8_t b);
Led setRGB(uint8_t r, uint8_t g, uint8_t b);
Led getHSB(Led *c);

// Led setHSB(double h, double sa, double ba);
Led getHSB(Led *c);
41 changes: 16 additions & 25 deletions main.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#include "./STM32F446RE/stm32f4xx.h"

#include <math.h>
#include "pll.h"
#include "sysTick.h"

#include "neopixels.h"
#include <math.h>

#define ENABLE_FPU ((3 << 20) | (3 << 22))

#define LEDS_AMOUNT 74

Expand All @@ -17,37 +19,27 @@ int main(){
timer_init();
dma_init();

// SCB->CPACR |= ((3 << 10*2)|(3 << 11*2));

strip = createStrip(leds, LEDS_AMOUNT);
SCB->CPACR |= ENABLE_FPU;

// long prevTime = 0;
float movement = 0;

while(1){

strip = createStrip(leds, LEDS_AMOUNT);

// for(volatile long i = 0; i < 1000000; i++){
// // __ASM("NOP");
// }
while(1){

// long time = getMillis();
if(strip.state == STATE_IDLE){

// if(time - prevTime > 50){
movement += 0.05;
for(int i = 0; i < strip.size; i++){
float sine = (sin((movement + ((i+1)*0.1))) + 1) / 2;

for(int i = 0; i < strip.size; ++i){
float sine = sinf(movement + (i+1) * 0.1 );
sine = (sine + 1) / 2;
sine *= 360;
// uint8_t val = (uint8_t)sine;
// if(val == 0) val += 10;
strip.led[i] = setHSB(sine, 100,10);
// printf()
strip.led[i] = setHSB(sine, 100, 20);
}
// strip.led[strip.size-1] = setRGB(0,0,0);

updateStrip(&strip);
//movement += 0.01;
// prevTime = time;
// }
}
}

}
Expand All @@ -65,8 +57,7 @@ void TIM2_IRQHandler(void) {
strip.state = STATE_RESETTING;
}
else if(strip.state == STATE_RESETTING){
TIM2->ARR = WS2812_FREQUENCY_800KHZ_TICKS;
// TIM2->CR1 &=~ TIM_CR1_CEN;
TIM2->ARR = WS2812_FREQ_800KHZ_TICKS;
strip.state = STATE_IDLE;
}
}
Expand Down
1 change: 1 addition & 0 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ compile:
arm-none-eabi-objcopy -O ihex ./build/$(PROJECT_NAME).elf ./build/$(PROJECT_NAME).hex
arm-none-eabi-objcopy -O binary ./build/$(PROJECT_NAME).elf ./build/$(PROJECT_NAME).bin


upload:
$(STLINK_DIR)/st-flash --reset write ./build/$(PROJECT_NAME).bin 0x8000000

Expand Down
94 changes: 5 additions & 89 deletions sources/neopixels.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
#include "sysTick.h"
#include "neopixels.h"

#define PA5_AF_MODE (1 << 11)
#define PA5_AF1 (1 << 20)
#define CH1_PWM_MODE_1 (6 << 4)
#define PA5_AF_MODE (1 << 11)
#define PA5_AF1 (1 << 20)
#define CH1_PWM_MODE_1 (6 << 4)

#define DMA_CHANNEL_3 (3 << 25)
#define DMA_MEM_TO_PERIPHERAL (1 << 6)
Expand All @@ -19,7 +19,7 @@ void timer_init(){
GPIOA->AFR[0] |= PA5_AF1;

TIM2->PSC = TIMER_PRESCALER - 1;
TIM2->ARR = WS2812_FREQUENCY_800KHZ_TICKS;
TIM2->ARR = WS2812_FREQ_800KHZ_TICKS;
TIM2->CCMR1 = CH1_PWM_MODE_1;
// TIM2->CCMR1 |= TIM_CCMR1_OC1PE;
TIM2->DIER |= TIM_DIER_UIE;
Expand Down Expand Up @@ -166,88 +166,4 @@ void clearStrip(LedStrip* strip){
for(int i=0; i<strip->size; ++i){
strip->led[i] = setRGB(0,0,0);
}
}


/*
Led setHSB(double h, double sa, double ba){
// ba /= 100;
// sa /= 100;
if (h >= 360) h = 0;
double c = ba * sa;
double x = c * (1 - fabs(fmod((h / 60), 2) - 1));
double m = ba - c;
double red =0;
double green = 0;
double blue = 0;
int a = (h / 60);
switch (a) {
case 0: red = c + m; green = x + m; blue = 0; break;
case 1: red = x + m; green = c + m; blue = 0; break;
case 2: red = 0; green = c + m; blue = x + m; break;
case 3: red = 0; green = x + m; blue = c + m; break;
case 4: red = x + m; green = 0; blue = c + m; break;
case 5: red = c + m; green = 0; blue = x + m; break;
}
// printf("%llf\n", red);
// printf("%d, %d, %d\n",round(red ),round(green),round(blue ));
// uint64_t holder = (uint64_t)red;
// printf("%d\n", a);
// uint8_t buf[8] = {0};
// memcpy(buf, &red, 8);
// printf("%lld\n", (uint64_t)red);
Led newColor = {(uint8_t)red, (uint8_t)green, (uint8_t)blue};
return newColor;
}
*/


/*
Led getHSB(Led* color){
float H = 0;
float S = 0;
float B = 0;
float delta = 0;
B = fmax(fmax(color->r, color->g), color->b);
delta = B - fmin(fmin(color->r, color->g), color->b);
if(B == 0){
S = 0;
}
else {
S = delta / B;
}
if(S == 0){
H = 0;
}
else{
if(color->r == B) H = (color->g - color->b) / delta;
else if(color->g == B) H = 2 + (color->b = color->r) / delta;
else if(color->b == B) H = 4 + (color->r - color->g) / delta;
H *= 60;
if(H < 0){
H += 360;
}
}
B /= 255;
Led newColor = {H, S, B};
return newColor;
};
*/
}

0 comments on commit d39b952

Please sign in to comment.