rt-thread驱动篇之serialX阻塞超时返回

电子说

1.2w人已加入

描述

前言
尽管仍然很多痴男怨女在 v1 v2 身上跌倒、跌倒、继续跌倒,仍然阻止不了他们飞蛾扑火式的被 v1 v2 的缺陷所吸引而殉情。

它一如既往的保持着优良的特性,也有可能是很多人没发现,主要是接受它的人很少。不过,这不影响今天它带来新的特性。

阻塞超时
我们一直强调,它有与之前非同一般的两个概念“阻塞”“非阻塞”。虽然 v2 热火朝天的提出了这两个概念,但是实现的效果却不尽人意。

在之前的正式文档里,我没胆量承认一个事实,那就是,阻塞读在无数据可读的时候将永远阻塞下去。某些应用场景并不希望这样,我们希望等待某设备响应,若干时间后无响应超时,我们返回继续做其它工作,而不是被无响应的设备永久占用。

给某些论坛提问里的解答时,我提到过几次 serialX 可以通过以下技术手段应对这种场景。

方法一
使用非阻塞模式打开,超时读过程伪代码可能如下这样:

while(timeout > 0) {
read
sleep 1
timeout -= 1
}
if (timeout == 0) {
// timeout here
} else {
// no timeout
}
方法二
使用完成中断 indicate 回调函数发消息,这也是官方提供的读串口设备的“标准”方式

static void serial_thread_entry(void parameter)
{
char ch;
while (1)
{
/
从串口读取一个字节的数据,没有读取到则等待接收信号量 /
while (rt_device_read(serial, -1, &ch, 1) != 1)
{
/
阻塞等待接收信号量,等到信号量后再次读取数据 */
rt_sem_take(&rx_sem, timeout);
}
...
}
}
注:此段代码改编自官方文档

大多数人第一次使用 rtt 的串口设备也是用这段代码测试的。但是,serialX 不提倡大家使用 indicate 回调函数。

serialX 有它自己的特性,它有它自己的优美性,那就是尽可能不给应用层代码带来麻烦,不要写太复杂难懂的逻辑,不要给应用层引入莫名未知的后果。

我们希望在不影响不改变之前的使用的前提下,rt_device_read 能够在预定的时间内超时返回退出,并不是用于阻塞下去,同时 rt_device_read 返回 -RT_ETIMEOUT 错误码。应用层可以根据 rt_device_read 返回值

== 0 无数据
0 有数据

< 0 有错误(-RT_ETIMEOUT 超时)
分别处理不同情况。
serialX 的实现
首先,rtdef.h 添加定义,用于设备超时配置

#define RT_DEVICE_CTRL_TIMEOUT 0x30 /**< timeout for blocking */
其次,struct rt_serial_device 添加 rt_tick_t timeout_tick; 变量,设备超时时间 tick 。

然后,rt_serial_control 函数添加超时配置宏选项处理

case RT_DEVICE_CTRL_TIMEOUT:
    rt_tick_t timeout_tick = (rt_tick_t)args;
    serial- >timeout_tick = timeout_tick;
break;

最后,在 serialX.c 文件中所有涉及到阻塞的地方(包括读写,不包含 flush)修改 rt_completion_wait 第二个参数为 serial->timeout_tick。并当 rt_completion_wait 返回 -RT_ETIMEOUT 时退出当前读写操作返回应用层。

注意:特别说明,我们希望一个设备以阻塞模式打开时,默认的阻塞超时时间是“永久”,所以,每次 rt_device_open 后 serialX 设定阻塞超时时间时间是 RT_WAITING_FOREVER 。如果需要指定某超时时间需要 rt_device_control(serial_dev, RT_DEVICE_CTRL_TIMEOUT, &timeout);

rt_tick_t timeout = 50;
if (rt_device_open(scpi_uart_dev, RT_DEVICE_OFLAG_RDWR
                           | RT_DEVICE_FLAG_INT_RX
                           | RT_DEVICE_FLAG_INT_TX
                           | RT_DEVICE_OFLAG_BLOCKING) != RT_EOK)
{
    rt_kprintf("Open device: %s failedn", UART_DEV_NAME);
    return;
}
rt_device_control(serial_dev, RT_DEVICE_CTRL_TIMEOUT, &timeout);
rt_ssize_t ret = rt_device_read(serial_dev, -1, &recvbuf[0], 128);
if (ret == -RT_ETIMEOUT) {
    rt_device_close(serial_dev);
    return;
} else {
}

这就是今天我们要讲的第三种方法,相比前两种,这种方法更优雅些,代码逻辑也清晰。

总结
欢迎大家入坑 serialX。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分