]> git.sur5r.net Git - freertos/commitdiff
Update Zynq serial.c to be interrupt driven.
authorrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>
Wed, 29 Jan 2014 15:20:34 +0000 (15:20 +0000)
committerrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>
Wed, 29 Jan 2014 15:20:34 +0000 (15:20 +0000)
git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@2185 1d2547de-c912-0410-9cb9-b8ca96c0e9e2

FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/.project
FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOSConfig.h
FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/main_full.c
FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/serial.c

index 15d9fcbe6ce7091caa46f8ad8572fde20394951a..1d9dcfbc6d4c48e8b2058b19cb3aa9699d307aaf 100644 (file)
                        <type>2</type>\r
                        <locationURI>FREERTOS_ROOT/FreeRTOS/Source</locationURI>\r
                </link>\r
+               <link>\r
+                       <name>src/Sample-CLI-commands.c</name>\r
+                       <type>1</type>\r
+                       <locationURI>FREERTOS_ROOT/FreeRTOS-Plus/Demo/Common/FreeRTOS_Plus_CLI_Demos/Sample-CLI-commands.c</locationURI>\r
+               </link>\r
                <link>\r
                        <name>src/Standard_Demo_Tasks</name>\r
                        <type>2</type>\r
index 14a58f5952a76b96747623bcb1e0da682394be81..53d62550feb0995979accfe767f0dad66645f5be 100644 (file)
@@ -169,9 +169,9 @@ Zynq MPU. */
 unsigned long ulGetRunTimeCounterValue( void );\r
 void vInitialiseRunTimeStats( void );\r
 \r
-#define configGENERATE_RUN_TIME_STATS  0\r
-//#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vInitialiseRunTimeStats()\r
-//#define portGET_RUN_TIME_COUNTER_VALUE() ulGetRunTimeCounterValue()\r
+#define configGENERATE_RUN_TIME_STATS  1\r
+#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vInitialiseRunTimeStats()\r
+#define portGET_RUN_TIME_COUNTER_VALUE() ulGetRunTimeCounterValue()\r
 \r
 /* The size of the global output buffer that is available for use when there\r
 are multiple command interpreters running at once (for example, one on a UART\r
index 081e0f5a2c28cc2ca3200018aa637e2f16d015d3..4a37911f781bf0e1afd590bfafb6e9c8756fc354 100644 (file)
@@ -226,7 +226,6 @@ extern void vRegTest2Implementation( void );
  * defined in CLI-Commands.c and File-Related-CLI-Command.c respectively.\r
  */\r
 extern void vRegisterSampleCLICommands( void );\r
-extern void vRegisterFileSystemCLICommands( void );\r
 \r
 /*\r
  * The task that manages the FreeRTOS+CLI input and output.\r
@@ -264,7 +263,7 @@ void main_full( void )
        vUARTCommandConsoleStart( mainUART_COMMAND_CONSOLE_STACK_SIZE, mainUART_COMMAND_CONSOLE_TASK_PRIORITY );\r
 \r
        /* Register the standard CLI commands. */\r
-//     vRegisterSampleCLICommands();\r
+       vRegisterSampleCLICommands();\r
 \r
 \r
        /* Create the register check tasks, as described at the top of this\r
index 53e7fcdb16c68e5320d511696f5b0723ee48c0d0..cf154578b16742d6fdca68c27e4bc0f56fed0bd8 100644 (file)
  */\r
 \r
 /*\r
- BASIC INTERRUPT DRIVEN SERIAL PORT DRIVER FOR UART2.\r
-\r
- ***Note*** This example uses queues to send each character into an interrupt\r
- service routine and out of an interrupt service routine individually.  This\r
- is done to demonstrate queues being used in an interrupt, and to deliberately\r
- load the system to test the FreeRTOS port.  It is *NOT* meant to be an\r
- example of an efficient implementation.  An efficient implementation should\r
- use the DMA, and only use FreeRTOS API functions when enough has been\r
- received to warrant a task being unblocked to process the data.\r
- */\r
+       BASIC INTERRUPT DRIVEN SERIAL PORT DRIVER.\r
+\r
+       Note1:  This driver is used specifically to provide an interface to the\r
+       FreeRTOS+CLI command interpreter.  It is *not* intended to be a generic\r
+       serial port driver.  Nor is it intended to be used as an example of an\r
+       efficient implementation.  In particular, a queue is used to buffer\r
+       received characters, which is fine in this case as key presses arrive\r
+       slowly, but a DMA and/or RAM buffer should be used in place of the queue in\r
+       applications that expect higher throughput.\r
+\r
+       Note2:  This driver does not attempt to handle UART errors.\r
+*/\r
 \r
 /* Scheduler includes. */\r
 #include "FreeRTOS.h"\r
 #include "task.h"\r
 #include "queue.h"\r
 #include "semphr.h"\r
