blackrain 发表于 2006-3-2 09:01:23

共享:最近搞了一个UART多机通讯模式,现在将源码共享

主机MEGA16L,代码如下

#include <iom16v.h>

#include <macros.h>

#define uchar unsigned char



unsigned char data;

unsigned char timerl;

unsigned char timerh;

unsigned char time,rr;

unsigned char sendbuf;

unsigned char recbuf;

uchar   addr,fun,message;

uchar   realaddr,realfun,keepaddr,alarm,realalarm;//for&sup2;&brvbar;&Acirc;&euml;&iquest;&ordf;&sup1;&Oslash;



void delay(unsigned char i)

{

    for(timerl=0;timerl<i;);

}

void port_init(void)

{

PORTA = 0x3F;

DDRA = 0x00;

PORTB |= 0x08;//485

DDRB |= 0x08;

PORTC = 0x00; //m103 output only

DDRC= 0xFF;

PORTD = 0xC0;

DDRD= 0x30;

}



//TIMER0 initialize - prescale:64

// WGM: Normal

// desired value: 1mSec

// actual value:1.000mSec (0.0%)

void timer0_init(void)

{

TCCR0 = 0x00; //stop

TCNT0 = 0x83; //set count

TCCR0 = 0x03; //start timer

}



#pragma interrupt_handler timer0_ovf_isr:10

void timer0_ovf_isr(void)

{

TCNT0 = 0x83; //reload counter value

timerl++;

timerh++;

}



//TIMER1 initialize - prescale:1024

// WGM: 0) Normal, TOP=0xFFFF

// desired value: 1Sec

// actual value:1.000Sec (0.0%)

void timer1_init(void)

{

TCCR1B = 0x00; //stop

TCNT1H = 0xE1; //setup

TCNT1L = 0x7C;

TCCR1A = 0x00;

TCCR1B = 0x05; //start Timer

}



#pragma interrupt_handler timer1_ovf_isr:9

void timer1_ovf_isr(void)

{

//TIMER1 has overflowed

TCNT1H = 0xE1; //reload counter high value

TCNT1L = 0x7C; //reload counter low value

time++;

}



//UART0 initialize

// desired baud rate: 19200

// actual: baud rate:19231 (0.2%)

// char size: 9 bit

// parity: Disabled

void uart0_init(void)

{

UCSRB = 0x00; //disable while setting baud rate

UCSRA = 0x00;

UCSRC = BIT(URSEL) | 0x06;

UBRRL = 0x19; //set baud rate lo

UBRRH = 0x00; //set baud rate hi

UCSRB = 0xDC;

}



#pragma interrupt_handler uart0_rx_isr:12

void uart0_rx_isr(void)

{

//uart has received a character in UDR

}



#pragma interrupt_handler uart0_tx_isr:14

void uart0_tx_isr(void)

{

unsigned char i;

rr ++;

if(rr < 4){

    // for(i=0;i<100;i++);

       UCSRB &= ~(1<<TXB8);//

   UDR = sendbuf;       

}



}



//call this routine to initialize all peripherals

void init_devices(void)

{

//stop errant interrupts until set up

CLI(); //disable all interrupts

port_init();

timer0_init();

timer1_init();

uart0_init();



MCUCR = 0x00;

GICR= 0x00;

TIMSK = 0x05; //timer interrupt sources

SEI(); //re-enable interrupts

//all peripherals are now initialized

}



voidReadSwitch(){

    unsigned char    i;

    realaddr = 0;

    realfun = 0;

    PORTB &= 0xF8;

    DDRB &= 0xF8;

    DDRB |= 0x01;

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

    if(!(PINA & 0x20))realaddr |= 0x01;

    if(!(PINA & 0x10))realaddr |= 0x02;

    if(!(PINA & 0x08))realaddr |= 0x04;

    if(!(PINA & 0x04))realaddr |= 0x08;

    if(!(PINA & 0x02))realaddr |= 0x10;

    if(!(PINA & 0x01))realaddr |= 0x20;



    DDRB &= 0xF8;

    DDRB |= 0x02;

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

    if(!(PINA & 0x01))realaddr |= 0x40;

    if(!(PINA & 0x02))realaddr |= 0x80;

    if(!(PINA & 0x04))realfun |= 0x01;

    if(!(PINA & 0x08))realfun |= 0x02;

    if(!(PINA & 0x10))realfun |= 0x04;

    if(!(PINA & 0x20))realfun |= 0x08;



    DDRB &= 0xF8;

    DDRB |= 0x04;

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

    if(!(PINA & 0x08))realfun |= 0x40;

    if(!(PINA & 0x10))realfun |= 0x20;

    if(!(PINA & 0x20))realfun |= 0x10;



    PORTB |= 0x01;         

    DDRB &= 0xF8;

    DDRB |= 0x01;

}





