Skip to content

BUG RMT cannot be re-init after calling rmtDeinit  #6363

Closed
@0x0fe

Description

@0x0fe

Board

ESP32-C3 dev module

Device Description

DevKitC

Hardware Configuration

tact switch and assignable RGB LED on GPIO9

Version

latest master

IDE Name

arduino IDE

Operating System

windows 10

Flash frequency

80M

PSRAM enabled

no

Upload speed

CDC

Description

in this use case IO9 is shared between assignable LED and tact switch, the RMT driver should take over IO9 only when sending datas to the LED and tact switch interrupt should be attached otherwise. This concept is long proven on other SoCs but it does not work on esp32c3 yet, due to RMT driver bug.

Actually there is two bugs, the first is minor and can be fixed easily with a check, for now by commenting during the test, the second is of unknown cause yet, nothing shows up in the log.

1 rmtDeinit throws an error when used in TX only mode, i suspect stopping the (non exsting) RX channel is the cause.

2 once rmtDeinit has been called the RMT driver cannot be re-init or used anymore, any subsequent call to rmtInit will just lock everything up until hard reset or power cycle.

It seems something has not been cleared somewhere when RMT driver is deInit.

Sketch

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "Arduino.h"
#include "freertos/timers.h"
#include "esp32-hal.h"

#define LED_IO      9
#define LED_QTY     1

#define LED_TICK    10
#define BITS        24*LED_QTY
#define TICK_CHK()  if(millis()-ledfx.led_l_ts<LED_TICK)return   
#define TICK_INC()  ledfx.led_l_ts=millis() 
#define MIN(a,b)    ((a)<(b)?(a):(b))
#define MAX(a,b)    ((a)>(b)?(a):(b))

enum {
  SLOW_BREATH,
  FAST_BREATH,
  SLOW_SBLINK,
  FAST_SBLINK,
  SLOW_LBLINK,
  FAST_LBLINK,  
  SLOW_FADIN,
  FAST_FADIN,
  SLOW_FADOUT,
  FAST_FADOUT  
}LED_MODE;
struct LED_FX{
  uint32_t fade_in;
  uint32_t hold;
  uint32_t fade_out;
  uint32_t wait;
  uint8_t cycles;
  uint32_t cycles_cnt;
  float color;
  float bright;
  uint32_t hold_cnt;
  uint32_t wait_cnt;
  uint8_t state;
  uint8_t fxmode;
  bool polling_mode;
  uint32_t led_ticker;
  uint32_t led_l_ts;
  float h;
  float s;
  float l;  
};
typedef struct rgb{float r,g,b;}RGB;
typedef struct hsl{float h,s,l;}HSL;
rmt_data_t led_data[BITS];
rmt_obj_t* rmt_send=NULL;
TimerHandle_t ledTimer;
LED_FX ledfx;

