搜索
bottom↓
回复: 48

modbus程序,我模拟成功【恢复】

[复制链接]

出0入0汤圆

发表于 2009-1-20 17:23:30 | 显示全部楼层 |阅读模式
这段时间研究了一下modbus,试着编了一段程序,如下:

M8,8MHz时钟

#include <avr/io.h>

#include <avr/interrupt.h>

#include <avr/pgmspace.h>

#include <avr/sleep.h>

#include <util/crc16.h>

#include <avr/wdt.h>



#define  DELAY_TIME  0x82  //modbus T3.5=4ms

#define  BAUD        51    // 8M 9600 

#define  Timer0_stop TCCR0 = 0x00   // timer0 stop

#define  Timer0_star TCCR0 = 0x04   //256分频  timer0 start



#define  ADDR             0x01       //本机地址

#define  MAX_REC_BYTE      10        //最大数据量

#define  MAX_SEND_BYTE     30

uint16_t data[]={1,2,3,4,5,6,7,9,12,56,43,67,87,90};

    char rec_data[MAX_REC_BYTE];             //接收数据区

    char send_data[MAX_SEND_BYTE]={ADDR};    //发送数据区

volatile uint8_t rec_counter;                //接收字符计数

volatile uint8_t rec_complete;               //接收完毕

         uint8_t send_counter;               //发送字符数

                 





int getcrc16(char data[],uint8_t counter)

  {

        int crc16=0xffff;

        unsigned char i;

        for(i=0;i<counter;i++)

        crc16=_crc16_update(crc16,data);

        return crc16;

  }



void Modbus_Function_1(void){};

void Modbus_Function_3(void)

        {

         uint8_t i;

         char *pointer;

         send_counter=2*rec_data[5]+3;

         send_data[2]=send_counter-3;

         pointer=&data[0];

         for(i=0;i<2*rec_data[5];i++)send_data[3+i]=*(2*rec_data[3]+pointer++);

    }



void Modbus_Function_5(void){};

void Modbus_Function_2(void){};

void Modbus_Function_16(void){};













void USART_Init( unsigned int baud )

{

/* 设置波特率*/

UBRRH = (unsigned char)(baud>>8);

UBRRL = (unsigned char)baud;

/* 接收器与发送器使能 接收中断使能 */

UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);

/* 设置帧格式: 8 个数据位, 2 个停止位*/

UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);

}



void USART_Transmit( unsigned char data )

{

/* 等待发送缓冲器为空 */

while ( !( UCSRA & (1<<UDRE)) )

;

/* 将数据放入缓冲器,发送数据 */

UDR = data;

}







ISR(USART_RXC_vect )/*   串口接收 中断程序     */

{

   TCNT0  = DELAY_TIME;

   Timer0_star;

   rec_data[rec_counter]=UDR;

   if(rec_counter<MAX_REC_BYTE-1)rec_counter++;

}



/*   定时器0 中断程序     */



ISR(TIMER0_OVF_vect )

{

 Timer0_stop;

 rec_complete = 1;

 

 }



void Timer0_init(void)

{

  TIMSK |=_BV(TOIE0); //timer interrupt sources 

  Timer0_star;

}





void modbus_Communication(void)  

{  

    unsigned short crcresult;  

    unsigned char   temp[2];  

        unsigned char        i;

    crcresult= getcrc16(rec_data,rec_counter-2);  

    temp[1]=crcresult & 0xff;  

    temp[0]=(crcresult>> 8) & 0xff;  

    if((rec_data[0]==ADDR)&&(rec_data[rec_counter-1]==temp[0])

                              &&(rec_data[rec_counter-2]==temp[1]))  

                {  

                        

            send_data[0]=ADDR;

                send_data[1]=rec_data[1];

                        switch(rec_data[1])  

                                {  

                                        case 0x01:  

                                                        Modbus_Function_1();  

                                                        break;   

                                        case 0x03:  

                                                        Modbus_Function_3();  

                                                        break;   

                                        case 0x05:  

                                                        Modbus_Function_5();  

                                                        break;              

                                        case 0x02:  

                                                        Modbus_Function_2();  

                                                        break;  

                                        case 0x10:  

                                                        Modbus_Function_16();  

                                                        break;        

                                        default:  

                                                        {  

                                                                

                                                                send_data[1]=rec_data[1]+0x80;  

                                send_data[2]=1;  

                                send_counter=3;  

                                 

                                                        }  

                                }  

           

                        crcresult= getcrc16(send_data,send_counter);  

                        send_data[send_counter++]=crcresult & 0xff;  

                        send_data[send_counter++]=(crcresult>> 8) & 0xff;  

                        for(i=0;i<send_counter;i++)USART_Transmit(send_data);



                }

                        

}  