unsigned charchecksum()

{

   return (sendbuf+sendbuf+sendbuf);

}



void main(void)

{

unsigned char i;

init_devices();

timerl = 0;

timerh = 0;

time   = 0;

for(;;){

       if(timerl > 200){

             PORTB ^= 0x01;

             timerl = 0;

                   ReadSwitch();

                   if(realfun & 0x01){

                     sendbuf=realaddr;                     

               sendbuf=0x77;

               sendbuf='1';

               sendbuf= checksum();

                     rr = 0;

                   UCSRB |= (1<<TXB8);                            

                     UDR = realaddr;

                     

                   }

                   if(realfun & 0x02){

                     sendbuf=realaddr;

                     sendbuf=0x77;

               sendbuf='2';

               sendbuf= checksum();

                     rr = 0;

                   UCSRB |= (1<<TXB8);                  

                     UDR = realaddr;

                   }

                   if(realfun & 0x04){

                     sendbuf=realaddr;

                     sendbuf=0x77;

               sendbuf='3';

               sendbuf= checksum();

                     rr = 0;

                   UCSRB |= (1<<TXB8);

                     UDR = realaddr;

                   }

                   if(realfun & 0x08){

                     sendbuf=realaddr;

                     sendbuf=0x77;

               sendbuf='4';

               sendbuf= checksum();

                     rr = 0;

                   UCSRB |= (1<<TXB8);

                     UDR = realaddr;

                   }

                   if(realfun & 0x10){

                     sendbuf= realaddr;

                     sendbuf= 0x77;

               sendbuf= 'F';

               sendbuf= checksum();

                     rr = 0;

                   UCSRB |= (1<<TXB8);

                     UDR = realaddr;

                   }                  

                   if(realfun & 0x40){

                     sendbuf=realaddr;

                     sendbuf=0x77;

                            sendbuf=0x88;                                                                                                                                                                                                          sendbuf= checksum();

                     rr = 0;

                   UCSRB |= (1<<TXB8);

                     UDR = realaddr;

                   }

                   if(realfun & 0x20){

                     sendbuf=realaddr;

                     sendbuf=0x88;

               sendbuf='8';

               sendbuf= checksum();

                     rr = 0;

                   UCSRB |= (1<<TXB8);

                     UDR = realaddr;

                   }                  

           }

}

}


-----此内容被blackrain于2006-03-02,09:02:52编辑过

avrboy 发表于 2006-3-2 09:09:37

通信頭文件如何定義!

blackrain 发表于 2006-3-2 09:19:05

多机通讯与普通的USART通讯差不多,只不过注意几点:

1,需在复位时将UCSRA寄存器中的MPCM位置1

2,上位机发送地址数据时将第九位置1,这样一来,所有的从机都能通过中断接收到此数据啦各个从机判断接收到的数据是否与自己的地址相符,不符的返回,符合的(只能有1台符合)将MPCM位置0,以便正常的接收发给自己的数据.这些数据的第九位必须为0.其他的从机不会对这些数据产生中断.从而实现多机通讯.

上位机发送 UCSRB |= (1<<TXB8);及第九位数据是1的时候,从机能够能够产生中断,如果第九位0,则从机将数据忽略。如果你发的数据帧:

地址+数据1+数据2+...   

你可以在从机接收到地址数据后,与本机的地址比较,如果相同。说明是发给本机的指令,你可以在中断中将本设置普通的数据模式,用以接收后面的数据。

以上是本人的体会,可能不是很完整,希望大家题建议。

MSN:hxdlj@hotmail.com

我一直在线

blackrain 发表于 2006-3-2 09:35:30

上面的MEGA16程序是完整的程序,能够在ICCAVR上运行通过的。



