|
发表于 2018-3-24 15:55:38
|
显示全部楼层
编写USART驱动程序
我们将需要USART功能来以便捷的方式调试和显示信息。所以首先我们需要可以在任务中使用的驱动程序。使用USART可能是最方便的方式是通过队列发送消息。这样,任何任务都可以通过使用消息传递服务直接访问外设来添加与USART的通信。所以我们要实现两个队列 - 一个用于TX,另一个用于RX频道。
//receive and transmit queues
xQueueHandlexRxedChars=NULL;
xQueueHandlexCharsForTx=NULL;
然后在USART初始化期间,我们创建队列。
xRxedChars=xQueueCreate(uxQueueLength,(signedchar)sizeof(signedchar));
xCharsForTx=xQueueCreate(uxQueueLength,(signedchar)sizeof(signedchar));
队列长度在USART初始化时给出(在我们的例子中为30)。现在队列准备就绪时,它们可以用来与USART进行通信。通过两个自定义函数来放入和读取队列中的消息,使生活更轻松:
portBASE_TYPE xUSART0PutChar(unsigned char cOutChar)
{
//Return false if after the block time there is no room on the Tx queue.
if( xQueueSend( xCharsForTx, &cOutChar, xBlockTime ) != pdPASS )
{
return pdFAIL;
}
//enable usart UDRE interrupt to transmit
prvUDRIE0InterruptOn();
return pdPASS;
}
portBASE_TYPE xUSART0GetChar(unsigned char *pcRxedChar)
{
/* Get the next character from the buffer. Return false if no characters
are available, or arrive before xBlockTime expires. */
if( xQueueReceive( xRxedChars, pcRxedChar, xBlockTime ) )
{
return pdTRUE;
}
else
{
return pdFALSE;
}
}
当接收器队列中没有字符并且传输队列已满时,这些函数提供了额外的安全性。正如你可能注意到的那样,在xUSART0PutChar()中调用了一个私有函数prvUDRIE0InterruptOn()。一旦传输队列中至少有一个字符,这只会启用USART数据就绪中断。
USART发送和接收是通过中断例程执行的。
ISR( USART0_RX_vect )
{
signed char cChar;
signed portBASE_TYPE xHigherPriorityTaskWoken;
cChar = UDR0;
xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken );
}
ISR( USART0_UDRE_vect )
{
signed char cChar, cTaskWoken;
if( xQueueReceiveFromISR( xCharsForTx, &cChar, &cTaskWoken ) == pdTRUE )
{
/* Send the next character queued for Tx. */
UDR0 = cChar;
}
else
{
/* Queue empty, nothing to send. */
prvUDRIE0InterruptOff();
}
}
这是健全有效的沟通方式。如上所述 - 发射机ISR仅在有数据要在队列中传输时才启用。只有在RX缓冲区中存在任何数据时才会调用接收器ISR。
放单个字符并不方便发送消息。所以有两个额外的功能允许发送整串文本进行排队
1
portBASE_TYPExUSART0SendData(constunsignedchar*data)
JLCPCB - Prototype PCBs for $2 + Free Shipping on First Order
China’s Largest PCB Prototype Manufacturer, 290,000+ Customers & 8000+ Online Orders Per Day
10 PCBs Price: $2 for 2-layer, $15 for 4-layer, $74 for 6-layer
当字符串存储在RAM和
1
portBASE_TYPExUSART0SendDataP(constunsignedchar*data)
当消息从Flash发送时。为了节省宝贵的RAM,建议将静态文本消息存储在闪存中,如:
1
staticconstuint8_tbutton[]PROGMEM="Button ON\r\n";
这是适用于此演示程序的USART的基本实现。值得注意的是,对于单独的消息类型(如错误,实际数据)可以有不同的消息队列。这样,不同的消息在队列中混合的可能性就会降低。只要工作正常,我们就使用单队列发射器。
为了测试USART接收器,在LCD任务中添加一小段代码,它只是测试是否收到一个字符,然后显示在LCD屏幕上:
if (xUSART0GetChar(&rxchar)!=pdFALSE)
{
LCDGotoXY(14,0);
LCDsendChar(rxchar);
}
创建USART任务
当我们向列表添加新资源时,显然我们可以创建另一个演示任务,利用发送USART消息。
void vUSART0TxTask( void *pvParameters )
{
static const uint8_t button[] PROGMEM="Button ON\r\n";
static const uint8_t rtos[] PROGMEM="Button OFF\r\n";
portTickType xLastWakeTime;
const portTickType xFrequency = 2000;
vUSART0Init(30);
xLastWakeTime=xTaskGetTickCount();
for( ;; )
{
if(xButtonSemaphore!=NULL)
{
if (xSemaphoreTake(xButtonSemaphore, (portTickType)10)==pdTRUE)
{
xUSART0SendDataP(button);
//don't give back semaphore as it is one way trigger
}else{
xUSART0SendDataP(rtos);
}
}
vTaskDelayUntil(&xLastWakeTime,xFrequency);
}
}
这个任务只是从按钮任务中获取信号量,并在终端窗口中每2秒显示按钮状态。
XMEM测试任务
正如我们为我们的设备添加外部存储器,我们将测试其功能。首先我们用malloc()函数分配256个字节的堆内存。然后写一些虚拟数据,然后测试写入的数据是否正确。这确保了数据被物理写入外部RAM。测试状态消息显示在终端窗口中。
void vXMEMTestTask( void *pvParameters )
{
static const uint8_t xmemok[] PROGMEM="XMEM OK\r\n";
static const uint8_t xmemfail[] PROGMEM="XMEM FAIL!\r\n";
static const uint8_t heapfull[] PROGMEM="Heap Full\r\n";
static const uint8_t heaprdfail[] PROGMEM="Heap Test Fail\r\n";
static const uint8_t heaprdok[] PROGMEM="Heap Test OK\r\n";
portSHORT *xmem;
portSHORT xdata;
unsigned portSHORT index, testflag=0;
portTickType xLastWakeTime;
const portTickType xFrequency = 10000;
xmem = malloc(BUFFER_SIZE);
xLastWakeTime=xTaskGetTickCount();
if (xmem!=NULL)
{
xUSART0SendDataP(xmemok);
}
else
{
xUSART0SendDataP(xmemfail);
}
for (;;)
{
xdata=1;
for(index = 0; index < BUFFER_SIZE; index++)
{
xmem[index] = xdata++;
}
xUSART0SendDataP(heapfull);
//read heap and test
xdata=1;
for(index = 0; index < BUFFER_SIZE; index++)
{
if (xmem[index] != xdata++)
{
testflag=1;
break;
}
}
if (!testflag)
{
xUSART0SendDataP(heaprdok);
}
else
{
//reset flag
testflag=0;
xUSART0SendDataP(heaprdfail);
}
vTaskDelayUntil(&xLastWakeTime,xFrequency);
}
}
堆内存测试每10秒运行一次。
运行系统
到目前为止,我们已经在调度器中增加了两项任务:
xTaskCreate( vUSART0TxTask, ( signed char * ) "USART", configMINIMAL_STACK_SIZE, NULL, mainUSART_TASK_PRIORITY, NULL );
xTaskCreate( vXMEMTestTask, ( signed char * ) "XMEM", configMINIMAL_STACK_SIZE, NULL, mainXMEM_TASK_PRIORITY, NULL );
包括闲置任务,我们已经有6个任务在运行。这是一个终端窗口视图,您可以在其中查看堆栈状态和堆内存测试结果。 |
|