RMT input on ESP32-S3 not compatible with ESP32 code
Posted: Mon May 15, 2023 5:14 am
<edit>
There doesn't seem to be an example sketch for simple RMT input for measuring PWM period, specifically for ESP32-S3 boards. Not only would that solve my issue, but would allow more developers to use RMT peripheral. Looking for help getting ESP32-S3's RMT to measure and report on the period of a PWM signal.
</edit>
I've been working on a logger/controller for a remote control airplane. In order to work as I intend, it needs to be able to read the PWM commands going to servos. These are standard 50hz servos, where the duty cycle is generally 1ms to 2ms. I had the code working with an original ESP32 (using the M5Stack Atom Lite board for now), which I found here https://newscrewdriver.com/2021/04/05/n ... ng-rc-pwm/ (actual code link is here: https://github.com/JustinOng/sumo/blob/ ... gure_rmt.c). I was using Arduino IDE (1.8.19) with the ESP32 board package, build 1.0.5.
Recently, i decided to upgrade to ESP32-S3 boards. I updated my ESP32 package to version 2.0.9, because the 1.0.X didn't have support for the S3 boards. Sure enough, my code wouldn't even compile. After some trial and error, i was able to modify the code with "#ifdef"s so that it would compile the "original" way for ESP32, and with tweaked code for ESP32-S3. Problem is, it now compiles for ESP32-S3, but doesn't actually detect any pulses.
Below is the code for simply readying the PWM signal and printing it to Serial. Can anyone help? I don't fully understand what the RMT commands are doing, and that's where my problem is (I think). Thank you!!!
-Ian-
There doesn't seem to be an example sketch for simple RMT input for measuring PWM period, specifically for ESP32-S3 boards. Not only would that solve my issue, but would allow more developers to use RMT peripheral. Looking for help getting ESP32-S3's RMT to measure and report on the period of a PWM signal.
</edit>
I've been working on a logger/controller for a remote control airplane. In order to work as I intend, it needs to be able to read the PWM commands going to servos. These are standard 50hz servos, where the duty cycle is generally 1ms to 2ms. I had the code working with an original ESP32 (using the M5Stack Atom Lite board for now), which I found here https://newscrewdriver.com/2021/04/05/n ... ng-rc-pwm/ (actual code link is here: https://github.com/JustinOng/sumo/blob/ ... gure_rmt.c). I was using Arduino IDE (1.8.19) with the ESP32 board package, build 1.0.5.
Recently, i decided to upgrade to ESP32-S3 boards. I updated my ESP32 package to version 2.0.9, because the 1.0.X didn't have support for the S3 boards. Sure enough, my code wouldn't even compile. After some trial and error, i was able to modify the code with "#ifdef"s so that it would compile the "original" way for ESP32, and with tweaked code for ESP32-S3. Problem is, it now compiles for ESP32-S3, but doesn't actually detect any pulses.
Below is the code for simply readying the PWM signal and printing it to Serial. Can anyone help? I don't fully understand what the RMT commands are doing, and that's where my problem is (I think). Thank you!!!
-Ian-
Code: Select all
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/rmt.h"
#include "esp_log.h"
static const char* TAG = "RMT";
//define the pins to use
#if ARDUINO_M5Stack_ATOM
#define channel_0_input_pin 21 //unused pin on M5Atom-GPS module
#define channel_1_input_pin 25 //unused pin on M5Atom-GPS module
#define spareInputPin 2 //this pin is not actually correct. just a placeholder
#elif ARDUINO_M5Stack_ATOMS3 //THIS IS AN ESP32-S3 BASED BOARD
#define channel_0_input_pin 39 //unused pin on M5Atom-GPS module (with AtomS3-lite installed)
#define channel_1_input_pin 38 //unused pin on M5Atom-GPS module (with AtomS3-lite installed)
#define spareInputPin 14 //this pin is NOT actually correct. Just a placeholder
#endif
#define RECEIVER_CHANNELS_NUM 3
volatile uint16_t ReceiverChannels[RECEIVER_CHANNELS_NUM] = {0};
const uint8_t RECEIVER_CHANNELS[RECEIVER_CHANNELS_NUM] = { 1, 2, 3 };
const uint8_t RECEIVER_GPIOS[RECEIVER_CHANNELS_NUM] = { channel_0_input_pin, channel_1_input_pin, spareInputPin };
void setup() {
Serial.begin(115200);
ESP_LOGI(TAG, "Started");
rmt_init(); //initialize the PWM monitor
}
void loop() {
Serial.print("CH0=");
Serial.print(ReceiverChannels[0]/8);
Serial.print("us \t CH1=");
Serial.print(ReceiverChannels[1]/8);
Serial.print("us \t CH2=");
Serial.print(ReceiverChannels[2]/8);
Serial.println("us");
}
// receiver pulse length ranges from 8000 to 16000 centered around 12000 (units: ticks)
#define RECEIVER_CH_MIN 8000
#define RECEIVER_CH_CENTER 12000
#define RECEIVER_CH_MAX 16000
//note that the actual duty cycle is between 1000 and 2000 microseconds, so we're dividing by 8 to get the value we want
#define RMT_TICK_PER_US 8
// determines how many clock cycles one "tick" is
// [1..255], source is generally 80MHz APB clk
#define RMT_RX_CLK_DIV (80000000/RMT_TICK_PER_US/1000000)
// time before receiver goes idle
#define RMT_RX_MAX_US 3500
static void rmt_isr_handler(void* arg){
// with reference to https://www.esp32.com/viewtopic.php?t=7116#p32383
// but modified so that this ISR only checks chX_rx_end
uint32_t intr_st = RMT.int_st.val;
// see declaration of RMT.int_st:
// takes the form of
// bit 0: ch0_tx_end
// bit 1: ch0_rx_end
// bit 2: ch0_err
// bit 3: ch1_tx_end
// bit 4: ch1_rx_end
// ...
// thus, check whether bit (channel*3 + 1) is set to identify
// whether that channel has changed
uint8_t i;
for(i = 0; i < RECEIVER_CHANNELS_NUM; i++) {
uint8_t channel = RECEIVER_CHANNELS[i];
uint32_t channel_mask = BIT(channel*3+1);
if (!(intr_st & channel_mask)) continue;
#ifdef CONFIG_IDF_TARGET_ESP32S3 //NEW CODE FOR ESP32-S3 BOARD
//if it's an S3 board, use different terminology
RMT.chmconf[channel].conf1.rx_en_m = 0; //NEW CODE FOR ESP32-S3 BOARD
RMT.chmconf[channel].conf1.mem_owner_m = RMT_MEM_OWNER_TX; //NEW CODE FOR ESP32-S3 BOARD
#else
RMT.conf_ch[channel].conf1.rx_en = 0;
RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_TX;
#endif
volatile rmt_item32_t* item = RMTMEM.chan[channel].data32;
if (item) {
ReceiverChannels[i] = item->duration0;
}
#ifdef CONFIG_IDF_TARGET_ESP32S3 //NEW CODE FOR ESP32-S3 BOARD
//if it's an S3 board, use different terminology. Updated code made using a mixture of guessing, and the following files/links:
// (ESP32 reference: https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/rmt_struct.h)
// (ESP32S3 compare: https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/rmt_struct.h)
// C:\Users\<USERNAME>\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.9\tools\sdk\esp32s3\include\soc\esp32s3\include\soc\rmt_struct.h
// C:\Users\<USERNAME>\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.9\tools\sdk\esp32s3\include\hal\esp32s3\include\hal\rmt_ll.h
RMT.chmconf[channel].conf1.mem_wr_rst_m = 1; //NEW CODE FOR ESP32-S3 BOARD
RMT.chmconf[channel].conf1.mem_owner_m = RMT_MEM_OWNER_RX; //NEW CODE FOR ESP32-S3 BOARD
RMT.chmconf[channel].conf1.rx_en_m = 1; //NEW CODE FOR ESP32-S3 BOARD
#else
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_RX;
RMT.conf_ch[channel].conf1.rx_en = 1;
#endif
//clear RMT interrupt status.
RMT.int_clr.val = channel_mask;
}
}
void rmt_init(void) {
uint8_t i;
rmt_config_t rmt_channels[RECEIVER_CHANNELS_NUM] = {};
for (i = 0; i < RECEIVER_CHANNELS_NUM; i++) {
ReceiverChannels[i] = RECEIVER_CH_CENTER;
rmt_channels[i].channel = (rmt_channel_t) RECEIVER_CHANNELS[i];
rmt_channels[i].gpio_num = (gpio_num_t) RECEIVER_GPIOS[i];
rmt_channels[i].clk_div = RMT_RX_CLK_DIV;
rmt_channels[i].mem_block_num = 1;
rmt_channels[i].rmt_mode = RMT_MODE_RX;
rmt_channels[i].rx_config.filter_en = true;
rmt_channels[i].rx_config.filter_ticks_thresh = 100;
rmt_channels[i].rx_config.idle_threshold = RMT_RX_MAX_US * RMT_TICK_PER_US;
rmt_config(&rmt_channels[i]);
rmt_set_rx_intr_en(rmt_channels[i].channel, true);
rmt_rx_start(rmt_channels[i].channel, 1);
}
rmt_isr_register(rmt_isr_handler, NULL, 0, NULL);
ESP_LOGI(TAG, "Init ISR on %d", xPortGetCoreID());
}