【RoboMaster】A板接收JY-ME01角度传感器数据--modebus协议&CRC软件校验

本文档详细记录了基于STM32的Modbus协议传感器数据接收流程,包括Cubemx配置、串口DMA设置、中断处理以及CRC校验的查表法实现。通过在串口中断处理函数内进行CRC校验,并解析出角度数据,确保数据的准确性和完整性。提供了一个开源代码仓库链接,包含关键代码段,便于后续查阅和使用。
摘要由CSDN通过智能技术生成

新车上要使用的角度传感器JY-ME01使用的是modbus协议进行数据传输,需要使用A板进行数据接收并解析出角度数据,记录一下方便以后使用时查找。

20231019开源
源码开源链接:https://gitee.com/zhang-how/engine2022.git
(有需要的自行克隆)
指路
主要代码路径
串口中断处理:engine2022/Src/stm32f4xx_it.c 500行:void USART6_IRQHandler(void)
CRC校验:engine2022/Bsp/bsp_sensor.c 68行:uint16_t GetModbusCRC16_Tab(uint8_t *data, uint32_t len)

1、cubemax配置底层

1.1 时钟及项目配置

基础的配置方法,全部通用。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 串口dma配置

1.2.1 开串口,该页默认
在这里插入图片描述
1.2.2 开串口中断,打勾即可
在这里插入图片描述
1.2.3 开启dma
添加一个RX的dma,一共3步
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述配置后与上图一致即可。(主要确认右下角的两个数据宽度为Byte

2、串口接收数据的代码

文件:main.c

unsigned char  RX_BUFFER[20];	//定义全局变量接收数组
//放在串口初始化完成之后
  __HAL_UART_ENABLE_IT(&huart8,UART_IT_IDLE);//使能中断
  HAL_UART_Receive_DMA(&huart8,RX_BUFFER,9);//开启dma接收到接收数组,长度9Byte

文件:stm32f4xx_it.c
UART8_IRQHandler - 串口中断函数

uint16_t temp;

if(__HAL_UART_GET_FLAG(&huart8,UART_FLAG_IDLE)==SET){
    __HAL_UART_CLEAR_IDLEFLAG(&huart8);  //清除标志位,准备下次接收数据
    HAL_UART_DMAStop(&huart8);			//停止dma传输
    temp=9-hdma_uart8_rx.Instance->NDTR; 	/*读取ndtr寄存器中dma已发送数据字节数,然后算出
    									      距离9字节还需要多少字节*/
    HAL_UART_Transmit_DMA(&huart8,RX_BUFFER,temp);  //将剩余字节发送到接收数组中
  }
  HAL_UART_Receive_DMA(&huart8,RX_BUFFER,9);  //重启dma

3.CRC校验

原理

由于是在应用CRC校验算法,所以对于原理不过分深入研究。
粗略看一看计算原理:
1、 预置1个16位的寄存器为十六进制FFFF(全1),此寄存器为CRC寄存器。
2、 把第一个8位二进制数据(即通讯信息帧的第一个字节)与16位的CRC寄存器的低八位相异或,吧结果存放于CRC寄存器。
3、 把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检测右移后的移出位。
4、 如果移出位为零,则重复第三步(再次右移一位);如果移出位为1,CRC寄存器与多项式A001进行异或。
5、 重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理。
6、 重复步骤2和5,进行通讯信息帧下一个字节的处理。
7、 将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换。
8、 最后得到的CRC寄存器内容即为:CRC校验码。

重点:stm32上常用的CRC校验方法
1.硬件CRC
节省计算资源,但是F4开启CRC后无法配置参数,很玄学,暂时放弃
2.实时计算CRC校验
占用FLASH小,但是计算速度慢一点
3.查表法CRC校验
查表计算速度快,但是占用FLASH(A板内存大随便干),所以选用这个方法。

代码实现

由CRC校验原理可知,CRC校验有一个特点就是每个八位二进制字节对应的CRC校验码相同。所以对一个字节可以直接查表得到对应的校验结果值。
校验结果对应数组
建议放在用户函数单独建立的文件内

    const unsigned char TabH[] = {  //CRC高位字节值表
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40  
    };  
    const unsigned char TabL[] = {  //CRC低位字节值表
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,  
        0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,  
        0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,  
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,  
        0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,  
        0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,  
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,  
        0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,  
        0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,  
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,  
        0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,  
        0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,  
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,  
        0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,  
        0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,  
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,  
        0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,  
        0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,  
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,  
        0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,  
        0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,  
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,  
        0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,  
        0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,  
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,  
        0x43, 0x83, 0x41, 0x81, 0x80, 0x40  
    } ;
float ANGLE;	//定义角度变量

uint16_t GetModbusCRC16_Tab(uint8_t *data, uint32_t len)//Modbus-CRC校验-----查表法
{
    unsigned int index;
    unsigned char crcH = 0xFF;  //高CRC字节
    unsigned char crcL = 0xFF;  //低CRC字节

    while (len--)  { //计算指定长度的CRC
        index = crcL ^ *data++;
        crcL = crcH	^ TabH[ index];
        crcH = TabL[ index];
    }
		return crcL<<8|crcH;//高低位置换
}  

推荐一个可以用于检验CRC结果是否正确的网站:http://www.ip33.com/crc.html
串口中断函数加入CRC计算的过程,对比无误后计算角度并输出
原计算公式:Angle = 90063 / 262144 * 360 = 123.682708

crc_val=GetModbusCRC16_Tab(RX_BUFFER,7);
    if(crc_val==((RX_BUFFER[7]<<8)|RX_BUFFER[8])){		
      ANGLE=(float)((RX_BUFFER[3]<<24)|(RX_BUFFER[4]<<16)|(RX_BUFFER[5]<<8)|RX_BUFFER[6]);
      ANGLE=ANGLE/0x40000*360;
    }

更改中断函数后的结果。
在这里插入图片描述

运行结果 在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值