int main(void)

{

 

 cli();

 Timer0_init();

 USART_Init(BAUD);//设定串口

 MCUCR = 0x00;

 GIMSK = 0x00;

 // wdt_enable(WDTO_15MS);

 sei();

 while(1)

 {   

    sleep_enable();

    sleep_cpu();

    sleep_disable();    

   // wdt_reset(); 

    cli();

    if(rec_complete == 1)

    {       

         rec_complete=0;

     modbus_Communication();         

     rec_counter=0; 

         }

    {}//数据采集

        sei();

  }

return (0);

}

出0入0汤圆

发表于 2009-1-20 18:06:28 | 显示全部楼层
因为中断方式容易受毛剌干扰.用查询方式时信号中的毛剌很难正好落在采样点上,但铁定会触发中断.想用中断方式,一定要先整形.

出0入0汤圆

 楼主| 发表于 2009-1-20 18:02:25 | 显示全部楼层
你的建议比较好,我再来研究一下发送中断方式

出0入0汤圆

 楼主| 发表于 2009-1-20 17:47:45 | 显示全部楼层
我是这样想的,作为从机,在接收完后就只做一件事,那就是送出数据。所以我只用了接收中断。关中断的目的是处理通信时,不再接收外部数据。

出0入0汤圆

发表于 2009-1-20 17:39:27 | 显示全部楼层
(3)3.5T和具体的波特率有关

出0入0汤圆

发表于 2009-1-20 17:37:36 | 显示全部楼层
我来挑挑刺。



 (1)、哇,老兄,发送数据用查询方式,怎么不用中断方式呢?

 如果是RS232通讯,可以只用“数据空中断”

 如果是RS485通讯,可以用“数据空中断”+“发送完成中断”呀!

 (2)、主程序中大段的程序代码使用CLI()和SEI()

     cli(); 

    if(rec_complete == 1) 

    {        

         rec_complete=0; 

     modbus_Communication();          

     rec_counter=0;  

         } 

    {}//数据采集 

        sei(); 







  

出0入0汤圆

 楼主| 发表于 2009-1-20 17:31:41 | 显示全部楼层
我仅考虑了T3.5,用来识别帧结束。我认为T1.5没有多大用处,不知道对不对?

串口接收采用中断方式。我还利用Proteus来模拟串口,以及modbus 通信都能成功。

不过由于AVR属于小端,传出的数据低位在前;数据输入数据区data数组时要作变换。



 (原文件名:111111.JPG) 



本贴被 scwxb 编辑过,最后修改时间:2009-01-20,17:41:57.

出0入0汤圆

发表于 2009-2-26 13:35:12 | 显示全部楼层
很好!

出0入0汤圆

发表于 2009-4-28 17:11:57 | 显示全部楼层
关注,学习!

出0入0汤圆

发表于 2009-4-28 18:39:13 | 显示全部楼层
学习了,多谢.

出0入0汤圆

发表于 2009-4-30 13:01:17 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-4-30 13:45:25 | 显示全部楼层
MARK

出0入0汤圆

发表于 2009-4-30 13:50:42 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-5-3 19:25:43 | 显示全部楼层
感谢楼主分享,也说说自己的理解,和大家讨论:
(1)同意ba_wang_mao的看法,从机除了侦听和响应主机的轮询,其自身也要做不少事情,因此发送和接收采用中断方式绝对有必要;
(2)main()中关中断,感觉太过慎重。如果从机仅局限于MODBUS通信,可以这样做。T3.5的间隔已足够宽裕,并且在实际操作中,定时一般设置得比T3.5要长一些,因此就算有中断插入,也来得及(除非其他的ISR函数耗时太长)。如果关中断,数据采集时间较长,主机此时又发来报文,或者从机其他重要中断到来,就可能被延误了;
(3)T3.5严格来讲是和波特率相关,但是实际做的时候是根据19200bps分两档,低于的时候严格定时;高的时候就设定为固定定时:T1.5建议取750us,T3.5取1750us;这样做的目的是:定时长度宽裕一点,抗干扰性好;高速通信的时候降低频繁中断响应的CPU负担;
(4)请教rainyss,毛刺干扰没理解,能否详细介绍一下,谢谢!
(5)这里设定9600bps,T1.5还是必要的,为了避免频繁的中断负担,感觉可以用软件中断+ICP时间戳捕获来做。
(6)请教楼主,AVR是小端的,我看MODBUS的帧结构,也是LSB在前,不需要换序啊?

