rmt: better support rx demodulation

This commit is contained in:
morris 2020-03-17 19:47:38 +08:00
parent c91565d538
commit 855b316045
4 changed files with 98 additions and 116 deletions

View File

@ -56,12 +56,11 @@ typedef struct {
uint16_t idle_threshold; /*!< RMT RX idle threshold */
uint8_t filter_ticks_thresh; /*!< RMT filter tick number */
bool filter_en; /*!< RMT receiver filter enable */
#ifdef RMT_SUPPORT_RX_DEMODULATION
bool rm_carrier; /*!< RMT receiver remove carrier enable */
uint16_t high_thres; /*!< The threshold of carrier high level tick number */
uint16_t low_thres; /*!< The threshold of carrier low level tick number */
rmt_carrier_level_t carrier_level; /*!< The level need to remove carrier */
#if RMT_SUPPORT_RX_DEMODULATION
bool rm_carrier; /*!< RMT receiver remove carrier enable */
uint32_t carrier_freq_hz; /*!< RMT carrier frequency */
uint8_t carrier_duty_percent; /*!< RMT carrier duty (%) */
rmt_carrier_level_t carrier_level; /*!< The level to remove the carrier */
#endif
} rmt_rx_config_t;

View File

@ -486,7 +486,6 @@ static esp_err_t rmt_internal_config(rmt_dev_t *dev, const rmt_config_t *rmt_par
}
rmt_ll_set_mem_blocks(dev, channel, mem_cnt);
rmt_ll_set_mem_owner(dev, channel, RMT_MEM_OWNER_HW);
rmt_ll_enable_carrier(dev, channel, false); // disable carrier feature by default
RMT_EXIT_CRITICAL();
s_rmt_src_clock_hz[channel] = rmt_source_clk_hz;
@ -534,10 +533,15 @@ static esp_err_t rmt_internal_config(rmt_dev_t *dev, const rmt_config_t *rmt_par
rmt_ll_enable_rx_pingpong(dev, channel, true);
#endif
#ifdef RMT_SUPPORT_RX_DEMODULATION
rmt_ll_enable_rx_carrier_rm(dev, channel, rmt_param->rx_config.rm_carrier);
rmt_ll_set_rx_carrier_high_low_ticks(dev, channel, rmt_param->rx_config.high_thres, rmt_param->rx_config.low_thres );
rmt_ll_set_carrier_on_level(dev, channel, rmt_param->rx_config.carrier_level);
#if RMT_SUPPORT_RX_DEMODULATION
rmt_ll_enable_carrier(dev, channel, rmt_param->rx_config.rm_carrier);
if (rmt_param->rx_config.rm_carrier) {
uint32_t duty_total = rmt_source_clk_hz / rmt_ll_get_counter_clock_div(dev, channel) / rmt_param->rx_config.carrier_freq_hz;
uint32_t duty_high = duty_total * rmt_param->rx_config.carrier_duty_percent / 100;
// there could be residual in timing the carrier pulse, so double enlarge the theoretical value
rmt_ll_set_rx_carrier_high_low_ticks(dev, channel, duty_high * 2, (duty_total - duty_high) * 2);
rmt_ll_set_carrier_on_level(dev, channel, rmt_param->rx_config.carrier_level);
}
#endif
RMT_EXIT_CRITICAL();

View File

@ -14,6 +14,9 @@
// CI ONLY: Don't connect any other signals to this GPIO
#define RMT_DATA_IO (12) // bind signal RMT_SIG_OUT0_IDX and RMT_SIG_IN0_IDX on the same GPIO
#define RMT_TESTBENCH_FLAGS_ALWAYS_ON (1<<0)
#define RMT_TESTBENCH_FLAGS_CARRIER_ON (1<<1)
static const char *TAG = "RMT.test";
static ir_builder_t *s_ir_builder = NULL;
static ir_parser_t *s_ir_parser = NULL;
@ -23,13 +26,28 @@ static void rmt_setup_testbench(int tx_channel, int rx_channel, uint32_t flags)
// RMT channel configuration
if (tx_channel >= 0) {
rmt_config_t tx_config = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, tx_channel);
tx_config.flags = flags;
if (flags & RMT_TESTBENCH_FLAGS_ALWAYS_ON) {
tx_config.flags |= RMT_CHANNEL_FLAGS_ALWAYS_ON;
}
if (flags & RMT_TESTBENCH_FLAGS_CARRIER_ON) {
tx_config.tx_config.carrier_en = true;
}
TEST_ESP_OK(rmt_config(&tx_config));
}
if (rx_channel >= 0) {
rmt_config_t rx_config = RMT_DEFAULT_CONFIG_RX(RMT_DATA_IO, rx_channel);
rx_config.flags = flags;
if (flags & RMT_TESTBENCH_FLAGS_ALWAYS_ON) {
rx_config.flags |= RMT_CHANNEL_FLAGS_ALWAYS_ON;
}
#if RMT_SUPPORT_RX_DEMODULATION
if (flags & RMT_TESTBENCH_FLAGS_CARRIER_ON) {
rx_config.rx_config.rm_carrier = true;
rx_config.rx_config.carrier_freq_hz = 38000;
rx_config.rx_config.carrier_duty_percent = 33;
rx_config.rx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
}
#endif
TEST_ESP_OK(rmt_config(&rx_config));
}
@ -168,7 +186,7 @@ TEST_CASE("RMT install/uninstall test", "[rmt][pressure]")
}
}
TEST_CASE("RMT NEC TX and RX", "[rmt][timeout=240]")
static void do_nec_tx_rx(uint32_t flags)
{
RingbufHandle_t rb = NULL;
rmt_item32_t *items = NULL;
@ -179,57 +197,73 @@ TEST_CASE("RMT NEC TX and RX", "[rmt][timeout=240]")
int tx_channel = 0;
int rx_channel = 1;
uint32_t test_flags[] = {0, RMT_CHANNEL_FLAGS_ALWAYS_ON}; // test REF_TICK clock source
// test on different flags combinations
for (int run = 0; run < sizeof(test_flags) / sizeof(test_flags[0]); run++) {
rmt_setup_testbench(tx_channel, rx_channel, test_flags[run]);
rmt_setup_testbench(tx_channel, rx_channel, flags);
// get ready to receive
TEST_ESP_OK(rmt_get_ringbuf_handle(rx_channel, &rb));
TEST_ASSERT_NOT_NULL(rb);
TEST_ESP_OK(rmt_rx_start(rx_channel, true));
// get ready to receive
TEST_ESP_OK(rmt_get_ringbuf_handle(rx_channel, &rb));
TEST_ASSERT_NOT_NULL(rb);
TEST_ESP_OK(rmt_rx_start(rx_channel, true));
vTaskDelay(pdMS_TO_TICKS(1000));
vTaskDelay(pdMS_TO_TICKS(1000));
// build NEC codes
cmd = 0x20;
while (cmd <= 0x30) {
ESP_LOGI(TAG, "Send command 0x%x to address 0x%x", cmd, addr);
// Send new key code
TEST_ESP_OK(s_ir_builder->build_frame(s_ir_builder, addr, cmd));
TEST_ESP_OK(s_ir_builder->get_result(s_ir_builder, &items, &length));
if (cmd & 0x01) {
TEST_ESP_OK(rmt_write_items(tx_channel, items, length, false)); // no wait
TEST_ESP_OK(rmt_wait_tx_done(tx_channel, portMAX_DELAY));
} else {
TEST_ESP_OK(rmt_write_items(tx_channel, items, length, true)); // wait until done
}
cmd++;
// build NEC codes
cmd = 0x20;
while (cmd <= 0x30) {
ESP_LOGI(TAG, "Send command 0x%x to address 0x%x", cmd, addr);
// Send new key code
TEST_ESP_OK(s_ir_builder->build_frame(s_ir_builder, addr, cmd));
TEST_ESP_OK(s_ir_builder->get_result(s_ir_builder, &items, &length));
if (cmd & 0x01) {
TEST_ESP_OK(rmt_write_items(tx_channel, items, length, false)); // no wait
TEST_ESP_OK(rmt_wait_tx_done(tx_channel, portMAX_DELAY));
} else {
TEST_ESP_OK(rmt_write_items(tx_channel, items, length, true)); // wait until done
}
// parse NEC codes
while (rb) {
items = (rmt_item32_t *) xRingbufferReceive(rb, &length, 1000);
if (items) {
length /= 4; // one RMT = 4 Bytes
if (s_ir_parser->input(s_ir_parser, items, length) == ESP_OK) {
if (s_ir_parser->get_scan_code(s_ir_parser, &addr, &cmd, &repeat) == ESP_OK) {
ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04x cmd: 0x%04x", repeat ? "(repeat)" : "", addr, cmd);
}
}
vRingbufferReturnItem(rb, (void *) items);
} else {
ESP_LOGI(TAG, "done");
break;
}
}
TEST_ASSERT_EQUAL(0x30, cmd);
rmt_clean_testbench(tx_channel, rx_channel);
cmd++;
}
// parse NEC codes
while (rb) {
items = (rmt_item32_t *) xRingbufferReceive(rb, &length, 1000);
if (items) {
length /= 4; // one RMT = 4 Bytes
if (s_ir_parser->input(s_ir_parser, items, length) == ESP_OK) {
if (s_ir_parser->get_scan_code(s_ir_parser, &addr, &cmd, &repeat) == ESP_OK) {
ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04x cmd: 0x%04x", repeat ? "(repeat)" : "", addr, cmd);
}
}
vRingbufferReturnItem(rb, (void *) items);
} else {
ESP_LOGI(TAG, "done");
break;
}
}
TEST_ASSERT_EQUAL(0x30, cmd);
rmt_clean_testbench(tx_channel, rx_channel);
}
// basic nec tx and rx test, using APB source clock, no modulation
TEST_CASE("RMT NEC TX and RX (APB)", "[rmt]")
{
do_nec_tx_rx(0);
}
// test with RMT_TESTBENCH_FLAGS_ALWAYS_ON will take a long time (REF_TICK is much slower than APB CLOCK)
TEST_CASE("RMT NEC TX and RX (REF_TICK)", "[rmt][timeout=240]")
{
do_nec_tx_rx(RMT_TESTBENCH_FLAGS_ALWAYS_ON);
}
#if RMT_SUPPORT_RX_DEMODULATION
// basic nec tx and rx test, using APB source clock, with modulation and demodulation on
TEST_CASE("RMT NEC TX and RX (Modulation/Demodulation)", "[rmt]")
{
do_nec_tx_rx(RMT_TESTBENCH_FLAGS_CARRIER_ON);
}
#endif
TEST_CASE("RMT TX (RMT_CHANNEL_MEM_WORDS-1) symbols", "[rmt][boundary]")
{
int tx_channel = 0;
@ -312,61 +346,3 @@ TEST_CASE("RMT TX stop", "[rmt]")
TEST_ASSERT(num < count);
rmt_clean_testbench(tx_channel, rx_channel);
}
#ifdef RMT_SUPPORT_RX_DEMODULATION
/**
* @brief RMT demoudulation receiver initialization
*/
static void rx_demoudulation_init(void)
{
rmt_rx_config_t rx_cfg = {
.filter_en = true,
.filter_ticks_thresh = 100,
.idle_threshold = RMT_ITEM32_TIMEOUT_US / 10 * (RMT_TICK_10_US),
.rm_carrier = true,
.high_thres = 20,
.low_thres = 20,
.carrier_level = RMT_CARRIER_LEVEL_HIGH,
};
rmt_config_t rmt_rx = {
.channel = RMT_RX_CHANNEL,
.gpio_num = RMT_RX_GPIO_NUM,
.clk_div = RMT_CLK_DIV,
.mem_block_num = 1,
.rmt_mode = RMT_MODE_RX,
.rx_config = rx_cfg,
};
rmt_config(&rmt_rx);
rmt_driver_install(rmt_rx.channel, (sizeof(rmt_item32_t) * DATA_ITEM_NUM * (RMT_TX_DATA_NUM + 6)), 0);
}
TEST_CASE("RMT carrier TX and RX", "[rmt][test_env=UT_T1_RMT]")
{
rx_demoudulation_init();
RingbufHandle_t rb = NULL;
rmt_get_ringbuf_handle(RMT_RX_CHANNEL, &rb);
rmt_rx_start(RMT_RX_CHANNEL, 1);
ESP_LOGI(TAG, "Star receiving RMT data...");
tx_init();
rmt_set_tx_carrier(RMT_TX_CHANNEL, true, 1052, 1052, RMT_CARRIER_LEVEL_HIGH);
uint16_t cmd = 0x0;
uint16_t addr = 0x11;
int num_items = DATA_ITEM_NUM * RMT_TX_DATA_NUM;
rmt_item32_t *items = calloc(num_items + 1, sizeof(rmt_item32_t));
vTaskDelay(pdMS_TO_TICKS(2000));
ESP_LOGI(TAG, "Sending RMT data...");
// send data
set_tx_data(RMT_TX_CHANNEL, cmd, addr, num_items, items, 0);
// wait until tx done
rmt_write_items(RMT_TX_CHANNEL, items, num_items, 1);
free(items);
// receive data
uint16_t tmp = get_rx_data(rb);
TEST_ASSERT(tmp == RMT_TX_DATA_NUM);
TEST_ESP_OK(rmt_driver_uninstall(RMT_TX_CHANNEL));
TEST_ESP_OK(rmt_driver_uninstall(RMT_RX_CHANNEL));
}
#endif

View File

@ -148,7 +148,10 @@ In receive mode, set **rx_config** and the following members of :cpp:type:`rmt_r
* Enable a filter on the input of the RMT receiver - **filter_en**
* A threshold of the filter, set in the number of ticks - **filter_ticks_thresh**. Pulses shorter than this setting will be filtered out. Note, that the range of entered tick values is [0..255].
* A pulse length threshold that will turn the RMT receiver idle, set in number of ticks - **idle_threshold**. The receiver will ignore pulses longer than this setting.
:esp32s2: * Enable the RMT carrier demodulation - **carrier_rm**
:esp32s2: * Frequency of the carrier in Hz - **carrier_freq_hz**
:esp32s2: * Duty cycle of the carrier signal in percent (%) - **carrier_duty_percent**
:esp32s2: * Level of the RMT input, where the carrier is modulated to - **carrier_level**
Finalize Configuration
^^^^^^^^^^^^^^^^^^^^^^