\r
\r
/*\r
- * This version of comtest. c is for use on systems that have limited stack\r
- * space and no display facilities. The complete version can be found in\r
- * the Demo/Common/Full directory.\r
+ * Creates a task and a timer that operate on an interrupt driven serial port.\r
+ * This demo assumes that the characters transmitted on a port will also be\r
+ * received on the same port. Therefore, the UART must either be connected to\r
+ * an echo server, or the uart connector must have a loopback connector fitted.\r
+ * See http://www.serialporttool.com/CommEcho.htm for a suitable echo server\r
+ * for Windows hosts.\r
*\r
- * Creates two tasks that operate on an interrupt driven serial port. A\r
- * loopback connector should be used so that everything that is transmitted is\r
- * also received. The serial port does not use any flow control. On a\r
- * standard 9way 'D' connector pins two and three should be connected together.\r
+ * The timer sends a string to the UART, toggles an LED, then waits resets\r
+ * itself by changing its own period. The period is calculated as a pseudo\r
+ * random number between comTX_MAX_BLOCK_TIME and comTX_MIN_BLOCK_TIME.\r
*\r
- * The first task posts a sequence of characters to the Tx queue, toggling an\r
- * LED on each successful post. At the end of the sequence it sleeps for a\r
- * pseudo-random period before resending the same sequence.\r
+ * The task blocks on an Rx queue waiting for a character to become\r
+ * available. Received characters are checked to ensure they match those\r
+ * transmitted by the Tx timer. An error is latched if characters are missing,\r
+ * incorrect, or arrive too slowly.\r
*\r
- * The UART Tx end interrupt is enabled whenever data is available in the Tx\r
- * queue. The Tx end ISR removes a single character from the Tx queue and\r
- * passes it to the UART for transmission.\r
- *\r
- * The second task blocks on the Rx queue waiting for a character to become\r
- * available. When the UART Rx end interrupt receives a character it places\r
- * it in the Rx queue, waking the second task. The second task checks that the\r
- * characters removed from the Rx queue form the same sequence as those posted\r
- * to the Tx queue, and toggles an LED for each correct character.\r
- *\r
- * The receiving task is spawned with a higher priority than the transmitting\r
- * task. The receiver will therefore wake every time a character is\r
- * transmitted so neither the Tx or Rx queue should ever hold more than a few\r
- * characters.\r
+ * How characters are actually transmitted and received is port specific. Demos\r
+ * that include this test/demo file will provide example drivers. The Tx timer\r
+ * executes in the context of the timer service (daemon) task, and must therefore\r
+ * never attempt to block.\r
*\r
*/\r
\r
#include <string.h>\r
#include "FreeRTOS.h"\r
#include "task.h"\r
+#include "timers.h"\r
+\r
+#ifndef configUSE_TIMERS\r
+ #error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h.\r
+#endif\r
+\r
+#if configUSE_TIMERS != 1\r
+ #error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h.\r
+#endif\r
+\r
\r
/* Demo program include files. */\r
#include "serial.h"\r
#include "comtest_strings.h"\r
#include "partest.h"\r
\r
+/* The size of the stack given to the Rx task. */\r
#define comSTACK_SIZE configMINIMAL_STACK_SIZE\r
+\r
+/* See the comment above the declaraction of uxBaseLED. */\r
#define comTX_LED_OFFSET ( 0 )\r
#define comRX_LED_OFFSET ( 1 )\r
-#define comTOTAL_PERMISSIBLE_ERRORS ( 2 )\r
\r
-/* The Tx task will transmit the sequence of characters at a pseudo random\r
-interval. This is the maximum and minimum block time between sends. */\r
+/* The Tx timer transmits the sequence of characters at a pseudo random\r
+interval that is capped between comTX_MAX_BLOCK_TIME and\r
+comTX_MIN_BLOCK_TIME. */\r
#define comTX_MAX_BLOCK_TIME ( ( portTickType ) 0x96 )\r
#define comTX_MIN_BLOCK_TIME ( ( portTickType ) 0x32 )\r
#define comOFFSET_TIME ( ( portTickType ) 3 )\r
\r
-/* We should find that each character can be queued for Tx immediately and we\r
-don't have to block to send. */\r
-#define comNO_BLOCK ( ( portTickType ) 0 )\r
+/* States for the simple state machine implemented in the Rx task. */\r
+#define comtstWAITING_START_OF_STRING 0\r
+#define comtstWAITING_END_OF_STRING 1\r
\r
-/* The Rx task will block on the Rx queue for a long period. */\r
-#define comRX_BLOCK_TIME ( ( portTickType ) 0xffff )\r
+/* A short delay in ticks - this delay is used to allow the Rx queue to fill up\r
+a bit so more than one character can be processed at a time. This is relative\r
+to comTX_MIN_BLOCK_TIME to ensure it is never longer than the shortest gap\r
+between transmissions. It could be worked out more scientifically from the\r
+baud rate being used. */\r
+#define comSHORT_DELAY ( comTX_MIN_BLOCK_TIME >> ( portTickType ) 2 )\r
\r
/* The string that is transmitted and received. */\r
-#define comTRANSACTED_STRING "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"\r
-\r
-#define comBUFFER_LEN ( ( unsigned portBASE_TYPE ) ( comLAST_BYTE - comFIRST_BYTE ) + ( unsigned portBASE_TYPE ) 1 )\r
-#define comINITIAL_RX_COUNT_VALUE ( 0 )\r
+#define comTRANSACTED_STRING "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"\r
\r
/* Handle to the com port used by both tasks. */\r
static xComPortHandle xPort = NULL;\r
\r
-/* The transmit task as described at the top of the file. */\r
-static void vComTxTask( void *pvParameters );\r
+/* The transmit timer as described at the top of the file. */\r
+static void vComTxTimerCallback( xTimerHandle xTimer );\r
\r
/* The receive task as described at the top of the file. */\r
-static portTASK_FUNCTION_PROTO( vComRxTask, pvParameters );\r
+static void vComRxTask( void *pvParameters );\r
\r
-/* The LED that should be toggled by the Rx and Tx tasks. The Rx task will\r
-toggle LED ( uxBaseLED + comRX_LED_OFFSET). The Tx task will toggle LED\r
-( uxBaseLED + comTX_LED_OFFSET ). */\r
+/* The Rx task will toggle LED ( uxBaseLED + comRX_LED_OFFSET). The Tx task\r
+will toggle LED ( uxBaseLED + comTX_LED_OFFSET ). */\r
static unsigned portBASE_TYPE uxBaseLED = 0;\r
\r
-/* Check variable used to ensure no error have occurred. The Rx task will\r
-increment this variable after every successfully received sequence. If at any\r
-time the sequence is incorrect the the variable will stop being incremented. */\r
-static volatile unsigned portBASE_TYPE uxRxLoops = comINITIAL_RX_COUNT_VALUE;\r
+/* The Rx task toggles uxRxLoops on each successful iteration of its defined\r
+function - provided no errors have ever been latched. If this variable stops\r
+incrementing, then an error has occurred. */\r
+static volatile unsigned portBASE_TYPE uxRxLoops = 0UL;\r
+\r
+/* The timer used to periodically transmit the string. */\r
+static xTimerHandle xTxTimer = NULL;\r
+\r
+/* The string length is held at file scope so the Tx timer does not need to\r
+calculate it each time it executes. */
+static size_t xStringLength = 0U;\r
\r
/*-----------------------------------------------------------*/\r
\r
void vStartComTestStringsTasks( unsigned portBASE_TYPE uxPriority, unsigned long ulBaudRate, unsigned portBASE_TYPE uxLED )\r
{\r
- /* Initialise the com port then spawn the Rx and Tx tasks. */\r
+ /* Store values that are used at run time. */\r
uxBaseLED = uxLED;\r
- xSerialPortInitMinimal( ulBaudRate, strlen( comTRANSACTED_STRING ) );\r
\r
- /* The Tx task is spawned with a lower priority than the Rx task. */\r
- xTaskCreate( vComTxTask, ( signed char * ) "COMTx", comSTACK_SIZE, NULL, uxPriority - 1, ( xTaskHandle * ) NULL );\r
+ /* Calculate the string length here, rather than each time the timer\r
+ executes. */\r
+ xStringLength = strlen( comTRANSACTED_STRING );\r
+\r
+ /* Include the null terminator in the string length as this is used to\r
+ detect the end of the string in the Rx task. */\r
+ xStringLength++;\r
+\r
+ /* Initialise the com port then spawn the Rx task and create the Tx\r
+ timer. */\r
+ xSerialPortInitMinimal( ulBaudRate, ( xStringLength * 2U ) );\r
+\r
+ /* Create the Rx task and the Tx timer. The timer is started from the\r
+ Rx task. */
xTaskCreate( vComRxTask, ( signed char * ) "COMRx", comSTACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );\r
+ xTxTimer = xTimerCreate( ( const signed char * ) "TxTimer", comTX_MIN_BLOCK_TIME, pdFALSE, NULL, vComTxTimerCallback );\r
+ configASSERT( xTxTimer );\r
}\r
/*-----------------------------------------------------------*/\r
\r
-static void vComTxTask( void * pvParameters )\r
+static void vComTxTimerCallback( xTimerHandle xTimer )\r
{\r
portTickType xTimeToWait;\r
-size_t xStringLength;\r
\r
/* Just to stop compiler warnings. */\r
- ( void ) pvParameters;\r
+ ( void ) xTimer;\r
\r
- xStringLength = strlen( comTRANSACTED_STRING );\r
+ /* Send the string. How this is actually performed depends on the\r
+ sample driver provided with this demo. However - as this is a timer,\r
+ it executes in the context of the timer task and therefore must not\r
+ block. */\r
+ vSerialPutString( xPort, ( const signed char * const ) comTRANSACTED_STRING, xStringLength );\r
\r
- /* Include the null terminator in the string length. */\r
- xStringLength++;\r
+ /* Toggle an LED to give a visible indication that another transmission\r
+ has been performed. */
+ vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET );\r
\r
- for( ;; )\r
- {\r
- /* Send the string. Setting the last parameter to pdTRUE ensures\r
- that vSerialPutString() will not return until the entire string has\r
- been sent to the UART. The UART interrupt is used to send more data\r
- to the UART as the UART FIFO empties, until the entire string has been\r
- sent. No CPU time is consumed by this task while it waits for the\r
- string to be sent to the UART. */\r
- vSerialPutString( xPort, ( const signed char * const ) comTRANSACTED_STRING, xStringLength );\r
-\r
- /* Toggle an LED to give a visible indication that another transmission\r
- has been performed. */
- vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET );\r
-\r
- /* Wait a pseudo random time before sending the string again. */\r
- xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME;\r
-\r
- /* Ensure the time to wait does not greater than comTX_MAX_BLOCK_TIME. */\r
- xTimeToWait %= comTX_MAX_BLOCK_TIME;\r
-\r
- /* Ensure the time to wait is not less than comTX_MIN_BLOCK_TIME. */\r
- if( xTimeToWait < comTX_MIN_BLOCK_TIME )\r
- {\r
- xTimeToWait = comTX_MIN_BLOCK_TIME;\r
- }\r
+ /* Wait a pseudo random time before sending the string again. */\r
+ xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME;\r
+\r
+ /* Ensure the time to wait does not greater than comTX_MAX_BLOCK_TIME. */\r
+ xTimeToWait %= comTX_MAX_BLOCK_TIME;\r
\r
- vTaskDelay( xTimeToWait );\r
+ /* Ensure the time to wait is not less than comTX_MIN_BLOCK_TIME. */\r
+ if( xTimeToWait < comTX_MIN_BLOCK_TIME )\r
+ {\r
+ xTimeToWait = comTX_MIN_BLOCK_TIME;\r
}\r
+\r
+ /* Reset the timer to run again xTimeToWait ticks from now. This function\r
+ is called from the context of the timer task, so the block time must not\r
+ be anything other than zero. */\r
+ xTimerChangePeriod( xTxTimer, xTimeToWait, 0 );\r
}\r
/*-----------------------------------------------------------*/\r
\r
-#define comtstWAITING_START_OF_STRING 0\r
-#define comtstWAITING_END_OF_STRING 1\r
-\r
-\r
static void vComRxTask( void *pvParameters )\r
{\r
portBASE_TYPE xState = comtstWAITING_START_OF_STRING, xErrorOccurred = pdFALSE;\r
-char *pcExpectedByte, cRxedChar;\r
+signed char *pcExpectedByte, cRxedChar;\r
const xComPortHandle xPort = NULL;\r
\r
-\r
/* Just to stop compiler warnings. */\r
( void ) pvParameters;\r
\r
- pcExpectedByte = comTRANSACTED_STRING;\r
+ /* Start the Tx timer. This only needs to be started once, as it will\r
+ reset itself thereafter. */\r
+ xTimerStart( xTxTimer, portMAX_DELAY );\r
+\r
+ /* The first expected Rx character is the first in the string that is\r
+ transmitted. */
+ pcExpectedByte = ( signed char * ) comTRANSACTED_STRING;\r
\r
for( ;; )\r
{\r
as it comes in until the entire string has been received. */\r
xState = comtstWAITING_END_OF_STRING;\r
pcExpectedByte++;\r
+\r
+ /* Block for a short period. This just allows the Rx queue to\r
+ contain more than one character, and therefore prevent\r
+ thrashing reads to the queue and repetitive context switches as\r
+ each character is received. */\r
+ vTaskDelay( comSHORT_DELAY );\r
}\r
break;\r
\r
case comtstWAITING_END_OF_STRING:\r
+\r
if( cRxedChar == *pcExpectedByte )\r
{\r
/* The received character was the expected character. Was\r
}\r
\r
/* Go back to wait for the start of the next string. */\r
- pcExpectedByte = comTRANSACTED_STRING;\r
+ pcExpectedByte = ( signed char * ) comTRANSACTED_STRING;\r
xState = comtstWAITING_START_OF_STRING;\r
}\r
else\r
/* If the count of successful reception loops has not changed than at\r
some time an error occurred (i.e. a character was received out of sequence)\r
and we will return false. */\r
- if( uxRxLoops == comINITIAL_RX_COUNT_VALUE )\r
+ if( uxRxLoops == 0UL )\r
{\r
xReturn = pdFALSE;\r
}\r
\r
/* Reset the count of successful Rx loops. When this function is called\r
again it should have been incremented. */\r
- uxRxLoops = comINITIAL_RX_COUNT_VALUE;\r
+ uxRxLoops = 0UL;\r
\r
return xReturn;\r
}\r
separate stack for interrupts. */\r
unsigned long *pulISRStack;\r
\r
+/* If an interrupt requests a context switch then ulTaskSwitchRequested will\r
+get set to 1, which in turn will cause vTaskSwitchContext() to be called\r
+prior to a task context getting restored on exit from the interrupt. This\r
+mechanism is used as a single interrupt can cause multiple peripherals handlers\r
+to get called, and vTaskSwitchContext() should not get called in each handler. */\r
+volatile unsigned long ulTaskSwitchRequested = 0UL;\r
+\r
/* The instance of the interrupt controller used by this port. */\r
static XIntc xInterruptControllerInstance;\r
\r
}\r
/*-----------------------------------------------------------*/\r
\r
-/*\r
- * The task context has already been saved when this is called.\r
- *\r
- * This handler determines the interrupt source and calls the relevant \r
- * peripheral handler.\r
- */\r
-void vTaskISRHandler( void )\r
-{\r
-static unsigned long ulPending; \r
-static XIntc_VectorTableEntry *pxTablePtr;\r
-static XIntc_Config *pxConfig;\r
-static unsigned long ulInterruptMask;\r
-\r
- /* Which interrupts are pending? */\r
- ulPending = XIntc_In32( ( XPAR_INTC_SINGLE_BASEADDR + XIN_IVR_OFFSET ) );\r
-\r
- if( ulPending < XPAR_INTC_MAX_NUM_INTR_INPUTS )\r
- {\r
-\r
- ulInterruptMask = ( unsigned long ) 1 << ulPending;\r
-\r
- /* Get the configuration data using the device ID */\r
- pxConfig = &XIntc_ConfigTable[ ( unsigned long ) XPAR_INTC_SINGLE_DEVICE_ID ];\r
-\r
- pxTablePtr = &( pxConfig->HandlerTable[ ulPending ] );\r
- if( pxConfig->AckBeforeService & ( ulInterruptMask ) )\r
- {\r
- XIntc_AckIntr( pxConfig->BaseAddress, ulInterruptMask );\r
- pxTablePtr->Handler( pxTablePtr->CallBackRef );\r
- }\r
- else\r
- {\r
- pxTablePtr->Handler( pxTablePtr->CallBackRef );\r
- XIntc_AckIntr( pxConfig->BaseAddress, ulInterruptMask );\r
- }\r
- }\r
-}\r
-/*-----------------------------------------------------------*/\r
-\r
void vPortEnableInterrupt( unsigned char ucInterruptID )\r
{\r
XIntc_Enable( &xInterruptControllerInstance, ucInterruptID );\r
*/\r
void vTickISR( void *pvUnused )\r
{\r
-extern void vApplicationClearTimerInterrupt();\r
+extern void vApplicationClearTimerInterrupt( void );\r
\r
/* Ensure the unused parameter does not generate a compiler warning. */\r
( void ) pvUnused;\r
/* If we are using the preemptive scheduler then we also need to determine\r
if this tick should cause a context switch. */\r
#if configUSE_PREEMPTION == 1\r
- vTaskSwitchContext();\r
+ /* Force vTaskSwitchContext() to be called as the interrupt exits. */\r
+ ulTaskSwitchRequested = 1;\r
#endif\r
}\r
/*-----------------------------------------------------------*/\r