出0入0汤圆

发表于 2010-7-7 15:52:16 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-8 17:13:47 | 显示全部楼层

好贴 (原文件名:好贴.jpg)

出0入0汤圆

发表于 2010-7-8 19:52:49 | 显示全部楼层
mark 3Q

出0入0汤圆

发表于 2010-8-22 17:34:09 | 显示全部楼层
mark

出0入50汤圆

发表于 2010-8-26 09:48:02 | 显示全部楼层
收藏

出0入0汤圆

发表于 2010-8-28 21:55:53 | 显示全部楼层
收藏

出0入0汤圆

发表于 2010-9-6 12:26:29 | 显示全部楼层
我下载了那个调试精灵1.024,提示dll出错

出0入0汤圆

发表于 2010-9-6 13:18:13 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-17 21:56:09 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-22 10:18:08 | 显示全部楼层
MARK,谢谢,留着以后用。

出0入0汤圆

发表于 2011-2-22 11:45:57 | 显示全部楼层
想问个问题,发送干嘛要用中断呢?

出0入0汤圆

发表于 2011-2-22 11:59:07 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-2-22 12:15:30 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-28 22:38:33 | 显示全部楼层
楼主,向你请教一下,在你这段程序中休闲模式中MCUSR中的SE不要置1吗?像你这样可否进入休闲模式呢?谢谢

出0入0汤圆

发表于 2011-3-9 21:27:18 | 显示全部楼层
回复【1楼】scwxb 别克
-----------------------------------------------------------------------

1楼你好,你的modbus调试精灵能否发我一份,我下载的好多个都不能用总是提示“没有找到MFC42D.DLL,因为这个应用程序未能启动。重新安装应用程序可能会修复此问题。”
邮箱:fangyijun08@sina.com    .
不胜感激!

出0入0汤圆

发表于 2011-7-19 09:39:50 | 显示全部楼层
路过...
严重mark...

出0入0汤圆

发表于 2011-7-19 13:27:34 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-8-5 09:06:28 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-8-5 14:02:23 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-8-5 15:41:01 | 显示全部楼层
标记

出0入0汤圆

发表于 2011-8-5 15:57:06 | 显示全部楼层
好帖

出0入0汤圆

发表于 2011-10-23 23:03:37 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-11-18 19:29:22 | 显示全部楼层
不错,学一下

出0入0汤圆

发表于 2012-1-12 17:43:14 | 显示全部楼层
不错,学一下

出0入0汤圆

发表于 2012-1-13 12:46:44 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-1-15 18:19:17 | 显示全部楼层
同样要modbus调试精灵,邮箱:shin.woo@qq.com,谢谢楼主。

出0入0汤圆

发表于 2012-1-17 11:08:55 | 显示全部楼层
好东西,学习学习!

出0入0汤圆

发表于 2012-1-17 14:42:27 | 显示全部楼层
顶一下

出0入0汤圆

发表于 2012-1-17 15:08:19 | 显示全部楼层
MARK

出0入0汤圆

发表于 2012-1-17 20:12:53 | 显示全部楼层
正在弄,mark

出0入0汤圆

发表于 2012-1-17 22:28:01 | 显示全部楼层
modus 协议

出0入0汤圆

发表于 2012-1-24 22:23:37 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2012-1-26 10:15:16 | 显示全部楼层
好帖

出0入0汤圆

发表于 2012-1-26 10:34:36 | 显示全部楼层
mark ,modbus~

出0入0汤圆

发表于 2012-1-29 21:16:30 | 显示全部楼层
标记
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-29 01:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表