|
| 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 | +#ifMICROPY_PY_MACHINE_BITSTREAM |
| 36 | + |
| 37 | +/******************************************************************************/ |
| 38 | +// Bit-bang implementation |
| 39 | + |
| 40 | +#defineNS_TICKS_OVERHEAD (6) |
| 41 | + |
| 42 | +// This is a translation of the cycle counter implementation in ports/stm32/machine_bitstream.c. |
| 43 | +staticvoidIRAM_ATTRmachine_bitstream_high_low_bitbang(mp_hal_pin_obj_tpin,uint32_t*timing_ns,constuint8_t*buf,size_tlen) { |
| 44 | +uint32_tpin_mask,gpio_reg_set,gpio_reg_clear; |
| 45 | +#ifSOC_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_tfcpu_mhz=esp_rom_get_cpu_ticks_per_us(); |
| 60 | +for (size_ti=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_tirq_state=mp_hal_quiet_timing_enter(); |
| 72 | + |
| 73 | +for (size_ti=0;i<len;++i) { |
| 74 | +uint8_tb=buf[i]; |
| 75 | +for (size_tj=0;j<8;++j) { |
| 76 | +GPIO_REG_WRITE(gpio_reg_set,pin_mask); |
| 77 | +uint32_tstart_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 | +#defineRMT_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 | +typedefstruct { |
| 104 | +uint32_tresolution;/*!< Encoder resolution, in Hz */ |
| 105 | +int32_tbit0_duration0; |
| 106 | +int32_tbit0_duration1; |
| 107 | +int32_tbit1_duration0; |
| 108 | +int32_tbit1_duration1; |
| 109 | +int32_treset_duration; |
| 110 | +}led_strip_encoder_config_t; |
| 111 | + |
| 112 | + |
| 113 | +typedefstruct { |
| 114 | +rmt_encoder_tbase; |
| 115 | +rmt_encoder_t*bytes_encoder; |
| 116 | +rmt_encoder_t*copy_encoder; |
| 117 | +intstate; |
| 118 | +rmt_symbol_word_treset_code; |
| 119 | +}rmt_led_strip_encoder_t; |
| 120 | + |
| 121 | + |
| 122 | +RMT_ENCODER_FUNC_ATTR |
| 123 | +staticsize_trmt_encode_led_strip(rmt_encoder_t*encoder,rmt_channel_handle_tchannel,constvoid*primary_data,size_tdata_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_tbytes_encoder=led_encoder->bytes_encoder; |
| 127 | +rmt_encoder_handle_tcopy_encoder=led_encoder->copy_encoder; |
| 128 | +rmt_encode_state_tsession_state=RMT_ENCODING_RESET; |
| 129 | +rmt_encode_state_tstate=RMT_ENCODING_RESET; |
| 130 | +size_tencoded_symbols=0; |
| 131 | +switch (led_encoder->state) { |
| 132 | +case0:// 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 | + gotoout;// yield if there's no free space for encoding artifacts |
| 140 | + } |
| 141 | +// fall-through |
| 142 | +case1:// 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 | + gotoout;// yield if there's no free space for encoding artifacts |
| 152 | + } |
| 153 | + } |
| 154 | +out: |
| 155 | +*ret_state=state; |
| 156 | +returnencoded_symbols; |
| 157 | +} |
| 158 | + |
| 159 | + |
| 160 | +staticesp_err_trmt_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 | +returnESP_OK; |
| 167 | +} |
| 168 | + |
| 169 | + |
| 170 | +RMT_ENCODER_FUNC_ATTR |
| 171 | +staticesp_err_trmt_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 | +returnESP_OK; |
| 178 | +} |
| 179 | + |
| 180 | + |
| 181 | +esp_err_trmt_new_led_strip_encoder(constled_strip_encoder_config_t*config,rmt_encoder_handle_t*ret_encoder) |
| 182 | +{ |
| 183 | +esp_err_tret=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_tbit0_duration0; |
| 193 | +uint32_tbit0_duration1; |
| 194 | + |
| 195 | +uint32_tbit1_duration0; |
| 196 | +uint32_tbit2_duration1; |
| 197 | + |
| 198 | +if (config->bit0_duration0<0)bit0_duration0= (uint32_t)-config->bit0_duration0; |
| 199 | +elsebit0_duration0= (uint32_t)config->bit0_duration0; |
| 200 | + |
| 201 | +if (config->bit0_duration1<0)bit0_duration1= (uint32_t)-config->bit0_duration1; |
| 202 | +elsebit0_duration1= (uint32_t)config->bit0_duration1; |
| 203 | + |
| 204 | +if (config->bit1_duration0<0)bit1_duration0= (uint32_t)-config->bit1_duration0; |
| 205 | +elsebit1_duration0= (uint32_t)config->bit1_duration0; |
| 206 | + |
| 207 | +if (config->bit1_duration1<0)bit1_duration1= (uint32_t)-config->bit1_duration1; |
| 208 | +elsebit1_duration1= (uint32_t)config->bit1_duration1; |
| 209 | + |
| 210 | +rmt_bytes_encoder_config_tbytes_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_tcopy_encoder_config= {}; |
| 229 | +ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config,&led_encoder->copy_encoder),err,TAG,"create copy encoder failed"); |
| 230 | + |
| 231 | +uint32_treset_duration; |
| 232 | + |
| 233 | +if (config->reset_duration<0)reset_duration= (uint32_t)-config->reset_duration; |
| 234 | +elsereset_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 | +returnESP_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 | +returnret; |
| 256 | + |
| 257 | + |
| 258 | +// Use the reserved RMT channel to stream high/low data on the specified pin. |
| 259 | +staticvoidmachine_bitstream_high_low_rmt(mp_hal_pin_obj_tpin,uint32_t*timing_ns,constuint8_t*buf,size_tlen,uint8_tchannel_id) { |
| 260 | + |
| 261 | + ((void)channel_id); |
| 262 | + |
| 263 | +rmt_tx_channel_config_tchannel_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_tencoder_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_tchannel_handle=NULL; |
| 279 | +check_esp_err(rmt_new_tx_channel(&channel_config,&channel_handle)); |
| 280 | + |
| 281 | +rmt_encoder_handle_tencoder_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_ttx_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_ttimeout_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 | +voidmachine_bitstream_high_low(mp_hal_pin_obj_tpin,uint32_t*timing_ns,constuint8_t*buf,size_tlen) { |
| 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 |