下位机是MEGA8



void uart0_init_mulmodel(void)

{

UCSRB = 0x00; //disable while setting baud rate

UCSRA = 0x01; //mul. processor

UCSRC = BIT(URSEL) | 0x06;

UBRRL = 0x19; //set baud rate lo

UBRRH = 0x00; //set baud rate hi

UCSRB = 0x94; //9 bit data

}

void uart0_init_normodel(void)

{

UCSRB = 0x00; //disable while setting baud rate

UCSRA = 0x00; //normal model

UCSRC = BIT(URSEL) | 0x06;

UBRRL = 0x19; //set baud rate lo

UBRRH = 0x00; //set baud rate hi

UCSRB = 0x94; //9 bit data

}



#pragma interrupt_handler uart0_rx_isr:12

void uart0_rx_isr(void)

{

//uart has received a character in UDR

    rrr = UDR;

    rr++;

    receivetask();

}

//数据接收处理函数

void R_KeLi(void)

{

       kelitimer = 0;

       switch(rr){

               case 1:

                   if(rrr == addr){//判断地址是否与本机地址相等

                           uart0_init_normodel();//如果相等,将本设置位普通模式来接收后面的数据,如果没有这句,后面的数据(第九位为0)将被忽略                       

                         }

                         else{

                           rr = 0;

                         }

                               break;

        }

                                  

}



MEGA8程序我为公司开发的项目,只能提供上面的部分代码,请见谅!!!!!!!!!!!

gxlujd 发表于 2006-3-2 09:50:12

最近也在做多机通讯的实验,总是不成功,有两个疑问想请教大家:

1、多机通讯启用了9位数据模式,是否能够同时启用硬件奇或偶校验?

2、网上所有多机通讯的例子代码都是使用接收中断的,是否也可以使用查询?

HJJourAVR 发表于 2006-3-2 10:06:34

1 奇偶校验应该是还有的,硬件自动生成,跟51不同。

奇偶校验产生电路为串行数据帧生成相应的校验位。

校验位使能(UPM1 = 1) 时,发送控制逻辑电路会在数据的最后一位与第一个停止位之间插入奇偶校验位。

不过,奇偶校验只是一个很简单的保护,作用不强。CRC检验才是更可靠的方案。

2 查询也行,如果MCU有空的话。

gxlujd 发表于 2006-3-2 10:14:26

一般民用的情况下,奇偶检验应该足够了吧?大不了错了再发一次。

HJJourAVR 发表于 2006-3-2 10:25:19

看来彩虹糊涂了:

奇偶检验只能识别一个位的错误,同时错2个位是没法识别的。(具体要看书才行)

如果干扰稍大(特别是无线通讯),根本是无能为力。



CRC16校验模式(X^16 + X^12 + X^5 + 1),纠错率高达99.9984%。

jackrich 发表于 2006-3-2 11:18:58

多多学习。

kinsey 发表于 2006-3-2 11:26:47

CRC16校验模式(X^16 + X^12 + X^5 + 1),纠错率高达99.9984%。 ??



CRC16没有纠错能力吧?

HJJourAVR 发表于 2006-3-2 11:33:08

呵呵!应该是 CRC16配合重发机制能达到纠错率高达99.9984%。

CRC16只有识别错误能力。



那个是摘抄网上的XMODEM的资料的。不知道英文原版是怎么写的?

XMODEM有重发机制的。

CRC16配合重发机制能达到纠错率高达99.9984%,这样应该正确了吧?



一般来说,既然用了CRC,就必然会考虑重发机制的。

gxlujd 发表于 2006-3-2 15:29:40

我知道奇偶检验同时错2位无法识别,所以我在数据包最后还有一个逐字节异或的软件校验码,有错应该能检查得出来,然后就重发。

HJJourAVR 发表于 2006-3-2 15:46:22

既然如此,干嘛不直接用CRC16呢?

而且GCCAVR里面还有现成的CRC16函数。

gxlujd 发表于 2006-3-2 19:31:34

从机多,怕CRC16花太多CPU时间,不能保证实时性。

blackrain 发表于 2006-3-3 11:58:53

CRC表会占用很多的存储空间,如果对传输数据要求很严。建议你在数据帧的后面加上校验字节:例如你可以使用如下的帧格式,

