Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
0x0fe opened this issue Mar 1, 2022 · 3 comments · Fixed by #6369
Closed
1 task done

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

0x0fe opened this issue Mar 1, 2022 · 3 comments · Fixed by #6369
Assignees
Milestone

Comments

@0x0fe
Copy link

0x0fe commented Mar 1, 2022

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.
@0x0fe 0x0fe added the Status: Awaiting triage Issue is waiting for triage label Mar 1, 2022
@SuGlider SuGlider self-assigned this Mar 1, 2022
@0x0fe
Copy link
Author

0x0fe commented Mar 2, 2022

a better test case

#include "Arduino.h"

#define TACT_LED_IO       9

#define LED_QTY           1
#define BITS              24*LED_QTY
rmt_data_t led_data[BITS];
rmt_obj_t* rmt_send=NULL;
volatile int tactFlag=0,irqInit=0;

void IRAM_ATTR tactIsr(){
    tactFlag=1;
}  

void led_set(int r,int g,int b){
  
  int color[3]={r,g,b};
  int led=0, col, bit, i=0;
  
  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++;
    }
  }
  Serial.printf("RMT write %d %d %d\n",color[0],color[1],color[2]);
  rmtWrite(rmt_send, led_data, BITS);
}

void led_blink(int r,int g,int b,int ms){

  Serial.println("IRQ detach");
  if(irqInit) detachInterrupt(TACT_LED_IO);
  delay(5);
  led_init();
    
  led_set(r,g,b);
  delay(ms);
  led_set(0,0,0);
  delay(5);
  Serial.println("RMT De init");
  rmtDeinit(rmt_send);
  tact_init(); 
}

void led_init(void){
  
  Serial.println("RMT Init");
  if((rmt_send=rmtInit(TACT_LED_IO,true,RMT_MEM_64))==NULL)Serial.println("RMT init failed\n"); 
  float realTick = rmtSetTick(rmt_send, 100);
}

void tact_init(){
  
  pinMode(TACT_LED_IO, INPUT_PULLUP);
  Serial.println("IRQ attach");
  attachInterrupt(TACT_LED_IO,tactIsr,FALLING);
  irqInit=1;
}

void setup(){

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

  tact_init();
}

void loop(){
  
  if(tactFlag){  
    Serial.println("tact handler");
    while(!digitalRead(TACT_LED_IO))delay(1);
    led_blink(127,10,75,50);
    tactFlag=0;
  }
}

@SuGlider
Copy link
Collaborator

SuGlider commented Mar 2, 2022

@0x0fe

Thanks for reporting the issue!

Issue solved with PR#6369.
There was a missing line to set lock pointer to NULL in rmtDeinit().

I tested the sketch you provided with the S2 and C3. Now it works fine.
I also eliminated the issue 1. rmtDeinit throws an error when used in TX only mode, i suspect stopping the (non exsting) RX channel is the cause.

@0x0fe
Copy link
Author

0x0fe commented Mar 3, 2022

thank you for the update and fix !

@0x0fe 0x0fe closed this as completed Mar 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging a pull request may close this issue.

2 participants