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