胡子阳
请注意阅读顺序一定要从头到尾!!否则容易忽略一些小细节!!
ModbusRS485:一种通信协议,另一种RS232已被淘汰。
Modbus主站(modbusMaster):主动发出指令,要求读取数据的一端(此实验中为arduino)。
Modbus从站(modbusslaver):被动接收指令,并随指令读自己寄存器内的信息给主站,或是改写寄存器内的信息(此实验中为DDS283-1ZN电能表)。
1. DDS283-1ZN电能表(你可以选用其他任意表,只要支持ModbusRS485的都行)图中两根线分别是RS485A与RS485B
2. RS485转TTL模块
这个模块与上图电能表的连接方式如图左侧所示。如果你需要远程读表,建议你在RS485A与RS485B之间加一个75欧姆电阻。图的右侧是此模块与Arduino的接法,注意tx要接arduino的rx,rx要接arduino的tx!!!!
另外我想说,看准这块板,成功的关键点在于你是否买对这块板,它的价格在12元左右。不推荐使用其他便宜的板,那会导致数据传输不稳定。
3. arduinoUNO板 这是最常见的板,无图不解释。
4. Lcd1602液晶显示屏(无图)
这款显示屏是日本人发明的,只支持显示英文和日文以及数字,字符等。
关于这款液晶屏的用法,详见我的另一篇文章。本实验lcd1602的接法与那文章中介绍的接法一模一样。
5. 面包板与面包线(无图)
本实验线路复杂,最好使用面包板与面包线。
Arduino代码:
/*
此例子是modbus03功能(读取功能)的lcd1602打印实现。
用arduinoIDE 1.6.7编译通过 ,在在uno板上成功运行。
*/
#include //modbusmaster库
#include //arduino自带的库
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);//定义一些引脚接法
uint16_t m_startAddress = 12;//要读取的从站起始地址
uint8_t m_length = 1;//从从站起始地址开始你需要读取的数据的长度
uint8_t result;//串口通信结果,不用明白是什么,只要知道这不是你最终需要的数据就行
ModbusMaster node(1);//对1号从站进行通信
void setup() {
node.begin(9600);//定义arduino与从站之间的串口波特率
lcd.begin(16, 2);//初始化lcd1602
}
void loop() {
result = node.readHoldingRegisters(m_startAddress, m_length);//调用相关函数
if (result == node.ku8MBSuccess) {//如果通信成功
lcd.setCursor(0, 0);//打印lcd第1行信息
lcd.print("DATA:");
for (uint8_t j = 0; j < m_length; j++)
{
lcd.print( node.getResponseBuffer(j), DEC );//以DEC(十)进制显示你要的数据
}
}
else {
lcd.setCursor(0, 1);//打印lcd第2行信息
lcd.print("ERROR:");
lcd.print(result, HEX);//以HEX(16)进制显示错误信息(E2:你的TX,RX断线,E0:你的485转ttl板子有问题)
}
delay(500);//延时500毫秒
lcd.clear();//lcd清屏,所以屏幕会闪属正常现象
}
/*
以上代码中的注释仅为个人有限水平的理解,如果有什么说的不对的地方欢迎改写。
*/
上述代码需要加载两个库。
一个是#include
这是关于lcd的自带库直接用就行。
另一个是#include
需要手动连网加载,否则程序会显示编译错误。
搜索modbusmaster选择版本0.11.0,点安装。
然后它会自动下载这个库。
注意!!!!Very important!!!!在上传数据的时候要先断开rx与tx!!!!
无论你的从站是电能表还是其他的,你一定要知道它们的每个寄存器里面放的都是什么类型的内容。比如我这个电能表的寄存器情况:
地址Address | 变量名称 Variable | 读写属性Belong to R/W | 数据格式Data format | 符号sign | 数据类型DataModel |
---|---|---|---|---|---|
0000H 0001H |
当前总电能 Current total energy |
R | 每个电能占用4个字节 Each electric energy occupy 4 byte |
无符号 unsigned |
Dword |
0002H 0003H |
保留 Reserved |
保留 Reserved |
保留 Reserved |
无符号 unsigned |
Dword |
0004H 0005H |
保留 Reserved |
保留 Reserved |
保留 Reserved |
无符号 unsigned |
Dword |
0006H 0007H |
保留 Reserved |
保留 Reserved |
保留 Reserved |
无符号 unsigned |
Dword |
0008H 0009H |
当前反向电能 Current reversing energy |
R | 每个电能占用4个字节 Each electric energy occupy 4 byte |
无符号 unsigned |
Dword |
000AH 000BH |
当前正向电能 Current_forword_energy |
R | 每个电能占用4个字节 Each electric energy occupy 4 byte |
无符号 unsigned |
Dword |
000CH | 电压 Voltage |
R | XXX.X_V | 无符号 unsigned |
Word |
000DH | 电流 Current |
R | XX.XX_A | 无符号 unsigned |
Word |
000EH | 有功功率 Active power |
R | XX.XXX_KW | 有符号 signed |
Word |
000FH | 无功功率 Reactive power |
R | XX.XXX_Kvar | 有符号 signed |
Word |
0010H | 功率因数 Power factor |
R | X.XXX | 无符号 unsigned |
Word |
0011H | 频率 Frequency |
R | XX.XX_ | 无符号 unsigned |
Word |
0012H | 保留 Reserved |
保留 Reserved |
保留 Reserved |
无符号 unsigned | Word |
0013H | 保留 Reserved |
保留 Reserved |
保留 Reserved | 无符号 unsigned |
Word |
0014H | 保留 Reserved |
保留 Reserved |
保留 Reserved | 无符号 unsigned |
Word |
0015H 高字节 High Byte |
通信地址 Communication address |
R/W | 001-247 | 无符号 unsigned |
Char |
0015H 低字节 Low byte |
通信波特率 Communication baudrate |
R/W | 01–9600bps (默认)(default) 02-4800bps 03–2400bps 04–1200bps |
无符号 unsigned |
Char |
从中可以看出地址为000CH也就是12号地址的寄存器放的是电压的情况。所以代码中uint16_t m_startAddress = 12;m_startAddress写成地址12 (16位)
uint8_t m_length = 1;读取1个地址的信息(8位)这样就可以愉快地读电压了。(uint16_t 与uint8_t是类型名,类似与int,float….就比如说0100 1001就是一个uint8_t变量,0010 0011 1100 1111 就是一个uint16_t变量)
这是另一个类似的代码实现的,图中“D:2279”表示此时的电压是227.9v “82”是我用于调试的一个无关数据。