/
i2s_ws2812b_drive.c
189 lines (170 loc) · 6.49 KB
/
i2s_ws2812b_drive.c
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/* Copyright (c) 2016 Takafumi Naka. All Rights Reserved.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "i2s_ws2812b_drive.h"
#include "app_util_platform.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
volatile uint8_t i2s_ws2812b_drive_flag_buffer_cnt = 0;
static void i2s_ws2812b_drive_handler(uint32_t const * p_data_received,
uint32_t * p_data_to_send,
uint16_t number_of_words)
{
if (p_data_to_send != NULL) {
if (i2s_ws2812b_drive_flag_buffer_cnt == 0) {
// == 0 called before start - do nothing
// == 1 is called just before the second half gets output
} else if (i2s_ws2812b_drive_flag_buffer_cnt == 2) {
// ==3 called before we go back to the beginning.
// Now start setting the next part of the buffer to 0
// to ensure we don't get data repeated
for (int i=0;i<number_of_words;i++)
p_data_to_send[i] = 0;
}
i2s_ws2812b_drive_flag_buffer_cnt++;
}
}
ret_code_t i2s_ws2812b_drive_xfer(rgb_led_t *led_array, uint16_t num_leds, uint8_t drive_pin)
{
#ifdef USE_NEOPIXEL
ret_code_t err_code;
// save pin state before
#ifdef NEOPIXEL_SCK_PIN
uint32_t sckPinNumber = NEOPIXEL_SCK_PIN;
#if NRF_SD_BLE_API_VERSION>5
NRF_GPIO_Type *sckReg = nrf_gpio_pin_port_decode(&sckPinNumber);
#else
NRF_GPIO_Type *sckReg = NRF_GPIO;
#endif
uint32_t sckConf = sckReg->PIN_CNF[sckPinNumber];
#endif
#ifdef NEOPIXEL_LRCK_PIN
uint32_t lrckPinNumber = NEOPIXEL_LRCK_PIN;
#if NRF_SD_BLE_API_VERSION>5
NRF_GPIO_Type *lrckReg = nrf_gpio_pin_port_decode(&lrckPinNumber);
#else
NRF_GPIO_Type *lrckReg = NRF_GPIO;
#endif
uint32_t lrckConf = lrckReg->PIN_CNF[lrckPinNumber];
#endif
// define configs
nrf_drv_i2s_config_t config;
/* The I2S interface refuses to work if SCK isn't defined,
* so we have to define it on a pin where it doesn't matter. */
#ifdef NEOPIXEL_SCK_PIN
config.sck_pin = NEOPIXEL_SCK_PIN;
#else
#error "Must define an SCK pin if using neopixels"
#endif
#ifdef NEOPIXEL_LRCK_PIN
// On nRF52840 lrck needs defining too - http://forum.espruino.com/conversations/354468/
config.lrck_pin = NEOPIXEL_LRCK_PIN;
#else
config.lrck_pin = NRF_DRV_I2S_PIN_NOT_USED;
#endif
config.mck_pin = NRF_DRV_I2S_PIN_NOT_USED;
config.sdout_pin = drive_pin;
config.sdin_pin = NRF_DRV_I2S_PIN_NOT_USED;
config.irq_priority = APP_IRQ_PRIORITY_HIGH;
config.mode = NRF_I2S_MODE_MASTER;
config.format = NRF_I2S_FORMAT_I2S;
config.alignment = NRF_I2S_ALIGN_LEFT;
config.sample_width = NRF_I2S_SWIDTH_16BIT;
config.channels = NRF_I2S_CHANNELS_STEREO;
config.mck_setup = NRF_I2S_MCK_32MDIV10;
config.ratio = NRF_I2S_RATIO_32X;
// initialize i2s
err_code = nrf_drv_i2s_init(&config, i2s_ws2812b_drive_handler);
APP_ERROR_CHECK(err_code);
if ( err_code != NRF_SUCCESS )
{
return err_code;
}
// make buffer
size_t tx_buffer_size = num_leds * (I2S_WS2812B_DRIVE_BUF_SIZE_PER_LED / 4); // size in 32 bit words
tx_buffer_size = (tx_buffer_size+1) & (~1); // round up to ensure it's even (nrf_drv_i2s_start needs this)
uint32_t m_buffer_tx[tx_buffer_size];
i2s_ws2812b_drive_flag_buffer_cnt = 0;
// Set up buffer with all the LED waveform in
uint8_t *p_xfer = (uint8_t *)m_buffer_tx;
uint8_t *p_xfer_end = (uint8_t *)&m_buffer_tx[tx_buffer_size];
rgb_led_t* p_led = led_array;
for(int i_led=0;i_led<num_leds;i_led++) {
uint32_t rgb_data = (p_led->green << 16) | (p_led->red << 8 ) | p_led->blue;
for(uint8_t i_rgb=0;i_rgb<I2S_WS2812B_DRIVE_BUF_SIZE_PER_LED;i_rgb+=2) {
switch(rgb_data & 0x00300000 ) {
case ( 0x00100000 ):
*(p_xfer) = (uint8_t)(( I2S_WS2812B_DRIVE_PATTERN_0 << 4 ) | I2S_WS2812B_DRIVE_PATTERN_1);
break;
case ( 0x00200000 ):
*(p_xfer) = (uint8_t)(( I2S_WS2812B_DRIVE_PATTERN_1 << 4 ) | I2S_WS2812B_DRIVE_PATTERN_0);
break;
case ( 0x00300000 ):
*(p_xfer) = (uint8_t)(( I2S_WS2812B_DRIVE_PATTERN_1 << 4 ) | I2S_WS2812B_DRIVE_PATTERN_1);
break;
default:
*(p_xfer) = (uint8_t)(( I2S_WS2812B_DRIVE_PATTERN_0 << 4 ) | I2S_WS2812B_DRIVE_PATTERN_0);
}
p_xfer++;
switch(rgb_data & 0x00c00000 ) {
case ( 0x00400000 ):
*(p_xfer) = (uint8_t)(( I2S_WS2812B_DRIVE_PATTERN_0 << 4 ) | I2S_WS2812B_DRIVE_PATTERN_1);
break;
case ( 0x00800000 ):
*(p_xfer) = (uint8_t)(( I2S_WS2812B_DRIVE_PATTERN_1 << 4 ) | I2S_WS2812B_DRIVE_PATTERN_0);
break;
case ( 0x00c00000 ):
*(p_xfer) = (uint8_t)(( I2S_WS2812B_DRIVE_PATTERN_1 << 4 ) | I2S_WS2812B_DRIVE_PATTERN_1);
break;
default:
*(p_xfer) = (uint8_t)(( I2S_WS2812B_DRIVE_PATTERN_0 << 4 ) | I2S_WS2812B_DRIVE_PATTERN_0);
}
p_xfer++;
rgb_data <<= 4;
}
p_led++;
}
// fill the remainder with 0
while (p_xfer < p_xfer_end)
*(p_xfer++) = 0;
// start transfer
#if NRF_SD_BLE_API_VERSION>5
nrfx_i2s_buffers_t buffers;
buffers.p_rx_buffer = 0;
buffers.p_tx_buffer = m_buffer_tx;
err_code = nrf_drv_i2s_start(&buffers, tx_buffer_size, 0/*flags*/);
#else
err_code = nrf_drv_i2s_start(NULL, m_buffer_tx, tx_buffer_size, 0/*flags*/);
#endif
APP_ERROR_CHECK(err_code);
if ( err_code != NRF_SUCCESS )
{
return err_code;
}
// wait for transmit to finish - for some reason we have to
// wait for the 5th call to i2s_ws2812b_drive_handler.
// Not 100% sure why this is - presumably it's pre-buffering
// as far in advance as it can.
while (i2s_ws2812b_drive_flag_buffer_cnt < 5);
// finally, stop the output
nrf_drv_i2s_stop();
// un-initialize i2s
nrf_drv_i2s_uninit();
// I2S not only uses pins when it's not supposed to, it doesn't
// even de-allocate them after so we must do it manually
// https://github.com/espruino/Espruino/issues/2071
NRF_I2S->PSEL.SCK = NRF_DRV_I2S_PIN_NOT_USED;
NRF_I2S->PSEL.LRCK = NRF_DRV_I2S_PIN_NOT_USED;
#ifdef NEOPIXEL_SCK_PIN
sckReg->PIN_CNF[sckPinNumber] = sckConf;
#endif
#ifdef NEOPIXEL_LRCK_PIN
lrckReg->PIN_CNF[lrckPinNumber] = lrckConf;
#endif
#endif
return NRF_SUCCESS;
}