嵌入式技术william hill官网
直播中

王璐

7年用户 490经验值
私信 关注
[问答]

请教大神怎样使用串口空闲中断来接收数据呢?

在使用串口发送数据的时候,一包数据大概260个字节左右,发送的时候是整包发的,但是好像在发送的过程中被打断了,接收方可以接收到,但是可以看到是分了几次才接收到的,这种情况怎么处理?

接收方用的是串口空闲中断来接收数据。
数据发送方,用的是rt的发送接口,代码如下:

rt_err_t my_uart_putchar(const char *name,const rt_uint8_t c)
{
rt_size_t len = 0;
rt_uint32_t timeout = 0;
rt_device_t uart_device = RT_NULL;
uart_device = rt_device_find(name);
if (RT_NULL == uart_device)
{
rt_kprintf("when uart putchar, %s device not found!\n",name);
return -RT_ERROR;
}
do
{
len = rt_device_write(uart_device, 0, &c, 1);
timeout++;
}
while (len != 1 && timeout < 500);
return RT_EOK;
}
void my_uart_putbytes(const char *name,const rt_uint8_t *s, rt_uint16_t len)
{
for (int j = 0; j < len; ++j) {
my_uart_putchar(name,s[j]);
}
}

回帖(1)

贾大林

2023-1-10 17:01:09
接收方使用串口空闲中断的提前是:发送方要连续发送。如果接收方超过一个字节周期接收不到数据视为空闲,则发送方的发送线程不能被打断超过一个字节周期。如果是115200波特率的话,这个周期大概是86us。被打断超过这个时间是很容易的,比如在另一个线程打印了一条日志。

解释方法有很多,以下是几种方案:

提高发送线程的优先级,并保证比他高优先级的线程和中断函数中不执行长时间的操作,比如打印日志。更简单一点,将发送线程优先级提至最高,那只需要保证中断函数中不执行长时间操作即可。不建议关中断,115200波特率的一个字节是86us,十几个字节能达到1ms,这是相当长的时间。关中断的话,可能会丢失很多数据。
使用DMA发送。
接收方使用软件来检测空闲,而不是硬件。这样可以放宽实时要求,比如1ms接收不到数据视为本包结束。当然,这样会牺牲一点传输带宽。人家每86us就可以发新包,而你得等1ms。
下面贴出我日常使用的接收代码,即第3种方案。

初始化串口时通过rt_device_set_rx_indicate设置回调,在回调中发送一个数据事件。

/**
* 硬件串口接收数据的回调函数
*
* @param stream 串口流
* @param drv   串口设备
* @param size  待接收的数据大小
*
* @return
*/
rt_err_t serial_stream_rx_notify(serial_stream_t *stream, rt_device_t dev, rt_size_t size)
{
    rt_event_send(stream->serial_rx_event, SERIAL_RX_EVENT);
    return RT_EOK;
}
这是接收一个字节的函数,其先调用rt_device_read读取,若有数据则直接返回。若无数据,则等待数据事件。等待成功了再读取数据,若超时则返回失败。

/**
* 从串口流读取单个字节数据
*
* @param stream    串口流
* @param timeout   超时时间
*
* @Return 实际读取的字节数
*/
rt_int32_t serial_stream_read_byte(serial_stream_t *stream, rt_tick_t timeout)
{
    rt_uint8_t data;
    rt_err_t ret;
    rt_uint32_t event;
    while(RT_TRUE)
    {
        if(rt_device_read(stream->serial, 0, &data, 1) == 1)
        {
            return data;
        }
        ret = rt_event_recv(stream->serial_rx_event, SERIAL_RX_EVENT, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, timeout, &event);
        if(ret != RT_EOK)
        {
            return -1;
        }
    }
}
当然,你肯定不是想一个字节一个字节的接收的,所以请看下面这个函数。其负责接收一整包数据。请注意,有两个超时时间。begin_timeout是首字节超时,cont_timeout是字节间超时。cont_timeout是负责分包的,那为什么还要有个begin_timeout呢?想象一下,我并不知道对方什么时候会发数据,它可能1个小时只发1包数据,如果我使用1ms超时来接收,那将不停地收到空数据。虽然这也没啥问题,不过笔者不喜欢哈哈。如果长时间无数据也不需要做额外操作的话,可以把begin_timeout设置为RT_WAITING_FOREVER。

rt_size_t  serial_stream_read_bytes2(serial_stream_t *stream, rt_uint8_t *buf, rt_size_t size,
        rt_tick_t begin_timeout, rt_tick_t cont_timeout)
{
    rt_size_t i;
    rt_int32_t data;
    for(i = 0; i < size; i++)
    {
        data = serial_stream_read_byte(stream, i == 0 ? begin_timeout : cont_timeout);
        if(data == -1)
        {
            break;
        }
        buf = (rt_uint8_t)data;
    }
    return i;
}
举报

更多回帖

×
20
完善资料,
赚取积分