ES =1;
// 使能串口中断
TR1 = 1;
//启动 T1
}
unsigned char UartRead(unsigned char *buf, unsigned char len) //
串口数据读取函数,数
据接收指针 buf ,读取数据长度
len ,返回值为实际读取到的数据长度
{
unsigned char i;
if (len > cntRxd) // 读取长度大于接收到的数据长度时, {
len = cntRxd; // 读取长度设置为实际接收到的数据长度
}
for (i=0; i { *buf = bufRxd[ i]; buf++; } cntRxd = 0; // 清零接收计数器 return len; // 返回实际读取长度 } void DelayX10us(unsigned char t) // 软件延时函数,延时时间 { do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } while (--t); } (t*10)us void UartWrite(unsigned char *buf, unsigned char len) // 串口数据写入函数,即串口发送函数,待发送数据指针 buf ,数据长度 len { RS485_DIR = 1; //RS485 设置为发送 while (len--) //发送数据 { flagOnceTxd = 0; SBUF = *buf; buf++; while (!flagOnceTxd); } DelayX10us(5); // 等待最后的停止位完成,延时时间由波特率决定 RS485_DIR = 0; //RS485 设置为接收 } void UartDriver() // 串口驱动函数,检测接收到的命令并执行相应动作 { unsigned char i; unsigned char cnt; unsigned char len; unsigned char buf[30]; unsigned char str[4]; unsigned int crc; unsigned char crch, crcl; if (cmdArrived) // 有命令到达时,读取处理该命令 { cmdArrived = 0; len = UartRead(buf, sizeof(buf)); // 将接收到的命令读取到缓冲区中 if (buf[0] == 0x01) // 核对地址以决定是否响应命令,本例本机地址为 0x01 { crc = GetCRC16(buf, len-2); // 计算 CRC校验值 crch = crc >> 8; crcl = crc & 0xFF; if ((buf[len-2] == crch) && (buf[len-1] == crcl)) // 判断 CRC校验是否正确 { switch (buf[1]) // 按功能码执行操作 { case 0x03: // 读取一个或连续的寄存器 if ((buf[2] == 0x00) && (buf[3] <= 0x05)) //寄存器地址支持 0x0000 ~ 0x0005 { if (buf[3] <= 0x04) { 定义的寄存器为i = buf[3]; // 提取寄存器地址 cnt = buf[5]; //提取待读取的寄存器数量 buf[2] = cnt*2; // 读取数据的字节数,为寄存器数 16 位 len = 3; while (cnt--) { buf[len++] = 0x00; // 寄存器高字节补 0buf[len++] = regGroup[ i++]; // 低字节 } } else // 地址 0x05 为蜂鸣器状态 { buf[2] = 2; // 读取数据的字节数 buf[3] = 0x00; buf[4] = flagBuzzOn; len = 5; } break; } else // 寄存器地址不被支持时,返回错误码 { buf[1] = 0x83; // 功能码最高位置 1 buf[2] = 0x02; // 设置异常码为 02- 无效地址 len = 3; break; } *2 ,因 Modbus 0x0005 case 0x06: // 写入单个寄存器 if ((buf[2] == 0x00) && (buf[3] <= 0x05)) //寄存器地址支持 0x0000 ~ { if (buf[3] <= 0x04) { i = buf[3]; // 提取寄存器地址 regGroup[ i] = buf[5]; //保存寄存器数据 cnt = regGroup[ i] >> 4; // 显示到液晶上 if (cnt >= 0xA) str[0] = cnt - 0xA + 'A'; else str[0] = cnt + '0'; cnt = regGroup[ i] & 0x0F; if (cnt >= 0xA) str[1] = cnt - 0xA + 'A'; else str[1] = cnt + '0'; str[2] = '\\0'; LcdShowStr(i*3, 0, str); } else // 地址 0x05 为蜂鸣器状态 { flagBuzzOn = (bit)buf[5]; // 寄存器值转换为蜂鸣器的开关 } len -= 2; // 长度 -2 以重新计算 CRC并返回原帧 break; } else // 寄存器地址不被支持时,返回错误码 { buf[1] = 0x86; // 功能码最高位置 1 buf[2] = 0x02; // 设置异常码为 02- 无效地址 len = 3; break; } default: // 其它不支持的功能码 buf[1] |= 0x80; // 功能码最高位置 1 buf[2] = 0x01; //设置异常码为 01- 无效功能 len = 3; break; } crc = GetCRC16(buf, len); // 计算 CRC校验值 buf[len++] = crc >> 8; //CRC 高字节 buf[len++] = crc & 0xFF; //CRC 低字节 UartWrite(buf, len); // 发送响应帧 } } } } void UartRxMonitor(unsigned char ms) // 串口接收监控函数 { static unsigned char cntbkp = 0; static unsigned char idletmr = 0; if (cntRxd > 0) // 接收计数器大于零时,监控总线空闲时间 { if (cntbkp != cntRxd) // 接收计数器改变,即刚接收到数据时,清零空闲计时 { cntbkp = cntRxd; idletmr = 0; } else { if (idletmr < 5) // 接收计数器未改变,即总线空闲时,累积空闲时间 { idletmr += ms; if (idletmr >= 5) // 空闲时间超过 4 个字节传输时间即认为一帧命令接收完毕{ cmdArrived = 1; // 设置命令到达标志 }
相关推荐: