杰里AC692X uart1奇校验使用
万军从中过,滴水不沾衣。大家好,我是Rangers。最近在使用杰里AC6926A与另一款芯片进行串口通讯也是踩了一下这个奇校验的坑。由于通讯的芯片只支持奇校验,所以不得不对我们蓝牙芯片进行奇校验配置。
一丶串口帧格式
那么在配置奇校验之前,首先我们简单的了解一下串口的帧格式:
上图所示:
-
起始位: 开始位,意味着数据传输的开始
-
数据位:bit0~bit7 就是我们要传输的8位数据
-
校验位:奇偶校验位
-
停止位:1~2bit
看到这应该都清楚了,串口在传输数据的过程中,首先发出起始信号(下降沿)说明数据传输开始,紧接着“由低位到高位”传输我们的8bit数据,然后“第9位”则是我们的奇偶校验位,当uart被设置为无校验时,此位是被忽略的,最后是停止位说明数据传输结束。
二丶奇校验
了解完串口的帧格式,我们可以发现,起始奇校验就是通过操作帧格式中的校验位来实现的,也就是所谓的“第9位”。
首先我们了解一下什么是奇校验:
1.当传输的8bit数据位中1的个数为奇数时,校验位置0
2.当传输的8bit数据位中1的个数为偶数时,校验位置1 补成奇数
什么意思?我们举个例子:
1.假设我们现在需要通过串口传输 0x05 ,0x05的二进制表示为:0000 0101 不难看出0x05的二进制中1的个数为偶数,这时候我们就需要将第9位校验位设置为1
2.当我们传输 0x07 , 0x07的二进制表示为: 0000 0111 ,它的二进制中1的个数为奇数,这时候我们将第九位校验位设置为0
也就是说,奇校验 实际上就是,传输的8bit 和 校验位 为1 的个数加起来 要是奇数
三丶串口初始化
void user_uart_init(u32 boud)
{
JL_IOMAP->CON1 &= ~(BIT(2) | BIT(3));///片选PB0 PB1
JL_PORTB->OUT |= BIT(0) ;
JL_PORTB->DIR |= BIT(1) ;
JL_PORTB->DIR &= ~BIT(0) ;
JL_UART1->BAUD = (UART_CLK / boud) / 4 - 1;
JL_UART1->CON0 = BIT(14) | BIT(13) | BIT(12) | BIT(10) | BIT(0) | BIT(1) |BIT(3);
uart_info[1].callback_fun = user_common_uart_isr; //接收到buf会跳进这个函数
IRQ_REQUEST(IRQ_UART1_IDX, uart1_isr_fun);///请求中断
irq_set_prio(IRQ_UART1_IDX, 2); //优先级设置为2,最高为3
}
`
初始化我就直接贴代码了,不多做解释,可以直接照抄。
四丶奇校验代码段
void user_uart_send_odd(u8 buf)
{
u8 prd = buf;
u8 i;
static u8 cnt = 0;
for(i = 0; i < 8; i++)
{
if(buf & BIT(7)){
cnt++;
}
prd = prd << 1;
}
if(cnt % 2) //如果二进制1的个数是偶数 置1
{
JL_UART1->CON0 |= BIT(9);
}
else //如果二进制1的个数是奇数 置0
{
JL_UART1->CON0 &= ~BIT(9);
}
cnt = 0;
user_uart_tx(buf); //发送buf
}
void user_uart_tx(char a)
{
JL_UART1->BUF = a;
__asm__ volatile("csync");
while ((JL_UART1->CON0 & BIT(15)) == 0); //TX IDLE
}
五丶 重头戏:串口接收丢包
配置完上面,我们发送是没什么大问题的了,用串口工具抓发送的数据到电脑上也是正常的了。
但是!!当我们通过串口工具下发数据到芯片上接收时,瞬间爆炸。发送多几条之后直接出现丢包、串数据等现象。要知道我们做串口通讯一般都会包含 头-命令-数据-校验的,其中的数据都是不能出现闪失的,一但数据出现丢包就会导致我们无法通过校验。意味着这次的数据传输失败,那么对于用户在使用工程中出现这种问题导致操作失效,这是很致命的。
我去查这个问题的时候也很头痛。
void uart1_isr_fun()
{
u8 uto_buf;
u8 odd_bit;
if ((JL_UART1->CON0 & BIT(3)) && (JL_UART1->CON0 & BIT(14))) {
uto_buf = JL_UART1->BUF;
odd_bit = (JL_UART1->CON0 & BIT(8));
ReadBufSucced = 0;
JL_UART1->CON0 |= BIT(12);
if (uart_info[1].callback_fun) {
uart_info[1].callback_fun(uto_buf, rx_uart1_buf, UART_ISR_TYPE_DATA_COME);
}
}
}
这段是我串口中断的代码,一看上去也没什么问题,导致我非常的纳闷,最奇怪的是,当我再取出接收的buf再发送出去的时候,在Music任务下居然接收的非常稳定,只是在BT模式下出现丢包,搞得我非常的头痛,百思不得其“姐”。
正常来说,我们接收是不需要关第九位校验位的,所以实在是想不到引起丢包的原因。即使由校验引起的,也应该所有数据都出错,不会有正常的现象。
void uart1_isr_fun()
{
u8 uto_buf;
u8 odd_bit;
if ((JL_UART1->CON0 & BIT(3)) && (JL_UART1->CON0 & BIT(14))) {
uto_buf = JL_UART1->BUF;
odd_bit = (JL_UART1->CON0 & BIT(8));
ReadBufSucced = 0;
JL_UART1->CON0 |= BIT(12);
user_uart_tx(uto_buf);///接收返回
if (uart_info[1].callback_fun) {
uart_info[1].callback_fun(uto_buf, rx_uart1_buf, UART_ISR_TYPE_DATA_COME);
}
}
}
后面又百度了奇校验相关的博客文章,都没有找到有相关问题的原因,也在杰里的技术群询问了一下可能引起丢包的原因,结果没有人理我(卑微且弱小…)。就在我准备要放弃的时候,突然想起试一下无检验下,接收是否会出现问题?
说干就干,首先将初始化里面的校验位失能掉:
void user_uart_init(u32 boud)
{
JL_IOMAP->CON1 &= ~(BIT(2) | BIT(3));///片选PB0 PB1
JL_PORTB->OUT |= BIT(0) ;
JL_PORTB->DIR |= BIT(1) ;
JL_PORTB->DIR &= ~BIT(0) ;
JL_UART1->BAUD = (UART_CLK / boud) / 4 - 1;
JL_UART1->CON0 = BIT(14) | BIT(13) | BIT(12) | BIT(10) | BIT(0) | BIT(3);///不使能第九位
uart_info[1].callback_fun = user_common_uart_isr; //接收到buf会跳进这个函数
IRQ_REQUEST(IRQ_UART1_IDX, uart1_isr_fun);
irq_set_prio(IRQ_UART1_IDX, 2); //优先级设置为2,最高为3
}
然后再通过串口助手下发数据到芯片,发现就不会出现丢包事件了。
原来还是因为奇校验的问题导致接收丢包,至于是不是芯片缺陷还是我操作不当这里我无法分析。只能说既然找到是配置了校验位的问题,那么我们就可以找解决的方法了。
当时我测出是由于配置了检验位的原因引起的,我第一反应就是:那我在需要校验时候再使能校验位,不需要的时候失能,是不是就能规避这个问题?
于是我保留了初始化不再使能 第九位 ,而是在发送数据时加入了以下两条代码:
void user_uart_send_odd(u8 buf)
{
JL_UART1->CON0 |= BIT(1); //使能校验位
u8 prd = buf;
u8 i;
static u8 cnt = 0;
for(i = 0; i < 8; i++)
{
if(buf & BIT(7)){
cnt++;
}
prd = prd << 1;
}
if(cnt % 2) //如果二进制1的个数是偶数 置1
{
JL_UART1->CON0 |= BIT(9);
}
else //如果二进制1的个数是奇数 置0
{
JL_UART1->CON0 &= ~BIT(9);
}
cnt = 0;
user_uart_tx(buf); //发送buf
JL_UART1->CON0 &= ~BIT(1); //失能校验位
}
做完以上改动,升级程序后再测试,发现接收数据就不再出现丢包问题了。
下面是我回调函数的处理代码:
void user_common_uart_isr(u8 uto_buf, void *p, u8 isr_flag)
{
if(UART_ISR_TYPE_DATA_COME == isr_flag) //如果接收到数据
{
if(0 != ((Uart0_RecvFIFO.fifo_size -1 +
Uart0_RecvFIFO.fifo_front -
Uart0_RecvFIFO.fifo_rear) % Uart0_RecvFIFO.fifo_size))//fifo is not full
{
Uart0_RecvFIFO.fifo_addr[Uart0_RecvFIFO.fifo_rear] = uto_buf;
Uart0_RecvFIFO.fifo_rear++;
Uart0_RecvFIFO.fifo_rear = Uart0_RecvFIFO.fifo_rear % Uart0_RecvFIFO.fifo_size;
}
}
}
这里我是先接收到缓冲区,然后再通过代码将缓冲区的数据拿去到一个buf里面进行校验操作。
六丶 总结
类似这种有可能是由于芯片或者底层原因导致校验位使能的情况下接受丢包的问题,我们搞应用层的千万不要过多去纠结,可以反馈给原厂去解决。而我们应用层的就需要通过应用层的方式去规避它,不要想着它这个就是底层问题,我就是要去干底层,搞它底层的东西给它修复好。