freertos操作系统串口中断发送数据接收数据问题
最近刚开始上手操作系统,以前都是裸跑。有一点疑问请大家帮忙指点一下。在FreeRtos中串口中断接收数据。在中断函数里用中断队列发送函数发送数据到队列里,接收任务用队列中断接收函数接收函数完成一次完整的数据传输。
不过我查看了几个FREERTOS历程后,发现是没有用队列函数,只是按照裸跑程序来用一个固定长度的全局变量来实现数据的传输。
到底大家在平时使用时,用那种方式来实现中断任务里数据的传输?
邮箱.xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken ); 编写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 = xdata++;
}
xUSART0SendDataP(heapfull);
//read heap and test
xdata=1;
for(index = 0; index < BUFFER_SIZE; index++)
{
if (xmem != 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个任务在运行。这是一个终端窗口视图,您可以在其中查看堆栈状态和堆内存测试结果。 凌海滨 发表于 2018-3-24 15:53
邮箱.xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );
你接收的是一个字节吧,如果要是数据量多了的话,只能用队列函数吧
xQueueSendFromISR(Message_Queue,USART_RX_BUF,&xHigherPriorityTaskWoken); 凌海滨 发表于 2018-3-24 15:55
编写USART驱动程序
我们将需要USART功能来以便捷的方式调试和显示信息。所以首先我们需要可以在任务中使用 ...
谢谢你的解释与帮助,我看了你的回复,意思是直接用你给的函数就能实现串口的读取与发送 学习 freertos标记! 想问下,楼主的队列还是软件模拟实现的吧,如果大量数据接收,应该是会每个字节就进入中断,这样会照成系统资源占用过大,还是用dma能好一些吧! 木桥 发表于 2018-3-28 09:22
想问下,楼主的队列还是软件模拟实现的吧,如果大量数据接收,应该是会每个字节就进入中断,这样会照成系统 ...
DMA好像不够灵活吧,理论上是DMA方案更好 先标记一下 Mark,做个记号
页:
[1]