学习工控值得收藏 51单片机与MCGS的modbusRTU通信
目前,工业组态软件已经广泛应用于工业控制领域。用户无需了解复杂的编程知识,就可以用工业组态软件在短时间内完成一个具备专业水准的计算机监控系统的开发工作。MCGS是一套基于Windows平台的、为工业过程控制和实时监测服务的32位全中文界面组态软件系统。为了与现场设备进行交互,它提供了国内外各种常用的工控设备的工控设备的驱动程序;但现场很多设备是厂家根据本厂要求定制的设备,这就需要设计定制新设备与MCGS的数据通讯程序。本文以STC89C52单片机为例,编写单片机与MSGS的modbus rtu通讯协议,本文只列出了MODBUS RTU协议的子程序。
sbit LED0 = P1^0; // 对应线圈0
sbit LED1 = P1^1; // 对应线圈1
sbit LED2 = P1^2; // 对应线圈2
sbit LED3 = P1^3; // 对应线圈3
sbit LED4 = P1^4; // 对应线圈4
sbit LED5 = P1^5; // 对应线圈5
sbit LED6 = P1^6; // 对应线圈6
sbit LED7 = P1^7; // 对应线圈7
sbit KEY0 = P3^2;
sbit KEY1 = P3^3;
sbit KEY2 = P3^4;
sbit KEY3 = P3^5;
//字地址 0 - 255 (只取低8位)
//位地址 0 - 255 (只取低8位)
/* CRC 高位字节值表 */
const UINT8 code auchCRCHi[] = {
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
} ;
// CRC低位字节值表
const UINT8 code auchCRCLo[] = {
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
} ;
UINT8 testCoil; //用于测试 位地址1
UINT8 localAddr = 1; //单片机控制板的地址
UINT16 testRegister0,// 测试寄存器
testRegister1,
testRegister2,
testRegister3,//存放当前温度
testRegister4,//读写寄存器 控制P1口输出
testRegister5,
testRegister6,
testRegister7,
testRegister8,
testRegister9;
UINT16 crc16(const UINT8 *puchMsg, UINT16 usDataLen)
{
UINT8 uchCRCHi = 0xFF ; /* 高CRC字节初始化 */
UINT8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */
UINT32 uIndex ; /* CRC循环中的索引 */
while (usDataLen--) { /* 传输消息缓冲区 */
uIndex = uchCRCHi ^ *puchMsg++ ; /* 计算CRC */
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
uchCRCLo = auchCRCLo[uIndex] ;
}
return (uchCRCHi << 8 | uchCRCLo) ;
}
//开始发送
void beginSend(void)
{
UartSendBytes (sendBuf, sendCount);
}
//读线圈状态
void readCoil(void)
{
UINT8 addr;
UINT8 tempAddr;
UINT8 byteCount;
UINT8 bitCount;
UINT16 crcData;
UINT8 position;
UINT8 i, k;
UINT16 tempData;
UINT8 exit = 0;
//addr = (receBuf[2]<<8) + receBuf[3];
//tempAddr = addr & 0xfff;
addr = receBuf[3];
tempAddr = addr;
//bitCount = (receBuf[4]<<8) + receBuf[5]; //读取的位个数
bitCount = receBuf[5];
byteCount = bitCount / 8; //字节个数
if (bitCount % 8 != 0)
byteCount++;
for (k = 0; k < byteCount; k++) {
//字节位置
position = k + 3;
sendBuf[position] = 0;
for (i = 0; i < 8; i++) {
getCoilVal(tempAddr, &tempData);
sendBuf[position] |= (tempData << i);
tempAddr++;
if (tempAddr >= addr + bitCount) {
//读完
exit = 1;
break;
}
}
if (exit == 1)
break;
}
sendBuf[0] = localAddr;
sendBuf[1] = 0x01;
sendBuf[2] = byteCount;
byteCount += 3;
crcData = crc16(sendBuf, byteCount);
sendBuf[byteCount] = crcData >> 8;
byteCount++;
sendBuf[byteCount] = crcData & 0xff;
sendCount = byteCount + 1;
beginSend();
}
//读寄存器
void readRegisters(void)
{
UINT8 addr;
UINT8 tempAddr;
UINT16 crcData;
UINT8 readCount;
UINT8 byteCount;
UINT16 i;
UINT16 tempData = 0;
//addr = (receBuf[2]<<8) + receBuf[3];
//tempAddr = addr & 0xfff;
addr = receBuf[3];
tempAddr = addr;
//readCount = (receBuf[4]<<8) + receBuf[5]; //要读的个数
readCount = receBuf[5];
byteCount = readCount * 2;
for (i = 0; i < byteCount; i += 2, tempAddr++) {
getRegisterVal(tempAddr, &tempData);
sendBuf[i+3] = tempData >> 8;
sendBuf[i+4] = tempData & 0xff;
}
sendBuf[0] = localAddr;
sendBuf[1] = 3;
sendBuf[2] = byteCount;
byteCount += 3;
crcData = crc16(sendBuf, byteCount);
sendBuf[byteCount] = crcData >> 8;
byteCount++;
sendBuf[byteCount] = crcData & 0xff;
sendCount = byteCount + 1;
beginSend();
}//void readRegisters(void)
//强制单个线圈
void forceSingleCoil(void)
{
UINT8 addr;
UINT8 tempAddr;
UINT16 tempData;
UINT8 onOff;
UINT8 i;
//addr = (receBuf[2]<<8) + receBuf[3];
//tempAddr = addr & 0xfff;
addr = receBuf[3];
tempAddr = addr;
//onOff = (receBuf[4]<<8) + receBuf[5];
onOff = receBuf[4];
//if(onOff == 0xff00)
if (onOff == 0xff) {
//设为ON
tempData = 1;
}
//else if(onOff == 0x0000)
else if (onOff == 0x00) {
//设为OFF
tempData = 0;
}
setCoilVal(tempAddr, tempData);
for (i = 0; i < receCount; i++) {
sendBuf[i] = receBuf[i];
}
sendCount = receCount;
beginSend();
}
void presetSingleRegister(void) //设置单个寄存器
{
U8 addr;
U8 tempAddr;
U8 setCount;
U16 crcData;
U16 tempData;
//addr = (receBuf[2]<<8) + receBuf[3];
//tempAddr = addr & 0xfff;
addr = receBuf[3];
tempAddr = addr; //& 0xff
tempData = ( receBuf[4]<<8 ) + receBuf[5];
setRegisterVal(tempAddr,tempData);
sendBuf[0] = localAddr;
sendBuf[1] = 6;
sendBuf[2] = addr >> 8;
sendBuf[3] = addr & 0xff;
sendBuf[4] = receBuf[4];
sendBuf[5] = receBuf[5] ;
setCount = 6; //共6个字节
crcData = crc16(sendBuf,6);
sendBuf[6] = crcData >> 8;
sendBuf[7] = crcData & 0xff;
sendCount = 8;
beginSend();
}
//设置多个寄存器
void presetMultipleRegisters(void)
{
UINT8 addr;
UINT8 tempAddr;
UINT8 byteCount;
UINT8 setCount;
UINT16 crcData;
UINT16 tempData;
UINT8 i;
//addr = (receBuf[2]<<8) + receBuf[3];
//tempAddr = addr & 0xfff;
addr = receBuf[3];
tempAddr = addr & 0xff;
//setCount = (receBuf[4]<<8) + receBuf[5];
setCount = receBuf[5];
byteCount = receBuf[6];
for (i = 0; i < setCount; i++, tempAddr++) {
//SBUF = receBuf[i*2+7];
//SBUF = receBuf[i*2+8];
tempData = (receBuf[i*2+7] << 8) + receBuf[i*2+8];
setRegisterVal(tempAddr, tempData);
}
sendBuf[0] = localAddr;
sendBuf[1] = 16;
sendBuf[2] = addr >> 8;
sendBuf[3] = addr & 0xff;
sendBuf[4] = setCount >> 8;
sendBuf[5] = setCount & 0xff;
crcData = crc16(sendBuf, 6);
sendBuf[6] = crcData >> 8;
sendBuf[7] = crcData & 0xff;
sendCount = 8;
beginSend();
}
//取线圈状态 返回0表示成功
UINT16 getCoilVal(UINT16 addr, UINT16 *tempData)
{
UINT16 result = 0;
UINT16 tempAddr;
tempAddr = addr & 0xfff;
//只取低8位地址
switch( tempAddr ) //只取低8位地址 & 0xff
{
case 0:
if (KEY0)
*tempData = 1;
else
*tempData = 0;
break;
case 1:
if (KEY1)
*tempData = 1;
else
*tempData = 0;
break;
case 2:
if (KEY2)
*tempData = 1;
else
*tempData = 0;
break;
case 3:
if (KEY3)
*tempData = 1;
else
*tempData = 0;
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
case 9:
break;
case 10:
break;
case 11:
break;
case 12:
break;
case 13:
break;
case 14:
break;
case 15:
break;
default:
break;
}
return result;
}
//设定线圈状态 返回0表示成功
UINT16 setCoilVal(UINT16 addr, UINT16 tempData)
{
UINT16 result = 0;
UINT16 tempAddr;
tempAddr = addr & 0xfff;
switch(tempAddr) // & 0xff
{
case 0:
if (tempData)
LED0 = 1;
else
LED0 = 0;
break;
case 1:
if (tempData)
LED1 = 1;
else
LED1 = 0;
break;
case 2:
if (tempData)
LED2 = 1;
else
LED2 = 0;
break;
case 3:
if (tempData)
LED3 = 1;
else
LED3 = 0;
break;
case 4:
if (tempData)
LED4 = 1;
else
LED4 = 0;
break;
case 5:
if (tempData)
LED5 = 1;
else
LED5 = 0;
break;
case 6:
if (tempData)
LED6 = 1;
else
LED6 = 0;
break;
case 7:
if (tempData)
LED7 = 1;
else
LED7 = 0;
break;
case 10:
break;
case 11:
break;
case 12:
break;
case 13:
break;
case 14:
break;
case 15:
break;
case 16:
break;
case 17:
break;
default:
break;
}
return result;
}
//取寄存器值 返回0表示成功
UINT16 getRegisterVal(UINT16 addr, UINT16 *tempData)
{
UINT16 result = 0;
UINT16 tempAddr;
tempAddr = addr & 0xfff;
switch(tempAddr) //& 0xff
{
case 0:
*tempData = testRegister0;
break;
case 1:
*tempData = testRegister1;
break;
case 2:
*tempData = testRegister2;
break;
case 3:
//testRegister3= sdate;
*tempData = testRegister3;
break;
case 4:
*tempData = testRegister4;
break;
case 5:
*tempData = testRegister5;
break;
case 6:
*tempData = testRegister6;
break;
case 7:
*tempData = testRegister7;
break;
case 8:
*tempData = testRegister8;
break;
case 9:
*tempData = testRegister9;
break;
case 10:
break;
case 11:
break;
case 12:
break;
case 13:
break;
case 14:
break;
case 15:
break;
case 16:
break;
default:
break;
}
return result;
}
//设置寄存器值 返回0表示成功
UINT16 setRegisterVal(UINT16 addr, UINT16 tempData)
{
UINT16 result = 0;
UINT16 tempAddr;
tempAddr = addr & 0xfff;
switch(tempAddr) //& 0xff
{
case 0:
testRegister0 = tempData;
break;
case 1:
testRegister1 = tempData;
break;
case 2:
testRegister2 = tempData;
break;
case 3:
testRegister3 = tempData;
break;
case 4:
testRegister4 = tempData;
P1=tempData ; //&& 0X00FF
break;
case 5:
testRegister5 = tempData;
break;
case 6:
testRegister6 = tempData;
break;
case 7:
testRegister7 = tempData;
break;
case 8:
testRegister8 = tempData;
break;
case 9:
testRegister9 = tempData;
break;
case 10:
break;
case 11:
break;
case 12:
break;
case 13:
break;
case 14:
break;
case 15:
break;
case 16:
break;
default:
break;
}
return result;
}
void ModbusDelay (unsigned int nDelay)
{
volatile unsigned int i, j;
for (i = 0; i < nDelay; i ++)
for (j = 0; j < 10; j ++);
}
//检查uart0数据
void checkComm0Modbus(void)
{
UINT16 crcData;
UINT16 tempData;
if (receCount > 4) {
switch (receBuf[1]) {
case 1://读取线圈状态(读取点 16位以内)
case 3://读取保持寄存器(一个或多个)
case 5://强制单个线圈
case 6://设置单个寄存器
if (receCount >= 8) {
//接收完成一组数据
//应该关闭接收中断
UART_DISABLE_INTERRUPT();
if (receBuf[0] == localAddr) {
ModbusDelay (10);
crcData = crc16(receBuf, 6);
if (crcData == receBuf[7] + (receBuf[6] << 8)) {
//校验正确
if (receBuf[1] == 1) {
//读取线圈状态(读取点 16位以内)
readCoil();
}
else if (receBuf[1] == 3) {
//读取保持寄存器(一个或多个)
readRegisters();
}
else if (receBuf[1] == 5) {
//强制单个线圈
forceSingleCoil();
}
else if (receBuf[1] == 6) {
//写单个寄存器
presetSingleRegister();
}
}
}
RI = 0;
receCount = 0;
UART_ENABLE_INTERRUPT();
}
break;
case 15://设置多个线圈
tempData = receBuf[6];
tempData += 9; //数据个数
if (receCount >= tempData) {
if (receBuf[0] == localAddr) {
crcData = crc16(receBuf, tempData - 2);
if (crcData == (receBuf[tempData-2] << 8) + receBuf[tempData-1]) {
//forceMultipleCoils();
}
}
receCount = 0;
}
break;
case 16://设置多个寄存器
tempData = (receBuf[4] << 8) + receBuf[5];
tempData = tempData * 2; //数据个数
tempData += 9;
if (receCount >= tempData) {
UART_DISABLE_INTERRUPT();
if (receBuf[0] == localAddr) {
crcData = crc16(receBuf, tempData - 2);
if (crcData == (receBuf[tempData-2] << 8) + receBuf[tempData-1]) {
presetMultipleRegisters();
}
}
RI = 0;
receCount = 0;
UART_ENABLE_INTERRUPT();
}
break;
default:
break;
}
}
}
ModBus的单片机实现方法核心思想
ModBus简单来说就是一种半双工通讯的一主多从闭环式传输方式。
核心就是主机发送查询从机命令。
无论是查询还是写入都是一种查询机制!
从机不会主动发送任何数据在总线上。主机会发布查询消息,查询某一从机的状态,从机负责回复状态。
为了实现一主多从,消息帧内加入了地址选项。也就是每一个从机对应一个地址
ModBusRTU消息帧
数据接收到一帧数据后以3.5个字符时间作为一帧数据结束的标志!
这句话这么理解,如果是串口,串口接收完这么一堆数据后如何判定这是ModBus的数据,就是看有没有这后面的T1-T2-T2-T4时间段。这时间段就是用3.5个字符传输时间总和来计算的。
判定一帧数据结束:3.5Byte时间。
假设速度9600bit/s
那么1字节=10位=1起始位+8数据位+0校验位+1结束位
1秒传输960Byte
1Byte =1.04ms
3.5Byte =3.64ms
总结:如果用单片机定时器以1Ms定时器计算,就是4ms以上定时判定Modbus的结束标志。
自此我们可以去设计一下实现方法:
单片机资源:
定时器一个,串口一个
单片机资源设置:
定时器设置1ms中断,串口设置9600 1-8-n,开启接收中断
ModBus的程序设计:
1,初始化(主程序大循环之前初始化)
初始化MODBUS
2,Modbus服务函数(主程序大循环内执行)
ModBus服务函数
初始化需要设置Modbus从机的地址,也就是本机地址
服务函数需要查询数据帧是否接收完毕,并且处理数据!
串口中断负责接收数据并且开始计时(测定接收时间),如果数据没有接收完整计时清零,如果数据接收完整则不清零
串口服务函数
定时器负责计时,计时时间到4ms以上判定数据帧发送完成,清楚计时器标志。交给服务函数处理。
定时器服务函数
基本上流程走完了,就可以写功能代码服务函数了!
ModBus功能码
对于功能码,没必要全部实现,一般我们实现功能3,功能6就可以了。也就是读写操作。
至此ModBus实现完毕!祝大家玩的开心!
Modbus
相关问答
怎么实现pc机与三个 单片机 串行通讯?rs485-ZOL问答pc机用RS232-485,再按485连接3个单片机,当然都要用485接口协议嘛,当然是modbusRTU单片机与的单片机串口通讯仿真实例,可以参考一下,单片机串口与P...
51 单片机 怎么与触摸屏连接?实现51单机片与触摸屏连接有四个步骤:1触摸屏与单片机的硬件联接2建立触摸屏与单片机的内部存储器地址对应关系3触摸屏组态软件编辑4MODBUSRTU(远程...
Modbus 协议 RTU 格式中通信格式字一定有起始位吗?Modbus-RTU协议中,不用判断起始位。这种通信采用的是主机轮询、从机应答式的通讯方式,从机通过地址来判断主机的通信对象是否为自己,所以不用判断起始位。1M...
如何用 MODBUS 读写 单片机 的32位数据?使用Modbus访问16bit功能码,连续访问2个代表一个32位数据,两个16bit数据的工作需要自己完成:如PDU=功能码+数据域=0X03+[0X0000、0X0002];其中数据域...
PLC和 单片机 可以一起用吗?PLC可以与单片机一起使用,只需要互相通信即可。西门子PLC与单片机共有三种办法进行信号交换:1信号线连接.这是一种最简单的方式,即在单片机和PLC之间进行连...
我想实现组态王 单片机 之间的 modbus 通信,完全不知道该怎么设置?主要看你是什么品牌的PLC,不同品牌的PLC支持的协议是不一样的,一般组态王是支持Modbus和ModbusTCP协议,如果PLC支持这两种协议,可以直接通信,如果不支持需...
个人电脑如何控制 单片机 ?个人电脑控制单片机,主要通过以下步骤:1)建立个人电脑和单片机的通信连接普通的单片机通常有UART、SPI、IIC、USB等通信模块。和电脑通信最常用的是UART,可...
三菱PLC与 单片机 怎么实现485通讯?有2种方案:1、PLC做从机,单片机做主机2、单片机做从机,PLC做主机对上面两种,PLC需要加485拓展模块或拓展小板,同时在PLC内部需要通过写程序设置好D8140、...
自己编制的 MODBUS单片机 程序,与 MODBUS OPCserver通讯有问题?modscan32测试软件可以测试MODBUSRTU、ASCII以及MODBUSTCP。但modbusopcserver是按照OPC规范的,而且它是opcserver,那么你的MODBUS单...
力控组态软件怎样和 单片机 串口通信?这个你用标准MODBUS协议,然后调用里边的通用通讯模块就可以了,如果是自定义的协议,可以找力控开发通讯模块,前提是1肯出钱,2有很大的量可以免费开发这个你用...