1. 背景

为了屏蔽不同OS之间的差别,ARM公司开发了一套OS接口--CMSIS_OS。

在使用STM32 cube生成的free rtos工程中,遇到一些问题。

问题1:osMessageGet 和 osMessagePut 发送和接收队列(结构体,数组等)。

问题2:osMailGet 和 osMailPut发送和接收队列(结构体,数组等)。

2. 问题分析( osMessagePut 和osMessageGet 为例)

osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
{
portBASE_TYPE taskWoken = pdFALSE;
TickType_t ticks; ticks = millisec / portTICK_PERIOD_MS;
if (ticks == ) {
ticks = ;
} if (inHandlerMode()) {
if (xQueueSendFromISR(queue_id, &info, &taskWoken) != pdTRUE) {
return osErrorOS;
}
portEND_SWITCHING_ISR(taskWoken);
}
else {
if (xQueueSend(queue_id, &info, ticks) != pdTRUE) {
return osErrorOS;
}
} return osOK;
}

发送函数本质是调用xQueueSendFromISR 和xQueueSend。这个函数调用

BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition )

继续调用

( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

最终调用

( void ) memcpy( ( void * ) pxQueue->u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize );

所以本质就是memcpy,把用户需要发送的数据拷贝到队列的内部内存保存。

osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)接口上,传入的只能是一个数据或指针,所以传入数组或结构体时候,就必须以指针方式传入。

对于osMessageGet ,通用关键的也是memcpy,不过包装后,有一点非常特殊。

和free rtos接口的调用时经过xQueueReceiveFromISR(queue_id, &event.value.v, &taskWoken)。

那么传入的产生就是一个指针,而后面的memcpy会往这个指针里拷贝内容。

所以如果创建的消息长度大于4个字节,指针自加,就会把队列里面的数据拷贝到栈空间的某一个未知的位置。

osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
{
portBASE_TYPE taskWoken;
TickType_t ticks;
osEvent event; event.def.message_id = queue_id;
event.value.v = ; if (queue_id == NULL) {
event.status = osErrorParameter;
return event;
} taskWoken = pdFALSE; ticks = ;
if (millisec == osWaitForever) {
ticks = portMAX_DELAY;
}
else if (millisec != ) {
ticks = millisec / portTICK_PERIOD_MS;
if (ticks == ) {
ticks = ;
}
} if (inHandlerMode()) {
if (xQueueReceiveFromISR(queue_id, &event.value.v, &taskWoken) == pdTRUE) {
/* We have mail */
event.status = osEventMessage;
}
else {
event.status = osOK;
}
portEND_SWITCHING_ISR(taskWoken);
}
else {
if (xQueueReceive(queue_id, &event.value.v, ticks) == pdTRUE) {
/* We have mail */
event.status = osEventMessage;
}
else {
event.status = (ticks == ) ? osOK : osEventTimeout;
}
} return event;
}

那么问题出现了,结构体或数组,必须以指针的方式传入,然后指针方式读出。

若生产者比消费者频率高,如何保证一包数据没有被接收前不能在发送端覆盖了。

本来这个工作free rtos已经做了,但是经过这么一封装之后,free rtos保护的只是一个指针而已。

实际的发送端,怎么保证源数据空间不被新数据覆盖,就需要用户考虑了。

好在cmsis考虑了这个情况。例子来源 https://os.mbed.com/handbook/CMSIS-RTOS

typedef struct {
float voltage; /* AD result of measured voltage */
float current; /* AD result of measured current */
uint32_t counter; /* A counter value */
} message_t; osPoolDef(mpool, , message_t);
osPoolId mpool; osMessageQDef(queue, , message_t);
osMessageQId queue; void send_thread (void const *args) {
uint32_t i = ;
while (true) {
i++; // fake data update
message_t *message = (message_t*)osPoolAlloc(mpool);
message->voltage = (i * 0.1) * ;
message->current = (i * 0.1) * ;
message->counter = i;
osMessagePut(queue, (uint32_t)message, osWaitForever);
osDelay();
}
} osThreadDef(send_thread, osPriorityNormal, DEFAULT_STACK_SIZE); int main (void) {
mpool = osPoolCreate(osPool(mpool));
queue = osMessageCreate(osMessageQ(queue), NULL); osThreadCreate(osThread(send_thread), NULL); while (true) {
osEvent evt = osMessageGet(queue, osWaitForever);
if (evt.status == osEventMessage) {
message_t *message = (message_t*)evt.value.p;
printf("\nVoltage: %.2f V\n\r" , message->voltage);
printf("Current: %.2f A\n\r" , message->current);
printf("Number of cycles: %u\n\r", message->counter); osPoolFree(mpool, message);
}
}
}