地址+data1+data2+...+dataN+sam

如果接收机地址+data1+data2+...+dataN!=sam可以要求主机重发,我现在做都是使用此方法

HJJourAVR 发表于 2006-3-3 12:06:01

CRC16也可以不查表的,软件处理,反正AVR速度高。

速度?? 真郁闷,串口能有多高的速度?而且应答模式的收/发之间本来就允许处理时间。



#include <util/crc16.h>

/*

GCCAVR内置函数,可以不用头痛CRC16了

关于CRC的详细说明,可以查看一下网站:

http://www.nongnu.org/avr-libc/user-manual/group__avr__crc.html

函数原形

static __inline__ uint16_t _crc16_update(uint16_t __crc, uint8_t __data);

        多项式Polynomial: x^16 + x^15 + x^2 + 1 (0xa001)

        crc初始值Initial value: 0xffff

    通常用于磁盘控制器(disk-drive controllers)

static __inline__ uint16_t _crc_xmodem_update(uint16_t __crc, uint8_t __data);

        多项式Polynomial: x^16 + x^12 + x^5 + 1 (0x1021)

        crc初始值Initial value: 0x0

        专用于XMODEM通讯协议,等效于C写的

        uint16_t crc_xmodem_update (uint16_t crc, uint8_t data)

    {

      int i;

      crc = crc ^ ((uint16_t)data << 8);

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

      {

            if (crc & 0x8000)

                crc = (crc << 1) ^ 0x1021;

            else

                crc <<= 1;

      }

      return crc;

    }

static __inline__ uint16_t _crc_ccitt_update (uint16_t __crc, uint8_t __data)

        多项式Polynomial: x^16 + x^12 + x^5 + 1 (0x8408)

        crc初始值Initial value: 0xffff

    专用于PPP和IrDA通讯协议

*/

blackrain 发表于 2006-3-3 13:02:09

回答4楼的问题,如果在多机通讯模式,就不能使用奇偶校验位了。因为第9位是区分地址码和普通数据,如果加了奇偶校验后,第九位数据可能是随机了。多机通讯时,ICCAVR里的设置在图片中。主机选择的是普的九位数据格式,第九位数据发1或0的根据你发的数据,地址则第九为1,数据则第九为0,从机选择九位数据,多机模式。

meizhouxy 发表于 2006-5-8 15:27:31

1.当从机接到地址后,MPCM位置0,这时如果去读第九位地址RXB8行吗?可以的话读到的会是什么?MPCM=0时是禁止多机通信了,这时收到的RXB8=?

         

2.按这种机制发的数据,有没可能出现上位机发数据的时候第九位本来是0的,结果接收到的却是1(两级之间距离5米)?

         

3.这种方式,从机怎么判断自己接收完了数据?如果出现data1或者data2传输过程中丢失了,用计数的方式(设定一个寄存器接到一个字节+1)来判断的话,丢了一个data就会造成错两帧了,这个问题怎么解决才好呢?



请教,谢谢

mj933 发表于 2006-7-14 21:37:13

请问,多机通信时,主机与从机的接线是怎样接啊?能否把你的原理图给我呢?

邮箱:lmer_933@163.com.cn谢谢!

igoal 发表于 2006-7-15 07:51:56

我觉得多机通讯中的9位模式实用性不大,因为有许多芯片不支持9位通讯格式,做一主多从还是用MODBUS等协议比较好一些,而且可以和其他串口芯片很容易的联网。

zhanbanie 发表于 2006-9-12 17:29:49

多谢,我正在做一个RS485主从通信,这个代码对我很有价值.

panxiaoyi 发表于 2006-10-10 17:35:01

也可以参考一下这里,我实验通过的例子

http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=676309&bbs_page_no=1&bbs_id=1000

huangjianga 发表于 2006-10-20 15:32:13

我现在也在弄多机通讯。我听我大学老师说,好象用MAX232不好。最好是用MAX485。

不知道你们是用什么的片子呢?

Tim_yao 发表于 2007-7-1 16:11:24

看了一下,想问下版主通过试验了没有!

为什么我编译都通不过的啊,要修改个别地方才可以勉强通过,请问下这里是什么意思?

unsigned charchecksum( )

