MODBUS协议在dspic上通信应用程序
in modbus.h
extern uint8 sendCount; extern uint8 receCount; extern uint8 sendPosi; void beginSend(void); void checkModbus(void); void readMultipleRegister(void); void writeSingleRegister(void); uint8 getRegisterVal(uint16 addr,uint16 *tempData); uint8 setRegisterVal(uint16 addr,uint16 tempData); void ERROR_Response(uint8 funcCode, uint8 ERROR);
in modbus.c
#include "main.h" //字地址 0 - 255 (只取低8位) //位地址 0 - 255 (只取低8位) /* CRC 高位字节值表 */ const uint8 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 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 localAddr = 1; //单片机控制板的地址 uint8 sendCount; //发送字节个数 uint8 receCount; //接收到的字节个数 uint8 sendPosi; //发送位置 uint16 crc16(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) { uint8 i; for(i=0; i<sendCount;i++) { U2TXREG = sendBuf[i]; while(U2STAbits.TRMT==0); //等待发送完毕 } receCount = 0; } void readMultipleRegister(void) { uint8 result = 0; 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++) { result = getRegisterVal(tempAddr,&tempData); if(result != 0) { ERROR_Response(0x03, result); return; } 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 writeSingleRegister(void) { uint8 result = 0; uint8 addr; uint8 tempAddr; uint16 crcData; uint16 tempData; //addr = (receBuf[2]<<8) + receBuf[3]; //tempAddr = addr & 0xfff; addr = receBuf[3]; tempAddr = addr & 0xff; tempData = receBuf[4]; tempData = tempData<<8; tempData |= receBuf[5]; //setCount = (receBuf[4]<<8) + receBuf[5]; result = setRegisterVal(tempAddr,tempData); if(result != 0) { ERROR_Response(0x06, result); return; } sendBuf[0] = localAddr; sendBuf[1] = 0x06; sendBuf[2] = addr >> 8; sendBuf[3] = addr & 0xff; sendBuf[4] = (tempData & 0xff00)>>8; sendBuf[5] = (tempData & 0xff); crcData = crc16(sendBuf,6); sendBuf[6] = crcData >> 8; sendBuf[7] = crcData & 0xff; sendCount = 8; beginSend(); } void checkModbus(void) { uint16 crc=0; uint8 crcHi = 0, crcLo=0; if(receCount == 8) { crc = crc16(receBuf,6); crcHi = (crc>>8) & 0xff; crcLo = (crc & 0xff); if((crcHi == receBuf[6]) && (crcLo == receBuf[7])) { switch(receBuf[1]) { case 0x03: readMultipleRegister(); break; case 0x06: writeSingleRegister(); break; default: ERROR_Response(receBuf[1], FUNCTION_ERROR); return; break; } } else ERROR_Response(receBuf[1], CHECK_CRC16_ERROR); } else ERROR_Response(receBuf[1], RCV_COUNT_ERROR_T35); } uint8 getRegisterVal(uint16 addr,uint16 *tempData) { uint8 result = 0; switch(addr & 0xff) { case 0: *tempData = deviceRef[0]; break; case 1: *tempData = deviceRef[1]; break; case 2: *tempData = deviceRef[2]; break; case 3: *tempData = deviceRef[3]; break; case 4: *tempData = deviceRef[4]; break; case 5: *tempData = deviceRef[5]; break; case 6: *tempData = deviceRef[6]; break; case 7: *tempData = deviceRef[7]; break; default: result = DATA_ADDR_ERROR; break; } return result; } uint8 setRegisterVal(uint16 addr,uint16 tempData) { uint8 result = 0; switch(addr & 0xff) { case 0: deviceRef[0] = tempData; break; case 1: deviceRef[1] = tempData; break; case 2: deviceRef[2] = tempData; break; case 3: deviceRef[3] = tempData; break; case 4: deviceRef[4] = tempData; break; case 5: deviceRef[5] = tempData; break; case 6: deviceRef[6] = tempData; break; case 7: deviceRef[7] = tempData; break; default: result = DATA_ADDR_ERROR; break; } return result; } void ERROR_Response(uint8 funcCode, uint8 ERROR) { uint16 crcData = 0; sendBuf[0] = 0x01; sendBuf[1] = funcCode | 0x80; sendBuf[2] = ERROR; crcData = crc16(sendBuf,3); sendBuf[3] = (crcData>>8) & 0xff; sendBuf[4] = (crcData & 0xff); sendCount = 5; beginSend(); }
in main.h
#include <p30f6014a.h> typedef unsigned char uint8; typedef unsigned int uint16; typedef unsigned long uint32; #define FUNCTION_ERROR 0X01 #define DATA_ADDR_ERROR 0X02 #define DATA_VALUE_ERROR 0X03 #define RCV_COUNT_ERROR_T35 0X04 #define RCV_CHAR_TIMEOUT_T15 0X05 #define CHECK_CRC16_ERROR 0X06 extern uint8 sendBuf[16],receBuf[16]; extern uint16 deviceRef[16]; #include "modbus.h"
in main.c
#include "main.h" _FOSC(CSW_FSCM_OFF & XT_PLL4); //XT振荡,4倍频晶振. _FWDT(WDT_OFF); //关闭看门狗定时器 _FBORPOR(PBOR_OFF & MCLR_EN); //掉电复位禁止,MCLR复位使能。 _FGS(CODE_PROT_OFF); //代码保护禁止 uint8 sendBuf[16],receBuf[16]; //发送接收缓冲区 uint16 deviceRef[16]; uint8 T35FLAG = 0; uint8 T15FLAG = 0; void initUART(void) { U2BRG =64; //波特率设置为9600,外部10M U2MODEbits.PDSEL = 0;//8位数据,无奇偶校验 U2MODEbits.STSEL = 0;//一位停止位 U2STAbits.UTXISEL = 0;//发送数据就产生中断 IEC1bits.U2RXIE = 1;//使能UART2接受中断 IPC6bits.U2RXIP = 7; U2STAbits.URXISEL = 0;//每当一个数据字从接收移位寄存器传输到接收缓冲器之后就产生中断 U2MODEbits.UARTEN = 1; U2STAbits.UTXEN =1; //允许发送 IFS1bits.U2TXIF = 0;//发送使能之后,会自动将此为置1, //如果UTXISEL = 0,当一个字从发送缓冲器传输到 //发送移位寄存器(UxTSR)时产生中断 } void initTimer() //采用方式1,定时4ms { T1CONbits.TCKPS = 3;//256分频 T1CONbits.TCS = 0;//使用内部时钟fosc/4 TMR1 = 0; PR1 = 40000/256;//定时时间为4ms IEC0bits.T1IE = 1; T2CONbits.TCKPS = 3; T2CONbits.TCS = 0; TMR2 = 0; PR2 = 20000/256;//定时时间为2ms IEC0bits.T2IE = 1; } int main(void) { initUART(); initTimer(); while(1) { if(T35FLAG == 1) { if(T15FLAG > 1) ERROR_Response(receBuf[1], RCV_CHAR_TIMEOUT_T15); else checkModbus(); T15FLAG = 0; T35FLAG = 0; } } return 0; } /*如果ISR 引用了const 变量或字符串常量(字符串常量在程序空间里),则应当加上auto_psv 属性*/ void __attribute__((__interrupt__, auto_psv)) _T1Interrupt(void) { IFS0bits.T1IF = 0; T1CONbits.TON = 0; T35FLAG = 1; } void __attribute__((__interrupt__, auto_psv)) _T2Interrupt(void) { IFS0bits.T2IF = 0; T2CONbits.TON = 0; T15FLAG++; } /****************************************************************/ /*关于t3.5,与t1.5的实现:根据文档,当接收时间超过t3.5时判断此时*/ /*接收的数据为一个RTU帧,但是如果在这个帧之中,有字符间的接收间隔*/ /*大于t1.5时,这个就为错误帧,需要将此帧丢弃。*/ /*个人解决t3.5,t1.5的方式:1)设定变量标志T35FLAG*/ /*在UART接收中断 末尾 开启定时器,如果在接收下个字符之前 没有发生*/ /*定时器中断既:t3.5没有超时,在接收中断中将Timer1 归零,继续接收,*/ /*如果在某一个接收之后定时器t3.5发生了中断,则要在主程序中处理接收*/ /*到的字符。2)在UART接收中断的末尾开启定时器2,如果在两个字符接收*/ /*之间有t1.5超时,则需要将T15FLAG++,但是在帧结束的时候T15FLAG肯定会*/ /*被加+1,因此只要T15FLAG > 1 说明在帧中的字符 间肯定发生了 t1.5超时*/ /*需要在主程序中,抛弃此帧*/ /****************************************************************/ void __attribute__((__interrupt__, auto_psv)) _U2RXInterrupt(void) { T1CONbits.TON = 0; TMR1 = 0; T2CONbits.TON = 0; TMR2 = 0; IFS1bits.U2RXIF = 0; //清除中断标志位 receBuf[receCount] = U2RXREG; receCount++; //接收地址偏移寄存器加1 T1CONbits.TON = 1; T2CONbits.TON = 1; }