发送端:osPoolAlloc,从pool中申请空间。也就是说队列不在保存数据,只保存数据指针。

如果有N份数据,原来是N份保存在队列中,现在队列中只申请N个指针的空间。

发送数据时候,从pool中拿到一个空间,保存数据,然后发送指针给messageQ。

接收时候:从队列中取到指针的地址,然后使用数据。因为此数据在pool有空间,并且不会被覆盖,所以不需要在做拷贝到本地变量中。

数据使用完成之后,就可以释放空间,就是osPoolFree。

问题在哪里:

1)看发送,先osPoolAlloc,后osMessagePut。如果队列空间不够,在osMessagePut是可以设置阻塞时间的。

而osPoolAlloc没有阻塞时间,那么就会直接返回错误,上面的例子是官方的例子,就没有考虑申请空间失败的情况。

如果返回了错误,那么后面的osMessagePut超时机制就是空架子了,反正执行不到这里。

2)看接收,osMessageGet之后,队列计数就会减1,以便于发生端数据入队。

如果有数据等待入队,那么这里不会入队,因为靠osPoolFree释放pool后才会入队。所以时序要求高的应用下需要特别注意。

3)上面分析的同样的问题,申请队列时候,不能用sizeof队列大小作为itemSize。

上面的官方例子有bug,osMessageQDef(queue, 16, message_t); 这样申请会导致接收队列时候,free rtos按照itemSize大小拷贝内容到evt.value.p地址。

正确的方法是osMessageQDef(queue, 16, uing32_t)。

实际上这种用法下,已经不需要向队列拷贝具体的内容了,对列发数据前申请空间,收数据后释放空间。

osMailGet 和 osMailPut的问题和上面的messageQ一样,另外,还发现一个更严重的问题。

osMailPut没有做超时时间,实现里面调用的是xQueueSend(queue_id->handle, &mail, 0)。

本以为发送队列满的时候靠申请数据空间来做超时等待,查看源码发现根本没有。

osStatus osMailPut (osMailQId queue_id, void *mail)
{
portBASE_TYPE taskWoken; if (queue_id == NULL) {
return osErrorParameter;
} taskWoken = pdFALSE; if (inHandlerMode()) {
if (xQueueSendFromISR(queue_id->handle, &mail, &taskWoken) != pdTRUE) {
return osErrorOS;
}
portEND_SWITCHING_ISR(taskWoken);
}
else {
if (xQueueSend(queue_id->handle, &mail, ) != pdTRUE) {
return osErrorOS;
}
} return osOK;
}

因为在调用put之前,会先申请数据空间。而osMailAlloc调用osPoolAlloc,根本没有使用超时参数millisec。

void *osMailAlloc (osMailQId queue_id, uint32_t millisec)
{
(void) millisec;
void *p; if (queue_id == NULL) {
return NULL;
} p = osPoolAlloc(queue_id->pool); return p;
}

3. 其他问题

osMessagePut可以通过传递复杂数据结构的指针,来实现任务间通信。

不过这些数据结构应该是动态分配的,传入参数是pool空间内存的指针,不是任务中的局部变量和全局变量的指针,否则就hardfault。

调用是 message_t *message = (message_t*)osPoolAlloc(mpool);  和osMessagePut(queue, (uint32_t)message, osWaitForever);

入口是osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec);

内部调用是xQueueSend(queue_id, &info, ticks) != pdTRUE)

这里调用关系复杂,使用局部变量和全局变量的指针频频出错,原因还未知。

4. 附录

期待ST出面解决,Will come back to you soon..

