久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费
標題:
支持作為Modbus從站設備的Modbus RTU模式的源碼
[打印本頁]
作者:
ybssrs
時間:
2018-6-27 20:47
標題:
支持作為Modbus從站設備的Modbus RTU模式的源碼
//----------------------------------------------------------------------------//
//此代碼只支持作為Modbus從站設備的Modbus RTU模式
//
//支持的功能碼:
//0x03 讀保持寄存器(讀多個保持寄存器的值,有效地位為0-99)
//0x06 寫單個寄存器(寫入一個寄存器的值,有效地址為0-99)
//0x10 寫多個寄存器(寫入多個寄存器的值,有效地址為0-99)
//
//支持的異常碼:
//0x01 非法功能碼(不支持的功能碼)
//0x02 非法數據地址(起始地址不在有效范圍內)
//0x03 非法數據值(在起始地址的基礎上,數量是不合法的)
//----------------------------------------------------------------------------//
#include "Modbus.h"
#include "main.h"
/* 變量定義 ------------------------------------------------------------------*/
uint8_t Modbus_Send_Buff[Modbus_Max_Send_Buff]; //發送數據緩沖區
uint8_t Modbus_Rcv_Buff[Modbus_Max_Rcv_Buff]; //接收數據緩沖區
uint8_t Modbus_Timeout_Cnt; //定時器中斷計數
uint8_t Modbus_Rcv_Cnt; //接收字節計數
uint8_t Modbus_Rcv_flag; //設備進入接收狀態標志
uint8_t Modbus_Cmd_flag; //設備進入命令解析狀態標志
uint8_t Modbus_Exe_flag; //設備進入命令執行狀態標志
uint8_t Modbus_Function; //從站設備需執行的功能
uint16_t HoldingReg[100] = {0x0123, 0x4567, 0x89AB, 0xCDEF}; //保持寄存器
/* 函數定義 ------------------------------------------------------------------*/
//----------------------------------------------------------------------------//
//函數功能:逐位計算法CRC16校驗,在Modbus中CRC結果要進行高低字節交換,即低字節在前,高字節在后
//入口參數:puchMsg是要進行CRC校驗的消息;usDataLen是消息中字節數
//出口參數:計算出來的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;
}
//----------------------------------------------------------------------------//
//函數功能:Modbus初始化
//入口參數:ID是從站站號
//出口參數:無
//最后修改:2015.11.20
//備注:
//----------------------------------------------------------------------------//
void Modbus_Init(void)
{
uint16_t i;
//----------------------------------------------------------//
//Modbus相關變量初始化
//----------------------------------------------------------//
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++) //清除接收緩沖區
{
Modbus_Rcv_Buff[i] = '\0';
}
for(i = 0; i < Modbus_Max_Send_Buff; i++) //清除發送緩沖區
{
Modbus_Send_Buff[i] = '\0';
}
//----------------------------------------------------------//
//TIM2定時器使能
//----------------------------------------------------------//
TIM_Cmd(TIM2, ENABLE);
}
//----------------------------------------------------------------------------//
//函數功能:Modbus命令解析函數
//入口參數:無
//出口參數:無
//最后修改:2015.12.11
//備注:
//----------------------------------------------------------------------------//
void Modbus_Cmd(void)
{
uint8_t Modbus_CRC_Rcv_Hi; //接收到的ModbusCRC校驗碼高字節
uint8_t Modbus_CRC_Rcv_Lo; //接收到的ModbusCRC校驗碼低字節
uint16_t Modbus_CRC_Rcv; //接收到的ModbusCRC校驗碼
uint16_t Modbus_CRC_Cal; //根據接收到的數據計算出來的CRC值
//----------------------------------------------------------//
//開始命令解析
//----------------------------------------------------------//
if(Modbus_Cmd_flag == 1)
{
if(Modbus_Rcv_Cnt > 4) //如果接收到的一幀的字節數大于4 首先確保幀的長度在正常范圍
{
Modbus_CRC_Rcv_Lo = Modbus_Rcv_Buff[Modbus_Rcv_Cnt - 2]; //接收到的ModbusCRC校驗碼低字節
Modbus_CRC_Rcv_Hi = Modbus_Rcv_Buff[Modbus_Rcv_Cnt - 1]; //接收到的ModbusCRC校驗碼高字節
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); //根據接收到的數據計算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; //將從站設備需執行的功能賦值為讀保存寄存器
Modbus_Exe_flag = 1; //設備進入命令執行狀態
break; //跳出分支語句
case Modbus_WriteSingleReg:
Modbus_Function = Modbus_WriteSingleReg; //將從站設備需執行的功能賦值為寫單個寄存器
Modbus_Exe_flag = 1; //設備進入命令執行狀態
break; //跳出分支語句
case Modbus_WriteMultipleReg:
Modbus_Function = Modbus_WriteMultipleReg; //將從站設備需執行的功能賦值為寫多個寄存器
Modbus_Exe_flag = 1; //設備進入命令執行狀態
break; //跳出分支語句
default:
Modbus_ErrorHandling(0x01); //所有功能碼都不符合,則返回功能碼錯誤異常響應報文
return;
}
}
else //否則清空接收數據緩沖區和發送數據緩沖區
{
Modbus_ClearBuff();
}
}
else //否則清空接收數據緩沖區和發送數據緩沖區
{
Modbus_ClearBuff();
}
}
else //否則清空接收數據緩沖區和發送數據緩沖區
{
Modbus_ClearBuff();
}
Modbus_Cmd_flag = 0; //設備退出命令解析狀態標志
}
}
//----------------------------------------------------------------------------//
//函數功能:Modbus命令執行函數
//入口參數:無
//出口參數:無
//最后修改: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;
}
}
//----------------------------------------------------------------------------//
//函數功能:功能碼0x03,讀保持寄存器
//入口參數:無
//出口參數:無
//最后修改:2015.12.5
//備注:
//----------------------------------------------------------------------------//
void Modbus_ReadHoldingReg_Process(void)
{
uint8_t Send_Cnt; //發送字節數量
uint16_t StartAddress_Reg; //要讀取的寄存器起始地址
uint16_t Num_Reg; //要讀取的寄存器的數量
uint16_t CRC_Cal; //CRC校驗碼
uint16_t i, j; //臨時變量
StartAddress_Reg = Modbus_Rcv_Buff[2] << 8 | Modbus_Rcv_Buff[3]; //從接收數據緩沖區得到要讀取的寄存器起始地址
Num_Reg = Modbus_Rcv_Buff[4] << 8 | Modbus_Rcv_Buff[5]; //從接收數據緩沖區得到要讀取的寄存器數量
if(StartAddress_Reg < 100) //寄存器起始地址在正確范圍內
{
if(StartAddress_Reg + Num_Reg < 100 && Num_Reg > 0) //起始地址+寄存器數量位于正確范圍內 并且 寄存器數量正確
{
Send_Cnt = 3 + (Num_Reg << 1) + 2; //計算發送字節數量
Modbus_Send_Buff[0] = Slave_Address; //從站地址
Modbus_Send_Buff[1] = Modbus_ReadHoldingReg; //功能碼
Modbus_Send_Buff[2] = Num_Reg << 1; //寄存器字節數量 等于 寄存器數量乘2
for(i = StartAddress_Reg, j = 3; i < StartAddress_Reg + Num_Reg; i++, j += 2) //讀取寄存器的數據
{
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)); //計算發送數據的CRC校驗碼
Modbus_Send_Buff[3 + (Num_Reg << 1)] = (uint8_t)(CRC_Cal >> 8); //先是低字節
Modbus_Send_Buff[3 + (Num_Reg << 1) + 1] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節
USART_SendString(USART1, Modbus_Send_Buff, Send_Cnt); //發送響應報文
Modbus_ClearBuff(); //清空接收數據緩沖區和發送數據緩沖區
}
else
{
Modbus_ErrorHandling(0x03); //非法數據值
}
}
else
{
Modbus_ErrorHandling(0x02); //非法數據地址
}
}
//----------------------------------------------------------------------------//
//函數功能:功能碼0x06,寫單個寄存器
//入口參數:無
//出口參數:無
//最后修改:2015.12.6
//備注:
//----------------------------------------------------------------------------//
void Modbus_WriteSingleReg_Process(void)
{
uint8_t Send_Cnt; //發送字節數量
uint16_t Address_Reg; //要寫入的寄存器地址
uint16_t Value_Reg; //要寫入的寄存器值
uint16_t CRC_Cal; //CRC校驗碼
Address_Reg = Modbus_Rcv_Buff[2] << 8 | Modbus_Rcv_Buff[3]; //從接收數據緩沖區得到要寫入的寄存器地址
Value_Reg = Modbus_Rcv_Buff[4] << 8 | Modbus_Rcv_Buff[5]; //從接收數據緩沖區得到要寫入的寄存器值
if(Address_Reg < 100) //寄存器起始地址在正確范圍內
{
Send_Cnt = 6 + 2; //計算發送字節數量
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); //寄存器地址高字節
Modbus_Send_Buff[3] = (uint8_t)(Address_Reg & 0x00FF); //寄存器地址低字節
Modbus_Send_Buff[4] = (uint8_t)(HoldingReg[Address_Reg] >> 8); //寄存器值高字節
Modbus_Send_Buff[5] = (uint8_t)(HoldingReg[Address_Reg] & 0x00FF); //寄存器值低字節
CRC_Cal = Modbus_CRC16(Modbus_Send_Buff, 6); //計算發送數據的CRC校驗碼
Modbus_Send_Buff[6] = (uint8_t)(CRC_Cal >> 8); //先是低字節
Modbus_Send_Buff[7] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節
USART_SendString(USART1, Modbus_Send_Buff, Send_Cnt); //發送響應報文
Modbus_ClearBuff(); //清空接收數據緩沖區和發送數據緩沖區
}
else
{
Modbus_ErrorHandling(0x02); //非法數據地址
}
}
//----------------------------------------------------------------------------//
//函數功能:功能碼0x10,寫多個寄存器
//入口參數:無
//出口參數:無
//最后修改:2015.12.9
//備注:
//----------------------------------------------------------------------------//
void Modbus_WriteMultipleReg_Process(void)
{
uint8_t Send_Cnt; //發送字節數量
uint16_t StartAddress_Reg; //要寫入的寄存器起始地址
uint16_t Num_Reg; //要寫入的寄存器的數量
uint16_t CRC_Cal; //CRC校驗碼
uint16_t i, j; //臨時變量
StartAddress_Reg = Modbus_Rcv_Buff[2] << 8 | Modbus_Rcv_Buff[3]; //從接收數據緩沖區得到要寫入的寄存器起始地址
Num_Reg = Modbus_Rcv_Buff[4] << 8 | Modbus_Rcv_Buff[5]; //從接收數據緩沖區得到要寫入的寄存器數量
if(StartAddress_Reg < 100) //寄存器起始地址在正確范圍內
{
if(StartAddress_Reg + Num_Reg < 100 && Num_Reg > 0) //起始地址+寄存器數量位于正確范圍內 并且 寄存器數量正確
{
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); //寄存器起始地址高字節
Modbus_Send_Buff[3] = (uint8_t)(StartAddress_Reg & 0x00FF); //寄存器起始地址低字節
Modbus_Send_Buff[4] = (uint8_t)(Num_Reg >> 8); //寄存器數量高字節
Modbus_Send_Buff[5] = (uint8_t)(Num_Reg & 0x00FF); //寄存器數量低字節
CRC_Cal = Modbus_CRC16(Modbus_Send_Buff, 6); //計算發送數據的CRC校驗碼
Modbus_Send_Buff[6] = (uint8_t)(CRC_Cal >> 8); //先是低字節
Modbus_Send_Buff[7] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節
USART_SendString(USART1, Modbus_Send_Buff, Send_Cnt); //發送響應報文
Modbus_ClearBuff(); //清空接收數據緩沖區和發送數據緩沖區
}
else
{
Modbus_ErrorHandling(0x03);
}
}
else
{
Modbus_ErrorHandling(0x02);
}
}
//----------------------------------------------------------------------------//
//函數功能:錯誤處理
//入口參數:ErrorType是錯誤類型
//出口參數:無
//最后修改: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); //計算發送數據的CRC校驗碼
Modbus_Send_Buff[3] = (uint8_t)(CRC_Cal >> 8); //先是低字節
Modbus_Send_Buff[4] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節
USART_SendString(USART1, Modbus_Send_Buff, 5); //發送異常響應報文
break;
case 0x02: //非法數據地址
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); //計算發送數據的CRC校驗碼
Modbus_Send_Buff[3] = (uint8_t)(CRC_Cal >> 8); //先是低字節
Modbus_Send_Buff[4] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節
USART_SendString(USART1, Modbus_Send_Buff, 5); //發送異常響應報文
break;
case 0x03: //非法數據值
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); //計算發送數據的CRC校驗碼
Modbus_Send_Buff[3] = (uint8_t)(CRC_Cal >> 8); //先是低字節
Modbus_Send_Buff[4] = (uint8_t)(CRC_Cal & 0x00FF); //后是高字節
USART_SendString(USART1, Modbus_Send_Buff, 5); //發送異常響應報文
break;
// default:
// return;
}
Modbus_ClearBuff(); //清空接收數據緩沖區和發送數據緩沖區
}
//----------------------------------------------------------------------------//
//函數功能:清空接收數據緩沖區和發送數據緩沖區
//入口參數:無
//出口參數:無
//最后修改:2015.12.5
//備注:
//----------------------------------------------------------------------------//
void Modbus_ClearBuff(void)
{
uint16_t i;
for(i = 0; i < Modbus_Max_Rcv_Buff; i++) //清除接收緩沖區
{
Modbus_Rcv_Buff[i] = '\0';
}
Modbus_Rcv_Cnt = 0; //接收字節計數清0
for(i = 0; i < Modbus_Max_Send_Buff; i++) //清除發送緩沖區
{
Modbus_Send_Buff[i] = '\0';
}
}
//----------------------------------------------------------------------------//
//函數功能:把一個字節的高4位十六進制數存到另一個字節的低4位里
//入口參數:一個字節的數據
//出口參數:另一個字節
//最后修改:2015.11.28
//備注:
//----------------------------------------------------------------------------//
uint8_t High4BitsToOneByte(uint8_t Byte)
{
uint8_t tempByte;
tempByte = (Byte >> 4) & 0x0F;
return tempByte;
}
//----------------------------------------------------------------------------//
//函數功能:把一個字節的低4位十六進制數存到另一個字節的低4位里
//入口參數:一個字節的數據
//出口參數:另一個字節
//最后修改:2015.11.28
//備注:
//----------------------------------------------------------------------------//
uint8_t Low4BitsToOneByte(uint8_t Byte)
{
uint8_t tempByte;
tempByte = Byte & 0x0F;
return tempByte;
}
//----------------------------------------------------------------------------//
//函數功能:把低4位16進制數轉換為在OLED字庫上對應的0~9和A~F
//入口參數:HexByte是低4位16進制數
//出口參數:OLED字庫上對應的0~9和A~F
//最后修改:2015.11.28
//備注:
//----------------------------------------------------------------------------//
uint8_t HexToOLEDAsc(uint8_t HexByte)
{
if((HexByte >= 0x00) && (HexByte <= 0x09)) //數字0~9
{
HexByte += 0x30;
}
else if((HexByte >= 0x0A) && (HexByte <= 0x0F)) //數字A~F
{
HexByte += 0x37;
}
else
{
HexByte = 0xff;
}
return HexByte;
}
復制代碼
作者:
xtpsdly
時間:
2018-7-7 10:53
謝謝樓主分享
作者:
mjyghai1
時間:
2018-8-23 13:59
多謝樓主分享
作者:
389056325
時間:
2018-12-6 14:23
多謝樓主分享
作者:
p0083
時間:
2019-3-21 22:53
謝謝樓主分享
作者:
erzhu2007
時間:
2019-4-24 23:33
樓豬肯定辛苦了,謝謝
作者:
視覺℡
時間:
2020-1-4 16:13
學習了 收下代碼了
作者:
tourist209
時間:
2020-2-28 10:07
代碼很棒,謝謝分享
作者:
timeabcd
時間:
2022-6-22 10:38
沒有工程下載嗎?
作者:
zyftank
時間:
2023-5-25 18:47
樓主有完整代碼嗎?
作者:
zyftank
時間:
2023-5-26 11:38
感謝樓主,使我理解了清理緩沖區,現在大部分代碼跑不了,不少原因就是沒有清理緩沖區。
歡迎光臨 (http://www.zg4o1577.cn/bbs/)
Powered by Discuz! X3.1
主站蜘蛛池模板:
精品一区二区三区四区五区
|
毛片大全
|
久久天天躁狠狠躁夜夜躁2014
|
国产成人网
|
欧美高清视频一区
|
一本色道精品久久一区二区三区
|
欧美一区二区三区在线看
|
精品国产99
|
日韩中文字幕一区二区
|
欧美久久不卡
|
尤物在线
|
老头搡老女人毛片视频在线看
|
亚洲精品成人网
|
欧美视频 亚洲视频
|
亚洲久视频
|
国产不卡一
|
91在线精品一区二区
|
欧美专区在线
|
国产高清一区二区
|
日韩无
|
男女爱爱网站
|
亚洲免费一区
|
日韩视频中文字幕
|
国产免费播放视频
|
超碰在线人人
|
成人天堂
|
亚洲一区精品在线
|
99国内精品久久久久久久
|
久久久久久久网
|
国产精品欧美精品
|
亚洲成人日韩
|
亚洲一区二区综合
|
夜夜艹天天干
|
亚洲视频免费在线观看
|
免费观看一级特黄欧美大片
|
国产精品久久久久久福利一牛影视
|
天天操夜夜操
|
亚洲一区二区久久久
|
亚洲一区视频在线
|
久久99精品久久久
|
91av视频在线播放
|