我现在正在研究基于avr,mega128的modbus 做了个modbus 也是基于网友的代码调试的 上传
Avr单片机中使用modbus协议的方法
有以下程序用gcc实现,单片机用avr单片机。
ISR(USART0_RX_vect)//串口0接收中断服务程序
{
volatile unsigned char status,data;
cli();//关中断
status = UCSR0A;//ucsr0a赋值状态标志
data = UDR0;//接收的数据放入data变量
usart0_rx_complete=0;//接收完成标志赋值0,还没有完成
if ((status & (FRAMING_ERROR0 | PARITY_ERROR0 | DATA_OVERRUN0))==0)//如果各标志位正确则,执行以下
{
usart0_rx_count++;//接收缓冲区指针加一
switch (usart0_rx_count)
{
case 1:
if(data==add//第一个字节是地址,读入内部本机地址进行比较
{
usart0_rx_buf[0]=data;
TIMSK0=0x01;//启动定时器0,进行超时控制
}
else
{
usart0_rx_count=0;
}
break;
case 2:
if (((data==0x03)||(data==0x01)||(data==0x05)||(data==0x10))==0)//如果第一位不等于读指令0x03,01,05,10功能码,则清接收缓冲区指针
{
usart0_rx_count=0;
}
else//等于这几个功能码则进行,则将他放入接收数组,并预计接收数组长度,不是10码时都是8个字节
{
usart0_rx_buf[1]=data;
if (data!=0x10)
{
rx0_buf_size=8;
}
}
break;
case 3:
usart0_rx_buf[2]=data;
break;
case 4:
usart0_rx_buf[3]=data;
break;
case 5:
usart0_rx_buf[4]=data;
break;
case 6:
usart0_rx_buf[5]=data;
break;
case 7:
usart0_rx_buf[6]=data;//10码时接收的字节计数
if (usart0_rx_buf[1]==0x10)
{
rx0_buf_size=9+usart0_rx_buf[6];
}
break;
case 8:
usart0_rx_buf[7]=data;//1,用10功能码时有效的数据位,system_reg_data的数据,这里规定最多接收26个字节(不带crc)
break;
case 9:
usart0_rx_buf[8]=data;//2
break;
case 10:
usart0_rx_buf[9]=data;//3
break;
case 11:
usart0_rx_buf[10]=data;//4
break;
case 12:
usart0_rx_buf[11]=data;//5
break;
case 13:
usart0_rx_buf[12]=data;//6
break;
case 14:
usart0_rx_buf[13]=data;//7
break;
case 15:
usart0_rx_buf[14]=data;//8
break;
case 16:
usart0_rx_buf[15]=data;//9
break;
case 17:
usart0_rx_buf[16]=data;//10
break;
case 18:
usart0_rx_buf[17]=data;//11
break;
case 19:
usart0_rx_buf[18]=data;//12
break;
case 20:
usart0_rx_buf[19]=data;//13
break;
case 21:
usart0_rx_buf[20]=data;//14
break;
case 22:
usart0_rx_buf[21]=data;//15
break;
case 23:
usart0_rx_buf[22]=data;//16
break;
case 24:
usart0_rx_buf[23]=data;//17
break;
case 25:
usart0_rx_buf[24]=data;//18
break;
case 26:
usart0_rx_buf[25]=data;//19
break;
case 27:
usart0_rx_buf[26]=data;//20
break;
case 28:
usart0_rx_buf[27]=data;//21
break;
case 29:
usart0_rx_buf[28]=data;//22
break;
case 30:
usart0_rx_buf[29]=data;//23
break;
case 31:
usart0_rx_buf[30]=data;//24
break;
case 32:
usart0_rx_buf[31]=data;//25
break;
case 33:
usart0_rx_buf[32]=data;//26
break;
case 34:
usart0_rx_buf[33]=data;//27
break;
case 35:
usart0_rx_buf[34]=data;//28
break;
}
if(usart0_rx_count>=rx0_buf_size)//串口0接收到了指定个数的数组则
{
usart0_rx_count=0;//接收缓冲区指针清零
usart0_rx_complete=1;//串口0接收完标志
time0_num=0;//串口0的中断次数清零。它是超时控制的依据
TIMSK0=0x00;//收到数据后则停止定时器0不进行超时控制
}
}
else
{
usart0_rx_count=0;
}
sei(); //开中断
}
以上中断接收程序可以实现上位机发来的0x03,01,05,10功能码指令,并且可以自动判断上位机发来10功能码的包长。大家首先要深入了解modbus通讯协议的内涵,仔细体会各行程序的含义。其中本人加入了对通讯的超时控制,实际应用中很有必要。
前面已经将上位机发来的命令,用中断接收的方式存入了单片机的接收缓冲区,中断服务程序不能执行太长时间,所以要将对指令的解读放入了主程序里。
把对modbus指令的解读程序列出,如下,只给出框架,因为每种应用是不一样的,需自己加入。
void run_modbus(void)
{
switch (usart0_rx_buf[1])
{
//判断主机发来的modbus 功能码是什么
case 0x01://读继电器输出的当前状
要加入回传数据
break;
case 0x03://功能码03
要加入回传数据
break;
case 0x05://05功能码
要加入回传数据
break;
case 0x10://10功能码
要加入回传数据
break;
}
}
以上程序完全依赖一个提供给上位机数据的数组,自己要仔细排列好数据顺序,定义好长度。
|