licensing and training services.\r
*/\r
\r
+/*\r
+ * The documentation page for this demo available on http://www.FreeRTOS.org\r
+ * documents the hardware configuration required to run this demo. It also\r
+ * provides more information on the expected demo application behaviour.\r
+ *\r
+ * main() creates all the demo application tasks, then starts the scheduler.\r
+ * A lot of the created tasks are from the pool of "standard demo" tasks. The\r
+ * web documentation provides more details of the standard demo tasks, which\r
+ * provide no particular functionality but do provide good examples of how to\r
+ * use the FreeRTOS API.\r
+ *\r
+ * In addition to the standard demo tasks, the following tasks, interrupts and\r
+ * tests are defined and/or created within this file:\r
+ *\r
+ * "LCD" task - The LCD task is a 'gatekeeper' task. It is the only task that\r
+ * is permitted to access the LCD and therefore ensures access to the LCD is\r
+ * always serialised and there are no mutual exclusion issues. When a task or\r
+ * an interrupt wants to write to the LCD, it does not access the LCD directly\r
+ * but instead sends the message to the LCD task. The LCD task then performs\r
+ * the actual LCD output. This mechanism also allows interrupts to, in effect,\r
+ * write to the LCD by sending messages to the LCD task.\r
+ *\r
+ * The LCD task is also a demonstration of a 'controller' task design pattern.\r
+ * Some tasks do not actually send a string to the LCD task directly, but\r
+ * instead send a command that is interpreted by the LCD task. In a normal\r
+ * application these commands can be control values or set points, in this\r
+ * simple example the commands just result in messages being displayed on the\r
+ * LCD.\r
+ *\r
+ * "Button Poll" task - This task polls the state of the 'up' key on the\r
+ * joystick input device. It uses the vTaskDelay() API function to control\r
+ * the poll rate to ensure debouncing is not necessary and that the task does\r
+ * not use all the available CPU processing time.\r
+ *\r
+ * Button Interrupt and run time stats display - The select button on the\r
+ * joystick input device is configured to generate an external interrupt. The\r
+ * handler for this interrupt sends a message to LCD task, which interprets the\r
+ * message to mean, firstly write a message to the LCD, and secondly, generate\r
+ * a table of run time statistics. The run time statistics are displayed as a\r
+ * table that contains information on how much processing time each task has\r
+ * been allocated since the application started to execute. This information\r
+ * is provided both as an absolute time, and as a percentage of the total run\r
+ * time. The information is displayed in the terminal IO window of the IAR\r
+ * embedded workbench. The online documentation for this demo shows a screen\r
+ * shot demonstrating where the run time stats can be viewed.\r
+ *\r
+ * Idle Hook - The idle hook is a function that is called on each iteration of\r
+ * the idle task. In this case it is used to place the processor into a low\r
+ * power mode. Note however that this application is implemented using standard\r
+ * components, and is therefore not optimised for low power operation. Lower\r
+ * power consumption would be achieved by converting polling tasks into event\r
+ * driven tasks, and slowing the tick interrupt frequency.\r
+ *\r
+ * "Check" function called from the tick hook - The tick hook is called during\r
+ * each tick interrupt. It is called from an interrupt context so must execute\r
+ * quickly, not attempt to block, and not call any FreeRTOS API functions that\r
+ * do not end in "FromISR". In this case the tick hook executes a 'check'\r
+ * function. This only executes every five seconds. Its main function is to\r
+ * check that all the standard demo tasks are still operational. Each time it\r
+ * executes it sends a status code to the LCD task. The LCD task interprets the\r
+ * code and displays an appropriate message - which will be PASS if no tasks\r
+ * have reported any errors, or a message stating which task has reported an\r
+ * error.\r
+ *\r
+ * "Reg test" tasks - These fill the registers with known values, then check\r
+ * that each register still contains its expected value. Each task uses\r
+ * different values. The tasks run with very low priority so get preempted\r
+ * very frequently. A check variable is incremented on each iteration of the\r
+ * test loop. A register containing an unexpected value is indicative of an\r
+ * error in the context switching mechanism and will result in a branch to a\r
+ * null loop - which in turn will prevent the check variable from incrementing\r
+ * any further and allow the check task (described a above) to determine that an\r
+ * error has occurred. The nature of the reg test tasks necessitates that they\r
+ * are written in assembly code.\r
+ *\r
+ * *NOTE 2* vApplicationSetupTimerInterrupt() is called by the kernel to let\r
+ * the application set up a timer to generate the tick interrupt. In this\r
+ * example a timer A0 is used for this purpose.\r
+ *\r
+*/\r
+\r
/* Standard includes. */\r
#include <stdio.h>\r
\r
to send messages from tasks and interrupts the the LCD task. */\r
#define mainQUEUE_LENGTH ( 5 )\r
\r
+/* Priorities used by the test and demo tasks. */\r
#define mainLCD_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 )\r
#define mainCOM_TEST_PRIORITY ( tskIDLE_PRIORITY + 2 )\r
#define mainGENERIC_QUEUE_TEST_PRIORITY ( tskIDLE_PRIORITY )\r
\r
/* The LED used by the comtest tasks. See the comtest.c file for more\r
-information. In this case it is deliberately out of range as there are only\r
-two LEDs, and they are both already in use. */\r
-#define mainCOM_TEST_LED ( 3 )\r
+information. */\r
+#define mainCOM_TEST_LED ( 1 )\r
\r
/* The baud rate used by the comtest tasks described at the top of this file. */\r
-#define mainCOM_TEST_BAUD_RATE ( 9600 )\r
+#define mainCOM_TEST_BAUD_RATE ( 38400 )\r
+\r
+/* The maximum number of lines of text that can be displayed on the LCD. */\r
+#define mainMAX_LCD_LINES ( 8 )\r
/*-----------------------------------------------------------*/\r
\r
+/*\r
+ * The reg test tasks as described at the top of this file.\r
+ */\r
extern void vRegTest1Task( void *pvParameters );\r
extern void vRegTest2Task( void *pvParameters );\r
+\r
+/*\r
+ * Configures clocks, LCD, port pints, etc. necessary to execute this demo.\r
+ */\r
static void prvSetupHardware( void );\r
-static void prvTerminalIOTask( void *pvParameters );\r
+\r
+/*\r
+ * Definition of the LCD/controller task described in the comments at the top\r
+ * of this file.\r
+ */\r
+static void prvLCDTask( void *pvParameters );\r
+\r
+/*\r
+ * Definition of the button poll task described in the comments at the top of\r
+ * this file.\r
+ */\r
static void prvButtonPollTask( void *pvParameters );\r
+\r
+/*\r
+ * Converts a status message value into an appropriate string for display on\r
+ * the LCD. The string is written to pcBuffer.\r
+ */\r
static void prvGenerateStatusMessage( char *pcBuffer, long lStatusValue );\r
\r
/*-----------------------------------------------------------*/\r
\r
+/* Variables that are incremented on each iteration of the reg test tasks -\r
+provided the tasks have not reported any errors. The check task inspects these\r
+variables to ensure they are still incrementing as expected. If a variable\r
+stops incrementing then it is likely that its associate task has stalled. */\r
volatile unsigned short usRegTest1Counter = 0, usRegTest2Counter = 0;\r
-volatile unsigned long ulStatsOverflowCount = 0;\r
\r
/* The handle of the queue used to send messages from tasks and interrupts to\r
the LCD task. */\r
task. */\r
typedef struct\r
{\r
- char cMessageID; /* << States what the message is. */\r
- unsigned long ulMessageValue; /* << States the message value (can be an integer, string pointer, etc. depending on the value of cMessageID. */\r
+ char cMessageID; /* << States what the message is. */\r
+ unsigned long ulMessageValue; /* << States the message value (can be an integer, string pointer, etc. depending on the value of cMessageID). */\r
} xQueueMessage;\r
+\r
/*-----------------------------------------------------------*/\r
\r
+/* The linker script tests the FreeRTOS ports use of 20bit addresses by\r
+locating all code in high memory. The following pragma ensures that main\r
+remains in low memory. The ISR_CODE segment is used for convenience as ISR\r
+functions are always placed in low memory. */\r
+#pragma location="ISR_CODE"\r
void main( void )\r
{\r
+ /* Configure the peripherals used by this demo application. This includes\r
+ configuring the joystick input select button to generate interrupts. */\r
prvSetupHardware();\r
\r
/* Create the queue used by tasks and interrupts to send strings to the LCD\r
task. */\r
xLCDQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( xQueueMessage ) );\r
\r
+ /* If the queue could not be created then don't create any tasks that might\r
+ attempt to use the queue. */\r
if( xLCDQueue != NULL )\r
{\r
/* Add the created queue to the queue registry so it can be viewed in\r
vStartDynamicPriorityTasks();\r
vStartGenericQueueTasks( mainGENERIC_QUEUE_TEST_PRIORITY );\r
\r
- /* Create the terminal IO and button poll tasks, as described at the top\r
- of this file. */\r
- xTaskCreate( prvTerminalIOTask, ( signed char * ) "IO", configMINIMAL_STACK_SIZE * 2, NULL, mainLCD_TASK_PRIORITY, NULL );\r
+ /* Create the LCD, button poll and register test tasks, as described at\r
+ the top of this file. */\r
+ xTaskCreate( prvLCDTask, ( signed char * ) "LCD", configMINIMAL_STACK_SIZE * 2, NULL, mainLCD_TASK_PRIORITY, NULL );\r
xTaskCreate( prvButtonPollTask, ( signed char * ) "BPoll", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );\r
+ xTaskCreate( vRegTest1Task, ( signed char * ) "Reg1", configMINIMAL_STACK_SIZE, NULL, 0, NULL );\r
+ xTaskCreate( vRegTest2Task, ( signed char * ) "Reg2", configMINIMAL_STACK_SIZE, NULL, 0, NULL );\r
\r
- /* Create the register test tasks as described at the top of this file. */\r
- xTaskCreate( vRegTest1Task, "Reg1", configMINIMAL_STACK_SIZE, NULL, 0, NULL );\r
- xTaskCreate( vRegTest2Task, "Reg2", configMINIMAL_STACK_SIZE, NULL, 0, NULL );\r
+ /* Start the scheduler. */\r
vTaskStartScheduler();\r
}\r
\r
+ /* If all is well then this line will never be reached. If it is reached\r
+ then it is likely that there was insufficient (FreeRTOS) heap memory space\r
+ to create the idle task. This may have been trapped by the malloc() failed\r
+ hook function, if one is configured. */ \r
for( ;; );\r
}\r
/*-----------------------------------------------------------*/\r
\r
-static void prvTerminalIOTask( void *pvParameters )\r
+static void prvLCDTask( void *pvParameters )\r
{\r
xQueueMessage xReceivedMessage;\r
\r
xQueueReceive( xLCDQueue, &xReceivedMessage, portMAX_DELAY );\r
\r
/* Clear the LCD if no room remains for any more text output. */\r
- if( ucLine > 8 )\r
+ if( ucLine > mainMAX_LCD_LINES )\r
{\r
halLcdClearScreen();\r
ucLine = 0;\r
the LCD - in this case the\r
pointer to the string to print\r
is sent directly in the\r
- lMessageValue member of the\r
+ ulMessageValue member of the\r
message. This just demonstrates\r
a different communication\r
technique. */\r
break;\r
}\r
\r
+ /* Output the message that was placed into the cBuffer array within the\r
+ switch statement above, then move onto the next line ready for the next\r
+ message to arrive on the queue. */\r
halLcdPrintLine( cBuffer, ucLine, OVERWRITE_TEXT );\r
ucLine++;\r
}\r
break;\r
case mainERROR_DYNAMIC_TASKS : sprintf( pcBuffer, "Err: Dynamic tsks" );\r
break;\r
- case mainERROR_COM_TEST : sprintf( pcBuffer, "Err: COM test" ); /* Error in COM test - is the Loopback connector connected? */ \r
+ case mainERROR_COM_TEST : sprintf( pcBuffer, "Err: COM test" );\r
break;\r
case mainERROR_GEN_QUEUE_TEST : sprintf( pcBuffer, "Error: Gen Q test" );\r
break;\r
\r
if( ucState != 0 )\r
{\r
+ /* The button was pressed. */\r
ucState = pdTRUE;\r
}\r
\r
\r
static void prvSetupHardware( void )\r
{\r
+/* Convert a Hz value to a KHz value, as required by the Init_FLL_Settle()\r
+function. */\r
unsigned long ulCPU_Clock_KHz = ( configCPU_CLOCK_HZ / 1000UL );\r
\r
halBoardInit();\r
\r
halButtonsInit( BUTTON_ALL );\r
halButtonsInterruptEnable( BUTTON_SELECT );\r
+\r
+ /* Initialise the LCD, but note that the backlight is not used as the\r
+ library function uses timer A0 to modulate the backlight, and this file\r
+ defines vApplicationSetupTimerInterrupt() to also use timer A0 to generate\r
+ the tick interrupt. If the backlight is required, then change either the\r
+ halLCD library or vApplicationSetupTimerInterrupt() to use a different\r
+ timer. Timer A1 is used for the run time stats time base6. */\r
halLcdInit();\r
- halLcdBackLightInit();\r
- halLcdSetBackLight( 0 );\r
halLcdSetContrast( 100 );\r
halLcdClearScreen();\r
\r
halLcdPrintLine( " www.FreeRTOS.org", 0, OVERWRITE_TEXT );\r
- \r
-while( ( halButtonsPressed() & BUTTON_UP ) == 0 );\r
-}\r
-/*-----------------------------------------------------------*/\r
-\r
-void vApplicationSetupTimerInterrupt( void )\r
-{\r
-const unsigned short usACLK_Frequency_Hz = 32768;\r
-\r
- /* Ensure the timer is stopped. */\r
- TA0CTL = 0;\r
-\r
- /* Run the timer from the ACLK. */\r
- TA0CTL = TASSEL_1;\r
-\r
- /* Clear everything to start with. */\r
- TA0CTL |= TACLR;\r
-\r
- /* Set the compare match value according to the tick rate we want. */\r
- TA0CCR0 = usACLK_Frequency_Hz / configTICK_RATE_HZ;\r
-\r
- /* Enable the interrupts. */\r
- TA0CCTL0 = CCIE;\r
-\r
- /* Start up clean. */\r
- TA0CTL |= TACLR;\r
-\r
- /* Up mode. */\r
- TA0CTL |= MC_1;\r
-}\r
-/*-----------------------------------------------------------*/\r
-\r
-void vApplicationMallocFailedHook( void )\r
-{\r
- for( ;; );\r
-}\r
-/*-----------------------------------------------------------*/\r
-\r
-void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskName )\r
-{\r
- ( void ) pxTask;\r
- ( void ) pcTaskName;\r
- \r
- for( ;; );\r
-}\r
-/*-----------------------------------------------------------*/\r
-\r
-void vApplicationIdleHook( void )\r
-{\r
- /* Want to leave the SMCLK running so the COMTest tasks don't fail. */\r
- __bis_SR_register( LPM1_bits + GIE );\r
}\r
/*-----------------------------------------------------------*/\r
\r
xStatusMessage.ulMessageValue = mainERROR_GEN_QUEUE_TEST;\r
} \r
\r
- /* Check the reg test tasks are still cycling. They will stop incrementing\r
- their loop counters if they encounter an error. */\r
+ /* Check the reg test tasks are still cycling. They will stop\r
+ incrementing their loop counters if they encounter an error. */\r
if( usRegTest1Counter == usLastRegTest1Counter )\r
{\r
xStatusMessage.ulMessageValue = mainERROR_REG_TEST;\r
ulCounter = 0;\r
}\r
\r
+ /* Just periodically toggle an LED to show that the tick interrupt is\r
+ running. Note that this access LED_PORT_OUT in a non-atomic way, so tasks\r
+ that access the same port must do so from a critical section. */\r
if( ( ulCounter & 0xff ) == 0 )\r
{\r
if( ( LED_PORT_OUT & LED_1 ) == 0 )\r
{\r
LED_PORT_OUT |= LED_1;\r
- LED_PORT_OUT &= ~LED_2;\r
}\r
else\r
{\r
LED_PORT_OUT &= ~LED_1;\r
- LED_PORT_OUT |= LED_2;\r
}\r
}\r
}\r
}\r
/*-----------------------------------------------------------*/\r
\r
-void vConfigureTimerForRunTimeStats( void )\r
+/* The MSP430X port uses this callback function to configure its tick interrupt.\r
+This allows the application to choose the tick interrupt source.\r
+configTICK_VECTOR must also be set in FreeRTOSConfig.h to the correct\r
+interrupt vector for the chosen tick interrupt source. This implementation of\r
+vApplicationSetupTimerInterrupt() generates the tick from timer A0, so in this\r
+case configTICK_VECTOR is set to TIMER0_A0_VECTOR. */\r
+void vApplicationSetupTimerInterrupt( void )\r
{\r
+const unsigned short usACLK_Frequency_Hz = 32768;\r
+\r
/* Ensure the timer is stopped. */\r
- TA1CTL = 0;\r
+ TA0CTL = 0;\r
\r
- /* Run the timer from the ACLK/4. */\r
- TA1CTL = TASSEL_1 | ID__4;\r
+ /* Run the timer from the ACLK. */\r
+ TA0CTL = TASSEL_1;\r
\r
/* Clear everything to start with. */\r
- TA1CTL |= TACLR;\r
+ TA0CTL |= TACLR;\r
+\r
+ /* Set the compare match value according to the tick rate we want. */\r
+ TA0CCR0 = usACLK_Frequency_Hz / configTICK_RATE_HZ;\r
\r
/* Enable the interrupts. */\r
- TA1CCTL0 = CCIE;\r
+ TA0CCTL0 = CCIE;\r
\r
/* Start up clean. */\r
- TA1CTL |= TACLR;\r
+ TA0CTL |= TACLR;\r
\r
- /* Continuous mode. */\r
- TA1CTL |= MC__CONTINOUS;\r
+ /* Up mode. */\r
+ TA0CTL |= MC_1;\r
}\r
/*-----------------------------------------------------------*/\r
\r
-#pragma vector=TIMER1_A0_VECTOR\r
-static __interrupt void prvRunTimeStatsOverflowISR( void )\r
+void vApplicationIdleHook( void )\r
{\r
- ulStatsOverflowCount++;\r
+ /* Called on each iteration of the idle task. In this case the idle task\r
+ just enters a low(ish) power mode. */\r
+ __bis_SR_register( LPM1_bits + GIE );\r
}\r
/*-----------------------------------------------------------*/\r
\r
-inline unsigned long ulGetRunTimeStatsTime( void )\r
+void vApplicationMallocFailedHook( void )\r
{\r
-unsigned long ulReturn;\r
+ /* Called if a call to pvPortMalloc() fails because there is insufficient\r
+ free memory available in the FreeRTOS heap. pvPortMalloc() is called\r
+ internally by FreeRTOS API functions that create tasks, queues or\r
+ semaphores. */\r
+ taskDISABLE_INTERRUPTS();\r
+ for( ;; );\r
+}\r
+/*-----------------------------------------------------------*/\r
\r
- TA1CTL &= ~MC__CONTINOUS;\r
- ulReturn = ( ( ulStatsOverflowCount << 16UL ) | ( unsigned long ) TA1R );\r
- TA1CTL |= MC__CONTINOUS;\r
+void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskName )\r
+{\r
+ ( void ) pxTask;\r
+ ( void ) pcTaskName;\r
\r
- return ulReturn;\r
+ /* Run time stack overflow checking is performed if\r
+ configconfigCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook\r
+ function is called if a stack overflow is detected. */\r
+ taskDISABLE_INTERRUPTS();\r
+ for( ;; );\r
}\r
-\r
-\r
-\r
+/*-----------------------------------------------------------*/\r
\r