#include "task.h"\r
#include "semphr.h"\r
\r
-#define diceMIN 1\r
-#define diceMAX 6\r
-#define diceRUN_MIN 600000L\r
-#define diceRUN_MAX 1200000L\r
+#define diceDELAY_BETWEEN_RANDOM_NUMBERS_ms ( 20 )\r
+#define diceRUN_TIME ( 2000 / diceDELAY_BETWEEN_RANDOM_NUMBERS_ms )\r
\r
-#define diceSTATE_STOPPED 0\r
-#define diceSTATE_STARTUP 1\r
-#define diceSTATE_RUNNING 2\r
\r
#define diceEND_DELAY ( 5000 / portTICK_RATE_MS )\r
\r
\r
void vDiceTask( void *pvParameters )\r
{\r
-char cDiceState = diceSTATE_STOPPED;\r
unsigned char ucDiceValue, ucIndex;\r
-unsigned long ulDiceRunTime, ulDiceDelay, ulDiceDelayReload;\r
-extern void vToggleFlashTaskSuspendState( void );\r
+unsigned long ulDiceRunTime;\r
+extern void vSuspendFlashTasks( unsigned char ucIndex, short sSuspendTasks );\r
\r
+ /* Two instances of this task are created so the task parameter is used\r
+ to pass in an index that allows this task to know which file scope variables\r
+ it should use. Cast this index into a usable type. */\r
ucIndex = ( unsigned char ) pvParameters;\r
+ \r
+ /* A binary semaphore is used to signal button push events. Create the\r
+ semaphore before it is used. */\r
vSemaphoreCreateBinary( xSemaphores[ ucIndex ] );\r
- srand( ( unsigned char ) diceRUN_MIN );\r
+\r
+ /* Make sure the semaphore starts in the wanted state - no button pushes \r
+ pending. This call will just clear any button pushes that are latched.\r
+ Passing in 0 as the block time means the call will not wait for any further\r
+ button pushes. */\r
+ prvButtonHit( ucIndex, 0 );\r
+\r
+ /* Seed the random number generator. */\r
+ srand( ( unsigned char ) diceRUN_TIME );\r
\r
for( ;; )\r
{\r
- switch( cDiceState )\r
+ /* Wait for a button push. This task will enter the Blocked state\r
+ (will not run again) until after a button has been pushed. */\r
+ prvButtonHit( ucIndex, portMAX_DELAY );\r
+ \r
+ /* The next line will only execute after a button has been pushed -\r
+ initialise the variable used to shake the dice. */\r
+ ulDiceRunTime = diceRUN_TIME;; \r
+\r
+ /* Suspend the flash tasks so this task has exclusive access to the\r
+ display. */\r
+ vSuspendFlashTasks( ucIndex, pdTRUE );\r
+\r
+ while( ulDiceRunTime > 0 )\r
+ {\r
+ ulDiceRunTime--;\r
+\r
+ /* Generate and display a random number. */\r
+ ucDiceValue = rand() % 6 + 1;\r
+ dice7SEG_Value( ucIndex ) = ( dice7SEG_Value( ucIndex ) | 0xf7 ) & cDisplaySegments[ ucIndex ][ ucDiceValue ];\r
+\r
+ /* Block/sleep for a very short time before generating the next\r
+ random number. */\r
+ vTaskDelay( diceDELAY_BETWEEN_RANDOM_NUMBERS_ms / portTICK_RATE_MS );\r
+ }\r
+\r
+ /* Wait for a short time before resuming (un-suspending) the flash \r
+ task. The flash tasks are only restarted if a button is not pushed\r
+ during this delay - if a button is pushed then the dice are shaken\r
+ again. \r
+\r
+ First...clear any button pushes that are already pending. Again a\r
+ block time of zero is used so the function does not wait for any \r
+ pushes. */\r
+ prvButtonHit( ucIndex, 0 );\r
+\r
+ /* Second...peek the semaphore. This task will block/sleep until a\r
+ button is pushed again, but because the peek function is used a \r
+ button being pushed will unblock the task but remain pending. */\r
+ if( xQueuePeek( xSemaphores[ ucIndex ], NULL, diceEND_DELAY ) == pdFALSE )\r
{\r
- case diceSTATE_STOPPED:\r
-\r
- prvButtonHit( ucIndex, portMAX_DELAY );\r
- ulDiceRunTime = diceRUN_MIN; \r
- cDiceState = diceSTATE_RUNNING;\r
- ulDiceDelay = 1;\r
- ulDiceDelayReload = 1;\r
- cDiceState = diceSTATE_RUNNING;\r
- if( ucIndex == 0 )\r
- {\r
- vToggleFlashTaskSuspendState();\r
- }\r
-\r
- break;\r
-\r
- case diceSTATE_RUNNING:\r
-\r
- ulDiceRunTime--;\r
- ulDiceDelay--;\r
-\r
- if( !ulDiceDelay )\r
- {\r
- ucDiceValue = rand() % 6 + 1;\r
- dice7SEG_Value( ucIndex ) = ( dice7SEG_Value( ucIndex ) | 0xf7 ) & cDisplaySegments[ ucIndex ][ ucDiceValue ];\r
- ulDiceDelayReload = ulDiceDelayReload + 100;\r
- ulDiceDelay = ulDiceDelayReload;\r
- }\r
-\r
- if( ulDiceRunTime == 0 )\r
- {\r
- dice7SEG_Value( ucIndex ) = ( dice7SEG_Value( ucIndex ) | 0xf7 ) & cDisplaySegments[ ucIndex ][ rand() % 6 + 1 ];\r
- cDiceState = diceSTATE_STOPPED;\r
-\r
- if( ucIndex == 0 )\r
- {\r
- vTaskDelay( diceEND_DELAY );\r
- *pucDisplayOutput[ ucIndex ] = 0xff;\r
- vToggleFlashTaskSuspendState();\r
- }\r
- }\r
-\r
- break;\r
+ *pucDisplayOutput[ ucIndex ] = 0xff;\r
+ vSuspendFlashTasks( ucIndex, pdFALSE );\r
}\r
}\r
}\r
/* Scheduler include files. */\r
#include "FreeRTOS.h"\r
#include "task.h"\r
+#include "croutine.h"\r
\r
/* Demo program include files. */\r
#include "partest.h"\r
#include "flash.h"\r
\r
#define ledSTACK_SIZE configMINIMAL_STACK_SIZE\r
-#define ledNUMBER_OF_LEDS ( 3 )\r
+#define ledNUMBER_OF_LEDS ( 7 )\r
#define ledFLASH_RATE_BASE ( ( portTickType ) 333 )\r
\r
-/* Variable used by the created tasks to calculate the LED number to use, and\r
-the rate at which they should flash the LED. */\r
-static volatile unsigned portBASE_TYPE uxFlashTaskNumber = 0;\r
+#define ledMAX_FLASH_CO_ROUTINES 7\r
+#define ledCO_ROUTINE_PRIORITY 0\r
\r
/* The task that is created three times. */\r
-static portTASK_FUNCTION_PROTO( vLEDFlashTask, pvParameters );\r
+static void vLEDFlashTask( void *pvParameters );\r
+static void prvFixedDelayCoRoutine( xCoRoutineHandle xHandle, unsigned short usIndex );\r
+\r
+/* This task is created once, but itself creates 7 co-routines. */\r
+static void vLEDCoRoutineControlTask( void *pvParameters );\r
\r
static xTaskHandle xFlashTaskHandles[ ledNUMBER_OF_LEDS ] = { 0 };\r
+static xTaskHandle xCoroutineTask;\r
\r
/*-----------------------------------------------------------*/\r
\r
void vStartLEDFlashTasks( unsigned portBASE_TYPE uxPriority )\r
{\r
-signed portBASE_TYPE xLEDTask;\r
+signed short sLEDTask;\r
\r
- /* Create the three tasks. */\r
- for( xLEDTask = 0; xLEDTask < ledNUMBER_OF_LEDS; ++xLEDTask )\r
+ /* Create the three tasks that flash segments on the first LED. */\r
+ for( sLEDTask = 0; sLEDTask < ledNUMBER_OF_LEDS; ++sLEDTask )\r
{\r
/* Spawn the task. */\r
- xTaskCreate( vLEDFlashTask, ( signed portCHAR * ) "LEDx", ledSTACK_SIZE, NULL, uxPriority, &( xFlashTaskHandles[ xLEDTask ] ) );\r
+ xTaskCreate( vLEDFlashTask, ( signed char * ) "LEDt", ledSTACK_SIZE, ( void * ) sLEDTask, uxPriority, &( xFlashTaskHandles[ sLEDTask ] ) );\r
}\r
+\r
+ /* Create the task in which the co-routines run. */\r
+ xTaskCreate( vLEDCoRoutineControlTask, ( signed char * ) "LEDc", ledSTACK_SIZE, NULL, tskIDLE_PRIORITY, &xCoroutineTask );\r
}\r
/*-----------------------------------------------------------*/\r
\r
-void vSuspendFlashTasks( short sSuspendTasks )\r
+void vSuspendFlashTasks( unsigned char ucIndex, short sSuspendTasks )\r
{\r
-signed portBASE_TYPE xLEDTask;\r
+short sLEDTask;\r
\r
- for( xLEDTask = 0; xLEDTask < ledNUMBER_OF_LEDS; ++xLEDTask )\r
+ if( ucIndex == 0 )\r
{\r
- if( xFlashTaskHandles[ xLEDTask ] != NULL )\r
+ for( sLEDTask = 0; sLEDTask < ledNUMBER_OF_LEDS; ++sLEDTask )\r
{\r
- if( sSuspendTasks == pdTRUE )\r
+ if( xFlashTaskHandles[ sLEDTask ] != NULL )\r
{\r
- vTaskSuspend( xFlashTaskHandles[ xLEDTask ] );\r
- }\r
- else\r
- {\r
- vTaskResume( xFlashTaskHandles[ xLEDTask ] );\r
+ if( sSuspendTasks == pdTRUE )\r
+ {\r
+ vTaskSuspend( xFlashTaskHandles[ sLEDTask ] );\r
+ }\r
+ else\r
+ {\r
+ vTaskResume( xFlashTaskHandles[ sLEDTask ] );\r
+ }\r
}\r
}\r
}\r
+ else\r
+ {\r
+ if( sSuspendTasks == pdTRUE )\r
+ {\r
+ vTaskSuspend( xCoroutineTask );\r
+ }\r
+ else\r
+ {\r
+ vTaskResume( xCoroutineTask );\r
+ }\r
+ }\r
}\r
/*-----------------------------------------------------------*/\r
\r
-static portTASK_FUNCTION( vLEDFlashTask, pvParameters )\r
+static void vLEDFlashTask( void * pvParameters )\r
{\r
portTickType xFlashRate, xLastFlashTime;\r
-unsigned portBASE_TYPE uxLED;\r
+unsigned short usLED;\r
\r
- /* The parameters are not used. */\r
- ( void ) pvParameters;\r
-\r
- /* Calculate the LED and flash rate. */\r
- portENTER_CRITICAL();\r
- {\r
- /* See which of the eight LED's we should use. */\r
- uxLED = uxFlashTaskNumber;\r
-\r
- /* Update so the next task uses the next LED. */\r
- uxFlashTaskNumber++;\r
- }\r
- portEXIT_CRITICAL();\r
+ /* The LED to flash is passed in as the task parameter. */\r
+ usLED = ( unsigned short ) pvParameters;\r
\r
- xFlashRate = ledFLASH_RATE_BASE + ( ledFLASH_RATE_BASE * ( portTickType ) uxLED );\r
+ /* Calculate the rate at which this task is going to toggle its LED. */\r
+ xFlashRate = ledFLASH_RATE_BASE + ( ledFLASH_RATE_BASE * ( portTickType ) usLED );\r
xFlashRate /= portTICK_RATE_MS;\r
\r
/* We will turn the LED on and off again in the delay period, so each\r
{\r
/* Delay for half the flash period then turn the LED on. */\r
vTaskDelayUntil( &xLastFlashTime, xFlashRate );\r
- vParTestToggleLED( uxLED );\r
+ vParTestToggleLED( usLED );\r
\r
/* Delay for half the flash period then turn the LED off. */\r
vTaskDelayUntil( &xLastFlashTime, xFlashRate );\r
- vParTestToggleLED( uxLED );\r
+ vParTestToggleLED( usLED );\r
}\r
-} /*lint !e715 !e818 !e830 Function definition must be standard for task creation. */\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void vLEDCoRoutineControlTask( void *pvParameters )\r
+{\r
+unsigned short usCoroutine;\r
+\r
+ ( void ) pvParameters;\r
+\r
+ for( usCoroutine = 0; usCoroutine < ledMAX_FLASH_CO_ROUTINES; usCoroutine++ )\r
+ {\r
+ xCoRoutineCreate( prvFixedDelayCoRoutine, ledCO_ROUTINE_PRIORITY, usCoroutine );\r
+ }\r
+\r
+ for( ;; )\r
+ {\r
+ vCoRoutineSchedule();\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvFixedDelayCoRoutine( xCoRoutineHandle xHandle, unsigned short usIndex )\r
+{\r
+/* The usIndex parameter of the co-routine function is used as an index into\r
+the xFlashRates array to obtain the delay period to use. */\r
+static const portTickType xFlashRates[ ledMAX_FLASH_CO_ROUTINES ] = { 150 / portTICK_RATE_MS,\r
+ 300 / portTICK_RATE_MS,\r
+ 450 / portTICK_RATE_MS,\r
+ 600 / portTICK_RATE_MS,\r
+ 750 / portTICK_RATE_MS,\r
+ 900 / portTICK_RATE_MS,\r
+ 1050 / portTICK_RATE_MS };\r
+\r
+ /* Co-routines MUST start with a call to crSTART. */\r
+ crSTART( xHandle );\r
+\r
+ for( ;; )\r
+ {\r
+ vParTestToggleLED( usIndex + 8 );\r
+ crDELAY( xHandle, xFlashRates[ usIndex ] );\r
+ }\r
+\r
+ /* Co-routines MUST end with a call to crEND. */\r
+ crEND();\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
\r