Semaphore Tips
This tip was posted by jpearman over at http://www.vexforum.com/showpost.php?p=227893&postcount=31.
StopAllTasks(); |
Semaphores are used when two or more tasks want to access a resource that should only be used by one at a time. The resource could be a data structure or physical device such as the LCD display. There is lots of information about semaphores on the internet, as usual a good place to start is here http://en.wikipedia.org/wiki/Semaphore_(programming)
There are only a few calls needed to use semaphores, first a semaphore has to be defined in a similar way to any other variable.
TSemaphore MySemaphore; |
Somewhere before the semaphore is used, normally in the main task, the semaphore needs to be initialized.
// Init the semaphore SemaphoreInitialize(MySemaphore); |
When a task wants to gain exclusive access to a resource, the semaphore needs to be locked. A time can be specified for this call to give up if the semaphore cannot be locked, this defaults to around 32 seconds (hex 7FFF milliseconds), if there is any doubt that the semaphore was obtained then it’s status can be checked or the resource could be used with the possibility of incorrect program operation.
Lock with default timeout
SemaphoreLock( MySemaphore ); |
Lock with a 10mS timeout
SemaphoreLock( MySemaphore , 10 ); |
Check if the task has obtained the semaphore.
if ( bDoesTaskOwnSemaphore(MySemaphore) ) { // do my thing } |
Finally, the semaphore needs to be released when the task has finished using the resource. The task should not release a semaphore it has not successfully locked it so ownership should be checked.
// release the semaphore if we have it if ( bDoesTaskOwnSemaphore(MySemaphore) ) SemaphoreUnlock(MySemaphore); |
Here is a trivial example that has two tasks trying to write to the LCD, it is written to enhance the problem of not using a semaphore to make the issue easier to understand. Both tasks use the same function, DisplayChar, which first clears the character at the requested position and then writes a new character in the next column. A delay has been placed after the character clearing to cause the conflict to happen more often, if the second task runs at this point the first task may then write to the wrong LCD position. Run the program with ENABLE_SEMAPHORES disabled (commented out or deleted) to see the effect of not using a semaphore. The program runs just fine under the RobotC PC emulator so you can still try it if you don’t own an LCD.
#pragma config(UART_Usage, UART1, VEX_2x16_LCD, baudRate19200, IOPins, None, None) //*!!Code automatically generated by 'ROBOTC' configuration wizard !!*// // comment out this to show the issue of not using a semaphore #define ENABLE_SEMAPHORES 1 // the semaphore variable TSemaphore MySemaphore; /*-----------------------------------------------------------------------------*/ /* */ /* A function to display a character at position row, col+1 after clearing */ /* the character at position row, col. */ /* */ /* There is no purpose to this function other than part of the semaphore demo */ /* */ /*-----------------------------------------------------------------------------*/ void DisplayChar( int &col, int row, char c ) { // col is passed by reference, ie. a pointer // Set to display at the current position setLCDPosition(row, col); // Wait a while to enhance problems with two tasks // calling this function wait1Msec( 100 ); // Clear current character displayNextLCDChar( ' ' ); // Increment the column, wrap if we reached the end if( ++col == 16 ) col = 0; // Display the character at the new position setLCDPosition(row, col); displayNextLCDChar( c ); } /*-----------------------------------------------------------------------------*/ /* */ /* A task to display a moving # on the upper row of the Vex Lcd */ /* */ /*-----------------------------------------------------------------------------*/ task OtherTask() { int i = 0; while( true ) { #ifdef ENABLE_SEMAPHORES // Lock the semaphore - we will wait so no need for a timeout SemaphoreLock( MySemaphore ); #endif // Display the moving # DisplayChar( i, 0, '#' ); // release the semaphore if we have it if ( bDoesTaskOwnSemaphore(MySemaphore) ) SemaphoreUnlock(MySemaphore); // Sleep wait1Msec( 500 ); } } /*-----------------------------------------------------------------------------*/ /* */ /* The main task, display a moving x on the lower row of the Vex Lcd */ /* */ /*-----------------------------------------------------------------------------*/ task main() { int i = 0; // LCD backlight on bLCDBacklight = true; // Init the semaphore SemaphoreInitialize(MySemaphore); // Start the other task StartTask( OtherTask ); // clear both lines on the LCD clearLCDLine(0); clearLCDLine(1); // Do our own thing while( true ) { #ifdef ENABLE_SEMAPHORES // Lock the semaphore - we will wait so no need for a timeout SemaphoreLock( MySemaphore ); #endif // Display the moving x DisplayChar(i, 1, 'x' ); // release the semaphore if we have it if ( bDoesTaskOwnSemaphore(MySemaphore) ) SemaphoreUnlock(MySemaphore); // Sleep wait1Msec( 50 ); } } |