Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
UART
2.1.0版本串口驱动中队列函数rt_err_t rt_data_queue_push的疑问
发布于 2017-08-12 14:34:03 浏览:5585
订阅该版
```rt_err_t rt_data_queue_push(struct rt_data_queue *queue, const void *data_ptr, rt_size_t data_size, rt_int32_t timeout) { rt_uint16_t mask; rt_ubase_t level; rt_thread_t thread; rt_err_t result; RT_ASSERT(queue != RT_NULL); result = RT_EOK; thread = rt_thread_self(); mask = queue->size - 1; level = rt_hw_interrupt_disable(); while (queue->put_index - queue->get_index == queue->size) { queue->waiting_lwm = RT_TRUE; /* queue is full */ if (timeout == 0) { result = -RT_ETIMEOUT; goto __exit; } /* current context checking */ RT_DEBUG_NOT_IN_INTERRUPT; /* reset thread error number */ thread->error = RT_EOK; /* suspend thread on the push list */ rt_thread_suspend(thread); rt_list_insert_before(&(queue->suspended_push_list), &(thread->tlist)); /* start timer */ if (timeout > 0) { /* reset the timeout of thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); rt_timer_start(&(thread->thread_timer)); } /* enable interrupt */ rt_hw_interrupt_enable(level); /* do schedule */ rt_schedule(); /* thread is waked up */ result = thread->error; level = rt_hw_interrupt_disable(); if (result != RT_EOK) goto __exit; } queue->queue[queue->put_index & mask].data_ptr = data_ptr; queue->queue[queue->put_index & mask].data_size = data_size; queue->put_index += 1; if (!rt_list_isempty(&(queue->suspended_pop_list))) { /* there is at least one thread in suspended list */ /* get thread entry */ thread = rt_list_entry(queue->suspended_pop_list.next, struct rt_thread, tlist); /* resume it */ rt_thread_resume(thread); rt_hw_interrupt_enable(level); /* perform a schedule */ rt_schedule(); return result; } __exit: rt_hw_interrupt_enable(level); if ((result == RT_EOK) && queue->evt_notify != RT_NULL) { queue->evt_notify(queue, RT_DATAQUEUE_EVENT_PUSH); } return result; }``` 最近一直在看RTT的串口的驱动,虽说串口简单,但对于新人来说还是想仔细研究一下,大神略过。 这个函数是在串口DMA发送是调用的,根据名字可以看出是push到队列中(队列就是先进先出的fifo),既然有push那么肯定会有pop。pop函数在中断里,也就是dma发送的中断里。开始对这个函数不是很理解,多看几遍终于弄得差不多懂了。 1、push函数就是往队列里写数据,如果队列满则挂起当前任务,并将任务放到push的suspend队列中,函数中queue->waiting_lwm就是标记push队列满有挂起的任务(在pop函数中会用到这个变量,根据条件会resume在 push中的挂起的任务)。 2、对于put_index和get_index为什么没有限制范围呢?这里用到了mask,每次取值或者读取数值都有 queue->put_index & mask 这里将范围限制在离mask,感觉很是巧妙,学习了。
查看更多
11
个回答
默认排序
按发布时间排序
qq_哎
2017-08-12
这家伙很懒,什么也没写!
``` rt_err_t rt_data_queue_pop(struct rt_data_queue *queue, const void** data_ptr, rt_size_t *size, rt_int32_t timeout) { rt_ubase_t level; rt_thread_t thread; rt_err_t result; rt_uint16_t mask; RT_ASSERT(queue != RT_NULL); RT_ASSERT(data_ptr != RT_NULL); RT_ASSERT(size != RT_NULL); result = RT_EOK; thread = rt_thread_self(); mask = queue->size - 1; level = rt_hw_interrupt_disable(); while (queue->get_index == queue->put_index) { /* queue is empty */ if (timeout == 0) { result = -RT_ETIMEOUT; goto __exit; } /* current context checking */ RT_DEBUG_NOT_IN_INTERRUPT; /* reset thread error number */ thread->error = RT_EOK; /* suspend thread on the pop list */ rt_thread_suspend(thread); rt_list_insert_before(&(queue->suspended_pop_list), &(thread->tlist)); /* start timer */ if (timeout > 0) { /* reset the timeout of thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); rt_timer_start(&(thread->thread_timer)); } /* enable interrupt */ rt_hw_interrupt_enable(level); /* do schedule */ rt_schedule(); /* thread is waked up */ result = thread->error; level = rt_hw_interrupt_disable(); if (result != RT_EOK) goto __exit; } *data_ptr = queue->queue[queue->get_index & mask].data_ptr; *size = queue->queue[queue->get_index & mask].data_size; queue->get_index += 1; if ((queue->waiting_lwm == RT_TRUE) && (queue->put_index - queue->get_index) <= queue->lwm) { queue->waiting_lwm = RT_FALSE; /* * there is at least one thread in suspended list * and less than low water mark */ if (!rt_list_isempty(&(queue->suspended_push_list))) { /* get thread entry */ thread = rt_list_entry(queue->suspended_push_list.next, struct rt_thread, tlist); /* resume it */ rt_thread_resume(thread); rt_hw_interrupt_enable(level); /* perform a schedule */ rt_schedule(); } if (queue->evt_notify != RT_NULL) queue->evt_notify(queue, RT_DATAQUEUE_EVENT_LWM); return result; } __exit: rt_hw_interrupt_enable(level); if ((result == RT_EOK) && (queue->evt_notify != RT_NULL)) { queue->evt_notify(queue, RT_DATAQUEUE_EVENT_POP); } return result; } ``` 这个是pop函数,这个是判断是否resume 挂起的push任务的条件 ``` if ((queue->waiting_lwm == RT_TRUE) && (queue->put_index - queue->get_index) <= queue->lwm) ``` 除了waiting_lwm ,另一个是lwm,lwm的值是在初始化设置的,我看初始化size=8,lwm=4,这个值是要设置为size的一半吗?
qq_哎
2017-08-12
这家伙很懒,什么也没写!
``` rt_err_t rt_data_queue_peak(struct rt_data_queue *queue, const void** data_ptr, rt_size_t *size) { rt_ubase_t level; rt_uint16_t mask; RT_ASSERT(queue != RT_NULL); mask = queue->size - 1; level = rt_hw_interrupt_disable(); if (queue->get_index == queue->put_index) { rt_hw_interrupt_enable(level); return -RT_EEMPTY; } *data_ptr = queue->queue[queue->get_index & mask].data_ptr; *size = queue->queue[queue->get_index & mask].data_size; rt_hw_interrupt_enable(level); return RT_EOK; } ``` 这个是peak函数,跟pop函数很相似,区别是没有resume任务和读取完了以后get_index没有加1. 下面是调用函数的一部分 ``` rt_data_queue_pop(&(tx_dma->data_queue), &last_data_ptr, &data_size, 0); if (rt_data_queue_peak(&(tx_dma->data_queue), &data_ptr, &data_size) == RT_EOK) { /* transmit next data node */ tx_dma->activated = RT_TRUE; serial->ops->dma_transmit(serial, data_ptr, data_size, RT_SERIAL_DMA_TX); } else { tx_dma->activated = RT_FALSE; } /* invoke callback */ if (serial->parent.tx_complete != RT_NULL) { serial->parent.tx_complete(&serial->parent, (void*)last_data_ptr); } ``` 1、这里为什么读了两次queue中的数据? 2、第一次读取用的pop,数据放在last_data_ptr中,这是最后的数据?这个在coplete中发送,为什么这么操作?没看太懂哦 3、第二次读取用的peak函数,数据放在data_ptr然后dma发送。
bernard
2017-08-12
这家伙很懒,什么也没写!
peak的意思是,舌尖舔一下,看看是否有数据,并不拿取。pop的意思是,要把数据拿走。
armink
2017-08-12
这家伙很懒,什么也没写!
建议看下 GitHub 上最新的 dataqueue 源码,做过一些 Bug 修复,同时 ``` queue->put_index & mask ``` 也改为 ``` queue->put_index % queue->size ``` 了。
qq_哎
2017-08-12
这家伙很懒,什么也没写!
>建议看下 GitHub 上最新的 dataqueue 源码,做过一些 Bug 修复,同时 > > >``` >queue->put_index & mask >``` > > > 也改为 > > >``` >queue->put_index % queue->size >``` > > > 了。 --- 好的谢谢啊,我去看看,但是对于刚才大神的指点还是有些疑问,总感觉丢了一组数据。。。
armink
2017-08-12
这家伙很懒,什么也没写!
疑惑的话,可以自己做一个 finsh 命令不停地 push/pop 试一试就知道了。
qq_哎
2017-08-12
这家伙很懒,什么也没写!
>疑惑的话,可以自己做一个 finsh 命令不停地 push/pop 试一试就知道了。 --- 嗯,好的,我用finsh试一试
qq_哎
2017-08-12
这家伙很懒,什么也没写!
>疑惑的话,可以自己做一个 finsh 命令不停地 push/pop 试一试就知道了。 --- 想了一下用finsh做这个的话有点麻烦,,, 总是感觉每次发数据调用一下两个函数后,会把第一个数据丢了 rt_data_queue_pop rt_data_queue_peak 因为pop读完queue的数据(读取到last_data中)后会使index加1,而后peak在读取到的是index加1的数据发送。如果通过peak判断不是最后一个数据,那么读取到last_data中的数据就丢了啊?
armink
2017-08-13
这家伙很懒,什么也没写!
>>疑惑的话,可以自己做一个 finsh 命令不停地 push/pop 试一试就知道了。 > >--- > > >想了一下用finsh做这个的话有点麻烦,,, >总是感觉每次发数据调用一下两个函数后,会把第一个数据丢了 >rt_data_queue_pop >rt_data_queue_peak >因为pop读完queue的数据(读取到last_data中)后会使index加1,而后peak在读取到的是index加1的数据发送。如果通过peak判断不是最后一个数据,那么读取到last_data中的数据就丢了啊? --- 写个命令一点都不麻烦吧,自己手动测试下,才能更直接的验证自己的想法。 帮你找了一个,我之前写的测试命令。 ``` static void dq(uint8_t argc, char **argv) { static struct rt_data_queue data_queue; if (argc >= 2) { if (!strcmp(argv[1], "init")) { rt_data_queue_init(&data_queue, 10, 1, NULL); } else if (!strcmp(argv[1], "push")) { static int i = 10; char *data = rt_malloc(++i); rt_data_queue_push(&data_queue, data, i, 20000); rt_kprintf("pop data size %d ", i); } else if (!strcmp(argv[1], "pop")) { char *data = NULL; rt_size_t size = 0; if(rt_data_queue_pop(&data_queue, (void const **)&data, &size, 20000) == RT_EOK) { rt_kprintf("pop data size %d ", size); rt_free(data); } } } } MSH_CMD_EXPORT(dq, dataqueue test 'dq
'); ```
qq_哎
2017-08-13
这家伙很懒,什么也没写!
>>>疑惑的话,可以自己做一个 finsh 命令不停地 push/pop 试一试就知道了。 >> >>--- >> >> >>想了一下用finsh做这个的话有点麻烦,,, >>总是感觉每次发数据调用一下两个函数后,会把第一个数据丢了 >>rt_data_queue_pop >>rt_data_queue_peak >>因为pop读完queue的数据(读取到last_data中)后会使index加1,而后peak在读取到的是index加1的数据发送。如果通过peak判断不是最后一个数据,那么读取到last_data中的数据就丢了啊? > >--- > > > >写个命令一点都不麻烦吧,自己手动测试下,才能更直接的验证自己的想法。 > >帮你找了一个,我之前写的测试命令。 > > > >``` >static void dq(uint8_t argc, char **argv) { > static struct rt_data_queue data_queue; > > if (argc >= 2) { > if (!strcmp(argv[1], "init")) { > rt_data_queue_init(&data_queue, 10, 1, NULL); > } else if (!strcmp(argv[1], "push")) { > static int i = 10; > char *data = rt_malloc(++i); > rt_data_queue_push(&data_queue, data, i, 20000); > rt_kprintf("pop data size %d >", i); > } else if (!strcmp(argv[1], "pop")) { > char *data = NULL; > rt_size_t size = 0; > if(rt_data_queue_pop(&data_queue, (void const **)&data, &size, 20000) == RT_EOK) { > rt_kprintf("pop data size %d >", size); > rt_free(data); > } > } > } >} >MSH_CMD_EXPORT(dq, dataqueue test 'dq
'); >``` --- 好吧,还是自己太懒了,,,,谢谢啊,多谢!动手试试哈。。。
撰写答案
登录
注册新账号
关注者
0
被浏览
5.6k
关于作者
qq_哎
这家伙很懒,什么也没写!
提问
6
回答
26
被采纳
0
关注TA
发私信
相关问题
1
rt thread 2.0.2 usart 接收缓存问题
2
关于STM32串口通信的问题
3
STM32F1+RTT串口接收终端数据丢失问题
4
UART TX丢数据?
5
RTT打开串口的时候如何自定义波特率呢?
6
STM32F4的USART数据接收问题
7
串口1234使用问题
8
串口接收回调函数
9
LPC18xx UART问题讨论
10
x1000串口配置的失败问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
RT-Thread Studio V2.2.9 Release Note
2
CherryUSB的bootuf2配置
3
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
4
GD32F450 片内 flash驱动适配
5
STM32H7R7运行CherryUSB
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
7
个答案
2
次被采纳
a1012112796
12
个答案
1
次被采纳
Ryan_CW
5
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
lizimu
2
篇文章
7
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部