RMT - simplest receive example working, but idle_threshold problem

jcsbanks
Posts: 291
Joined: Tue Mar 28, 2017 8:03 pm

RMT - simplest receive example working, but idle_threshold problem

Postby jcsbanks » Fri Mar 23, 2018 12:20 pm

Code: Select all

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/rmt.h"
#include "driver/periph_ctrl.h"
#include "soc/rmt_reg.h"

void app_main()
{
	rmt_config_t rmt_rx =
	{
		.channel = 0,
		.gpio_num = 17,
		.clk_div = 80, //1MHz
		.mem_block_num = 1,
		.rmt_mode = RMT_MODE_RX,
		.rx_config.filter_en = true,
		.rx_config.filter_ticks_thresh = 100,
		.rx_config.idle_threshold = 9500
	};

	rmt_config(&rmt_rx);
	rmt_driver_install(rmt_rx.channel, 1000, 0);
	RingbufHandle_t rb = NULL;
	rmt_get_ringbuf_handle(rmt_rx.channel, &rb);
	rmt_rx_start(rmt_rx.channel, 1);

	while(rb)
	{
		size_t rx_size = 0;
		rmt_item32_t* item = (rmt_item32_t*) xRingbufferReceive(rb, &rx_size, 1000);
		if(item)
		{
			for (int i = 0; i < rx_size>>2; i++)
			{
				printf("%d:%dus %d:%dus\n", (item+i)->level0, (item+i)->duration0, (item+i)->level1, (item+i)->duration1);
			}
			vRingbufferReturnItem(rb, (void*) item);
		}
		else
		{
			break;
		}
	}
}
If I feed it a 75% duty cycle on pin 17 at 64Hz, I get the expected output except only the shorter 25% part of the duty cycle has a period value, and the longer 75% part is above the 9500us timeout:

Code: Select all

0:3906us 1:0us
0:3906us 1:0us
0:3907us 1:0us
0:3906us 1:0us
0:3906us 1:0us
0:3906us 1:0us
0:3906us 1:0us
0:3906us 1:0us
0:3906us 1:0us
0:3906us 1:0us
0:3907us 1:0us
I want to use 30000us timeout (which should be possible on a 15 bit counter at 1MHz) but whilst 11000us works, 12000us or above gives an error:

Code: Select all

E (2016) rmt: RMT[0] ERR
E (2016) rmt: status: 0x13000040
Surprisingly I get the similar error if I feed it 128Hz on pin 17, or if I increase the clk_div without reducing the idle threshold in proportion.

Code: Select all

E (1014) rmt: RMT[0] ERR
E (1014) rmt: status: 0x13000040
I would like the minimum input frequency to be detected as 33Hz, hence the 1MHz clock and intended 30000us idle threshold. I would like to measure the periods with microsecond resolution.

jcsbanks
Posts: 291
Joined: Tue Mar 28, 2017 8:03 pm

Re: RMT - simplest receive example working, but idle_threshold problem

Postby jcsbanks » Fri Mar 23, 2018 3:41 pm

Also tried: different GPIO, different RMT channel, different CPU speed, different FreeRTOS tick rates, enabled verbose logging and checked the register is being written correctly by reading it back.

The error with idle_threshold over a value between 11000 and 12000 only occurs when an input with a pulse train input connected. It seems that a valid second period below the idle_threshold is the problem.

I'm 2 days into this now and running out of ideas, but will post any more data.

jcsbanks
Posts: 291
Joined: Tue Mar 28, 2017 8:03 pm

Re: RMT - simplest receive example working, but idle_threshold problem

Postby jcsbanks » Fri Mar 23, 2018 5:17 pm

Without a ring buffer enabled, there is no problem, but I need to do some work to get an example going to obtain the data without the ring buffer. It seems that a receive end interrupt with a zero period will not be suitable as that would be an error condition in my situation where I want to continuously measure the period and frequency of a pulse train.

jcsbanks
Posts: 291
Joined: Tue Mar 28, 2017 8:03 pm

Re: RMT - simplest receive example working, but idle_threshold problem

Postby jcsbanks » Fri Mar 23, 2018 7:57 pm

This really quick and dirty example works to read periods and allows expected setting of idle_threshold. It does not use the ring buffer, but simply reads the first entry in the receive buffer and resets, so the task delay and frequency are entangled.

Code: Select all

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/rmt.h"
#include "driver/periph_ctrl.h"
#include "soc/rmt_reg.h"

