summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libraries/ws2812.c88
-rw-r--r--libraries/ws2812.h52
-rw-r--r--ws2812/main.c105
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);
+}