{

   return (sendbuf+sendbuf+sendbuf);

}即便是求和校验,这样的程序能通过编译嘛,我用的是iccavr6.31a的编译器的!

Tim_yao 发表于 2007-7-3 11:17:52

版主怎么可以这样子啊,就写了一大队的代码

就没有下文了吗?

没注释,没说明,没图.............

bborisguo 发表于 2007-10-17 14:27:18

起用多机通讯模式,就用不着奇偶校验了,第9位就是地址和数据的区别,当然,你想用也无所谓

cyhmyl 发表于 2007-11-24 05:02:58

请教各位,一主机和多个从机之间进行USART多机通信。硬件上,可以直接把主机(ATmega8)的TXD和每个从机(ATmega8)的RXD直接相连,把主机的RXD和从机的TXD之间相连吗,或者必须加上485芯片才能进行USART多机通信???多谢了!

xk2yx 发表于 2007-11-26 19:17:33

不能直连,多机最好采用I2C方式来的好,抗干扰也可以控制。

lcgforward 发表于 2008-9-8 11:25:21

在PLC中232不能一主多从,只能1:1,多半都是转成485.才能多机通讯,而且要有能识别的站号.

lonely88 发表于 2008-9-8 12:49:10

楼上可以贴个图吗?

Love 发表于 2009-1-17 16:02:20

可以直连,我用mega16就是直连的,数据传输稳定

0620221 发表于 2009-6-9 09:59:31

mark

juventus 发表于 2009-6-9 10:00:47

收下在慢慢研究

yuanshi3 发表于 2009-6-9 10:13:28

做个记号

yunlong 发表于 2009-6-9 10:58:17

【22楼】 huangjianga
多机通讯的话必须用485,用232的话,在一个从机发送数据的时候,会将所有从机的发送端拉低,而发送端的电平在空闲时候应该是为高电平。这样就会造成冲突,一个主机不可能把所用从机的发送端口强行拉低。
不过要是仅仅做实验的话,可以直接用单片机的串口,加二极管做线与。而不用232或者485.
【17楼】 meizhouxy
我的代码的思路是,每接收到一个完整的数据后,产生接收中断,然后中断中先读取,奇偶,帧,溢出这三个标准位,如果为零则读取第九位,如果第九位为1 则读取UDR,并保存入缓冲区。如果三个标志位有一个为一则表示数据有误,然后同样要读取第九位和和UDR,(以达到清除接收缓冲区的目的)但此时读取的数据要丢弃。
启用了九位数据格式。在从机接收到地址位时候,如果该地址位为本机,那么本机就要立即关闭九位数据格式,以接收后面的数据,这样即使使能了硬件奇偶校验,也不会引起冲突。
至于如何判断数据长度是否接收完整,建议在数据中包加入一个数据长度字,同时配合接收时间(这个要根据速率计算一下)来判断。
【16楼】 blackrain 菜鸟 “如果在多机通讯模式,就不能使用奇偶校验位了”
这个是不矛盾的因为从机在接收到本机地址后,必须关闭九位数据模式,从而能正常接收后面的8位格式的数据。这样也就不会影响到奇偶校验位
【26楼】 cyhmyl
如果直接用单片机的串口TTL电平进行多机通讯,在串口初始话的时候,会自动启用端口的上拉电阻。如果把所有从的发送口连在一起然后再接到主机的接收口。
那么在一个从机发送数据的时候,会拉低所用从机的发送端口的电平,这样搞不好会烧掉发送口。
可以用二极管把所有发送口做一个线与后,接到主机的接收口。再用一个10K左右的电阻把主机的接收口拉高。

bbi3014 发表于 2009-6-9 12:58:39

mark

sunyouyuan 发表于 2009-6-9 13:32:34

记号 以后可能用得上

onebobo126com 发表于 2009-7-12 21:21:59

mark

stefgq 发表于 2009-7-12 23:51:48

标记下

guangan854214 发表于 2009-7-13 13:52:32

学习了

chengyuwang 发表于 2009-9-20 12:57:29

】 gxlujd 彩虹能加我吗?183163631细聊

chengyuwang 发表于 2009-9-20 12:57:40

】 gxlujd 彩虹能加我吗?183163631细聊

51hubao 发表于 2009-9-20 13:24:02

mark1

sunmy 发表于 2009-9-26 15:12:59

