FreeRTOS in atmel studio 6, PDC problem. Arduino Due Board. TWI problem.

mlundh wrote on Sunday, December 29, 2013:

Hello,

I am working on a project where i am using the FreeRTOS port provided in Atmel ASF. I run multiple tasks that uses different peripherals.

My plan was to use both TWI peripherals provided on the sam3x processor at the same time. The busses would be used from the same task and the transmissions started right after each other with only the slight delay of setting up the PDC controller between the start of the transmissions. Each of the buses communicates with two identical (except off course for the chip address) ESC units.

That was the background, now to my problem: When i do what i have described above, it only works for a few transmissions and then one of the buses stop working. I used my logic level analyzer to observe the bus behavior, and i can see that the two TWI buses do transmit a few messages simultaneously only to stop working after a short while. If i change the code so that I wait for each transfer to complete before initializing a TWI transfer on the other bus, then everything works well. This, however, requires twice the amount of time to complete all transfers.

I am currently using the following code to initiate a transfer(I am looping through all motors):

portBASE_TYPE twi_ans;
twi_ans = freertos_twi_write_packet_async(motors->motor[i].twi,
    &motors->motor[i].twi_data,
    motors->motor[i].xtransmit_block_time,
    motors->motor[i].twi_notification_semaphore);

if (twi_ans != STATUS_OK)
{
    //for (;;)
    {
        //Error! 
        toggle_pin(8);
    }

}

// TODO fix TWI so this is not needed!
xSemaphoreTake(motors->motor[i].twi_notification_semaphore,
motors->motor[i].xtransmit_block_time);

Here I am waiting for the transmission to complete before doing anything else. I would rather wait for the semaphore associated with the peripheral before trying to start the peripheral again.

This is only a problem since i have a very fast application (500-1000Hz) and i would like to use both buses to reduce communication time.

Has anyone else encountered this problem? Has this anything to do with freeRTOS or the FreeRTOS peripheral drivers provided in ASF or is this a hardware limitation/problem?

Also, I think that there is a problem with the configure_interrupt_controller function in the ASF verstion of FreeRTOS, I think that it is not configured to work with Cortex-M3/Coretx-M4 priority systems. I had to make a change to allow numerically higher values than configMAX_SYSCALL_INTERRUPT_PRIORITY, wich if i have understood things correctly, is a lower priority for these types of processors. Can anyone confirm this?

Regards
Martin Lundh

rtel wrote on Monday, December 30, 2013:

I think the two channels should be completely independent as each has its own PDC registers, so I’m not sure why it would stop. I would have to re-visit the code to see for sure. To be honest I’m not sure if I have ever run two channels at once myself.

I presume you are using different semaphores for the two channels (rather than maybe accidentally using the same semaphore for both)?

configure_interrupt_controller

I just had a look at this function and it seems to just pass through whichever interrupt priority you set in “interrupt_priority” member of the “freertos_driver_parameters” parameter you pass into freertos_twi_master_init(). It uses the CMSIS NVIC_SetPriority() function, so expects a value from 0 (highest priority) to 0x0f (lowest priority).

Regards.

mlundh wrote on Wednesday, January 01, 2014:

Thank you for the fast response!

I thought that the two channels would be completely independent too, so I am a bit confused by the behavior i am seeing. It seems like i have similar problems when using other peripherals via PDC too, if i have a task sending data rapidly on a USART channel, sometimes the TWI stops. I have to do some more tests before i can say with any certainty that the two are related.

Do you think that it could be a problem accessing the PDC control from multiple threads simultaneously? It was a thought i had when i first started looking in to the problem, but after reading the datasheet for the processor, and checking the code, it seems less and less likely…

I just double checked, and I am using different semaphore for each channel, so i do not think that is the problem.

