前言:数据接收的稳定性决定了产品的质量
目的
为了解决频繁使用定时器,造成数据错误(不稳定)
问题描述
我之前写的博客(串口通信详解),如果大家看了,会有一定的逻辑,在中断初始化定时器,一旦接受数据的时间超时,则会保存数据,等待线程的判断与处理,逻辑上是没有问题的,但是小编在之后的数据传输中,出现了极少部分数据丢失,这可急坏了小编,赶紧看日志,打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
清除临时缓存
临时数据个数变量置零
}
}
}
结果
对于较短的数据,定时器是没有问题的,但是,此定制化的数据接收处理会精准