I am using KEIL RTX RTOS which used pre-emptive round robin scheduler.I have a LCD for displaying data and a few tasks have access to this LCD(there are some other tasks also),These tasks need fixed time for handling the LCD(e.g. first task handle LCD for displaying it's data for 50 seconds and after 50 seconds,second task handle and displays it's data for 10 seconds). I know i must use mutex for managing accessing to LCD.but i dont know how i must manage its for fixed times?LCD tasks are in the lowest priority and if there are not any other task to execution these tasks will be execute for displaying messages.
3 Answers
I'll try to answer your question first but then I'm going to propose an alternate design for you to consider.
You should use a Timer in order to base anything in real-world time, especially a period that is relatively long, like these which are measured in seconds. Create two Timer objects, one with a 50 second period and one with a 10 second period. Both timers should be one-shot type. Also create two Signal objects, one to indicate that the 50 second period has expired and another to indicate that the 10 second period has expired. The two Timer objects each call distinct callback functions when they expire. The 50 second callback function should set the 50 second expiration Signal and then start the 10 second Timer. The 10 second callback function should set the 10 second expiration Signal and then restart the 50 second Timer. The timers will ping-pong back and forth, alternately setting the two signals.
Now your resource using tasks should periodically check for the appropriate expiration Signal. When the task observes that the Signal is set it can give up the resource and clear the Signal. The other task does the same thing with the other Signal. This way the two tasks know when to give up the resource and allow the other task to obtain it.
One thing that bothers me about your design is that you have two synchronization mechanisms protecting your resource. A mutex is a synchronization mechanism. When tasks are tying to use a resource asynchronously (i.e., at random times), a mutex can be used to synchronize those usages and ensure only one task is using the resource at any given time. If you already have another synchronization mechanism then you probably don't need the mutex. In other words, If your tasks have distinct time slots in which they use the resource then they're already synchronous. If they're not going to attempt to use the resource at random times then you may not need a mutex.
Your design also seems complicated and I wonder whether this alternate design might be simpler.
Consider making a single task that is responsible for the LCD display interface. This LDC task is the only task that will interface with the display. Your data tasks will send messages to the LCD task when they produce data to be displayed. The LCD task will simply wait for these messages and display the data appropriately when they arrive. You could use either a Message Queue or a Mail Queue for this message service depending upon how complicated and varied the data is. Now you don't need a mutex for the LCD display because there is only one task that uses it. And do you still need the 50/10 second time split with this design? I'm not sure because I don't know what was the source of that requirement.
Rather then have multiple threads accessing a single resource arbitrated by mutexes, it would be simpler to have a single thread handle the resource.
In this case I suggest a display manager thread, the other threads can register with the display manger providing perhaps a pointer to a display buffer, and the required display period. The display manager then simply cycles through each registered thread displaying its buffer for the required period before switching to the next.
For example (pseudo-code):
static struct
{
const char* frame_buffer ;
int display_seconds ;
OS_MUTEX mutex ;
} display_registry[MAX_DISPLAY_THREADS] = {0,0} ;
void displayLock( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
os_mutex_lock( display_registry[handle].mutex ) ;
}
}
void displayUnock( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
os_mutex_unlock( display_registry[handle].mutex ) ;
}
}
void lcd_thread
{
int display = 0 ;
for(;;)
{
int t = 0 ;
while( t < display_registry[display].display_seconds &&
display_registry[display].frame_buffer != 0 )
{
displayLock( display ) ;
render_display( display_registry[display].frame_buffer ) ;
displayUnlock( display ) ;
delay( ONE_SECOND ) ;
}
display = (display + 1) % MAX_DISPLAY_THREADS ;
}
}
int displayRegister( const char* frame_buffer, int display_seconds )
{
for( int i = MAX_DISPLAY_THREADS - 1;
frame_buffer[i] != 0 &&
i >= 0; i-- )
{
// do nothing
}
if( i >= 0 )
{
display_registry[i].display_seconds = display_seconds ;
display_registry[i].frame_buffer = frame_buffer ;
}
return i ; // handle for de-registering/locking
}
void displayDeregister( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
display_registry[handle].frame_buffer = 0 ;
}
}
Note that the mutexes are not to lock the LCD resource, but to lock the shared memory frame buffer resources.
Other threads then simply place data for display in their own frame buffers, entirely asynchronously to the display of that data as follows for example:
displayLock( my_display_handle ) ;
update_display_buffer() ;
displayUnlock( my_display_handle ) ;
As mentioned in previous answers, first make a single task for LCD display then use timer events to track the time slice.
Finally, Yield the task in the timer event handler (called after the time slice).
If you don't know about yield, yield is a way for a task to give up its execution to enable scheduler to move to next task.