Update Zynq serial.c to be interrupt driven.

pull/1/head
Richard Barry 11 years ago
parent d310ac4552
commit 6c72f470ac

@ -34,6 +34,11 @@
<type>2</type> <type>2</type>
<locationURI>FREERTOS_ROOT/FreeRTOS/Source</locationURI> <locationURI>FREERTOS_ROOT/FreeRTOS/Source</locationURI>
</link> </link>
<link>
<name>src/Sample-CLI-commands.c</name>
<type>1</type>
<locationURI>FREERTOS_ROOT/FreeRTOS-Plus/Demo/Common/FreeRTOS_Plus_CLI_Demos/Sample-CLI-commands.c</locationURI>
</link>
<link> <link>
<name>src/Standard_Demo_Tasks</name> <name>src/Standard_Demo_Tasks</name>
<type>2</type> <type>2</type>

@ -169,9 +169,9 @@ Zynq MPU. */
unsigned long ulGetRunTimeCounterValue( void ); unsigned long ulGetRunTimeCounterValue( void );
void vInitialiseRunTimeStats( void ); void vInitialiseRunTimeStats( void );
#define configGENERATE_RUN_TIME_STATS 0 #define configGENERATE_RUN_TIME_STATS 1
//#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vInitialiseRunTimeStats() #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vInitialiseRunTimeStats()
//#define portGET_RUN_TIME_COUNTER_VALUE() ulGetRunTimeCounterValue() #define portGET_RUN_TIME_COUNTER_VALUE() ulGetRunTimeCounterValue()
/* The size of the global output buffer that is available for use when there /* 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 are multiple command interpreters running at once (for example, one on a UART

@ -226,7 +226,6 @@ extern void vRegTest2Implementation( void );
* defined in CLI-Commands.c and File-Related-CLI-Command.c respectively. * defined in CLI-Commands.c and File-Related-CLI-Command.c respectively.
*/ */
extern void vRegisterSampleCLICommands( void ); extern void vRegisterSampleCLICommands( void );
extern void vRegisterFileSystemCLICommands( void );
/* /*
* The task that manages the FreeRTOS+CLI input and output. * 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 ); vUARTCommandConsoleStart( mainUART_COMMAND_CONSOLE_STACK_SIZE, mainUART_COMMAND_CONSOLE_TASK_PRIORITY );
/* Register the standard CLI commands. */ /* Register the standard CLI commands. */
// vRegisterSampleCLICommands(); vRegisterSampleCLICommands();
/* Create the register check tasks, as described at the top of this /* Create the register check tasks, as described at the top of this

@ -64,23 +64,24 @@
*/ */
/* /*
BASIC INTERRUPT DRIVEN SERIAL PORT DRIVER FOR UART2. BASIC INTERRUPT DRIVEN SERIAL PORT DRIVER.
***Note*** This example uses queues to send each character into an interrupt Note1: This driver is used specifically to provide an interface to the
service routine and out of an interrupt service routine individually. This FreeRTOS+CLI command interpreter. It is *not* intended to be a generic
is done to demonstrate queues being used in an interrupt, and to deliberately serial port driver. Nor is it intended to be used as an example of an
load the system to test the FreeRTOS port. It is *NOT* meant to be an efficient implementation. In particular, a queue is used to buffer
example of an efficient implementation. An efficient implementation should received characters, which is fine in this case as key presses arrive
use the DMA, and only use FreeRTOS API functions when enough has been slowly, but a DMA and/or RAM buffer should be used in place of the queue in
received to warrant a task being unblocked to process the data. applications that expect higher throughput.
*/
Note2: This driver does not attempt to handle UART errors.
*/
/* Scheduler includes. */ /* Scheduler includes. */
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
#include "queue.h" #include "queue.h"
#include "semphr.h" #include "semphr.h"
#include "comtest2.h"
/* Demo application includes. */ /* Demo application includes. */
#include "serial.h" #include "serial.h"
@ -90,14 +91,37 @@
#include "xscugic.h" #include "xscugic.h"
#include "xil_exception.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; static XUartPs xUARTInstance;
/* The interrupt controller, which is configred by the hardware setup routines
defined in main(). */
extern XScuGic xInterruptController; 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. * See the serial2.h header file.
@ -107,84 +131,186 @@ xComPortHandle xSerialPortInitMinimal( uint32_t ulWantedBaud, UBaseType_t uxQueu
BaseType_t xStatus; BaseType_t xStatus;
XUartPs_Config *pxConfig; 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. */ /* Look up the UART configuration then initialise the dirver. */
pxConfig = XUartPs_LookupConfig( XPAR_XUARTPS_0_DEVICE_ID ); 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 ); configASSERT( xStatus == XST_SUCCESS );
/* Misc. parameter configuration. */
XUartPs_SetBaudRate( &xUARTInstance, ulWantedBaud ); XUartPs_SetBaudRate( &xUARTInstance, ulWantedBaud );
XUartPs_SetOperMode( &xUARTInstance, XUARTPS_OPER_MODE_NORMAL ); 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 ) BaseType_t xSerialGetChar( xComPortHandle pxPort, signed char *pcRxedChar, portTickType xBlockTime )
{ {
TickType_t xTimeOnEntering; BaseType_t xReturn;
const TickType_t xDelay = 10UL / portTICK_PERIOD_MS;
BaseType_t xReturn = 0;
xTimeOnEntering = xTaskGetTickCount();
do /* Only a single port is supported. */
{ ( void ) pxPort;
/* 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 );
/* 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; return xReturn;
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength ) void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength )
{ {
static const xTxDelay = 10UL / portTICK_PERIOD_MS; const TickType_t xMaxWait = 200UL / portTICK_PERIOD_MS;
uint32_t ulBytesSent = 0UL;
/* Only a single port is supported. */
( void ) pxPort; ( void ) pxPort;
while( ulBytesSent < usStringLength ) /* Start the transmission. The interrupt service routine will complete the
{ transmission if necessary. */
ulBytesSent += XUartPs_Send( &xUARTInstance, pcString + ulBytesSent, usStringLength - ulBytesSent ); XUartPs_Send( &xUARTInstance, ( void * ) pcString, usStringLength );
while( XUartPs_IsSending( &xUARTInstance ) ) /* Wait until the string has been transmitted before exiting this function,
{ otherwise there is a risk the calling function will overwrite the string
vTaskDelay( xTxDelay ); 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 ) 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 ) ) /* Wait for the transmission to be complete so the mutex is left in the
{ correct state for the next time vSerialPutString() is called. */
vTaskDelay( xTxDelay ); xSemaphoreTake( xTxCompleteSemaphore, xBlockTime );
}
return 0; return pdPASS;
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
void vSerialClose(xComPortHandle xPort) void vSerialClose(xComPortHandle xPort)
{ {
/* Not supported as not required by the demo application. */ /* 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 );
}

Loading…
Cancel
Save