-#include "comtest2.h"\r
 \r
 /* Demo application includes. */\r
 #include "serial.h"\r
 #include "xscugic.h"\r
 #include "xil_exception.h"\r
 \r
+/* The UART interrupts of interest when receiving. */\r
+#define serRECEIVE_INTERRUPT_MASK      ( XUARTPS_IXR_RXOVR | XUARTPS_IXR_RXFULL | XUARTPS_IXR_TOUT )\r
+\r
+/* The UART interrupts of interest when transmitting. */\r
+#define serTRANSMIT_IINTERRUPT_MASK ( XUARTPS_IXR_TXEMPTY )\r
+\r
 /*-----------------------------------------------------------*/\r
 \r
+/* The UART being used. */\r
 static XUartPs xUARTInstance;\r
+\r
+/* The interrupt controller, which is configred by the hardware setup routines\r
+defined in main(). */\r
 extern XScuGic xInterruptController;\r
 \r
+/* The queue into which received key presses are placed.  NOTE THE COMMENTS AT\r
+THE TOP OF THIS FILE REGARDING THE USE OF QUEUES FOR THIS PURPOSE. */\r
+static QueueHandle_t xRxQueue = NULL;\r
+\r
+/* The semaphore used to indicate the end of a transmission. */\r
+static SemaphoreHandle_t xTxCompleteSemaphore = NULL;\r
+\r
 /*-----------------------------------------------------------*/\r
 \r
-static void prvISRHandler( void *pvUnused, uint32_t ulEvent, uint32_t ulUnused2 );\r
+/*\r
+ * The UART interrupt handler is defined in this file to provide more control,\r
+ * but still uses parts of the Xilinx provided driver.\r
+ */\r
+void prvUART_Handler( void *pvNotUsed );\r
+\r
+/*-----------------------------------------------------------*/\r
 \r
 /*\r
  * See the serial2.h header file.\r
@@ -107,84 +131,186 @@ xComPortHandle xSerialPortInitMinimal( uint32_t ulWantedBaud, UBaseType_t uxQueu
 BaseType_t xStatus;\r
 XUartPs_Config *pxConfig;\r
 \r
+       /* Create the queue used to hold received characters.  NOTE THE COMMENTS AT\r
+       THE TOP OF THIS FILE REGARDING THE QUEUE OF QUEUES FOR THIS PURPSOE. */\r
+       xRxQueue = xQueueCreate( uxQueueLength, sizeof( char ) );\r
+       configASSERT( xRxQueue );\r
+\r
+       /* Create the semaphore used to signal the end of a transmission, then take\r
+       the semaphore so it is in the correct state the first time\r
+       xSerialSendString() is called.  A block time of zero is used when taking\r
+       the semaphore as it is guaranteed to be available (it was just created). */\r
+       xTxCompleteSemaphore = xSemaphoreCreateMutex();\r
+       configASSERT( xTxCompleteSemaphore );\r
+       xSemaphoreTake( xTxCompleteSemaphore, 0 );\r
+\r
        /* Look up the UART configuration then initialise the dirver. */\r
        pxConfig = XUartPs_LookupConfig( XPAR_XUARTPS_0_DEVICE_ID );\r
-       configASSERT( pxConfig );\r
 \r
-       xStatus = XUartPs_CfgInitialize( &xUARTInstance, pxConfig, pxConfig->BaseAddress );\r
+       /* Initialise the driver. */\r
+       xStatus = XUartPs_CfgInitialize( &xUARTInstance, pxConfig, XPAR_PS7_UART_1_BASEADDR );\r
        configASSERT( xStatus == XST_SUCCESS );\r
 \r
+       /* Misc. parameter configuration. */\r
        XUartPs_SetBaudRate( &xUARTInstance, ulWantedBaud );\r
-\r
        XUartPs_SetOperMode( &xUARTInstance, XUARTPS_OPER_MODE_NORMAL );\r
 \r
