200 smart的PLC使用modbus rtu和stm32单板通讯
1. 200 smart的PLC部分程序
- modbus初始化部分:
EN:使能,一直为ON;
Mode:模式,1=modbus,0=PPI;
Baud:波特率,9600,19200等;
Parity:奇偶校验;0=无校验,1=奇校验,2=偶校验;
Port:端口;0=端口0,1=端口1。
Timeout:通信超时,1ms到32767ms 之间的任何值;典型值是1000ms (1 s),一般要足够大,让从站有足够的反应时间;
Done:完成位;
Error:错误字节。
- modbus消息发送:
EN,使能一直为1;
First,触发,使用沿指令;
Slave,从站地址;
RW,读写;0读,1写;
Addr,从站的数据区:
Count,是个数;
DataPtr,主站的数据区;如果RW处是写,则是把该引脚处数据写到从站,如果RW处是读,则是把从站数据读到该引脚处;必须使用指针符号(&)
注意事项:
离散量输出(线圈)和保持寄存器支持读请求和写请求。
离散量输入(触点)和输入寄存器仅支持读请求。
参数地址 (Addr) 是起始 Modbus 地址。S7?200 SMART 支持以下地址范围:
对于离散量输出(线圈),为 00001 至 09999
对于离散量输入(触点),为 10001 至 19999
对于输入寄存器,为 30001 至 39999
对于保持寄存器,为 40001 至 49999 和 400001 至 465535
Modbus 从站设备支持的地址决定了 Addr 的实际取值范围。
2.STM32 部分程序
使用9600波特率:加大通讯距离,项目本身对速率要求不高。
使用DMA方式,目的:减小CPU负担
中断开始处理函数
//注意,空闲中断,串口rx配置上拉
//stm32f1xx_it.c 文件中 USART3_IRQHandler()接口
//空闲中断是接收到一个数据以后,接收停顿超过一字节时间 认为桢收完,总线空闲中断是在检测到在接收数据后,数据总线上一个字节的时间内,没有再接到数据后发生。
void __MODBUS_UART_DMA_Recv_Begin_Handler(UART_HandleTypeDef *huart)
{
uint32_t tmp_flag = 0;
uint32_t dma_idle_num ;
uint32_t valid_num ;
tmp_flag =__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE); //获取IDLE标志位
if((tmp_flag != RESET))//idle标志被置位
{
__HAL_UART_CLEAR_IDLEFLAG(huart);//清除标志位
//temp = huart4.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
//temp = huart4.Instance->DR; //读取数据寄存器中的数据
//这两句和上面那句等效
//关闭串口接收的DMA通道。一是防止又有数据接收到,产生干扰;二是便于DMA重新配置赋值。
HAL_UART_DMAStop(huart); //
dma_idle_num = __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);// 获取DMA中未传输的数据个数
//temp = hdma_usart4_rx.Instance->NDTR;//读取NDTR寄存器 获取DMA中未传输的数据个数,
//这句和上面那句等效
//总计数减去未传输的数据个数,得到已经接收的数据个数
/// 使用RS485发送数据测试,该长度准确
valid_num = sizeof(modbus_dma_rx_buf) - dma_idle_num;
MODBUS_Package_Copy_2_buf(valid_num);
}
//中断退出时
//HAL_UART_Receive_DMA(p_huart_modbus, huart4_rx_buffer, huart4_BUF_SIZE);
return;
}
中断完成处理函数
//中断完成触发
//stm32f1xx_it.c 文件中 USART3_IRQHandler()接口
void __MODBUS_UART_DMA_Recv_Complete_Handler(UART_HandleTypeDef *huart)
{
//且此时接收到的数据长度为缓存器的数据长度
HAL_UART_Receive_DMA(p_huart_modbus, modbus_dma_rx_buf, sizeof(modbus_dma_rx_buf));
}
DMA接收完成二分之一处理函数
/*
通过对HAL库中HAL_UART_RxCpltCallback这个弱函数的重写可以实现对DMA完成中断的处理,
这个函数虽然声明在stm32f4xx_hal_uart.c中,其实DMA完成中断最后调用的是串口接收完成的回调函数。
*/
/* |half a | half b |*/
void __MODBUS_UART_RxHalfCpltCallback(void)
{
//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,
//且此时接收到的数据长度为缓存器的数据长度
HAL_UART_Receive_DMA(p_huart_modbus, modbus_dma_rx_buf, sizeof(modbus_dma_rx_buf));
}
3 .协议解析部分
读数据:
注意:本项目地址主要为寄存器,使用 40001-49999; 地址有要求,详见plc编程帮助。
slave=10,rw =0, addr=40001, count=1
数据:0A 03 00 00 00 01 85 71
slave=10,rw =0, addr=40002, count=1
数据:0A 03 00 01 00 01 D4 B1
总结:读1个字(不是字节)的格式:
slave地址+ 功能(03读)+寄存器地址(减去40001的值)+读取数量+CRC校验
1字节-------1字节------------- 2字节--------------------------------- 2字节------- 2字节
### 写数据:
写1个字:
slave=10,rw =1, addr=40001, count=1, 写入值:0 (如下图)
数据:0A 06 00 00 00 00 88 B1
slave=10,rw =1, addr=40001, count=1, 写入值:11
数据:0A 06 00 00 00 0B C9 76
slave=10,rw =1, addr=40003, count=1, 写入值:11
数据:0A 06 00 02 00 0B 68 B6
总结:写1个字(不是字节)的格式:
slave地址+ 功能(06写)+寄存器地址(减去40001的值)+写入值+CRC校验
1字节-------1字节------------- 2字节--------------------------------- 2字节------- 2字节
写2个字
slave=10,rw =1, addr=40003, count=2, 写入值:11 (如下图)
0A 10 00 02 00 02 04 00 0B 00 00 26 90
slave=10,rw =1, addr=40005, count=2, 写入值:11
0A 10 00 04 00 02 04 00 0B 00 00 A6 BA
写3个字
slave=10,rw =1, addr=40005, count=3, 写入值:11
0A 10 00 04 00 03 06 00 0B 00 00 00 00 18 1F
slave=10,rw =1, addr=40006, count=3, 写入值:11
0A 10 00 05 00 03 06 00 0B 00 00 00 00 49 DA
总结:写多个字(不是字节)的格式:
slave地址+ 功能(16)+寄存器地址(减去40001的值)+寄存器数量+ 写入字节数+写入值+CRC校验
1字节------- 1字节--------- 2字节---------------------------------- 2字节-----------1字节----------2n字节— 2字节
参考:
值得收藏 Modbus RTU 协议详解
Modbus调试软件–ModbusPoll、ModbusSlave使用详解