CMSIS_OS中osMailPut 和 osMessagePut 的问题的更多相关文章

  1. Python开源框架

    info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...

  2. CMSIS Example - osMessageQ osMessagePut osMessageGet

    #include "cmsis_os.h" void Thread0( void * arg); void Thread1( void * arg); osThreadDef( T ...

  3. 系统封装接口层 cmsis_os

    在这个实时操作系统泛滥的年代,有这么一个系统封装接口层还是蛮有必要的.前些时间偶然间在STM32最新的固件库中就发现了这个系统封装接口,当时就把自己所用的系统进行封装.直到最近KEIL5.0发现其中所 ...

  4. mapreduce中一个map多个输入路径

    package duogemap; import java.io.IOException; import java.util.ArrayList; import java.util.List; imp ...

  5. Hadoop 中利用 mapreduce 读写 mysql 数据

    Hadoop 中利用 mapreduce 读写 mysql 数据   有时候我们在项目中会遇到输入结果集很大,但是输出结果很小,比如一些 pv.uv 数据,然后为了实时查询的需求,或者一些 OLAP ...

  6. Python中的多进程与多线程(一)

    一.背景 最近在Azkaban的测试工作中,需要在测试环境下模拟线上的调度场景进行稳定性测试.故而重操python旧业,通过python编写脚本来构造类似线上的调度场景.在脚本编写过程中,碰到这样一个 ...

  7. .NET Core中的认证管理解析

    .NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...

  8. Angular杂谈系列1-如何在Angular2中使用jQuery及其插件

    jQuery,让我们对dom的操作更加便捷.由于其易用性和可扩展性,jQuer也迅速风靡全球,各种插件也是目不暇接. 我相信很多人并不能直接远离jQuery去做前端,因为它太好用了,我们以前做的东西大 ...

  9. 关于CryptoJS中md5加密以及aes加密的随笔

    最近项目中用到了各种加密,其中就包括从没有接触过得aes加密,因此从网上各种查,官方的一种说法: 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学 ...

随机推荐

  1. php源码,php网站源码,php源码下载

    网址:http://www.aspku.com/php/ 有时间,可以研究研究.

  2. 【SR】正则化超分辨率复原

    正则化超分辨率图像重建算法研究--中国科学技术大学 硕士学位论文--路庆春 最大后验概率(MAP)的含义就是在低分辨率图像序列已知的前提下,使高分辨率图像出现的概率达到最大.

  3. ipv6修改DNS服务-首选DNS服务器:240c::6666

    下一代互联网国家工程中心推出的IPv6 DNS服务 首选DNS服务器:240c::6666 备用DNS服务器:240c::6644   来自下一代互联网国家工程中心官网消息显示,日前,下一代互联网国家 ...

  4. Python_selenium之处理Alert窗

    Python_selenium之处理Alert窗 一.介绍 1. 介绍如何通过switch_to方法处理网页Alert窗口 2. 然后我们自己创建一个alert弹窗进行操作 二.测试脚本 1. 测试脚 ...

  5. java动态编译 (java在线执行代码后端实现原理)(二)

    在上一篇java动态编译 (java在线执行代码后端实现原理(一))文章中实现了 字符串编译成字节码,然后通过反射来运行代码的demo.这一篇文章提供一个如何防止死循环的代码占用cpu的问题. 思路: ...

  6. 【BZOJ4621】Tc605 DP

    [BZOJ4621]Tc605 Description 最初你有一个长度为 N 的数字序列 A.为了方便起见,序列 A 是一个排列. 你可以操作最多 K 次.每一次操作你可以先选定一个 A 的一个子串 ...

  7. 【BZOJ3207】花神的嘲讽计划Ⅰ Hash+主席树

    [BZOJ3207]花神的嘲讽计划Ⅰ Description 背景 花神是神,一大癖好就是嘲讽大J,举例如下: “哎你傻不傻的![hqz:大笨J]” “这道题又被J屎过了!!” “J这程序怎么跑这么快 ...

  8. [POJ] Brackets Sequence

    This problem can be solved elegantly using dynamic programming. We maintain two arrays: cnt[i][j] -- ...

  9. 滚动插件 animatescroll(可以设置要滚动到位置)

     1. 引入   <script src="js/animatescroll.js"></script> 2.设置要滚动到的位置  $('目标位置').an ...

  10. java URL 利用网址api 查出手机号归属地

    手机号码归属地查询api接口 1.淘宝网API地址: http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=手机号码参数:tel:手机号码返 ...