搜索
bottom↓
回复: 10

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

[复制链接]

出0入0汤圆

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

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2018-3-24 15:53:31 | 显示全部楼层
邮箱.xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );

出0入0汤圆

发表于 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个任务在运行。这是一个终端窗口视图,您可以在其中查看堆栈状态和堆内存测试结果。

出0入0汤圆

 楼主| 发表于 2018-3-24 15:56:33 | 显示全部楼层
凌海滨 发表于 2018-3-24 15:53
邮箱.xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );

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

出0入0汤圆

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

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

出0入4汤圆

发表于 2018-3-24 18:05:20 | 显示全部楼层
学习            

出0入0汤圆

发表于 2018-3-27 18:26:39 来自手机 | 显示全部楼层
freertos标记!

出0入0汤圆

发表于 2018-3-28 09:22:02 | 显示全部楼层
想问下,楼主的队列还是软件模拟实现的吧,如果大量数据接收,应该是会每个字节就进入中断,这样会照成系统资源占用过大,还是用dma能好一些吧!

出0入0汤圆

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

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

出0入0汤圆

发表于 2020-7-15 19:14:56 | 显示全部楼层
先标记一下

出0入42汤圆

发表于 2020-7-15 23:30:56 来自手机 | 显示全部楼层
Mark,做个记号
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-28 00:25

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表