How reset rmt memory?

clarkster
Posts: 47
Joined: Sat Sep 23, 2017 12:36 pm

How reset rmt memory?

Postby clarkster » Mon Dec 11, 2017 10:05 pm

I am working on a stepper motor driver using the rmt peripheral. I am certain the rmt peripheral will do what I need, I almost have a working driver, but I am having a problem I don't quite know how to handle. My code is based on the rmt_nec_tx_rx example project.

Like the example, I have a freeRTOS task in which I first build an array of rmt_item32_t objects based on the number of steps (and speed) I want to stepper to execute. Then I use rmt_write_items to write the rmt memory. This causes the rmt peripheral to begin generating the pulse train. At this point my program diverges from the example.

Here is the code for my task...

Code: Select all

static void stepper_tx_task() {
	stepper_cmd_t cmd;															
	rmt_item32_t* rmt_item = NULL;

	while (1) {
		BaseType_t res = xQueueReceive(stepper_cmd_queue, &cmd, 
			portMAX_DELAY); //5000 / portTICK_PERIOD_MS);
		int channel = cmd.channel;
		int rpm = cmd.rpm;
		int number_steps = cmd.number_steps;
		int direction = cmd.direction;
		
		if (res == pdTRUE) {
			rmt_tx_stop(channel);

			//free(rmt_item);
			//rmt_item = NULL;

			if (number_steps == 0) {
				rmt_tx[channel].tx_config.loop_en = true;
				number_steps = 1;
			}
			
			gpio_set_level(direction_gpio[channel], direction);

			uint32_t pulse_period = (uint32_t) 1000000 /(rpm / 60 * 200);
			size_t size = (sizeof(rmt_item32_t)) * number_steps;
			rmt_item = (rmt_item32_t*) malloc(size);							
			memset((void*) rmt_item, 0, size);

			for(int i = 0; i < number_steps; i++) { 					
				fill_stepper_pulse(rmt_item + i, pulse_period);
			}

			esp_err_t err = rmt_write_items(channel, rmt_item, number_steps, false);
		}
	}
	vTaskDelete(NULL);
}
As shown above, I set the wait_tx_done flag to false so that the rmt_write_items returns immediately. I do this because I might later want to issue another command to the stepper, even before the rmt peripheral has completed sending the pulses specified by the first command. If a second command is sent, the rmt needs to stop generating pulses based on the first command and begin generating pulses based on the more up to date command. For this reason the task cannot block while executing rmt_write_items and the wait_tx_done flag must be set to false. But when I do this I experience two problems.

First of all, the code still blocks at rmt_write_items. Looking at rmt.c, I can see this is because rmt_write_items waits for a semaphore that is only issued when the rmt transmitter is done sending pulses (TX END) in the rmt_driver_isr_default function. I believe this might be a bug. If I comment out the SemaphoreTake in rmt_write_items things work much better - the function no longer blocks. The wait_tx_done flag now works properly when set to false.

At this point the program works pretty well. It works perfectly if I let the rmt finish sending pulses before issuing another stepper command.

And it works pretty well if I issue a command before the rmt has completed sending its pulses. But there is one problem, there are a bunch of garbage pulses at the end of the pulse train that the rmt was supposed to send.

I notice rmt.h says
If wait_tx_done is set to false, this function will return immediately, and the driver interrupt will continue sending the items. We must make sure the item data will not be damaged when the driver is still sending items in driver interrupt.
I suspect that I am not doing something I need to do to reset rmt memory when sending a new command while the rmt is still issuing pulses. Or maybe the driver needs to be modified to accommodate this use case.

I am going to dig further into rmt.c to try to determine what is going wrong, but I am hoping someone with more experience with rmt.c might be able to more quickly find a good solution. If anyone needs it, I can provide more of my code.

Who is online

Users browsing this forum: No registered users and 96 guests