串口通信详解(更适合1K以上数据串接收,较定时器更稳定)

前言:数据接收的稳定性决定了产品的质量

目的

为了解决频繁使用定时器,造成数据错误(不稳定)
问题描述
我之前写的博客(串口通信详解),如果大家看了,会有一定的逻辑,在中断初始化定时器,一旦接受数据的时间超时,则会保存数据,等待线程的判断与处理,逻辑上是没有问题的,但是小编在之后的数据传输中,出现了极少部分数据丢失,这可急坏了小编,赶紧看日志,打log,终于找到了定时器的问题,不同的芯片,配置不同,性能也不同,本观点只针对瑞昱(realtek)XX芯片,如果读者的芯片足够强大,这不妨是另一种串口的接收机制。

逻辑

在这里我们只讲逻辑,环境不再提及,换一个环境,逻辑依旧如此:
首先我们还是需要用到调用(实际)操作系统的库函数,中断数据获取(一个字节大小),有一个原则就是,在数据接收的中断内,尽量少的工作处理,将接收与处理,放在其他线程(如while()),每次进入串口接收中断,直接将一个字节的数据传入数据处理函数,将值保存在数组内,当然,数据保存意味着数据,buff都正确,即数据格式正确,buff未溢出,buff未溢出这个条件很好判断,数据格式却条件很多:
数据格式条件
这里我们还是需要用时间来作为条件,一个读取北京(当前),当然其他时间也是可以的,此时间直接作为数据接受的间隔依据,只要在一定时间内一直都有数据传输过来,可以根据接受数据的特点或者标志位结束本段数据的接收,成功接收之后,立即复制到我们开启的buff中,反之,如果超过此段时间,本条缓存报讯的数据都将被清零!
正确数据被保存,就可以进行数据信息的解析,并对其进行相应回复与操作

代码

串口中断函数:

static void uart_irq(uint32_t id, SerialIrq event)
{
    serial_t *sobj = (void *)id;
    if (event == RxIrq)
    {
        volatile uint8_t Data = (uint8_t)serial_getc(sobj);//数据接收函数
        recive_data(Data);
    }
}

**serial_getc()是时间获取函数,定义一个局部变量,直接传输给recive_data(Data);**数据处理函数
数据过滤函数:
代码上的注释能够一定程度上解释

void recive_data(uint8_t data)
{
    static uint8_t Index = 0;
    static uint8_t flag = FALSE;
    static uint8_t g_rx_data[DATA_MAX_SIZE] = {0}; //数据接收缓存
    static uint16_t g_rx_cnt = 0;                  //数据接收个数
    static time_t time_sec = 0; //上一次接收到有效啊数据的时间
    uint8_t busy = 0;//判断数据是否繁忙
    
    if (g_rx_cnt > DATA_MAX_SIZE || rtc_read() - time_sec >= 2) //超过buff大小或者接收数据间时间超时
    {   
        memset(g_rx_data, 0, DATA_MAX_SIZE);//清零buff
        g_rx_cnt = 0;
        flag = FALSE;
    }
    if (flag == TRUE || data == 'A') //满足条件的头指针数据,标志存储开始
    {
        flag = TRUE;

        g_rx_data[g_rx_cnt++] = data;
        time_sec = rtc_read();
        if (data == '\n')//接收到'\0'单个结束字符
        {
            while (g_uart[Index].GotDataFlg == TRUE) //看存不存在空buff存到来的数据
            {
                my_printf("检测到有占用的buff\r\n");
                Index = (Index + 1) % UART_BUF_INDEX;
                busy++;
                if (busy > UART_BUF_INDEX)
                {
                    return; //无空buff,直接返回
                }
            }
            memset(g_uart[Index].Data, 0, DATA_MAX_SIZE);//开始赋值给全局变量
            g_uart[Index].RecvLen = g_rx_cnt;
            memcpy(g_uart[Index].Data, g_rx_data, strlen(g_rx_data));
            g_uart[Index].GotDataFlg = TRUE;

            memset(g_rx_data, 0, DATA_MAX_SIZE);
            g_rx_cnt = 0;
            flag = FALSE;
        }
    }
}

当然,根据具体的accord进行数据处理逻辑,只是一个例子

看到上面的逻辑可能会不清楚,下面我将程序换为简单的文字描述:

void 数据接收处理函数(uint8_t data)
{
    static uint8_t Index = 0;
    static uint8_t flag = FALSE;
    static uint8_t g_rx_data[DATA_MAX_SIZE] = {0}; //数据接收缓存
    static uint16_t g_rx_cnt = 0;                  //数据接收个数
    static time_t time_sec = 0; //上一次接收到有效啊数据的时间
    uint8_t busy = 0;//判断buff是否繁忙
    if (接收到的数据超过临时开启的buff或者超时) //超过buff大小或者接收数据间时间超时
    {
        //     my_printf("数据重新开始接收\r\n");
        清除本临时缓存数据;
        统计个数清零;
        标志位置FALSE;
    }
    if (flag == TRUE || 第一次到来的数据正确) //满足条件的数据开始存储,且头数据为字符A我才接收
    {
		    标志位置TRUE	;
		    数据存储;
		    如果数据未超时;
		    将最新的数据传给中间量;
        if (收到结束字符)
        {
            while (判断全局缓存是否被占用) //循环看存不存在空buff存到来的数据
            {
                my_printf("检测到有占用的buff\r\n");
                如果有占用,buff序号++;
                busy++;
                if (一个空的buff都不存在)
                {
                    无空buff,直接返回
                }
            }
            
            将成功得到的数据赋值给全局buff
            将数据的个数赋值给全局变量
            全局变量标志位置TRUE

            清除临时缓存
            临时数据个数变量置零
        }
    }
}

结果

对于较短的数据,定时器是没有问题的,但是,此定制化的数据接收处理会精准

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值