完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
源码链接:Modbus传感器采集协议
一、硬件 1、传感器:为液压传感器,12vDC,RS485数据输出,采用Modbus协议通信 2、电路:根据传感器属性,电路主要是两部分,通信电路和电源电源 (1)485电路:由于485是半双工通信,需要控制收发,所以索性在把电路设计成自动收发电路 接跳线帽W1、W2即使用RS485收发,不接就是普通串口收发。RE和DE是收发使能,选择485模式:3_TXD常高,使能接受;当发送数据时,数据的起始位(下降沿)将3_TXD引脚电平拉低,将顺便使能发送。传感器接3_A、3_B。 (2)开关电路:主要是为了控制传感器开关,以降低功耗 二、测试485电路 1、485电路测试程序 对于单片机来说,其实仍然是串口通信,只不过经过485芯片实现了RS232电平→RS485电平。 (1)初始化串口3,并在初始化时开启串口接收中断 void MX_USART3_UART_Init(void) { huart3.Instance = USART3; huart3.Init.BaudRate = 9600; huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart3) != HAL_OK) { Error_Handler(); } Usart3RecIT();//开启串口3接收中断 start_capture();//发送请求帧 } (2)接收中断回调函数中保存串口数据 (void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart-》Instance == USART3){//RS485设备 HAL_UART_Receive_IT(&huart3, &uart3Data, 1); uart3WriteByte(uart3Data);//接收到的数据写入缓存 uart3DataFlg = 1; } } 3)缓存数据:环形队列,不啰嗦 #define SENSOR_485_DATA_BUFFER_MAX_LENGTH 60 typedef struct { uint16_t front; uint16_t rear; uint8_t* buffer; uint32_t maxSize; }Buffer_t; static Buffer_t sensor485Buffer; uint8_t sensor485DataBuffer[SENSOR_485_DATA_BUFFER_MAX_LENGTH];//缓存数组 void uart3WriteByte(uint8_t data) { Buffer_Puts(&sensor485Buffer,&data,1);//入队 } bool Buffer_Puts(Buffer_t* buffer, uint8_t* data, uint16_t length) { if (buffer-》maxSize - Buffer_Size(buffer) 《= length)//队满 return false; for (uint16_t i = 0; i《length; ++i)//队列未满 { buffer-》rear = (buffer-》rear + 1) % buffer-》maxSize; buffer-》buffer[buffer-》rear] = data[i];//进队 } return true; } (4)这个接口,是为了向传感器发送指令,请求传感器数据,指令需要查看传感器指令定义,属于协议那部分,先测试 void start_capture(){ uint8_t TxData[10]= “1111111111”; HAL_UART_Transmit(&huart3,TxData,10,0xffff); HAL_Delay(100); } 2、在Keil中Debug,用串口助手向单片机发送数据,查看数组sensor485DataBuffer接收到了,接收是可以了 3、但是我遇到了问题,上位机没有接受到2-(4)发送的“1111111111”,单片机发送出现了问题 (1)分别检查TX、R15、R16都有信号出来 (2)第一反应是485芯片发送使能没有成功,检查RE和DE引脚,果然一直是低电平,说明三极管一直导通,没有阻塞过 (3)无奈之下,干脆用反相器替换了三极管 (4)然后,就可以了… 三、加入Modbus协议 1、协议原理 (1)以上测试说明:链路层硬件协议√; (2)但是问题来了,只有硬件协议可以和传感器通信吗? 当然不行,传感器又不是电脑,它没有上位机:你点一下发送就把数据发出去了。这个时候需要单片机来告知它发送。所以我们还需要:链路层软件协议Modbus协议√; (3)我们选取Modbus协议中对我们编程有帮助的几点: 以帧的形式通信,有ASCII和RTU两种模式,帧中的地址、功能码等都是一个或多个字节,每个字节是一个8位串口数据 如果一个串口连接多个4856设备,可以通过地址区分不同485设备,当然串口资源充足也可以挂载在多个串口上 RTU模式通过两帧数据的时间间隔,区分前后两帧数据,如果串口数据间隔大于3.5个字节,那么就认为一帧数据结束了,在9600bps/s波特率下,传输3.5个字节时间大概为4ms;ASCII模式读回车换行就行了 如果要读取传感器状态、数据,都需要发送请求帧 数据需要CRC校验 具体指令规则需查看传感器指令文档 2、 根据传感器指令文档编程 (1)打开传感器开关 void open_sensor(){ //控制电源开关的引脚拉高 } (2)发送读取命令 void start_read(){ uint8_t TxData[10]= “010300000001840A”; HAL_UART_Transmit(&huart3,TxData,10,0xffff); HAL_Delay(100); } (3)读取数据 在串口接收中断中接收数据,如果下一个串口数据在4ms之内到来,那么重新计时,定时器中断不会生效;如果没来,则认为接收完一帧数据,程序会进入定时器中断,这个很关键 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart-》Instance == USART3){ HAL_TIM_Base_Stop_IT(&htim5); HAL_UART_Receive_IT(&huart3, &uart3Data, 1); uart3WriteByte(uart3Data); HAL_TIM_Base_Start_IT(&htim5);//开始计时4ms,即modbus设备在9600波特率下传输一帧数据的时间间隔 uart3DataFlg = 1; } } 在定时器中断中进行处理数据 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim==(&htim5)) { //TIM_ClearITPendingBit(TIM5,TIM_IT_Update); //标准库需要清除TIMx更新中断标志,HAL库不需要,直接写业务逻辑就行 HAL_TIM_Base_Stop_IT(&htim5); Modbus_Work();//数据处理函数 Uart3BufClear(); RS485_RX_CNT++; } } (4)数据CRC校验 int16_t CRC_16( int8_t *vptr, int8_t len) { uint16_t MODBUSCRC = 0xffff; uint16_t POLYNOMIAL = 0xa001; uint8_t i, j; for (i = 0; i 《 len; i++) { MODBUSCRC ^= vptr[i] ; for (j = 0; j 《 8; j++) { if ((MODBUSCRC & 0x0001) != 0) { MODBUSCRC 》》= 1; MODBUSCRC ^= POLYNOMIAL; } else { MODBUSCRC 》》= 1; } } } return MODBUSCRC; } CRC原理就不啰嗦了 不同种类的CRC校验的多项式并不统一,这里是0xA001,文档中会说明 (5)数据解析 int Modbus_Work(void) { double depth; uint16_t depth_high; uint16_t depth_low; int len = strlen((char*)(sensor485DataBuffer+1)); if(sensor485DataBuffer[1] == 0x01)//从机地址正确则进行换算 { if((CRC_16((int8_t *)(sensor485DataBuffer+1),len))==0x0000){//CRC校验 depth_high = sensor485DataBuffer[3]; depth_low = sensor485DataBuffer[4]; depth = (double)((depth_high 《《 8) + depth_low)/1000; } }memset(sensor485DataBuffer,0,len+1); return depth;//这就是水深值了 } 四、注意事项 1、RS485通信,一定要A接A,B接B,不是反接。 2、采集线过长,要考虑使用屏蔽线。 |
|
|
|
只有小组成员才能发言,加入小组>>
4384个成员聚集在这个小组
加入小组3297 浏览 0 评论
航顺(HK)联合电子发烧友推出“近距离体验高性能Cortex-M3,免费申请价值288元评估板
4239 浏览 1 评论
4252 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-28 07:18 , Processed in 0.476208 second(s), Total 44, Slave 38 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号