Do I need Sem/Mutex for Variables used in Multiple Tasks?

Discussion to talk about software related topics only.
Post Reply
Haiyang
Posts: 11
Joined: Wed Mar 09, 2011 8:45 am

Do I need Sem/Mutex for Variables used in Multiple Tasks?

Post by Haiyang »

I am wondering if I need to use semaphore or mutex for variables used in multiple tasks. My application is to receive data from serial (serial receive task) and then generate a PWM signal using DMA timer (level 6 DMA interrupt for 1-2ms pulse width). However, I saw glitches once or twice in 15 min and it is repeatable at a certain time step(around 11 min after boot-up). My processor is NB Mod5213. My implementation is kind of like the following:
------------------------------------------
DWORD Var_Share;

INTERRUPT( int_level6, 0x2600)
{
sim.timer[3].dter = 0x03;
sim.timer[3].dtrr = Var_Share;
}

void Rx_Fun()
{
while(1){
if (packet_received == 1)
Var_Share = data_from_serial;
}
}

void UserMain()
{
OSTaskCreate(Rx_Fun, NULL, &TaskStack[384], TaskStack, MAIN_PRIO+1),
}
------------------------------------------

Can somebody shed some lights into my problem?

Thanks in advance.

Haiyang
Ridgeglider
Posts: 513
Joined: Sat Apr 26, 2008 7:14 am

Re: Do I need Sem/Mutex for Variables used in Multiple Tasks

Post by Ridgeglider »

Typically you would have one task that parses the serial data. That task might access a global variable that gets updated as the serial stream is parsed. When the variable changes you typically want to notify another task (your PWM task) of the change. The easiest way to do this is to post a semaphore from the serial task to the pending PWM task. The pending PWM task blocks until that semaphore is posted by the Serial task and so it uses essentially no CPU time until receiving the posted semaphore. Upon receipt of the semaphore, the PWM task reads the global variable and uses it to update the duty cycle. There are some caveats. If the variable is atomic (ie 32 bits and read/written in one chunk), and if the serial task is the only task that writes the variable, then other tasks can read the variable without running into problems. However, if the variable is non- atomic (larger than 32 bits, or perhaps a struct comprised of several variables that comprise a set, say an x,y coordinate) then you need to take some additional measures to protect the data. The problem is that when the pending task accesses the non-atomic data, part of it can be modified even while another part is being read. The workaround is to lock the data structure while it is being read or written so that all the elements get read or written together as a set. Then, when you're done reading or writing, unlock it. See the NBDK Programmer's Manual Section 15 on Protecting Shared Data Structures and in particular the OSCritEnter example in Section 15.1.5. OsLock does the same thing, but avoid it when possible because it stops all task swapping, not just the tasks that need access to the shared variable, but ALL tasks.
Haiyang
Posts: 11
Joined: Wed Mar 09, 2011 8:45 am

Re: Do I need Sem/Mutex for Variables used in Multiple Tasks

Post by Haiyang »

Hi, RidgeGlider.

Thanks for your suggestion. It is the right way to go. However, there are also many other functions involved which requires untrivial work to modify. Do you think it is OK for my implementation to use an interrupt for PWM generation and a serial task for receiving? The serial task only writes to a DWORD array(e.g. DWORD data[3]). A DMA interrupt only reads the same array and generate the PWM signal. Assume it is OK that data[1] is from time t and data[2] data[3] are from time t+1. Do you see any possibility that some potential problem will be caused by my implementation without protecting shared variables?

Haiyang
v8dave
Posts: 333
Joined: Thu Dec 31, 2009 8:31 pm

Re: Do I need Sem/Mutex for Variables used in Multiple Tasks

Post by v8dave »

Hi,

You don't want to be blocking in an interrupt handler.

If your variables can be made 32 bit then I would just consider making them volatile and let the compiler wrap them in code that ensures the value is what it should be when you use it.

This is how I handle variables in interrupt handlers that are accessed outside in the main code. It has worked for many years and I don't see any need to change this.

