基于 STM32的 modbus-rtu 实现
基于 STM32的 modbus-rtu 实现
本文引自http://bbs.21ic.com/club/bbs/ShowAnnounce.asp?v=&ID=3015141
/*****************************************************
modbus-rtu 通讯规约
通讯方式:rs-485
半双功
校验方式:crc16
停止位:2位
编写:孙可
编程思路:
1.串口中断允许自动接收总线上的信息,当接收的
字节后超过3.5个字节时间没有新的字节认为本次
接收完成,接收完成标志置1;如果接收完成标志已
经置1又有数据进来则丢弃新来的数据。
2.串口接收数据的处理,
当接收完成标志置1进入
接收数据处理,
(1)首先判断接收的第一位数据与
本机地址是否相同,如果不相同清空接收缓存不发
送任何信息;
(2)接收的第一位数据与本机地址相
同,则对接收缓存中的数据进行crc16校验,如果接
收的校验位与本校验结果不相同清空接收缓存不发
送任何信息;
(3)如果crc16校验正确则根据数据串中的命令码进
行相应的处理。
******************************************************/
#include
"modbus.h"
u8 Com0_id = 0x05;//本机串口0的通讯地址
u8
Uart0_rev_buff[100];//com0串口接收缓冲区
u8
Uart0_send_buff[100];//com0串口发送缓冲区
vu8 Uart0_rev_count;
vs8
Uart0_send_counter = 0;
vu8 Uart0_rev_comflag;
vu8 Crc_counter =
0;//com0校验计数器
vu8 *Uart0_send_pointer = Uart0_send_buff;//com0串口发送指针
vu16
Mkgz_bz = 0;//模块故障标志1:输入异常,2:过压,3:欠压,4:过温
vu16 Out_current =
50;//输出电流
vu16 Out_voltage = 240;//输出电压
vu16 Mkzt_bz = 0;//模块状态标志
vu16
OutX_current = 1000;//输出限流
vu16 Jc_voltage = 2530;//均充电压
vu16 Fc_voltage =
2400;//浮充电压
vu16 user_day = 1825;//使用天数
void Delay(vu32
nCount);
unsigned short getCRC16(volatile unsigned char *ptr,unsigned char
len) ;
void mov_data(u8 a[100],u8 b[100],u8 c);
void
Modbus_Function_3(void);
void
Modbus_Function_6(void);
/***************************************
函数名称:crc16校验
函数功能:crc16校验
函数输入:字节指针*ptr,数据长度len
函数返回:双字节crc
函数编写:孙可
编写日期:2008年6月9日
函数版本:v0.2
****************************************/
unsigned
short getCRC16(volatile unsigned char *ptr,unsigned char len)
{
unsigned char i;
unsigned short crc = 0xFFFF;
if(len==0)
{
len = 1;
}
while(len--)
{
crc ^= *ptr;
for(i=0;
i<8; i++)
{
if(crc&1)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>=
1;
}
}
ptr++;
}
return(crc);
}
/***************************************
块数据复制数据函数
功能:把数组a的c个数据复制到数组b中
输入:指针a,指针b,数据个数c
返回:无
编写:孙可
编写日期:2008年3月28日
版本:v0.1
****************************************/
void
mov_data(u8 a[100],u8 b[100],u8 c)
{
u8 i;
for(i=c; i>0;
i--)
{
a =
b;
}
}
///////////////////////////////////////////////////////////////////////
void Modbus_Function_3(void)
{
u16 tempdress = 0;
u8 i =
3;
u16 crcresult;
tempdress = (Uart0_rev_buff[2]
<< 8) + Uart0_rev_buff[3];
if((tempdress >= 0x0120) &
(tempdress + Uart0_rev_buff[5] <
0x0132))
{
Uart0_send_buff[0] =
Com0_id;
Uart0_send_buff[1] = 0x03;
Uart0_send_buff[2] =
2 * Uart0_rev_buff[5];
Uart0_send_counter = 2 * Uart0_rev_buff[5] +
3;
switch(tempdress)
{
case
0x0120:
{
Uart0_send_buff = Mkgz_bz &
0xff;
i++;
Uart0_send_buff = (Mkgz_bz >>
8) & 0xff;
i++;
}//后面不放break的目的是继续往下执行
case
0x0122:
{
Uart0_send_buff = Out_voltage &
0xff;
i++;
Uart0_send_buff = (Out_voltage
>> 8) & 0xff;
i++;
}
case
0x0124:
{
Uart0_send_buff = Out_current &
0xff;
i++;
Uart0_send_buff = (Out_current
>> 8) & 0xff;
i++;
}
case
0x0126:
{
Uart0_send_buff = Mkzt_bz &
0xff;
i++;
Uart0_send_buff = (Mkzt_bz >>
8) & 0xff;
i++;
}
case
0x0128://这个地址是备用的里面的数据没有意义
{
Uart0_send_buff =
0x00;
i++;
Uart0_send_buff =
0x00;
i++;
}
case
0x012A:
{
Uart0_send_buff = OutX_current &
0xff;
i++;
Uart0_send_buff = (OutX_current
>> 8) & 0xff;
i++;
}
case
0x012C:
{
Uart0_send_buff = Jc_voltage &
0xff;
i++;
Uart0_send_buff = (Jc_voltage
>> 8) & 0xff;
i++;
}
case
0x012E:
{
Uart0_send_buff = Fc_voltage &
0xff;
i++;
Uart0_send_buff = (Fc_voltage
>> 8) & 0xff;
i++;
}
case
0x0130:
{
Uart0_send_buff =
0x00;
i++;
Uart0_send_buff =
0x00;
i++;
}
}
//UCSRB |=
(1<<TXCIE)|(1<<TXEN);//发送、发送中断允许
crcresult =
getCRC16(Uart0_send_buff,Uart0_send_counter);
Uart0_send_buff[Uart0_send_counter] = crcresult & 0xff;
Uart0_send_buff[Uart0_send_counter+1] = (crcresult >> 8) &
0xff;
Uart0_send_counter = Uart0_send_counter+2;
Uart0_send_pointer =
Uart0_send_buff;
USART_SendData(USART1,
*Uart0_send_pointer++);
USART_ITConfig(USART1, USART_IT_TXE,
ENABLE);
}
}
/////////////////////////////////////////////////////////////
void
Modbus_Function_6(void)
{
u16 tempdress = 0;
u8 tx_flat =
0;
u16 crcresult;
tempdress = (Uart0_rev_buff[2]<<8) +
Uart0_rev_buff[3];
switch(tempdress)
{
case
0x0126:
{
Mkzt_bz = (Uart0_rev_buff[4]<<8) +
Uart0_rev_buff[5];
if(user_day >
0)
{
tx_flat =
1;
}
}break;
case
0x012A:
{
OutX_current = (Uart0_rev_buff[4]<<8) +
Uart0_rev_buff[5];
if(user_day >
0)
{
tx_flat =
1;
}
}break;
case
0x012C:
{
Jc_voltage = (Uart0_rev_buff[4]<<8) +
Uart0_rev_buff[5];
if(user_day >
0)
{
tx_flat =
1;
}
}break;
case
0x012E:
{
Fc_voltage = (Uart0_rev_buff[4]<<8) +
Uart0_rev_buff[5];
if(user_day >
0)
{
tx_flat =
1;
}
}break;
case
0x01EE:
{
user_day = (Uart0_rev_buff[4]<<8) +
Uart0_rev_buff[5];
tx_flat =
1;
//eeprom_write_word
(&user_day_eep,user_day);
}break;
default:
//命令码无效不应答
{
tx_flat =
0;
}
}
if(tx_flat ==
1)
{
Uart0_send_buff[0] =
Com0_id;
Uart0_send_buff[1] = 0x06;
Uart0_send_buff[2] =
Uart0_rev_buff[2];
Uart0_send_buff[3] = Uart0_rev_buff[3];
Uart0_send_buff[4] = Uart0_rev_buff[4];
Uart0_send_buff[5] = Uart0_rev_buff[5];
Uart0_send_counter = 6;
//UCSRB |=
(1<<TXCIE)|(1<<TXEN);//发送、发送中断允许
crcresult =
getCRC16(Uart0_send_buff,Uart0_send_counter);
Uart0_send_buff[Uart0_send_counter] = crcresult & 0xff;
Uart0_send_buff[Uart0_send_counter+1] = (crcresult >> 8) &
0xff;
Uart0_send_counter = Uart0_send_counter+2;
Uart0_send_pointer =
Uart0_send_buff;
USART_SendData(USART1,
*Uart0_send_pointer++);
USART_ITConfig(USART1, USART_IT_TXE,
ENABLE);
}
}
/////////////////////////////////////////////////////////////
void
Com0_Communication(void)
{
s8 i
=0;
if(Uart0_rev_comflag ==
1)//接收完成标志=1处理,否则退出
{
if(Uart0_rev_buff[0] ==
Com0_id)//地址错误不应答
{
unsigned short crcresult;
unsigned char temp[2];
crcresult =
getCRC16(Uart0_rev_buff,Crc_counter-2);
temp[1] = crcresult
& 0xff;
temp[0] = (crcresult >> 8) & 0xff;
if((Uart0_rev_buff[Crc_counter-1] ==
temp[0])&&(Uart0_rev_buff[Crc_counter-2] == temp[1]))//crc校验错误不应答
{
//SETBIT(PORTC,PC6);
Delay(1);
switch(Uart0_rev_buff[1])
{
case 0x03:
{
if(user_day >
0)
{
Modbus_Function_3();
}
}
break;
case
0x06:
{
Modbus_Function_6();
}
break;
}
}
}
Uart0_rev_comflag
= 0;
for(i = 100;i > -1;i--)
{
Uart0_rev_buff = 0;
}
}
}
/*******************************************************************************
*
Function Name : Delay
* Description : Inserts a delay time.
*
Input : nCount: specifies the delay time length.
* Output :
None
* Return :
None
*******************************************************************************/
void
Delay(vu32 nCount)
{
for(; nCount != 0; nCount--);
}