- //----------------------------------------------------------------------------//
- //此代碼只支持作為Modbus從站設備的Modbus RTU模式
- //
- //支持的功能碼:
- //0x03 讀保持寄存器(讀多個保持寄存器的值,有效地位為0-99)
- //0x06 寫單個寄存器(寫入一個寄存器的值,有效地址為0-99)
- //0x10 寫多個寄存器(寫入多個寄存器的值,有效地址為0-99)
- //
- //支持的異常碼:
- //0x01 非法功能碼(不支持的功能碼)
- //0x02 非法數(shù)據(jù)地址(起始地址不在有效范圍內(nèi))
- //0x03 非法數(shù)據(jù)值(在起始地址的基礎(chǔ)上,數(shù)量是不合法的)
- //----------------------------------------------------------------------------//
- #include "Modbus.h"
- #include "main.h"
- /* 變量定義 ------------------------------------------------------------------*/
- uint8_t Modbus_Send_Buff[Modbus_Max_Send_Buff]; //發(fā)送數(shù)據(jù)緩沖區(qū)
- uint8_t Modbus_Rcv_Buff[Modbus_Max_Rcv_Buff]; //接收數(shù)據(jù)緩沖區(qū)
- uint8_t Modbus_Timeout_Cnt; //定時器中斷計數(shù)
- uint8_t Modbus_Rcv_Cnt; //接收字節(jié)計數(shù)
- uint8_t Modbus_Rcv_flag; //設備進入接收狀態(tài)標志
- uint8_t Modbus_Cmd_flag; //設備進入命令解析狀態(tài)標志
- uint8_t Modbus_Exe_flag; //設備進入命令執(zhí)行狀態(tài)標志
- uint8_t Modbus_Function; //從站設備需執(zhí)行的功能
- uint16_t HoldingReg[100] = {0x0123, 0x4567, 0x89AB, 0xCDEF}; //保持寄存器
- /* 函數(shù)定義 ------------------------------------------------------------------*/
- //----------------------------------------------------------------------------//
- //函數(shù)功能:逐位計算法CRC16校驗,在Modbus中CRC結(jié)果要進行高低字節(jié)交換,即低字節(jié)在前,高字節(jié)在后
- //入口參數(shù):puchMsg是要進行CRC校驗的消息;usDataLen是消息中字節(jié)數(shù)
- //出口參數(shù):計算出來的CRC校驗碼,16位長度
- //最后修改:2015.11.29
- //備注:
- //----------------------------------------------------------------------------//
- uint16_t Modbus_CRC16(uint8_t *puchMsg, uint8_t usDataLen)
- {
- uint16_t CRC_Cal = 0xFFFF;
- uint8_t CRC_High, CRC_Low;
- uint8_t i, j;
-
- for(j = 0; j < usDataLen; j++)
- {
- CRC_Cal = CRC_Cal ^ *puchMsg++;
-
- for (i = 0; i < 8; i++)
- {
- if((CRC_Cal & 0x0001) == 0x0001)
- {
- CRC_Cal = CRC_Cal >> 1;
- CRC_Cal = CRC_Cal ^ 0xA001;
- }
- else
- {
- CRC_Cal = CRC_Cal >> 1;
- }
- }
- }
-
- CRC_High = (uint8_t)(CRC_Cal >> 8);
- CRC_Low = (uint8_t)(CRC_Cal & 0x00FF);
-
- return (CRC_Low << 8 | CRC_High);
-
- // return CRC_Cal;
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:Modbus初始化
- //入口參數(shù):ID是從站站號
- //出口參數(shù):無
- //最后修改:2015.11.20
- //備注:
- //----------------------------------------------------------------------------//
- void Modbus_Init(void)
- {
- uint16_t i;
-
- //----------------------------------------------------------//
- //Modbus相關(guān)變量初始化
- //----------------------------------------------------------//
- Modbus_Timeout_Cnt = 0;
- Modbus_Rcv_Cnt = 0;
- Modbus_Rcv_flag = 0;
- Modbus_Cmd_flag = 0;
- Modbus_Exe_flag = 0;
-
- for(i = 0; i < Modbus_Max_Rcv_Buff; i++) //清除接收緩沖區(qū)
- {
- Modbus_Rcv_Buff[i] = '\0';
- }
-
- for(i = 0; i < Modbus_Max_Send_Buff; i++) //清除發(fā)送緩沖區(qū)
- {
- Modbus_Send_Buff[i] = '\0';
- }
-
- //----------------------------------------------------------//
- //TIM2定時器使能
- //----------------------------------------------------------//
- TIM_Cmd(TIM2, ENABLE);
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:Modbus命令解析函數(shù)
- //入口參數(shù):無
- //出口參數(shù):無
- //最后修改:2015.12.11
- //備注:
- //----------------------------------------------------------------------------//
- void Modbus_Cmd(void)
- {
- uint8_t Modbus_CRC_Rcv_Hi; //接收到的ModbusCRC校驗碼高字節(jié)
- uint8_t Modbus_CRC_Rcv_Lo; //接收到的ModbusCRC校驗碼低字節(jié)
- uint16_t Modbus_CRC_Rcv; //接收到的ModbusCRC校驗碼
- uint16_t Modbus_CRC_Cal; //根據(jù)接收到的數(shù)據(jù)計算出來的CRC值
-
- //----------------------------------------------------------//
- //開始命令解析
- //----------------------------------------------------------//
- if(Modbus_Cmd_flag == 1)
- {
- if(Modbus_Rcv_Cnt > 4) //如果接收到的一幀的字節(jié)數(shù)大于4 首先確保幀的長度在正常范圍
- {
- Modbus_CRC_Rcv_Lo = Modbus_Rcv_Buff[Modbus_Rcv_Cnt - 2]; //接收到的ModbusCRC校驗碼低字節(jié)
- Modbus_CRC_Rcv_Hi = Modbus_Rcv_Buff[Modbus_Rcv_Cnt - 1]; //接收到的ModbusCRC校驗碼高字節(jié)
- Modbus_CRC_Rcv = (uint16_t)(Modbus_CRC_Rcv_Lo << 8 | Modbus_CRC_Rcv_Hi); //接收到的ModbusCRC校驗碼(16位)
- Modbus_CRC_Cal = Modbus_CRC16(Modbus_Rcv_Buff, Modbus_Rcv_Cnt - 2); //根據(jù)接收到的數(shù)據(jù)計算CRC值
-
- if(Modbus_CRC_Cal == Modbus_CRC_Rcv) //如果計算的CRC值與接受的CRC值相等
- {
- //USART_SendByte(USART1, 0xAC);
- if(Slave_Address == Modbus_Rcv_Buff[0]) //如果是本機地址
- {
- switch(Modbus_Rcv_Buff[1]) //用switch分支語句來確定功能
- {
- case Modbus_ReadHoldingReg: //如果是讀保存寄存器
- Modbus_Function = Modbus_ReadHoldingReg; //將從站設備需執(zhí)行的功能賦值為讀保存寄存器
- Modbus_Exe_flag = 1; //設備進入命令執(zhí)行狀態(tài)
- break; //跳出分支語句
-
- case Modbus_WriteSingleReg:
- Modbus_Function = Modbus_WriteSingleReg; //將從站設備需執(zhí)行的功能賦值為寫單個寄存器
- Modbus_Exe_flag = 1; //設備進入命令執(zhí)行狀態(tài)
- break; //跳出分支語句
-
- case Modbus_WriteMultipleReg:
- Modbus_Function = Modbus_WriteMultipleReg; //將從站設備需執(zhí)行的功能賦值為寫多個寄存器
- Modbus_Exe_flag = 1; //設備進入命令執(zhí)行狀態(tài)
- break; //跳出分支語句
-
- default:
- Modbus_ErrorHandling(0x01); //所有功能碼都不符合,則返回功能碼錯誤異常響應報文
- return;
- }
- }
-
- else //否則清空接收數(shù)據(jù)緩沖區(qū)和發(fā)送數(shù)據(jù)緩沖區(qū)
- {
- Modbus_ClearBuff();
- }
- }
-
- else //否則清空接收數(shù)據(jù)緩沖區(qū)和發(fā)送數(shù)據(jù)緩沖區(qū)
- {
- Modbus_ClearBuff();
- }
-
- }
-
- else //否則清空接收數(shù)據(jù)緩沖區(qū)和發(fā)送數(shù)據(jù)緩沖區(qū)
- {
- Modbus_ClearBuff();
- }
-
- Modbus_Cmd_flag = 0; //設備退出命令解析狀態(tài)標志
- }
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:Modbus命令執(zhí)行函數(shù)
- //入口參數(shù):無
- //出口參數(shù):無
- //最后修改:2015.12.6
- //備注:
- //----------------------------------------------------------------------------//
- void Modbus_Exe(void)
- {
- if(Modbus_Exe_flag == 1)
- {
- switch(Modbus_Function)
- {
- case Modbus_ReadHoldingReg:
- Modbus_ReadHoldingReg_Process();
- break;
-
- case Modbus_WriteSingleReg:
- Modbus_WriteSingleReg_Process();
- break;
-
- case Modbus_WriteMultipleReg:
- Modbus_WriteMultipleReg_Process();
- break;
-
- }
- Modbus_Exe_flag = 0;
- }
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:功能碼0x03,讀保持寄存器
- //入口參數(shù):無
- //出口參數(shù):無
- //最后修改:2015.12.5
- //備注:
- //----------------------------------------------------------------------------//
- void Modbus_ReadHoldingReg_Process(void)
- {
- uint8_t Send_Cnt; //發(fā)送字節(jié)數(shù)量
- uint16_t StartAddress_Reg; //要讀取的寄存器起始地址
- uint16_t Num_Reg; //要讀取的寄存器的數(shù)量
- uint16_t CRC_Cal; //CRC校驗碼
- uint16_t i, j; //臨時變量
-
- StartAddress_Reg = Modbus_Rcv_Buff[2] << 8 | Modbus_Rcv_Buff[3]; //從接收數(shù)據(jù)緩沖區(qū)得到要讀取的寄存器起始地址
- Num_Reg = Modbus_Rcv_Buff[4] << 8 | Modbus_Rcv_Buff[5]; //從接收數(shù)據(jù)緩沖區(qū)得到要讀取的寄存器數(shù)量
-
- if(StartAddress_Reg < 100) //寄存器起始地址在正確范圍內(nèi)
- {
- if(StartAddress_Reg + Num_Reg < 100 && Num_Reg > 0) //起始地址+寄存器數(shù)量位于正確范圍內(nèi) 并且 寄存器數(shù)量正確
- {
- Send_Cnt = 3 + (Num_Reg << 1) + 2; //計算發(fā)送字節(jié)數(shù)量
-
- Modbus_Send_Buff[0] = Slave_Address; //從站地址
- Modbus_Send_Buff[1] = Modbus_ReadHoldingReg; //功能碼
- Modbus_Send_Buff[2] = Num_Reg << 1; //寄存器字節(jié)數(shù)量 等于 寄存器數(shù)量乘2
-
- for(i = StartAddress_Reg, j = 3; i < StartAddress_Reg + Num_Reg; i++, j += 2) //讀取寄存器的數(shù)據(jù)
- {
- Modbus_Send_Buff[j] = (uint8_t)(HoldingReg[i] >> 8);
- Modbus_Send_Buff[j + 1] = (uint8_t)(HoldingReg[i] & 0x00FF);
- }
-
- CRC_Cal = Modbus_CRC16(Modbus_Send_Buff, 3 + (Num_Reg << 1)); //計算發(fā)送數(shù)據(jù)的CRC校驗碼
- Modbus_Send_Buff[3 + (Num_Reg << 1)] = (uint8_t)(CRC_Cal >> 8); //先是低字節(jié)
- Modbus_Send_Buff[3 + (Num_Reg << 1) + 1] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節(jié)
-
- USART_SendString(USART1, Modbus_Send_Buff, Send_Cnt); //發(fā)送響應報文
-
- Modbus_ClearBuff(); //清空接收數(shù)據(jù)緩沖區(qū)和發(fā)送數(shù)據(jù)緩沖區(qū)
- }
-
- else
- {
- Modbus_ErrorHandling(0x03); //非法數(shù)據(jù)值
- }
- }
-
- else
- {
- Modbus_ErrorHandling(0x02); //非法數(shù)據(jù)地址
- }
-
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:功能碼0x06,寫單個寄存器
- //入口參數(shù):無
- //出口參數(shù):無
- //最后修改:2015.12.6
- //備注:
- //----------------------------------------------------------------------------//
- void Modbus_WriteSingleReg_Process(void)
- {
- uint8_t Send_Cnt; //發(fā)送字節(jié)數(shù)量
- uint16_t Address_Reg; //要寫入的寄存器地址
- uint16_t Value_Reg; //要寫入的寄存器值
- uint16_t CRC_Cal; //CRC校驗碼
-
- Address_Reg = Modbus_Rcv_Buff[2] << 8 | Modbus_Rcv_Buff[3]; //從接收數(shù)據(jù)緩沖區(qū)得到要寫入的寄存器地址
- Value_Reg = Modbus_Rcv_Buff[4] << 8 | Modbus_Rcv_Buff[5]; //從接收數(shù)據(jù)緩沖區(qū)得到要寫入的寄存器值
-
- if(Address_Reg < 100) //寄存器起始地址在正確范圍內(nèi)
- {
- Send_Cnt = 6 + 2; //計算發(fā)送字節(jié)數(shù)量
-
- HoldingReg[Address_Reg] = Value_Reg; //將要寫入的寄存器值寫入寄存器
-
- Modbus_Send_Buff[0] = Slave_Address; //從站地址
- Modbus_Send_Buff[1] = Modbus_WriteSingleReg; //功能碼
- Modbus_Send_Buff[2] = (uint8_t)(Address_Reg >> 8); //寄存器地址高字節(jié)
- Modbus_Send_Buff[3] = (uint8_t)(Address_Reg & 0x00FF); //寄存器地址低字節(jié)
- Modbus_Send_Buff[4] = (uint8_t)(HoldingReg[Address_Reg] >> 8); //寄存器值高字節(jié)
- Modbus_Send_Buff[5] = (uint8_t)(HoldingReg[Address_Reg] & 0x00FF); //寄存器值低字節(jié)
-
- CRC_Cal = Modbus_CRC16(Modbus_Send_Buff, 6); //計算發(fā)送數(shù)據(jù)的CRC校驗碼
- Modbus_Send_Buff[6] = (uint8_t)(CRC_Cal >> 8); //先是低字節(jié)
- Modbus_Send_Buff[7] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節(jié)
-
- USART_SendString(USART1, Modbus_Send_Buff, Send_Cnt); //發(fā)送響應報文
-
- Modbus_ClearBuff(); //清空接收數(shù)據(jù)緩沖區(qū)和發(fā)送數(shù)據(jù)緩沖區(qū)
- }
-
- else
- {
- Modbus_ErrorHandling(0x02); //非法數(shù)據(jù)地址
- }
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:功能碼0x10,寫多個寄存器
- //入口參數(shù):無
- //出口參數(shù):無
- //最后修改:2015.12.9
- //備注:
- //----------------------------------------------------------------------------//
- void Modbus_WriteMultipleReg_Process(void)
- {
- uint8_t Send_Cnt; //發(fā)送字節(jié)數(shù)量
- uint16_t StartAddress_Reg; //要寫入的寄存器起始地址
- uint16_t Num_Reg; //要寫入的寄存器的數(shù)量
- uint16_t CRC_Cal; //CRC校驗碼
- uint16_t i, j; //臨時變量
-
- StartAddress_Reg = Modbus_Rcv_Buff[2] << 8 | Modbus_Rcv_Buff[3]; //從接收數(shù)據(jù)緩沖區(qū)得到要寫入的寄存器起始地址
- Num_Reg = Modbus_Rcv_Buff[4] << 8 | Modbus_Rcv_Buff[5]; //從接收數(shù)據(jù)緩沖區(qū)得到要寫入的寄存器數(shù)量
-
- if(StartAddress_Reg < 100) //寄存器起始地址在正確范圍內(nèi)
- {
- if(StartAddress_Reg + Num_Reg < 100 && Num_Reg > 0) //起始地址+寄存器數(shù)量位于正確范圍內(nèi) 并且 寄存器數(shù)量正確
- {
- for(i = StartAddress_Reg, j = 7; i < StartAddress_Reg + Num_Reg; i++, j += 2) //將要寫入的寄存器值寫入寄存器
- {
- HoldingReg[i] = Modbus_Rcv_Buff[j] << 8 | Modbus_Rcv_Buff[j + 1];
- }
-
- Send_Cnt = 6 + 2;
-
- Modbus_Send_Buff[0] = Slave_Address; //從站地址
- Modbus_Send_Buff[1] = Modbus_WriteMultipleReg; //功能碼
- Modbus_Send_Buff[2] = (uint8_t)(StartAddress_Reg >> 8); //寄存器起始地址高字節(jié)
- Modbus_Send_Buff[3] = (uint8_t)(StartAddress_Reg & 0x00FF); //寄存器起始地址低字節(jié)
- Modbus_Send_Buff[4] = (uint8_t)(Num_Reg >> 8); //寄存器數(shù)量高字節(jié)
- Modbus_Send_Buff[5] = (uint8_t)(Num_Reg & 0x00FF); //寄存器數(shù)量低字節(jié)
-
- CRC_Cal = Modbus_CRC16(Modbus_Send_Buff, 6); //計算發(fā)送數(shù)據(jù)的CRC校驗碼
- Modbus_Send_Buff[6] = (uint8_t)(CRC_Cal >> 8); //先是低字節(jié)
- Modbus_Send_Buff[7] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節(jié)
-
- USART_SendString(USART1, Modbus_Send_Buff, Send_Cnt); //發(fā)送響應報文
-
- Modbus_ClearBuff(); //清空接收數(shù)據(jù)緩沖區(qū)和發(fā)送數(shù)據(jù)緩沖區(qū)
- }
-
- else
- {
- Modbus_ErrorHandling(0x03);
- }
- }
-
- else
- {
- Modbus_ErrorHandling(0x02);
- }
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:錯誤處理
- //入口參數(shù):ErrorType是錯誤類型
- //出口參數(shù):無
- //最后修改:2015.12.11
- //備注:
- //----------------------------------------------------------------------------//
- void Modbus_ErrorHandling(uint8_t ErrorType)
- {
- uint16_t CRC_Cal; //CRC校驗碼
-
- switch(ErrorType) //用switch分支語句來確定Modbus異常碼
- {
- case 0x01: //非法功能碼
- Modbus_Send_Buff[0] = Slave_Address; //從站地址
- Modbus_Send_Buff[1] = Modbus_Rcv_Buff[1] + 0x80; //異常功能碼
- Modbus_Send_Buff[2] = 0x01; //異常碼
- CRC_Cal = Modbus_CRC16(Modbus_Send_Buff, 3); //計算發(fā)送數(shù)據(jù)的CRC校驗碼
- Modbus_Send_Buff[3] = (uint8_t)(CRC_Cal >> 8); //先是低字節(jié)
- Modbus_Send_Buff[4] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節(jié)
-
- USART_SendString(USART1, Modbus_Send_Buff, 5); //發(fā)送異常響應報文
- break;
-
- case 0x02: //非法數(shù)據(jù)地址
- Modbus_Send_Buff[0] = Slave_Address; //從站地址
- Modbus_Send_Buff[1] = Modbus_Rcv_Buff[1] + 0x80; //異常功能碼
- Modbus_Send_Buff[2] = 0x02; //異常碼
- CRC_Cal = Modbus_CRC16(Modbus_Send_Buff, 3); //計算發(fā)送數(shù)據(jù)的CRC校驗碼
- Modbus_Send_Buff[3] = (uint8_t)(CRC_Cal >> 8); //先是低字節(jié)
- Modbus_Send_Buff[4] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節(jié)
-
- USART_SendString(USART1, Modbus_Send_Buff, 5); //發(fā)送異常響應報文
- break;
-
- case 0x03: //非法數(shù)據(jù)值
- Modbus_Send_Buff[0] = Slave_Address; //從站地址
- Modbus_Send_Buff[1] = Modbus_Rcv_Buff[1] + 0x80; //異常功能碼
- Modbus_Send_Buff[2] = 0x03; //異常碼
- CRC_Cal = Modbus_CRC16(Modbus_Send_Buff, 3); //計算發(fā)送數(shù)據(jù)的CRC校驗碼
- Modbus_Send_Buff[3] = (uint8_t)(CRC_Cal >> 8); //先是低字節(jié)
- Modbus_Send_Buff[4] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節(jié)
-
- USART_SendString(USART1, Modbus_Send_Buff, 5); //發(fā)送異常響應報文
- break;
-
- // default:
- // return;
-
- }
-
- Modbus_ClearBuff(); //清空接收數(shù)據(jù)緩沖區(qū)和發(fā)送數(shù)據(jù)緩沖區(qū)
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:清空接收數(shù)據(jù)緩沖區(qū)和發(fā)送數(shù)據(jù)緩沖區(qū)
- //入口參數(shù):無
- //出口參數(shù):無
- //最后修改:2015.12.5
- //備注:
- //----------------------------------------------------------------------------//
- void Modbus_ClearBuff(void)
- {
- uint16_t i;
-
- for(i = 0; i < Modbus_Max_Rcv_Buff; i++) //清除接收緩沖區(qū)
- {
- Modbus_Rcv_Buff[i] = '\0';
- }
- Modbus_Rcv_Cnt = 0; //接收字節(jié)計數(shù)清0
-
- for(i = 0; i < Modbus_Max_Send_Buff; i++) //清除發(fā)送緩沖區(qū)
- {
- Modbus_Send_Buff[i] = '\0';
- }
-
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:把一個字節(jié)的高4位十六進制數(shù)存到另一個字節(jié)的低4位里
- //入口參數(shù):一個字節(jié)的數(shù)據(jù)
- //出口參數(shù):另一個字節(jié)
- //最后修改:2015.11.28
- //備注:
- //----------------------------------------------------------------------------//
- uint8_t High4BitsToOneByte(uint8_t Byte)
- {
- uint8_t tempByte;
-
- tempByte = (Byte >> 4) & 0x0F;
-
- return tempByte;
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:把一個字節(jié)的低4位十六進制數(shù)存到另一個字節(jié)的低4位里
- //入口參數(shù):一個字節(jié)的數(shù)據(jù)
- //出口參數(shù):另一個字節(jié)
- //最后修改:2015.11.28
- //備注:
- //----------------------------------------------------------------------------//
- uint8_t Low4BitsToOneByte(uint8_t Byte)
- {
- uint8_t tempByte;
-
- tempByte = Byte & 0x0F;
-
- return tempByte;
- }
- //----------------------------------------------------------------------------//
- //函數(shù)功能:把低4位16進制數(shù)轉(zhuǎn)換為在OLED字庫上對應的0~9和A~F
- //入口參數(shù):HexByte是低4位16進制數(shù)
- //出口參數(shù):OLED字庫上對應的0~9和A~F
- //最后修改:2015.11.28
- //備注:
- //----------------------------------------------------------------------------//
- uint8_t HexToOLEDAsc(uint8_t HexByte)
- {
- if((HexByte >= 0x00) && (HexByte <= 0x09)) //數(shù)字0~9
- {
- HexByte += 0x30;
- }
- else if((HexByte >= 0x0A) && (HexByte <= 0x0F)) //數(shù)字A~F
- {
- HexByte += 0x37;
- }
- else
- {
- HexByte = 0xff;
- }
-
- return HexByte;
- }
復制代碼 |