For sharing variables between tasks, I use the semaphores as there is no issue of the task blocking in this situation.

Dave...
User avatar
lgitlitz
Posts: 331
Joined: Wed Apr 23, 2008 11:43 am
Location: San Diego, CA
Contact:

Re: Do I need Sem/Mutex for Variables used in Multiple Tasks

Post by lgitlitz »

Dave is right about sharing the data. A 32-bit variable should have no issues being shared since it is written by one ISR/task in a single instruction and the other task/ISR will only read that variable. If you are doing math on the variable before the value is finalized then I would use a temp variable first and copy the temp to the real variable once the value is finalized. Definitely make this variable volatile to avoid any possible compiler optimization.

For the PWM, why use a timer interrupt? All the interrupts generated can consume quite a bit of CPU time. Also if there is any other interrupts with a 0x2600 or higher mask, the timer interrupt will be stalled. USER_ENTER_CRITICAL sections will also stall any maskable interrupts from executing. The MOD5213 has a pretty good PWM peripheral with 8 channels. This will allow you to maintain a consistent PWM signal without using any CPU time for interrupts. This should eliminate all possible glitches in the pulse if you code the width change correctly. Here is an application note on the PWM peripheral: http://www.netburner.com/downloads/mod5 ... 13-PWM.pdf

-Larry
User avatar
lgitlitz
Posts: 331
Joined: Wed Apr 23, 2008 11:43 am
Location: San Diego, CA
Contact:

Re: Do I need Sem/Mutex for Variables used in Multiple Tasks

Post by lgitlitz »

One more thing about sharing variables. Say you have two tasks that share a 4 DWORD array. One task/ISR will modify all 4 of them at once and the other task will only read them. If you want to prevent any interruption from the modify task while it is writing the 4 DWORDs then you will require some sort of protection. An OS object will not work here because you can not call a pend in an interrupt. What you can use is USER_ENTER_CRITICAL() and USER_EXIT_CRITICAL(). These will set a mask of 0x2700 and prevent all interrupts (except level 7) and task switches. These protection calls also generate much less overhead then the normal OS protections. You have to be careful not to spend much time inside these protections as they will completely stall any other task or interrupt that is ready to run.
Haiyang
Posts: 11
Joined: Wed Mar 09, 2011 8:45 am

Re: Do I need Sem/Mutex for Variables used in Multiple Tasks

Post by Haiyang »

Thanks for the suggestions, Dave. I already declared my variables to be volatile. But instead of one 32 bit variable "volatile DWORD var", I have an array of 10 "volatile DWORD var[10]".

Thanks, Larry, for offering suggestions to me one more time.

To your question that I don't use PWM module directly:
I need to generate 9 CH PWM signals (50Hz or 20ms for period), and the duty cycle is constrained by the hardware ([1-2ms]). So instead of use 0-255 representing the whole period, I need to use 0-255 only to represent the duty cycle for accuracy considerations. I don't think I can achieve this with a 8 bit PWM. This is why I use DMA timer to generate PWM signals.

You remember that I had glitches when using clock/16 for dma setting before. Glitches were much less if I use the original clock. However, the glitch is still there (1 or 2 in 15 min) although DMA is already the only level 6 interrupt with mask 0x2600. Other ISRs in my code include 2 GPT interrupt (level 5), one pit interrupt (level 1). I guess my problem probably comes from the fact that I put writes (33 bytes to serial and 40 bytes to spi.qdr ) in my PIT interrupts (50Hz). But I also want to make my serial and SPI writes as quick as possible in 50Hz. So what do you think is the correct way to do this? Move all the writes to a separate task and use semaphore to coordinate between them?

Some calculations I made regarding how long it takes to transmit through serial. I use 115200 bps serial setting. So 33bytes takes about 33/11520 = 0.003 sec to transmit. I guess 3 ms blocking write in level 5 ISR could prevent from entering level 6 interrupt.

Haiyang
Post Reply