summaryrefslogtreecommitdiff
path: root/libraries/ws2812.c
blob: 41d4fb558cad222a8cecd64c306cd29146b1fab0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125


/*

# 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 80 MHz
- You have to setup the clock and gpio yourself

*/


#include "ws2812.h"

#define NUMBER_NOPS_RESET 1080  // > 50 µs --> 60 µs (to be sure) = 4320 cycles @ 72 MHz = 1080 nops
#define NUMBER_NOPS_LONG 16  // 0.9 µs = 64.8 cycles @ 72 MHz = 16.2 nops --> 11.1 ns timing error
#define NUMBER_NOPS_SHORT 6  // 0.35 µs = 25.2 cycles @ 72 MHz = 6.3 nops --> 16.7 ns timing error


void ws2812_init(ws2812_init_typedef *control_struct, uint32_t port, uint16_t pin, uint32_t array_length, uint32_t matrix_width)
{
	control_struct->gpio_port = port;
	control_struct->gpio_pin = pin;
	control_struct->array_length = array_length;
	control_struct->matrix_width = matrix_width;
	control_struct->led_array = malloc(sizeof(uint8_t[array_length][3]));
	ws2812_clear_buffer(control_struct);
	ws2812_send_reset(control_struct);
}


void ws2812_send_reset(ws2812_init_typedef *control_struct)
{
	gpio_clear(control_struct->gpio_port, control_struct->gpio_pin);
	for (int i = 0; i < NUMBER_NOPS_RESET; i++) {
		__asm__("nop");
	}
}


void ws2812_send_zero(ws2812_init_typedef *control_struct)
{
	gpio_set(control_struct->gpio_port, control_struct->gpio_pin);
	for (int 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++) {
		__asm__("nop");
	}
}


void ws2812_send_one(ws2812_init_typedef *control_struct)
{
	gpio_set(control_struct->gpio_port, control_struct->gpio_pin);
	for (int 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++) {
		__asm__("nop");
	}
}


void ws2812_send_byte(ws2812_init_typedef *control_struct, uint8_t data)
{
	for (int i=7; i>=0; i--){
		if ( (data >> i) & 0x01 ){
			ws2812_send_one(control_struct);
		}
		else {
			ws2812_send_zero(control_struct);
		}
	}
}


void ws2812_send_led(ws2812_init_typedef *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_init_typedef *control_struct)
{
	for(uint32_t i=0; i<(control_struct->array_length); i++){
		ws2812_send_led(
			control_struct,
			control_struct->led_array[i][0],  // red
			control_struct->led_array[i][1],  // green
			control_struct->led_array[i][2]   // blue
		);
	}
	ws2812_send_reset(control_struct);
}


void ws2812_set_array_led(ws2812_init_typedef *control_struct, uint32_t index, uint8_t red, uint8_t green, uint8_t blue)
{
	control_struct->led_array[index][0] = red;
	control_struct->led_array[index][1] = green;
	control_struct->led_array[index][2] = blue;
}


void ws2812_set_matrix_led(ws2812_init_typedef *control_struct, uint32_t x, uint32_t y, uint8_t red, uint8_t green, uint8_t blue)
{
	ws2812_set_array_led(control_struct, (x + y * (control_struct->matrix_width)), red, green, blue);
}


void ws2812_clear_buffer(ws2812_init_typedef *control_struct)
{
	for (uint32_t i=0; i<control_struct->array_length; i++){
		ws2812_set_array_led(control_struct, i, 0, 0, 0);
	}
}