-       return 0;\r
+       /* Install the interrupt service routine that is defined within this\r
+       file. */\r
+       xStatus = XScuGic_Connect( &xInterruptController, XPAR_XUARTPS_1_INTR,  (Xil_ExceptionHandler) prvUART_Handler, (void *) &xUARTInstance );\r
+       configASSERT( xStatus == XST_SUCCESS );\r
+\r
+       /* Ensure interrupts start clear. */\r
+       XUartPs_WriteReg( XPAR_PS7_UART_1_BASEADDR, XUARTPS_ISR_OFFSET, XUARTPS_IXR_MASK );\r
+\r
+       /* Enable the UART interrupt within the GIC. */\r
+       XScuGic_Enable( &xInterruptController, XPAR_XUARTPS_1_INTR );\r
+\r
+       /* Enable the interrupts of interest in the UART. */\r
+       XUartPs_SetInterruptMask( &xUARTInstance, XUARTPS_IXR_RXFULL | XUARTPS_IXR_RXOVR | XUARTPS_IXR_TOUT | XUARTPS_IXR_TXEMPTY );\r
+\r
+       /* Set the receive timeout. */\r
+       XUartPs_SetRecvTimeout( &xUARTInstance, 8 );\r
+\r
+       return ( xComPortHandle ) 0;\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
 BaseType_t xSerialGetChar( xComPortHandle pxPort, signed char *pcRxedChar, portTickType xBlockTime )\r
 {\r
-TickType_t xTimeOnEntering;\r
-const TickType_t xDelay = 10UL / portTICK_PERIOD_MS;\r
-BaseType_t xReturn = 0;\r
-\r
-       xTimeOnEntering = xTaskGetTickCount();\r
+BaseType_t xReturn;\r
 \r
-       do\r
-       {\r
-               /* Only wanting to receive one key press at a time. */\r
-               if( XUartPs_Recv( &xUARTInstance, pcRxedChar, sizeof( pcRxedChar ) ) != 0 )\r
-               {\r
-                       xReturn = 1;\r
-                       break;\r
-               }\r
-               else\r
-               {\r
-                       vTaskDelay( xDelay );\r
-               }\r
-       } while( ( xTaskGetTickCount() - xTimeOnEntering ) <= xBlockTime );\r
+       /* Only a single port is supported. */\r
+       ( void ) pxPort;\r
 \r
+       /* Obtain a received character from the queue - entering the Blocked state\r
+       (so not consuming any processing time) to wait for a character if one is not\r
+       already available. */\r
+       xReturn = xQueueReceive( xRxQueue, pcRxedChar, xBlockTime );\r
        return xReturn;\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
 void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength )\r
 {\r
-static const xTxDelay = 10UL / portTICK_PERIOD_MS;\r
-uint32_t ulBytesSent = 0UL;\r
+const TickType_t xMaxWait = 200UL / portTICK_PERIOD_MS;\r
 \r
+       /* Only a single port is supported. */\r
        ( void ) pxPort;\r
 \r
-       while( ulBytesSent < usStringLength )\r
-       {\r
-               ulBytesSent += XUartPs_Send( &xUARTInstance, pcString + ulBytesSent, usStringLength - ulBytesSent );\r
+       /* Start the transmission.  The interrupt service routine will complete the\r
+       transmission if necessary. */\r
+       XUartPs_Send( &xUARTInstance, ( void * ) pcString, usStringLength );\r
 \r
-               while( XUartPs_IsSending( &xUARTInstance ) )\r
-               {\r
-                       vTaskDelay( xTxDelay );\r
-               }\r
-       }\r
+       /* Wait until the string has been transmitted before exiting this function,\r
+       otherwise there is a risk the calling function will overwrite the string\r
+       pointed to by the pcString parameter while it is still being transmitted.\r
+       The calling task will wait in the Blocked state (so not consuming any\r
+       processing time) until the mutex is available. */\r
+       xSemaphoreTake( xTxCompleteSemaphore, xMaxWait );\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
 signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, portTickType xBlockTime )\r
 {\r
-static const xTxDelay = 10UL / portTICK_PERIOD_MS;\r
+       /* Only a single port is supported. */\r
+       ( void ) pxPort;\r
 \r
-       XUartPs_Send( &xUARTInstance, &cOutChar, sizeof( cOutChar ) );\r
+       /* Send the character. */\r
+       XUartPs_Send( &xUARTInstance, ( void * ) &cOutChar, sizeof( cOutChar ) );\r
 \r
-       while( XUartPs_IsSending( &xUARTInstance ) )\r
-       {\r
-               vTaskDelay( xTxDelay );\r
-       }\r
+       /* Wait for the transmission to be complete so the mutex is left in the\r
+       correct state for the next time vSerialPutString() is called. */\r
+       xSemaphoreTake( xTxCompleteSemaphore, xBlockTime );\r
 \r
-       return 0;\r
+       return pdPASS;\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
 void vSerialClose(xComPortHandle xPort)\r
 {\r
        /* Not supported as not required by the demo application. */\r
+       ( void ) xPort;\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
+void prvUART_Handler( void *pvNotUsed )\r
+{\r
+extern unsigned int XUartPs_SendBuffer( XUartPs *InstancePtr );\r
+uint32_t ulActiveInterrupts, ulChannelStatusRegister;\r
+BaseType_t xHigherPriorityTaskWoken = pdFALSE;\r
+char cChar;\r
+\r
+       configASSERT( pvNotUsed == &xUARTInstance );\r
+\r
+       /* Read the interrupt ID register to see which interrupt is active. */\r
+       ulActiveInterrupts = XUartPs_ReadReg(XPAR_PS7_UART_1_BASEADDR,  XUARTPS_IMR_OFFSET);\r
+       ulActiveInterrupts &= XUartPs_ReadReg(XPAR_PS7_UART_1_BASEADDR,  XUARTPS_ISR_OFFSET);\r
+\r
+       /* Are any receive events of interest active? */\r
+       if( ( ulActiveInterrupts & serRECEIVE_INTERRUPT_MASK ) != 0 )\r
+       {\r
+               /* Read the Channel Status Register to determine if there is any data in\r
+               the RX FIFO. */\r
+               ulChannelStatusRegister = XUartPs_ReadReg( XPAR_PS7_UART_1_BASEADDR, XUARTPS_SR_OFFSET );\r
+\r
+               /* Move data from the Rx FIFO to the Rx queue.  NOTE THE COMMENTS AT THE\r
+               TOP OF THIS FILE ABOUT USING QUEUES FOR THIS PURPSOE. */\r
+               while( ( ulChannelStatusRegister & XUARTPS_SR_RXEMPTY ) == 0 )\r
+               {\r
+                       cChar = XUartPs_ReadReg( XPAR_PS7_UART_1_BASEADDR, XUARTPS_FIFO_OFFSET );\r
+\r
+                       /* If writing to the queue unblocks a task, and the unblocked task\r
+                       has a priority above the currently running task (the task that this\r
+                       interrupt interrupted), then xHigherPriorityTaskWoken will be set\r
+                       to pdTRUE inside the xQueueSendFromISR() function.\r
+                       xHigherPriorityTaskWoken is then passed to portYIELD_FROM_ISR() at\r
+                       the end of this interrupt handler to request a context switch so the\r
+                       interrupt returns directly to the (higher priority) unblocked\r
+                       task. */\r
+                       xQueueSendFromISR( xRxQueue, &cChar, &xHigherPriorityTaskWoken );\r
+                       ulChannelStatusRegister = XUartPs_ReadReg( XPAR_PS7_UART_1_BASEADDR, XUARTPS_SR_OFFSET );\r
+               }\r
+       }\r
+\r
+       /* Are any transmit events of interest active? */\r
+       if( ( ulActiveInterrupts & serTRANSMIT_IINTERRUPT_MASK ) != 0 )\r
+       {\r
+               if( xUARTInstance.SendBuffer.RemainingBytes == 0 )\r
+               {\r
+                       /* Give back the semaphore to indicate that the tranmission is\r
+                       complete.  If giving the semaphore unblocks a task, and the\r
+                       unblocked task has a priority above the currently running task (the\r
+                       task that this interrupt interrupted), then xHigherPriorityTaskWoken\r
+                       will be set     to pdTRUE inside the xSemaphoreGiveFromISR() function.\r
+                       xHigherPriorityTaskWoken is then passed to portYIELD_FROM_ISR() at\r
+                       the end of this interrupt handler to request a context switch so the\r
+                       interrupt returns directly to the (higher priority) unblocked\r
+                       task. */\r
+                       xSemaphoreGiveFromISR( xTxCompleteSemaphore, &xHigherPriorityTaskWoken );\r
+\r
+                       /* No more data to transmit. */\r
+                       XUartPs_WriteReg( XPAR_PS7_UART_1_BASEADDR, XUARTPS_IDR_OFFSET, XUARTPS_IXR_TXEMPTY );\r
+               }\r
+               else\r
+               {\r
+                       /* More data to send. */\r
+                       XUartPs_SendBuffer( &xUARTInstance );\r
+               }\r
+       }\r
+\r
+       /* portYIELD_FROM_ISR() will request a context switch if executing this\r
+       interrupt handler caused a task to leave the blocked state, and the task\r
+       that left the blocked state has a higher priority than the currently running\r
+       task (the task this interrupt interrupted).  See the comment above the calls\r
+       to xSemaphoreGiveFromISR() and xQueueSendFromISR() within this function. */\r
+       portYIELD_FROM_ISR( xHigherPriorityTaskWoken );\r
+\r
+       /* Clear the interrupt status. */\r
+       XUartPs_WriteReg( XPAR_PS7_UART_1_BASEADDR, XUARTPS_ISR_OFFSET, ulActiveInterrupts );\r
+}\r
+\r
+\r
+\r
+\r
+\r