其实很简单 发表于 2018-3-24 15:50:27

freertos操作系统串口中断发送数据接收数据问题

最近刚开始上手操作系统,以前都是裸跑。有一点疑问请大家帮忙指点一下。
在FreeRtos中串口中断接收数据。在中断函数里用中断队列发送函数发送数据到队列里,接收任务用队列中断接收函数接收函数完成一次完整的数据传输。
不过我查看了几个FREERTOS历程后,发现是没有用队列函数,只是按照裸跑程序来用一个固定长度的全局变量来实现数据的传输。
到底大家在平时使用时,用那种方式来实现中断任务里数据的传输?

凌海滨 发表于 2018-3-24 15:53:31

邮箱.xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );

凌海滨 发表于 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 = 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:56:33

凌海滨 发表于 2018-3-24 15:53
邮箱.xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );

你接收的是一个字节吧,如果要是数据量多了的话,只能用队列函数吧
xQueueSendFromISR(Message_Queue,USART_RX_BUF,&xHigherPriorityTaskWoken);

其实很简单 发表于 2018-3-24 16:40:35

凌海滨 发表于 2018-3-24 15:55
编写USART驱动程序
我们将需要USART功能来以便捷的方式调试和显示信息。所以首先我们需要可以在任务中使用 ...

谢谢你的解释与帮助,我看了你的回复,意思是直接用你给的函数就能实现串口的读取与发送

ztrx 发表于 2018-3-24 18:05:20

学习            

32MCU 发表于 2018-3-27 18:26:39

freertos标记!

木桥 发表于 2018-3-28 09:22:02

想问下,楼主的队列还是软件模拟实现的吧,如果大量数据接收,应该是会每个字节就进入中断,这样会照成系统资源占用过大,还是用dma能好一些吧!

其实很简单 发表于 2018-3-28 14:11:06

木桥 发表于 2018-3-28 09:22
想问下,楼主的队列还是软件模拟实现的吧,如果大量数据接收,应该是会每个字节就进入中断,这样会照成系统 ...

DMA好像不够灵活吧,理论上是DMA方案更好

shen978541732 发表于 2020-7-15 19:14:56

先标记一下

ahfong2006 发表于 2020-7-15 23:30:56

Mark,做个记号
页: [1]
查看完整版本: freertos操作系统串口中断发送数据接收数据问题