*/\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
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