volatile int tactFlag=0,irqInit=0;
void IRAM_ATTR tactIsr() {
    tactFlag=1;
}  
float hue2rgb(float p,float q,float t) {

  if (t < 0) t += 1;
  if (t > 1) t -= 1;
  if (t < 1./6) return p + (q - p) * 6 * t;
  if (t < 1./2) return q;
  if (t < 2./3) return p + (q - p) * (2./3 - t) * 6;    
  return p;
}
HSL rgb2hsl(float r,float g,float b) {
  
  HSL result;
  
  r /= 255.0;
  g /= 255.0;
  b /= 255.0;
  
  float max = MAX(MAX(r,g),b);
  float min = MIN(MIN(r,g),b);
  
  result.h = result.s = result.l = (max + min) / 2;

  if (max == min) {
    result.h = result.s = 0; // achromatic
  }
  else {
    float d = max - min;
    result.s = (result.l > 0.5) ? d / (2 - max - min) : d / (max + min);
    if (max == r) { result.h = (g - b) / d + (g < b ? 6 : 0);}
    else if (max == g) { result.h = (b - r) / d + 2; }
    else if (max == b) { result.h = (r - g) / d + 4;  }  
    result.h /= 6;
  }
  return result; 
}
RGB hsl2rgb(float h,float s,float l) {

  RGB result;
  
  if(0 == s) {
    result.r = result.g = result.b = l; // achromatic
  }
  else {
    float q = l < 0.5 ? l * (1 + s) : l + s - l * s;
    float p = 2 * l - q;
    result.r = hue2rgb(p, q, h + 1./3) * 255;
    result.g = hue2rgb(p, q, h) * 255;
    result.b = hue2rgb(p, q, h - 1./3) * 255;
    if(result.r<0)result.r=0;
    if(result.g<0)result.g=0;
    if(result.b<0)result.b=0;
  }
  return result;
}
void led_setbfr(int *color,int index) {

  int led = index, col, bit, i = 0;
  //Serial.printf("set %d %d %d\n",color[0],color[1],color[2]);
  for (col = 0; col < 3; col++ ) {
    for (bit = 0; bit < 8; bit++) {
      if ( (color[col] & (1 << (7 - bit))) /*&& (led == led_index)*/ ) {
        led_data[i].level0 = 1;
        led_data[i].duration0 = 8;
        led_data[i].level1 = 0;
        led_data[i].duration1 = 4;
      } else {
        led_data[i].level0 = 1;
        led_data[i].duration0 = 4;
        led_data[i].level1 = 0;
        led_data[i].duration1 = 8;
      }
      i++;
    }
  }
}
void led_setHSL(float h_,float s_,float l_,int index) {

  RGB val=hsl2rgb(h_,s_,l_);
  int col[]={val.r,val.g,val.b};
  led_setbfr(col,index);
  rmtWrite(rmt_send, led_data, BITS);     
}
void set_timer(TimerHandle_t xTimer,unsigned int ms ) {

  if(xTimerIsTimerActive( xTimer )!=pdFALSE){ xTimerDelete(xTimer,100); }
  else{
    if(xTimerChangePeriod( xTimer,  pdMS_TO_TICKS(ms), 100 ) == pdPASS ) {}
    else{Serial.println("timer set failed");}
  }
}
void led_fxhandler(void){

  if(ledfx.state==0) return;
  if(ledfx.polling_mode)TICK_CHK();
  switch(ledfx.state){
    
    case 1: //----------- ramp up /on 
      ledfx.wait_cnt=0; 
      switch(ledfx.fxmode){
        case SLOW_BREATH:
        case FAST_BREATH: 
        case SLOW_SBLINK: 
        case FAST_SBLINK:
        case SLOW_LBLINK: 
        case FAST_LBLINK:          
          led_setHSL(ledfx.h,ledfx.s,ledfx.l,0);
          if(ledfx.l>=ledfx.bright)ledfx.state=2;
          else ledfx.l+=(ledfx.bright/(ledfx.fade_in/LED_TICK));        
        break; 
      } 
    break;
    
    case 2: //----------- hold
      switch(ledfx.fxmode){
        case SLOW_BREATH:
        case FAST_BREATH:
        case SLOW_SBLINK: 
        case FAST_SBLINK:
        case SLOW_LBLINK: 
        case FAST_LBLINK:          
          if(ledfx.hold_cnt>=(ledfx.hold/LED_TICK))ledfx.state=3; 
          else ledfx.hold_cnt+=1;
        break;      
      }  
    break;
    
    case 3: //----------- ramp down / off
      ledfx.hold_cnt=0; 
      switch(ledfx.fxmode){
        case SLOW_BREATH:
        case FAST_BREATH: 
          led_setHSL(ledfx.h,ledfx.s,ledfx.l,0);                  
          if(ledfx.l<=0)ledfx.state=4; 
          else ledfx.l-=(ledfx.bright/(ledfx.fade_in/LED_TICK));     
        break; 
        case SLOW_SBLINK: 
        case FAST_SBLINK:
        case SLOW_LBLINK: 
        case FAST_LBLINK:        
          led_setHSL(ledfx.h,ledfx.s,ledfx.l,0);                  
          if(ledfx.l<=0)ledfx.state=4; 
          else ledfx.l-=(ledfx.bright/(ledfx.fade_out/LED_TICK));          
        break;        
      }    
    break;

    case 4: //----------- wait
      switch(ledfx.fxmode){
        case SLOW_BREATH:
        case FAST_BREATH: 
        case SLOW_SBLINK: 
        case FAST_SBLINK:
        case SLOW_LBLINK: 
        case FAST_LBLINK:          
          if(ledfx.wait_cnt>=(ledfx.wait/LED_TICK)){
            if(ledfx.cycles==255)ledfx.state=1; 
            else{
              if(ledfx.cycles_cnt>=ledfx.cycles-1){
                ledfx.state=0; 
                xTimerStop(ledTimer,0);
                Serial.println("de-init rmt");
                rmtDeinit(rmt_send);
                //rmt_ll_stop_tx(s_rmt.regs, REF_CLOCK_RMT_CHANNEL);
                //periph_module_disable(PERIPH_RMT_MODULE);
                tact_init();
              }
              else{ledfx.cycles_cnt+=1; ledfx.state=1; }
            }          
          }
          else ledfx.wait_cnt+=1;
        break;                 
      }  
    break;   
  }
  if(ledfx.polling_mode)TICK_INC(); 
}
void led_stop(int gracefully){
  
  switch(ledfx.fxmode){
    case 1: 
      if(gracefully)ledfx.cycles=1;
    break;
  }
}
void led_set(int fxmode,float color,float sat,float bright,int cnt){

  Serial.println("led set");
  ledfx.bright=bright; // brightness
  ledfx.h=color; 
  ledfx.s=sat; // saturation
  ledfx.fxmode=fxmode;
  ledfx.cycles=cnt; 
  switch(ledfx.fxmode){
    case SLOW_BREATH: ledfx.fade_in=800; ledfx.hold=100; ledfx.wait=1500; break;
    case FAST_BREATH: ledfx.fade_in=30; ledfx.hold=10; ledfx.wait=300; break;
    case SLOW_SBLINK: ledfx.fade_in=30; ledfx.hold=100; ledfx.fade_out=220; ledfx.wait=1500; break;
    case FAST_SBLINK: ledfx.fade_in=20; ledfx.hold=20; ledfx.fade_out=60; ledfx.wait=250; break;
    default: return;
  }
  
  Serial.println("detach tact irq");
  if(irqInit) detachInterrupt(9);
  Serial.println("init rmt");
  led_init();

  if(ledfx.polling_mode)TICK_INC();
  ledfx.state=1; 
  xTimerStart(ledTimer,0);  
}
void led_init(void){
  
  if((rmt_send=rmtInit(LED_IO,true,RMT_MEM_64))==NULL)Serial.println("LED init failed\n"); 
  float realTick = rmtSetTick(rmt_send, 100);
  //Serial.printf("tick: %.02fns\n", realTick);  
  if(!ledfx.polling_mode && ledTimer==NULL) ledTimer = xTimerCreate("ledtimer", pdMS_TO_TICKS(LED_TICK), pdTRUE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(led_fxhandler));
}
void tact_init(){
  pinMode(9, INPUT_PULLUP);
  Serial.println("attach tact irq");
  attachInterrupt(9,tactIsr, FALLING);
  irqInit=1;
}
void setup(){

  Serial.begin(115200);
  while(!Serial);
  delay(1500);
  Serial.println("System ready");

  led_set(FAST_SBLINK,0.628,0.75,0.10,3);
}
int ledFlag=0;
void loop(){
  if(tactFlag){    
    Serial.println("tact handler");
    tactFlag=0;
    ledFlag=1;
  }
  if(ledFlag){  
    Serial.println("led blink handler"); 
    led_set(FAST_SBLINK,0.628,0.75,0.10,2);
    ledFlag=0;
  }
}

Debug Message

none

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions