diff options
-rw-r--r-- | libraries/ws2812.c | 88 | ||||
-rw-r--r-- | libraries/ws2812.h | 52 | ||||
-rw-r--r-- | ws2812/main.c | 105 |
3 files changed, 145 insertions, 100 deletions
diff --git a/libraries/ws2812.c b/libraries/ws2812.c index aa64031..5fbdf6c 100644 --- a/libraries/ws2812.c +++ b/libraries/ws2812.c @@ -1,16 +1,8 @@ +// vim: tabstop=4 shiftwidth=4 noexpandtab /* - -# WS2812 Library for libopencm3 - -Please mind these restrictions: - -- Only usable with libopencm3 -- Newer versions of libopencm3 may be not compatibel (libopencm3 api is not stable yet) -- Just usable with a clock frequency of 72 MHz -- You have to setup the clock and gpio yourself - + Documentation is in ws2812.h */ @@ -21,6 +13,8 @@ Please mind these restrictions: #define NUMBER_NOPS_SHORT 6 // 0.35 µs = 25.2 cycles @ 72 MHz = 6.3 nops --> 16.7 ns timing error +// public API functions: + void ws2812_init(WS2812_ARRAY *control_struct, uint32_t port, uint16_t pin, uint8_t *buffer_ptr, uint32_t array_length) { control_struct->gpio_port = port; @@ -29,47 +23,65 @@ void ws2812_init(WS2812_ARRAY *control_struct, uint32_t port, uint16_t pin, uint control_struct->array_length = array_length; ws2812_clear_buffer(control_struct); ws2812_send_reset(control_struct); + ws2812_write_buffer_to_leds(control_struct); } +void ws2812_clear_buffer(WS2812_ARRAY *control_struct) +{ + for (uint32_t i=0; i<(control_struct->array_length*3); i++){ + control_struct->array_buffer[i] = 0x00; + } +} + +void ws2812_write_buffer_to_leds(WS2812_ARRAY *control_struct) +{ + for (uint32_t i=0; i<(control_struct->array_length); i++) + { + ws2812_send_byte(control_struct, control_struct->array_buffer[i*3+1]); // green first + ws2812_send_byte(control_struct, control_struct->array_buffer[i*3+0]); // red second + ws2812_send_byte(control_struct, control_struct->array_buffer[i*3+2]); // blue third + } + ws2812_send_reset(control_struct); +} + + +// internal functions: void ws2812_send_reset(WS2812_ARRAY *control_struct) { gpio_clear(control_struct->gpio_port, control_struct->gpio_pin); - for (int i = 0; i < NUMBER_NOPS_RESET; i++) { + for (uint16_t i = 0; i < NUMBER_NOPS_RESET; i++) { __asm__("nop"); } } - void ws2812_send_zero(WS2812_ARRAY *control_struct) { gpio_set(control_struct->gpio_port, control_struct->gpio_pin); - for (int i = 0; i < NUMBER_NOPS_SHORT; i++) { + for (uint8_t i = 0; i < NUMBER_NOPS_SHORT; i++) { __asm__("nop"); } gpio_clear(control_struct->gpio_port, control_struct->gpio_pin); - for (int i = 0; i < NUMBER_NOPS_LONG; i++) { + for (uint8_t i = 0; i < NUMBER_NOPS_LONG; i++) { __asm__("nop"); } } - void ws2812_send_one(WS2812_ARRAY *control_struct) { gpio_set(control_struct->gpio_port, control_struct->gpio_pin); - for (int i = 0; i < NUMBER_NOPS_LONG; i++) { + for (uint8_t i = 0; i < NUMBER_NOPS_LONG; i++) { __asm__("nop"); } gpio_clear(control_struct->gpio_port, control_struct->gpio_pin); - for (int i = 0; i < NUMBER_NOPS_SHORT; i++) { + for (uint8_t i = 0; i < NUMBER_NOPS_SHORT; i++) { __asm__("nop"); } } - void ws2812_send_byte(WS2812_ARRAY *control_struct, uint8_t data) { - for (int i=7; i>=0; i--){ + for (int8_t i=7; i>=0; i--){ if ( (data >> i) & 0x01 ){ ws2812_send_one(control_struct); } @@ -78,41 +90,3 @@ void ws2812_send_byte(WS2812_ARRAY *control_struct, uint8_t data) } } } - - -void ws2812_send_led(WS2812_ARRAY *control_struct, uint8_t red, uint8_t green, uint8_t blue) -{ - ws2812_send_byte(control_struct, green); - ws2812_send_byte(control_struct, red); - ws2812_send_byte(control_struct, blue); -} - - -void ws2812_write_leds(WS2812_ARRAY *control_struct) -{ - for(uint32_t i=0; i<(control_struct->array_length); i++){ - ws2812_send_led( - control_struct, - control_struct->array_buffer[i * WS2812_NUMBER_OF_COLORS + 0], // red - control_struct->array_buffer[i * WS2812_NUMBER_OF_COLORS + 1], // green - control_struct->array_buffer[i * WS2812_NUMBER_OF_COLORS + 2] // blue - ); - } - ws2812_send_reset(control_struct); -} - - -void ws2812_set_array_led(WS2812_ARRAY *control_struct, uint32_t index, uint8_t red, uint8_t green, uint8_t blue) -{ - control_struct->array_buffer[index * WS2812_NUMBER_OF_COLORS + 0] = red; - control_struct->array_buffer[index * WS2812_NUMBER_OF_COLORS + 1] = green; - control_struct->array_buffer[index * WS2812_NUMBER_OF_COLORS + 2] = blue; -} - - -void ws2812_clear_buffer(WS2812_ARRAY *control_struct) -{ - for (uint32_t i=0; i<control_struct->array_length; i++){ - ws2812_set_array_led(control_struct, i, 0, 0, 0); - } -} diff --git a/libraries/ws2812.h b/libraries/ws2812.h index c6c85d9..f738137 100644 --- a/libraries/ws2812.h +++ b/libraries/ws2812.h @@ -1,3 +1,42 @@ +// vim: tabstop=4 shiftwidth=4 noexpandtab + + +/* + ws2812 + + This low-level library gives you access to ws2812 LEDs and + LED strips via libopencm3. Please use it like this: + + - connect the data pin (DIN) of the ws2812 to a GPIO of + your MCU + - initialize clock to a sysclock speed of 72 MHz + - initialize the GPIO pin with GPIO_MODE_OUTPUT_50_MHZ and + GPIO_CNF_OUTPUT_PUSHPULL + - create a WS2812_ARRAY control struct + - create a uint8_t[ARRAY_LENGTH * 3] buffer + - call ws2812_init() with your control struct and buffer + - adjust values on the buffer + - use ws2812_write_buffer_to_leds() to set the LEDs + + The uint8_t buffer has the following structure: + + ----------------------------------------------------- ... + | | | | | | | | | | ... + | R | G | B | R | G | B | R | G | B | ... + | | | | | | | | | | ... + ----------------------------------------------------- ... + first led second led third led ... + + Please mention that this library will never have features + to make fancy animations and so on. It is designed just to + access the LEDs on a physical layer. To avoid bad code + quality due to unnecessary features it will never do more + than this ... + + Technical note: This library is based on delays. This is not + the best way. Maybe it will change to interrupt or DMA use in + the future. +*/ #ifndef WS2812_H @@ -20,18 +59,19 @@ typedef struct WS2812_ARRAY{ }WS2812_ARRAY; +// public API functions: + void ws2812_init(WS2812_ARRAY *control_struct, uint32_t port, uint16_t pin, uint8_t *buffer_ptr, uint32_t array_length); +void ws2812_clear_buffer(WS2812_ARRAY *control_struct); +void ws2812_write_buffer_to_leds(WS2812_ARRAY *control_struct); + + +// internal functions: void ws2812_send_reset(WS2812_ARRAY *control_struct); void ws2812_send_zero(WS2812_ARRAY *control_struct); void ws2812_send_one(WS2812_ARRAY *control_struct); - void ws2812_send_byte(WS2812_ARRAY *control_struct, uint8_t data); -void ws2812_send_led(WS2812_ARRAY *control_struct, uint8_t red, uint8_t green, uint8_t blue); - -void ws2812_write_leds(WS2812_ARRAY *control_struct); -void ws2812_set_array_led(WS2812_ARRAY *control_struct, uint32_t index, uint8_t red, uint8_t green, uint8_t blue); -void ws2812_clear_buffer(WS2812_ARRAY *control_struct); #endif /* WS2812_H */ diff --git a/ws2812/main.c b/ws2812/main.c index 323d03c..6721531 100644 --- a/ws2812/main.c +++ b/ws2812/main.c @@ -1,3 +1,4 @@ +// vim: tabstop=4 shiftwidth=4 noexpandtab #include <libopencm3/stm32/rcc.h> @@ -6,44 +7,34 @@ #include "ws2812.h" -#define DELAY 90000 // 18,000,000 nops are one second @ 72 MHz (one nop --> 4 clock cycles) -#define BRIGHTNESS 30 -#define LED_ARRAY_LENGTH 3 +#define LED_ARRAY_LENGTH 64 // ws2812 array is 64 LEDs long (adapt it to your needs) + +// pin B13 is connected to DIN of ws2812 array: +#define LED_ARRAY_PORT GPIOB +#define LED_ARRAY_PIN GPIO13 +#define RCC_GPIO_PORT RCC_GPIOB + +#define MAX_BRIGHTNESS 10 // maximum brightness per LED (value from 0 to 255) void clock_init(void); void gpio_init(void); -void delay(void); +void delay_ms(uint16_t delay); +void animation_full_color(WS2812_ARRAY *leds); int main(void) { + WS2812_ARRAY leds; // ws2812 control struct + uint8_t led_buffer[LED_ARRAY_LENGTH*3]; // reserve a uint8_t for each color in each LED + clock_init(); gpio_init(); - uint8_t led_array_buffer[LED_ARRAY_LENGTH*WS2812_NUMBER_OF_COLORS]; - WS2812_ARRAY led_panel; - ws2812_init(&led_panel, GPIOB, GPIO13, (uint8_t *)led_array_buffer, LED_ARRAY_LENGTH); + ws2812_init(&leds, LED_ARRAY_PORT, LED_ARRAY_PIN, (uint8_t *)led_buffer, LED_ARRAY_LENGTH); while(1) { - for (uint16_t i=0; i<=BRIGHTNESS; i++) - { - ws2812_set_array_led(&led_panel, 0, i, 0, 0); - ws2812_set_array_led(&led_panel, 1, 0, i, 0); - ws2812_set_array_led(&led_panel, 2, 0, 0, i); - - ws2812_write_leds(&led_panel); - delay(); - } - for (uint16_t i=BRIGHTNESS; i>0; i--) - { - ws2812_set_array_led(&led_panel, 0, i, 0, 0); - ws2812_set_array_led(&led_panel, 1, 0, i, 0); - ws2812_set_array_led(&led_panel, 2, 0, 0, i); - - ws2812_write_leds(&led_panel); - delay(); - } + animation_full_color(&leds); } return 0; @@ -52,27 +43,67 @@ int main(void) void clock_init(void) { - rcc_clock_setup_in_hse_12mhz_out_72mhz(); - rcc_periph_clock_enable(RCC_GPIOB); // for ws2812 - rcc_periph_clock_enable(RCC_GPIOC); // for PC13 blinking + rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use external oscillator for 72 MHz sysclock frequency + rcc_periph_clock_enable(RCC_GPIO_PORT); // enable gpio port clock to use LED_ARRAY_PORT } void gpio_init(void) { - // on board led blinking - gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); - gpio_set(GPIOC, GPIO13); - - // ws2812 output pin - gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); - gpio_set(GPIOB, GPIO13); + gpio_set_mode(LED_ARRAY_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, LED_ARRAY_PIN); // settings for LED_ARRAY_PIN + gpio_clear(LED_ARRAY_PORT, LED_ARRAY_PIN); // set LED_ARRAY_PIN to 0 V } -void delay(void) +void delay_ms(uint16_t delay) { - for (int i = 0; i < DELAY; i++) { + uint32_t delay_nops = delay * 4500; // just @ 72 MHz! + for (uint32_t i = 0; i < delay_nops; i++) { __asm__("nop"); } } + + +void animation_full_color(WS2812_ARRAY *leds) +{ + // set red + ws2812_clear_buffer(leds); + for (uint32_t i = 0; i < leds->array_length*3; i++) + { + if (i%3 == 0) leds->array_buffer[i] = MAX_BRIGHTNESS; + } + ws2812_write_buffer_to_leds(leds); + delay_ms(500); + + // set green + ws2812_clear_buffer(leds); + for (uint32_t i = 0; i < leds->array_length*3; i++) + { + if (i%3 == 1) leds->array_buffer[i] = MAX_BRIGHTNESS; + } + ws2812_write_buffer_to_leds(leds); + delay_ms(500); + + // set blue + ws2812_clear_buffer(leds); + for (uint32_t i = 0; i < leds->array_length*3; i++) + { + if (i%3 == 2) leds->array_buffer[i] = MAX_BRIGHTNESS; + } + ws2812_write_buffer_to_leds(leds); + delay_ms(500); + + // set white + ws2812_clear_buffer(leds); + for (uint32_t i = 0; i < leds->array_length*3; i++) + { + leds->array_buffer[i] = MAX_BRIGHTNESS; + } + ws2812_write_buffer_to_leds(leds); + delay_ms(500); + + // set off + ws2812_clear_buffer(leds); + ws2812_write_buffer_to_leds(leds); + delay_ms(500); +} |