ding!

tom030704 发表于 2009-9-26 21:10:01

MARK!

ming076 发表于 2009-11-22 20:32:54

mark!

potatoES 发表于 2009-11-22 21:59:33

mark!

lch922 发表于 2010-2-10 10:04:59

mark

youxin2004 发表于 2010-2-10 10:45:45

请教,多机通讯如何用PC来调试

boboo 发表于 2010-3-3 22:19:54

mark记号

jack_yu 发表于 2010-3-4 00:22:54

不错,学习了

ggyyll8683 发表于 2010-3-4 00:58:28

mark

jielove2003 发表于 2010-3-4 08:41:48

好帖收藏

zzh241 发表于 2010-3-4 20:17:31

不错啊

ggachu 发表于 2010-3-5 19:33:27

mark

dalianxwj 发表于 2010-3-9 15:01:29

不错!

sharp0 发表于 2010-3-30 13:06:43

dingding

thinki 发表于 2010-4-1 16:17:41

先mark

cenkey 发表于 2010-4-1 17:18:14

mark

avr19832010 发表于 2010-4-12 16:38:49

mark

taiwentaotwt 发表于 2010-6-9 12:26:59

学习下

BLDC2010 发表于 2010-6-23 10:09:28

mark

crose0106 发表于 2010-6-23 10:25:40

mark

hero751 发表于 2010-6-23 11:30:06

mark

jclwd8263 发表于 2010-8-11 23:28:12

????

fshunj 发表于 2010-8-12 00:10:36

mark

wsm80828 发表于 2010-8-12 08:06:59

好!

anxiangbo 发表于 2010-8-12 08:50:25

mark

eagle_avr 发表于 2011-3-11 09:48:27

【34楼】 yunlong 飞翔 验证了我的实验结果。

我是用 1个PIC32做主机 + N个PIC16做从机来通讯的。用232连接。

一个主机对一个从机正常通讯,但是一个主机对多个从机后,主机就收不到地址匹配的那个从机发送的数据了。
用示波器一看,从机的TX全为低。

现在准备用485来做一下实验,加上奇偶校验。

在此,感谢大家,感谢论坛!

lkwslk1 发表于 2011-4-6 16:05:44

M

wbanng 发表于 2011-4-8 10:20:00

正想搞个多机通信,mark

Elderfox 发表于 2011-4-11 23:08:38

MARK

wukaka 发表于 2011-4-12 12:47:13

认真学习!

zxs2000 发表于 2011-4-12 17:16:01

学习

pjdu 发表于 2011-4-12 22:19:31

mark

enovo2468 发表于 2012-2-16 19:26:24

mark

duduqiuhan 发表于 2012-2-17 12:03:16

不错,值得参考!

enric_lee 发表于 2012-2-19 11:44:30

mark

gxy508 发表于 2012-2-19 15:36:23

mark

x820111 发表于 2012-2-19 17:18:55

回复【3楼】blackrain 菜鸟
-----------------------------------------------------------------------

不错。我最近也在学这个。但一直不知道怎么去写协议。

luguobing 发表于 2012-2-19 18:32:37

mark

lmhtz 发表于 2012-2-19 20:23:58

不错,学习了

edkaifa 发表于 2014-3-23 17:29:23

传个文件好点

radar_12345 发表于 2014-3-23 19:05:52

mark            

雨中的风铃 发表于 2014-6-1 20:50:37

谢谢分享!

dhw5qq 发表于 2014-7-28 14:44:34

cyhmyl 发表于 2007-11-24 05:02
请教各位,一主机和多个从机之间进行USART多机通信。硬件上,可以直接把主机(ATmega8)的TXD和每个从机(A ...

需要加一个反向二极管和上拉电阻!
我现在已经成功了!

dhw5qq 发表于 2014-7-28 15:07:09

yunlong 发表于 2009-6-9 10:58
【22楼】 huangjianga
多机通讯的话必须用485,用232的话,在一个从机发送数据的时候,会将所有从机的发 ...

能详聊吗?
我的邮箱
gyang@iconergy.com
有些地方需要请教!

ZYBing 发表于 2014-7-28 18:39:32

谢谢分享!
页: [1]
查看完整版本: 共享:最近搞了一个UART多机通讯模式,现在将源码共享