Regarding the configure_interrupt_controller, i saw that there is the following code in the ASF version I have:
configASSERT(interrupt_priority <=
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
After that it just uses the CMSIS NVIC_SetPriority() function as you say.

Regards
Martin Lundh

rtel wrote on Wednesday, January 01, 2014:

Are you using the same interrupt priority for both TWI channels? If that makes a difference it will give a clue as to where to look in the code.

Also, are you using FreeRTOS V7.6.0 with configASSERT() defined? That will help trap any interrupt priority setting problems.

Regards.

mlundh wrote on Saturday, January 04, 2014:

I have now tried to use both equal and different priories and it does not make a difference.

Just to make sure: The peripheral drivers should have a priority lower than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY (higher number, for example: configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 4)?

I am using FreeRTOS V7.3.0 (from ASF) with ConfigASSERT() defined. But i am not sure the assertion in configure_interrupt_controller is correct for use with Cortex M3 processors… it stops execution if the priority is numerically higher than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY.

I have disabled the assertion and tried priories both higher and lower than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY, and surprisingly, it all gives the same result, even if the drivers are given the highest priority(i know this is wrong to do, but i wanted to try) numerically value 0.

If I instead change the order at which i write to the channels(starting transfer on channel 1 and then on channel 2 instead of the other way around) i get a different timing behavior, the same channel does fail after a while though.

Regards
Martin

rtel wrote on Saturday, January 04, 2014:

I have created the demo project that comes in the ASF and can look through the code. So far everything I have seen looks as expected - each channel has its own PDC channel and its own semaphores.

Please post the freertos_peripheral_options_t structure definition you are passing into each freertos_twi_master_init() call (the definition of the second parameter passed into the freertos_twi_master_init() function) so I can check your specific scenario.

Just to make sure: The peripheral drivers should have a priority lower than
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY (higher number, for example:
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 4)?

Almost - the peripheral drivers should have a priority lower than **or** equal to configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY. Yes, higher numbers are lower priorities so configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 4 is ok provided (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 4) is less than 15, because 15 is the very lowest valid priority.

I am using FreeRTOS V7.3.0 (from ASF) with ConfigASSERT() defined.

You can drop in the FreeRTOS V7.6.0 files to make better use of the configASSERT() macro. Additional error checks were put in since V7.3.0 to assist users as the interrupt model on Cortex-M parts is understandably confusion.

it stops execution if the priority is numerically higher than
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY.

Really? That does not seem right. Looking at the code I see the following line:

	configASSERT(interrupt_priority <=
			configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);

which would seem to pass if the interrupt priority was equal to or numerically lower than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY, and therefore fail if the interrupt priority was numerically higher than configMAX_SYSCALL_INTERRUPT_PRIORITY - so I’m guessing that is the line you are referring to. I think it should be >=

Regards.

rtel wrote on Saturday, January 04, 2014:

Have a look at http://asf.atmel.com/bugzilla/show_bug.cgi?id=3233

Regards.

mlundh wrote on Friday, January 10, 2014:

Hi,

First of all, thanks for all the help!

I read the bug report and made some changes to the freeRTOS TWI handler and it seems to work much better now. I have checked the timing of the different interrupts, and it looks alright.

I do still have the occasional glitch on the bus, but I think that is a hardware problem rather than anything else.

The freertos_peripheral_options_t structure I am using is posted below. It should be OK I think after the conversation in this thread, and the fact that it does seem to work now.

freertos_peripheral_options_t async_driver_options = {
    NULL,											                        /* This peripheral does not need a receive buffer, so this parameter is just set to NULL. */
    0,												                        /* There is no Rx buffer, so the rx buffer size is not used. */
    (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1),	                        /* The priority used by the TWI interrupts. It is essential that the priority does not have a numerically lower value than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY*/
    TWI_I2C_MASTER,									                        /* TWI is configured as an I2C master. */
    0,																		/* The asynchronous driver is used, so WAIT_TX_COMPLETE and WAIT_RX_COMPLETE are not set. */
};

I consider my problem solved (remaining problem has most likely no relation to freeRTOS at all). Thank you for your support, and for a great software!

Regards
Martin

rtel wrote on Friday, January 10, 2014:

Glad that solved the problem. I would be grateful if you could post the edited driver file here so I can ensure it gets updated in the driver library too.

Regards.

mlundh wrote on Sunday, January 12, 2014:

Sure! As soon as I have removed some debugging code I used, I will be happy to post it. This will hopefully not take to long!

Regards.

apsolar wrote on Tuesday, January 28, 2014:

Hello Martin,

Would you be willing to share your solution? I noticed you are not setting any option flags. I am struggling to get the TWI implementation working reliably on a Sam3N4B. The read works sometimes and doesn’t most of the time. I think that the issue is related to what is mentioned here.

To simplify things, all I am doing is single byte reads and I notice that the interrupt is never triggered.

regards
Ankit

mlundh wrote on Saturday, February 08, 2014:

Hi,

I am sorry it has taken me some time to get back to this problem, I was away on businesses. I have, since i came back, tested the code, and it seems to work well for me. I believe that my remaining problems are hardware related. I would be grateful if someone could test the code and provide feedback.

I have found that if there is an error on the bus(for whatever reason), it sometimes takes too long before it becomes operational again. I think better error handling might be needed, but om not sure how to implement this. This could also be hardware dependent, I am not sure yet, I will keep on working though!

In the attached file i have made the changes discussed above. Note that I have also changed the driver to only use one transaction_complete_semaphore just as only one mutex is used. Since the bus is half duplex, there is no need for a second semaphore. The second semaphore also created some problems since the code now uses the TXCOMP interrupt.

Also, feel free to remove/change any comments i made in the code, they are only there to remind me what changed I made.

In freertos_peripheral_control.c I made the following changes:

void configure_interrupt_controller(const enum IRQn peripheral_irq,
		uint32_t interrupt_priority)
{
	configASSERT(interrupt_priority >=
			configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);

	NVIC_ClearPendingIRQ(peripheral_irq);
	NVIC_SetPriority(peripheral_irq, interrupt_priority);
	NVIC_EnableIRQ(peripheral_irq);
}

and:

void freertos_start_pdc_transfer(
		freertos_dma_event_control_t *dma_event_control,
		const uint8_t *data, size_t len, void *pdc_base_address,
		xSemaphoreHandle notification_semaphore, bool is_transmitting)
{

(removed * from the semaphore handle)

I think that should be all, if there is anything missing or if I made any mistakes please let me know.

Regards
Martin

apsolar wrote on Friday, February 14, 2014:

Thank you Martin,

I had made similar changes but there are still some differences when I compare to your code.

I will try your changes and respond in a few days.

regards
Ankit