View Single Post
  #31  
Old 10-18-2011, 10:31 PM
jpearman's Avatar
jpearman jpearman is online now
Senior Member
VEX # 8888
 
Join Date: Apr 2011
Location: Los Angeles
Posts: 3,005
Re: RobotC programming tips

Semaphores


A short example using semaphores.

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.

Code:
TSemaphore  MySemaphore;
Somewhere before the semaphore is used, normally in the main task, the semaphore needs to be initialized.

Code:
// 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

Code:
SemaphoreLock( MySemaphore );
Lock with a 10mS timeout

Code:
SemaphoreLock( MySemaphore , 10 );
Check if the task has obtained the semaphore.

Code:
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.

Code:
// 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.

Enjoy.

Code:
#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 );
        }
}
Attached Files
File Type: c semaphoreExample.c (3.6 KB, 49 views)
Reply With Quote