Re: RMT - simplest receive example working, but idle_threshold problem
Posted: Wed Oct 09, 2019 1:29 pm
Thanks, I'll see if this works for my project. Cheers!
ESP32 Official Forum
https://esp32.com/
Code: Select all
rmt_item32_t* item = nullptr; // NULL
rmt_rx_start(RMT_CHANNEL_N, true);
for(;;)
{
item = (rmt_item32_t*) (RMT_CHANNEL_MEM(RMT_CHANNEL_N));
// Do math with item->duration0 and item->duration1
rmt_memory_rw_rst(RMT_CHANNEL_N);
vTaskDelay(DELAY_TIME_MS/portTICK_PERIOD_MS);
}
Code: Select all
#include "driver/rmt.h"
#include "soc/rmt_reg.h"
#include "freertos/task.h"
#include <driver/gpio.h>
#define IO_FI_1 GPIO_NUM_39
#define IO_FI_2 GPIO_NUM_36
#define IO_FI_3 GPIO_NUM_35
enum frequencyInput_t {FI_1, FI_2, FI_3 };
void frequencyInputBegin();
uint32_t getFrequency(frequencyInput_t index);
uint32_t getDutyCycle(frequencyInput_t index);
Code: Select all
#include <frequencyInput.hpp>
#define MIN(A,B) ((A)<=(B)?(A):(B))
#define MAX(A,B) ((A)>=(B)?(A):(B))
#define MEDIAN(A,B,C) ((B)<=(A)?(A):((B)>=(C)?(C):(B)))
#define FI_COUNT 3
typedef struct rmt_channel
{
rmt_channel_t channel;
gpio_num_t pin;
uint32_t frequency; //Hz x10
uint32_t dutyCycle; //% x10
} rmt_channel;
rmt_channel rmt_channels[FI_COUNT] = {
{
RMT_CHANNEL_0,
IO_FI_1,
0,
0
},
{
RMT_CHANNEL_1,
IO_FI_2,
0,
0
},
{
RMT_CHANNEL_2,
IO_FI_3,
0,
0
}
};
static void frequencyReadTask(void * pData)
{
int idleCount = 0;
while(1)
{ //infinite loop
for (int i = 0; i < FI_COUNT; i++)
{
uint32_t status = 0;
uint8_t state = 0;
rmt_get_status(rmt_channels[i].channel, &status);
state = (status & RMT_STATE_CH0) >> RMT_STATE_CH0_S;
//check for error states
if(status & RMT_MEM_OWNER_ERR_CH0)
{
/*
ownership of the memory block is violated
typically happens when frequency drops too low
*/
rmt_set_memory_owner(rmt_channels[i].channel, RMT_MEM_OWNER_RX);
rmt_channels[i].dutyCycle = 0;
rmt_channels[i].frequency = 0;
}
if(status & RMT_MEM_FULL_CH0)
{
/*
received more data that memory allows
typically happens if we capture too many pulses for our given sample time
*/
rmt_rx_memory_reset(rmt_channels[i].channel);
}
switch(state)
{
case 0x0: //idle
{
/*
Sometimes we get here if the frequency suddenly chops and the divider setting
hasn't kept up. Check if it's not default and reset it.
*/
uint8_t divider;
rmt_get_clk_div(rmt_channels[i].channel, ÷r);
if(divider < UINT8_MAX)
{
rmt_set_clk_div(rmt_channels[i].channel, UINT8_MAX);
rmt_rx_memory_reset(rmt_channels[i].channel);
}
if(idleCount++)
{
//came into the idle loop more than once in a row, assume 0 Hz
rmt_channels[i].dutyCycle = 0;
rmt_channels[i].frequency = 0;
}
break;
}
case 0x3: //receive
{
uint8_t cycleCount = status & 0xFF;
//check that we have more than one pulse captured to reduce jitter at low frequency
if(cycleCount > 1)
{
uint32_t highCount = 0;
uint32_t totalCount = 0;
uint32_t maxTick = 0;
uint8_t divider;
rmt_item32_t * item = (rmt_item32_t*) (RMT_CHANNEL_MEM(rmt_channels[i].channel));
//the RMT device may have captured multiple pulses, perform an average
for(int j = 1; j <= cycleCount; j++)
{
highCount += item->level0 ? item->duration0 : item->duration1;
totalCount += item->duration0 + item->duration1;
maxTick = MAX(maxTick, MAX(item->duration0, item->duration1));
item++;
}
highCount /= cycleCount;
totalCount /= cycleCount;
//capture the clock divider to calculate frequency and DC
rmt_get_clk_div(rmt_channels[i].channel, ÷r);
//offer some protection from garbage data with some basic checks
if(highCount && highCount != totalCount && totalCount > 2)
{
rmt_channels[i].dutyCycle =
static_cast<uint32_t>((1000u*highCount) / totalCount);
rmt_channels[i].frequency =
static_cast<uint32_t>((10.0f * APB_CLK_FREQ / divider) / totalCount);
/*
Recalculate the clock divider to stay near the target, this broadens the
frequency range we can accurately measure. Otherwise a low divider allows
us to accurately measure high frequencies, but limits our minimum. A high
divider lowers our minimum frequency, but reduces our accuracy at measuring
high frequencies.
*/
divider = MIN(maxTick * divider / 12000, UINT8_MAX);
divider = MAX(1, divider);
rmt_set_clk_div(rmt_channels[i].channel, divider);
}
//reset the memory to capture the next series of pulses
rmt_rx_memory_reset(rmt_channels[i].channel);
}
idleCount = 0;
break;
}
case 0x4: //wait
{
//rmt is stuck in wait set, change the idle threshold to 0 to reset it
int16_t temp = RMT.conf_ch[rmt_channels[i].channel].conf0.idle_thres;
RMT.conf_ch[rmt_channels[i].channel].conf0.idle_thres = 0;
RMT.conf_ch[rmt_channels[i].channel].conf0.idle_thres = temp;
idleCount = 0;
break;
}
case 0x1: //send
case 0x2: //read memory
default:
break;
}
}
vTaskDelay(pdMS_TO_TICKS(20));
}
}
void frequencyInputBegin()
{
/*
set up the RMT module to monitor the three frequency inputs
make use of two memory blocks per input so we can capture up to 128 cycles instead of just 64,
this halves how often we need to grab and empty the memory before risk of overflow
*/
for (int i = 0; i < FI_COUNT; i++)
{
rmt_config_t rmt_rx_config = {
.rmt_mode = RMT_MODE_RX,
.channel = rmt_channels[i].channel,
.gpio_num = rmt_channels[i].pin,
.clk_div = UINT8_MAX,
.mem_block_num = 2,
.flags = 0,
.rx_config = {
.idle_threshold = INT16_MAX,
.filter_ticks_thresh = UINT8_MAX,
.filter_en = false
}
};
assert(ESP_OK == rmt_config(&rmt_rx_config));
assert(ESP_OK == rmt_rx_start(rmt_channels[i].channel, true));
}
xTaskCreate (frequencyReadTask, "FIHandler", 2048, NULL, 4, NULL) ;
}
uint32_t getFrequency(frequencyInput_t index)
{
return rmt_channels[index].frequency;
}
uint32_t getDutyCycle(frequencyInput_t index)
{
return rmt_channels[index].dutyCycle;
}
Code: Select all
// main.cpp
#include <Arduino.h>
#include <Ticker.h>
#include "frequencyInput.hpp"
void setup()
{
Serial.begin(115200);
Serial.println("V0.1 310323");
// set pins to input
pinMode(GPIO_NUM_13, INPUT);
pinMode(GPIO_NUM_14, INPUT);
pinMode(GPIO_NUM_27, INPUT);
frequencyInputBegin();
}
void loop()
{
Serial.printf("Pin: 13 f: %lu d: %lu%% rawHighCount: %lu rawTotalCount: %lu divider; %hhu\n",
getFrequency(FI_1) / 10,
getDutyCycle(FI_1) / 10,
getRawHighCount(FI_1),
getRawTotalCount(FI_1),
getClkDivider(FI_1));
Serial.printf("Pin: 14 f: %lu d: %lu%% rawHighCount: %lu rawTotalCount: %lu divider; %hhu\n",
getFrequency(FI_2) / 10,
getDutyCycle(FI_2) / 10,
getRawHighCount(FI_2),
getRawTotalCount(FI_2),
getClkDivider(FI_2));
Serial.println("");
vTaskDelay(1000);
}
Code: Select all
// frequencyInput.hpp
#include "driver/rmt.h"
#include "soc/rmt_reg.h"
#include "freertos/task.h"
#include <driver/gpio.h>
#define IO_FI_1 GPIO_NUM_13
#define IO_FI_2 GPIO_NUM_14
#define IO_FI_3 GPIO_NUM_27
enum frequencyInput_t {FI_1, FI_2, FI_3 };
void frequencyInputBegin();
uint32_t getFrequency(frequencyInput_t index);
uint32_t getDutyCycle(frequencyInput_t index);
uint32_t getRawTotalCount(frequencyInput_t index);
uint32_t getRawHighCount(frequencyInput_t index);
uint8_t getClkDivider(frequencyInput_t index);
Code: Select all
// frequencyInput.cpp
#include <frequencyInput.hpp>
#define MIN(A, B) ((A) <= (B) ? (A) : (B))
#define MAX(A, B) ((A) >= (B) ? (A) : (B))
#define MEDIAN(A, B, C) ((B) <= (A) ? (A) : ((B) >= (C) ? (C) : (B)))
#define FI_COUNT 3
#define RMT_CLK_DIV 80 // 80 MHz
typedef struct rmt_channel
{
rmt_channel_t channel;
gpio_num_t pin;
uint32_t frequency; // Hz x10
uint32_t dutyCycle; //% x10
uint32_t rawHighCount; // raw High ticks
uint32_t rawTotalCount; // raw Low ticks
uint8_t divider; // actual channel devider
} rmt_channel;
rmt_channel rmt_channels[FI_COUNT] = {
{RMT_CHANNEL_0,
IO_FI_1,
0,
0,
0,
0,
0},
{RMT_CHANNEL_1,
IO_FI_2,
0,
0,
0,
0,
0},
{RMT_CHANNEL_2,
IO_FI_3,
0,
0,
0,
0,
0}};
static void frequencyReadTask(void *pData)
{
int idleCount = 0;
while (1)
{ // infinite loop
for (int i = 0; i < FI_COUNT; i++)
{
uint32_t status = 0;
uint8_t state = 0;
rmt_get_status(rmt_channels[i].channel, &status);
state = (status & RMT_STATE_CH0) >> RMT_STATE_CH0_S;
// check for error states
if (status & RMT_MEM_OWNER_ERR_CH0)
{
/*
ownership of the memory block is violated
typically happens when frequency drops too low
*/
rmt_set_memory_owner(rmt_channels[i].channel, RMT_MEM_OWNER_RX);
rmt_channels[i].dutyCycle = 0;
rmt_channels[i].frequency = 0;
}
if (status & RMT_MEM_FULL_CH0)
{
/*
received more data that memory allows
typically happens if we capture too many pulses for our given sample time
*/
rmt_rx_memory_reset(rmt_channels[i].channel);
}
switch (state)
{
case 0x0: // idle
{
/*
Sometimes we get here if the frequency suddenly chops and the divider setting
hasn't kept up. Check if it's not default and reset it.
*/
uint8_t divider;
rmt_get_clk_div(rmt_channels[i].channel, ÷r);
if (divider < UINT8_MAX)
{
rmt_set_clk_div(rmt_channels[i].channel, UINT8_MAX);
rmt_rx_memory_reset(rmt_channels[i].channel);
}
if (idleCount++)
{
// came into the idle loop more than once in a row, assume 0 Hz
rmt_channels[i].dutyCycle = 0;
rmt_channels[i].frequency = 0;
}
break;
}
case 0x3: // receive
{
uint8_t cycleCount = status & 0xFF;
// check that we have more than one pulse captured to reduce jitter at low frequency
if (cycleCount > 1)
{
uint32_t highCount = 0;
uint32_t totalCount = 0;
uint32_t maxTick = 0;
uint8_t divider;
rmt_item32_t *item = (rmt_item32_t *)(RMT_CHANNEL_MEM(rmt_channels[i].channel));
// the RMT device may have captured multiple pulses, perform an average
for (int j = 1; j <= cycleCount; j++)
{
highCount += item->level0 ? item->duration0 : item->duration1;
totalCount += item->duration0 + item->duration1;
maxTick = MAX(maxTick, MAX(item->duration0, item->duration1));
item++;
}
highCount /= cycleCount;
totalCount /= cycleCount;
// capture the clock divider to calculate frequency and DC
rmt_get_clk_div(rmt_channels[i].channel, ÷r);
// offer some protection from garbage data with some basic checks
if (highCount && highCount != totalCount && totalCount > 2)
{
rmt_channels[i].dutyCycle =
static_cast<uint32_t>((1000u * highCount) / totalCount);
rmt_channels[i].frequency =
static_cast<uint32_t>((10.0f * APB_CLK_FREQ / divider) / totalCount);
rmt_channels[i].rawHighCount =
static_cast<uint32_t>( highCount * divider / RMT_CLK_DIV); // highCount / (divider / 80)
rmt_channels[i].rawTotalCount =
static_cast<uint32_t>(totalCount * divider / RMT_CLK_DIV);
rmt_channels[i].divider =
static_cast<uint8_t>(divider);
/*
Recalculate the clock divider to stay near the target, this broadens the
frequency range we can accurately measure. Otherwise a low divider allows
us to accurately measure high frequencies, but limits our minimum. A high
divider lowers our minimum frequency, but reduces our accuracy at measuring
high frequencies.
*/
divider = MIN(maxTick * divider / 12000, UINT8_MAX);
divider = MAX(1, divider);
rmt_set_clk_div(rmt_channels[i].channel, divider);
}
// reset the memory to capture the next series of pulses
rmt_rx_memory_reset(rmt_channels[i].channel);
}
idleCount = 0;
break;
}
case 0x4: // wait
{
// rmt is stuck in wait set, change the idle threshold to 0 to reset it
int16_t temp = RMT.conf_ch[rmt_channels[i].channel].conf0.idle_thres;
RMT.conf_ch[rmt_channels[i].channel].conf0.idle_thres = 0;
RMT.conf_ch[rmt_channels[i].channel].conf0.idle_thres = temp;
idleCount = 0;
break;
}
case 0x1: // send
case 0x2: // read memory
default:
break;
}
}
vTaskDelay(pdMS_TO_TICKS(20));
}
}
void frequencyInputBegin()
{
/*
set up the RMT module to monitor the three frequency inputs
make use of two memory blocks per input so we can capture up to 128 cycles instead of just 64,
this halves how often we need to grab and empty the memory before risk of overflow
*/
for (int i = 0; i < FI_COUNT; i++)
{
rmt_config_t rmt_rx_config = {
.rmt_mode = RMT_MODE_RX,
.channel = rmt_channels[i].channel,
.gpio_num = rmt_channels[i].pin,
.clk_div = UINT8_MAX,
.mem_block_num = 2,
.flags = 0,
.rx_config = {
.idle_threshold = INT16_MAX,
.filter_ticks_thresh = UINT8_MAX,
.filter_en = false}};
assert(ESP_OK == rmt_config(&rmt_rx_config));
assert(ESP_OK == rmt_rx_start(rmt_channels[i].channel, true));
}
xTaskCreate(frequencyReadTask, "FIHandler", 2048, NULL, 4, NULL);
}
uint32_t getFrequency(frequencyInput_t index)
{
return rmt_channels[index].frequency;
}
uint32_t getDutyCycle(frequencyInput_t index)
{
return rmt_channels[index].dutyCycle;
}
uint32_t getRawHighCount(frequencyInput_t index)
{
return rmt_channels[index].rawHighCount;
}
uint8_t getClkDivider(frequencyInput_t index)
{
return rmt_channels[index].divider;
}
uint32_t getRawTotalCount(frequencyInput_t index)
{
return rmt_channels[index].rawTotalCount;
}
Code: Select all
_
pin13 | |________ p:1,4ms T:20ms
__
pin14 ___| |_______ P:1,6ms T:20ms
Pin: 13 f: 50 d: 6% rawHighCount: 1396 rawTotalCount: 19999 divider; 124
Pin: 14 f: 178 d: 86% rawHighCount: 4832 rawTotalCount: 5603 divider; 255
Pin: 13 f: 50 d: 6% rawHighCount: 1395 rawTotalCount: 19998 divider; 124
Pin: 14 f: 178 d: 86% rawHighCount: 4832 rawTotalCount: 5603 divider; 255
Pin: 13 f: 50 d: 6% rawHighCount: 1396 rawTotalCount: 19999 divider; 124
Pin: 14 f: 178 d: 86% rawHighCount: 4832 rawTotalCount: 5603 divider; 255