Skip to content

Commit 1fc4a38

Browse files
committed
updates bitstream to use new IDF API
1 parent 15a414b commit 1fc4a38

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Jim Mussared
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "py/mpconfig.h"
28+
#include "py/mphal.h"
29+
#include "modesp32.h"
30+
31+
#include "rom/gpio.h"
32+
#include "soc/gpio_reg.h"
33+
#include "soc/gpio_sig_map.h"
34+
35+
#if MICROPY_PY_MACHINE_BITSTREAM
36+
37+
/******************************************************************************/
38+
// Bit-bang implementation
39+
40+
#define NS_TICKS_OVERHEAD (6)
41+
42+
// This is a translation of the cycle counter implementation in ports/stm32/machine_bitstream.c.
43+
static void IRAM_ATTR machine_bitstream_high_low_bitbang(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) {
44+
uint32_t pin_mask, gpio_reg_set, gpio_reg_clear;
45+
#if SOC_GPIO_PIN_COUNT > 32
46+
if (pin >= 32) {
47+
pin_mask = 1 << (pin - 32);
48+
gpio_reg_set = GPIO_OUT1_W1TS_REG;
49+
gpio_reg_clear = GPIO_OUT1_W1TC_REG;
50+
} else
51+
#endif
52+
{
53+
pin_mask = 1 << pin;
54+
gpio_reg_set = GPIO_OUT_W1TS_REG;
55+
gpio_reg_clear = GPIO_OUT_W1TC_REG;
56+
}
57+
58+
// Convert ns to cpu ticks [high_time_0, period_0, high_time_1, period_1].
59+
uint32_t fcpu_mhz = esp_rom_get_cpu_ticks_per_us();
60+
for (size_t i = 0; i < 4; ++i) {
61+
timing_ns[i] = fcpu_mhz * timing_ns[i] / 1000;
62+
if (timing_ns[i] > NS_TICKS_OVERHEAD) {
63+
timing_ns[i] -= NS_TICKS_OVERHEAD;
64+
}
65+
if (i % 2 == 1) {
66+
// Convert low_time to period (i.e. add high_time).
67+
timing_ns[i] += timing_ns[i - 1];
68+
}
69+
}
70+
71+
uint32_t irq_state = mp_hal_quiet_timing_enter();
72+
73+
for (size_t i = 0; i < len; ++i) {
74+
uint8_t b = buf[i];
75+
for (size_t j = 0; j < 8; ++j) {
76+
GPIO_REG_WRITE(gpio_reg_set, pin_mask);
77+
uint32_t start_ticks = mp_hal_ticks_cpu();
78+
uint32_t *t = &timing_ns[b >> 6 & 2];
79+
while (mp_hal_ticks_cpu() - start_ticks < t[0]) {
80+
;
81+
}
82+
GPIO_REG_WRITE(gpio_reg_clear, pin_mask);
83+
b <<= 1;
84+
while (mp_hal_ticks_cpu() - start_ticks < t[1]) {
85+
;
86+
}
87+
}
88+
}
89+
90+
mp_hal_quiet_timing_exit(irq_state);
91+
}
92+
93+
/******************************************************************************/
94+
// RMT implementation
95+
96+
#define RMT_LED_STRIP_RESOLUTION_HZ 10000000
97+
98+
#include "driver/rmt_tx.h"
99+
#include "driver/rmt_common.h"
100+
#include "driver/rmt_encoder.h"
101+
102+
103+
typedef struct {
104+
uint32_t resolution; /*!< Encoder resolution, in Hz */
105+
int32_t bit0_duration0;
106+
int32_t bit0_duration1;
107+
int32_t bit1_duration0;
108+
int32_t bit1_duration1;
109+
int32_t reset_duration;
110+
} led_strip_encoder_config_t;
111+
112+
113+
typedef struct {
114+
rmt_encoder_t base;
115+
rmt_encoder_t *bytes_encoder;
116+
rmt_encoder_t *copy_encoder;
117+
int state;
118+
rmt_symbol_word_t reset_code;
119+
} rmt_led_strip_encoder_t;
120+
121+
122+
RMT_ENCODER_FUNC_ATTR
123+
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
124+
{
125+
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
126+
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
127+
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
128+
rmt_encode_state_t session_state = RMT_ENCODING_RESET;
129+
rmt_encode_state_t state = RMT_ENCODING_RESET;
130+
size_t encoded_symbols = 0;
131+
switch (led_encoder->state) {
132+
case 0: // send RGB data
133+
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
134+
if (session_state & RMT_ENCODING_COMPLETE) {
135+
led_encoder->state = 1; // switch to next state when current encoding session finished
136+
}
137+
if (session_state & RMT_ENCODING_MEM_FULL) {
138+
state |= RMT_ENCODING_MEM_FULL;
139+
goto out; // yield if there's no free space for encoding artifacts
140+
}
141+
// fall-through
142+
case 1: // send reset code
143+
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
144+
sizeof(led_encoder->reset_code), &session_state);
145+
if (session_state & RMT_ENCODING_COMPLETE) {
146+
led_encoder->state = RMT_ENCODING_RESET; // back to the initial encoding session
147+
state |= RMT_ENCODING_COMPLETE;
148+
}
149+
if (session_state & RMT_ENCODING_MEM_FULL) {
150+
state |= RMT_ENCODING_MEM_FULL;
151+
goto out; // yield if there's no free space for encoding artifacts
152+
}
153+
}
154+
out:
155+
*ret_state = state;
156+
return encoded_symbols;
157+
}
158+
159+
160+
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
161+
{
162+
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
163+
rmt_del_encoder(led_encoder->bytes_encoder);
164+
rmt_del_encoder(led_encoder->copy_encoder);
165+
free(led_encoder);
166+
return ESP_OK;
167+
}
168+
169+
170+
RMT_ENCODER_FUNC_ATTR
171+
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
172+
{
173+
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
174+
rmt_encoder_reset(led_encoder->bytes_encoder);
175+
rmt_encoder_reset(led_encoder->copy_encoder);
176+
led_encoder->state = RMT_ENCODING_RESET;
177+
return ESP_OK;
178+
}
179+
180+
181+
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
182+
{
183+
esp_err_t ret = ESP_OK;
184+
rmt_led_strip_encoder_t *led_encoder = NULL;
185+
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
186+
led_encoder = rmt_alloc_encoder_mem(sizeof(rmt_led_strip_encoder_t));
187+
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
188+
led_encoder->base.encode = rmt_encode_led_strip;
189+
led_encoder->base.del = rmt_del_led_strip_encoder;
190+
led_encoder->base.reset = rmt_led_strip_encoder_reset;
191+
192+
uint32_t bit0_duration0;
193+
uint32_t bit0_duration1;
194+
195+
uint32_t bit1_duration0;
196+
uint32_t bit2_duration1;
197+
198+
if (config->bit0_duration0 < 0) bit0_duration0 = (uint32_t)-config->bit0_duration0;
199+
else bit0_duration0 = (uint32_t)config->bit0_duration0;
200+
201+
if (config->bit0_duration1 < 0) bit0_duration1 = (uint32_t)-config->bit0_duration1;
202+
else bit0_duration1 = (uint32_t)config->bit0_duration1;
203+
204+
if (config->bit1_duration0 < 0) bit1_duration0 = (uint32_t)-config->bit1_duration0;
205+
else bit1_duration0 = (uint32_t)config->bit1_duration0;
206+
207+
if (config->bit1_duration1 < 0) bit1_duration1 = (uint32_t)-config->bit1_duration1;
208+
else bit1_duration1 = (uint32_t)config->bit1_duration1;
209+
210+
rmt_bytes_encoder_config_t bytes_encoder_config = {
211+
.bit0 = {
212+
.level0 = config->bit0_duration0 < 0 ? 0 : 1,
213+
.duration0 = bit0_duration0 * config->resolution / 1000000000,
214+
.level1 = config->bit0_duration1 < 0 ? 0 : 1,
215+
.duration1 = bit0_duration1 * config->resolution / 1000000000,
216+
},
217+
.bit1 = {
218+
.level0 = config->bit1_duration0 < 0 ? 0 : 1,
219+
.duration0 = bit1_duration0 * config->resolution / 1000000000,
220+
.level1 = config->bit1_duration1 < 0 ? 0 : 1,
221+
.duration1 = bit1_duration1 * config->resolution / 1000000000,
222+
},
223+
.flags.msb_first = config->bit_order // WS2812 transfer bit order: G7...G0R7...R0B7...B0
224+
};
225+
226+
227+
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
228+
rmt_copy_encoder_config_t copy_encoder_config = {};
229+
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
230+
231+
uint32_t reset_duration;
232+
233+
if (config->reset_duration < 0) reset_duration = (uint32_t)-config->reset_duration;
234+
else reset_duration = (uint32_t)config->reset_duration;
235+
236+
led_encoder->reset_code = (rmt_symbol_word_t) {
237+
.level0 = config->reset_duration < 0 ? 0 : 1,
238+
.duration0 = reset_duration * config->resolution / 1000000000 / 2,
239+
.level1 = config->reset_duration < 0 ? 0 : 1,
240+
.duration1 = reset_duration * config->resolution / 1000000000 / 2,
241+
};
242+
243+
*ret_encoder = &led_encoder->base;
244+
return ESP_OK;
245+
err:
246+
if (led_encoder) {
247+
if (led_encoder->bytes_encoder) {
248+
rmt_del_encoder(led_encoder->bytes_encoder);
249+
}
250+
if (led_encoder->copy_encoder) {
251+
rmt_del_encoder(led_encoder->copy_encoder);
252+
}
253+
free(led_encoder);
254+
}
255+
return ret;
256+
257+
258+
// Use the reserved RMT channel to stream high/low data on the specified pin.
259+
static void machine_bitstream_high_low_rmt(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len, uint8_t channel_id) {
260+
261+
((void)channel_id);
262+
263+
rmt_tx_channel_config_t channel_config = { 0 };
264+
channel_config.gpio_num = pin;
265+
channel_config.clk_src = RMT_CLK_SRC_DEFAULT;
266+
channel_config.resolution_hz = RMT_LED_STRIP_RESOLUTION_HZ;
267+
channel_config.mem_block_symbols = 64;
268+
channel_config.trans_queue_depth = 1;
269+
270+
led_strip_encoder_config_t encoder_config = { 0 };
271+
encoder_config.resolution = RMT_LED_STRIP_RESOLUTION_HZ;
272+
encoder_config.bit0_duration0 = (int32_t)timing_ns[0];
273+
encoder_config.bit0_duration1 = -(int32_t)timing_ns[1];
274+
encoder_config.bit1_duration0 = (int32_t)timing_ns[2];
275+
encoder_config.bit1_duration1 = -(int32_t)timing_ns[3];
276+
encoder_config.reset_duration = -50000;
277+
278+
rmt_channel_handle_t channel_handle = NULL;
279+
check_esp_err(rmt_new_tx_channel(&channel_config, &channel_handle));
280+
281+
rmt_encoder_handle_t encoder_handle = NULL;
282+
check_esp_err(rmt_new_led_strip_encoder(&encoder_config, &encoder_handle));
283+
check_esp_err(rmt_enable(channel_handle));
284+
285+
rmt_transmit_config_t tx_config = {
286+
.loop_count = 0, // no transfer loop
287+
};
288+
289+
check_esp_err(rmt_transmit(channel_handle, encoder_handle, buf, len, &tx_config)));
290+
291+
// Wait 50% longer than we expect (if every bit takes the maximum time).
292+
uint32_t timeout_ms = (3 * len / 2) * (1 + (8 * MAX(timing_ns[0] + timing_ns[1], timing_ns[2] + timing_ns[3])) / 1000);
293+
check_esp_err(rmt_tx_wait_all_done(config.channel, pdMS_TO_TICKS(timeout_ms)));
294+
295+
// Uninstall the driver.
296+
check_esp_err(rmt_del_led_strip_encoder(encoder_handle));
297+
check_esp_err(rmt_del_channel(channel_handle));
298+
299+
// Cancel RMT output to GPIO pin.
300+
esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false);
301+
}
302+
303+
/******************************************************************************/
304+
// Interface to machine.bitstream
305+
306+
void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) {
307+
if (esp32_rmt_bitstream_channel_id < 0) {
308+
machine_bitstream_high_low_bitbang(pin, timing_ns, buf, len);
309+
} else {
310+
machine_bitstream_high_low_rmt(pin, timing_ns, buf, len, esp32_rmt_bitstream_channel_id);
311+
}
312+
}
313+
314+
#endif // MICROPY_PY_MACHINE_BITSTREAM

0 commit comments

Comments
 (0)