void app_main()
{
    rmt_config_t rmt_rx =
	{
		.channel = 0,
		.gpio_num = 18,
		.clk_div = 80, //1MHz
		.mem_block_num = 1,
		.rmt_mode = RMT_MODE_RX,
		.rx_config.filter_en = true,
		.rx_config.filter_ticks_thresh = 255,
		.rx_config.idle_threshold = 65535
	};
	rmt_config(&rmt_rx);
	while (1)
	{
		rmt_rx_start(rmt_rx.channel, 1);
		vTaskDelay(100 / portTICK_PERIOD_MS);
		rmt_item32_t* item = (rmt_item32_t*) (RMT_CHANNEL_MEM(rmt_rx.channel));
		printf("high %dus | ", (item)->level0*(item)->duration0 + (item)->level1*(item)->duration1);
		printf("low %dus\n", !(item)->level0*(item)->duration0 + !(item)->level1*(item)->duration1);
	}
}

jcsbanks
Posts: 291
Joined: Tue Mar 28, 2017 8:03 pm

Re: RMT - simplest receive example working, but idle_threshold problem

Postby jcsbanks » Wed Mar 28, 2018 8:01 pm

After many more days work on this, I think that the errors I was seeing were buffer full based on experimentation with my own 5ms task to process the RMT receive buffer and the error codes I get. The error codes are undocumented.

If the ring buffer were able to be implemented in a task instead of an interrupt, it could work, but raising the idle_threshold above the period of the incoming pulse means an interrupt is not triggered.

In other words, the ring buffer cannot be used for continuous pulse trains (that are longer than the RMT receive buffer).

Neuromodulator
Posts: 3
Joined: Fri Sep 06, 2019 3:06 am

Re: RMT - simplest receive example working, but idle_threshold problem

Postby Neuromodulator » Fri Sep 06, 2019 3:35 am

Did you fix this?

jcsbanks
Posts: 291
Joined: Tue Mar 28, 2017 8:03 pm

Re: RMT - simplest receive example working, but idle_threshold problem

Postby jcsbanks » Fri Sep 06, 2019 6:14 pm

Yes by having a task empty the buffer. RMT doesn't work with continuous pulse trains otherwise that I could work out.

Thildemar
Posts: 3
Joined: Mon Oct 07, 2019 4:22 pm

Re: RMT - simplest receive example working, but idle_threshold problem

Postby Thildemar » Tue Oct 08, 2019 3:14 pm

Any chance you could share the code you are using to read the continuous stream? I think I am running into the same issue, but not very clear on the rmt receive without the ring buffer (as documentation is a little vague)...

Thanks!

jcsbanks
Posts: 291
Joined: Tue Mar 28, 2017 8:03 pm

Re: RMT - simplest receive example working, but idle_threshold problem

Postby jcsbanks » Wed Oct 09, 2019 8:20 am

Init, sorry the tabs don't display correctly. BUFS and INPUT need to be defined.

Code: Select all

    rmt_config_t rmt_rx =
	{
		.channel = 0,
		.gpio_num = INPUT,
		.clk_div = 80, //1MHz
		.mem_block_num = BUFS,
		.rmt_mode = RMT_MODE_RX,
		.rx_config = {.filter_en = true, .filter_ticks_thresh = 255, .idle_threshold = 30000}
	};
    rmt_config(&rmt_rx);
	rmt_item32_t* item = (rmt_item32_t*)(RMT_CHANNEL_MEM(rmt_rx.channel));
	item->val=-1;
	rmt_rx_start(rmt_rx.channel, 1);

jcsbanks
Posts: 291
Joined: Tue Mar 28, 2017 8:03 pm

Re: RMT - simplest receive example working, but idle_threshold problem

Postby jcsbanks » Wed Oct 09, 2019 8:31 am

Inside a task on a loop:

Code: Select all

		while (item->val==-1)
		{
			if (RMT.status_ch[rmt_rx.channel]==0x04000000){
				//overflow action
			}
		}
Receiving the buffer contents:

Code: Select all

		for(i = 0; i < (uint8_t)RMT.status_ch[rmt_rx.channel]; i++)
		{
			lowus = !(item+i)->level0*(item+i)->duration0 + !(item+i)->level1*(item+i)->duration1;
			highus = (item+i)->level0*(item+i)->duration0 + (item+i)->level1*(item+i)->duration1;
			items++;
			//do something with pulse widths
		}
Empty buffer:

Code: Select all

		if (items)
		{
			item->val=-1;
			rmt_memory_rw_rst(rmt_rx.channel);
		}
Sorry cannot give complete standalone example, it is a closed source project, but these snippets are generic enough to be OK to share. Code isn't pretty but it works. There could be situations where a sample is missed between the loop and the reset, but I don't mind that in my application as I'm averaging. Just use as an example, needs heavy mods for your needs.

Who is online

Users browsing this forum: No registered users and 18 guests