chiaming 发表于 2011-2-10 12:08:47

请问马老师:有关“ UART (USART)口的多机通信 ─ 利用 ZigBee 无线模块相互通信“的

整个系统由“1台PC和5组感测模块(M16L + 6轴传感器)“组成,

其中:
主机部分:1块ZigBee无线通信模块由RS-232 to USB到1台PC,标志为0号。
从机部分:5块ZigBee无线通信模块各自和5组感测模块的UART接口串接,分别标志为1~5号。

总共有6个节点,而全部的ZigBee无线通信模块皆设置为“广播模式”。

程序流程为:
一、1~5号模块分别上电后,进入INITIAL状态,等待接收Capture命令。
二、由0号(PC)透过自身的ZigBee模块”广播”发送一个Capture命令(0xFF0xEC),让1~5号的ZigBee模块一起接收此命令。
三、1~5号模块判断接收到Capture命令(认定为几乎同时收到!?)以后,禁止接收功能(RXEN = 0)。
四、开始对传感器信号进行ADC,各轴轮流采样转换,各5次并且取平均值后,进入INITIAL状态,始能接收功能(RXEN = 1),等待接收ID命令。
五、其中,当1号模块完成ADC后,会自动进入发送数据状态(无需等待接收ID命令),发送全部的数据后,进入INITIAL状态,始能接收功能(RXEN = 1),
    之后又再次地等待0号发送Capture命令。
    数据包格式为: “ID地址、数据1、数据2、数据3、数据4、数据5、数据6“,共7个字节。
六、当0号(PC)接收完1号发送的完整数据包后,开始发送2号的ID命令(0xFFID地址),让2号模块传回它自身模块的数据包。
七、2号模块判断接收到ID命令后,进入发送状态,并且禁止接收功能;数据包发送完成后,进入INITIAL状态,始能接收功能,
    之后又再次地等待0号发送Capture命令。
八、3、4、5号的状态分别类似于六、和七、。
九、最后,0号接收完5号模块的完整数据包后,判断并处理1~5号的数据,又再次地”广播”发送Capture命令,让1~5号的ZigBee模块一起准备接收此命令,
    回到流程二、。

问题描述如下:
发现了4种数据通信的问题情况,导致整体流程通信到一半,就卡住停止了:
1. 0号(PC)开始发送Capture命令,整体流程通信了一段时间,突然1~5号模块没有收到Capture命令?
2. 整体流程通信了一段时间,突然1~5号模块的某一号模块没有收到0号发送的ID命令,以致数据只显示到上一号模块。
3. 同上,但情况变为:0号(PC)没有收到1~5号某一号模块的数据包,以致无法发送下一组的ID命令。
4. 同上,情况为:0号(PC)没有收到5号模块的数据包,以致无法发送Capture命令。

检查过PC收到的每一号模块的数据包内容,皆正确无误。
而这些问题发生的时间都是随机不固定的,有时发生在启动后2秒内,有时发生在启动后10秒内,甚至有时发生在2分钟内,但是有时候通信却很稳定。

已经思考了很久,但仍不知道哪里有问题,在此麻烦请教马老师和各位,谢谢。

(下方为2号模块的M16L程序内容)

chiaming 发表于 2011-2-10 12:09:12

/*****************************************************************************************************************************
   CPU :                ATmega16L
   CLK :                7.3728 MHz
   Buad Rate :        57600                                                              
*****************************************************************************************************************************/
#define        F_CPU         7372800

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "ZIG100.h"

#define       ID            0x32      //编号2
#defineSTART      0xEC      //模块启动命令
#defineCALLFLAG      0xFF      //接收起始旗标
#defineSENSOR          6         //感测轴数
#define       TIMES         5         //采样次数

/* 状态参数 */
#defineINITIAL         0         //初始
#defineACTION          1         //AD转换
#defineTX            2         //传送
#defineWAIT            4         //等待ADC完成

/* 指令接收参数 */
#defineQUANTITY      2         //指令数据2笔
#defineFIRST         0         //索引值 0
#defineSECOND          1         //索引值 1

/* USART参数 */
#define       FOSC            7372800               // Clock频率 //
#define       BUAD            57600               // Buad Rate //
#define       UBRR            (FOSC/(16*BUAD))-1

/* ADC参数 */
#defineADC_INITI       0x60      // ADC参考电压:AVcc,数据向左对齐 //

/* 各个输入通道 */
#define       ADC1            0x01
#define       ADC2            0x02
#define       ADC3            0x03
#define       ADC4            0x04
#define       ADC5            0x05
#define       ADC6            0x06

volatile int State = 0;

int RxNum = 0;
int Axis = 0, Count = 0;                     //惯性轴和采样次数

unsigned int ADC_Data;      //组合ADC数据
unsigned int ADC_Average;            //ADC平均

unsigned char RX_Command;

//ADC取5次平均//
void AverageFilter(void)
{       
int axis, count;

for(axis = 0; axis < SENSOR; axis++)
{
    ADC_Average = INITIAL;                     //清空上一堆数据

    for(count = 0; count < TIMES; count++)
    {
      ADC_Average += ADC_Data;      //不断累加               
    }
               
    ADC_Average /= TIMES;                        //平均=累加和/5
}
}

//无线传送ADC值//
void ADC_Tx(void)
{
int axis;

ZIG100_Tx(ID);                                       //传送ID

for(axis = 0; axis < SENSOR; axis++)
{                               
    ZIG100_Tx((unsigned char)ADC_Average);       //数据1~6
}
}

//USART接收中断程序//
ISR(USART_RXC_vect)
{       
RX_Command = UDR;                   //读取UDR值,更新RXC

switch(RxNum)
{
    case FIRST:
      if(RX_Command == CALLFLAG)
      {
      RxNum = SECOND;
      }
      break;

    case SECOND:                                       
      switch(RX_Command)
      {
      case START:                   //Capture 命令                                               
          UCSRB &= ~_BV(RXEN);
          State = ACTION;
          break;

      case ID:                      //ID 命令                                               
          UCSRB &= ~_BV(RXEN);
          State = TX;
          break;
      }
      RxNum = FIRST;                       
      break;
}
}

//ADC中断程序//
ISR(ADC_vect)
{
//在取样次数内即可转换,超过即取平均//
if(Count < TIMES)
{       
    ADC_Data = ADCH;

    ADMUX++;                              //切换channel

    Axis++;                                 //切换axis

    //重新切换并采样次数+1//
    if(Axis == SENSOR)
    {
      ADMUX = ADC_INITI | ADC1;             //再从ADC1开始

      Axis = INITIAL;

      Count++;
    }

    State = ACTION;       
}
else
{       
    AverageFilter();                        //Count超过后,开始取平均

    Count = INITIAL;                        //重新计数采样次数

    State = INITIAL;
}
}

int main(void)
{
DDRA = 0x00;                     //PA为传感器输入
PORTA = 0x00;

//USART抗干扰//
DDRD = 0x02;                     //RXD输入,TXD输出
PORTD = 0x03;                  //RXD上拉电阻有效,TXD输出
       
ADMUX = ADC_INITI | ADC1;      //从 ADC1 开始 //

USART_Initi(UBRR);               //设定USART和buad rate

//ADC enable, ADC interrupt enable, ADC_clk=7.3728 M/64=115.2 KHz//
ADCSRA = _BV(ADEN) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1);
       
sei();                           //全局中断
       
while(1)
{                       
    switch(State)
    {                       
      case INITIAL:                               
      State = WAIT;
      UCSRB |= _BV(RXEN);      //启动RX,准备接收讯号进入中断                               
      break;

      case ACTION:
      ADCSRA |= _BV(ADSC);       //ADC start (单次转换, 每次到完成都需要25个ADC_clk周期)
      State = WAIT;                       
      break;

      case TX:
      ADC_Tx();
      State = INITIAL;
      break;

      case WAIT:                               
      // 等待 //
      break;                       
    }
}
}

------------------------------------ZIG100.h------------------------------------------------
void USART_Initi(unsigned int ubrr)
{
UBRRH = (unsigned char)(ubrr >> 8);
UBRRL = (unsigned char)ubrr;
UCSRB = (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);       
UCSRC = (1 << URSEL) | (3 << UCSZ0);
}

void ZIG100_Tx(unsigned char data)
{       
while( !(UCSRA & (1 << UDRE)) );       
UDR = data;
}

chiaming 发表于 2011-2-14 15:53:30

后来有尝试将数据包的内容修改,加了数据包头 (0xFF0xAA0x55),并且再增加一个测试节点,接收主/从机的所有数据。

全部使用的ZigBee模块是ZIG-100,模块上的芯片是CC2420。

PC(0号)端的程序是使用DataReceived触发的,接收到上一个节点的数据,处理后便会发送呼叫下一个节点回传数据。

但是通信一段时间,在某个时间点,PC端却没有进入触发程序 (下一个节点没有回传数据);透过测试端接收测试的话,确实有收到下一个节点发送的回传数据,这是不懂的问题所在。

烦请指导,谢谢。

ba_wang_mao 发表于 2011-2-16 10:07:00

上述现象和你的程序没有关联,问题出在通讯协议上。看来是你自已定义的“自定义协议”。


   问题如下:

      五、其中,当1号模块完成ADC后,会自动进入发送数据状态(无需等待接收ID命令),发送全部的数据后,进入INITIAL状态,始能接收功能(RXEN = 1),之后又再次地等待0号发送Capture命令。

      可能出现如下现象:

            模块1 ---> 模块5 同时给PC机发送数据,导致通讯线路混乱。


解决方法:
       仔细阅读 MODBUS 协议吧!一个主站最多可以挂接127个从站。

      (1)、主站不请求,从站不应答。
      (2)、主站发送广播时,从站只是接收,不能回送应答报文。(否则导致通讯线路混乱)
      (3)、主站给1号从站发送报文时,其它从站(2--->5)也能收到,但是经过地址比较,发现不是呼叫自己,因此2--->5号从站不予理睬,只有1号从站给主站回送应答报文。

       注:其它广泛应用于工业的现场总线都采用上述通讯方式,例如:modbus , profibus dp

chiaming 发表于 2011-2-16 16:56:07

回复【3樓】ba_wang_mao
-----------------------------------------------------------------------

我将「自定义协议」修改了一下:

1. 主站0号 (PC机) 发送广播,从站模块1~5号等待接收。
2. 等待一段时间,主站0号开始发送寻址命令。
3. 主站0号先发送从站1号的寻址命令,从站1号接收比较后,发现是呼叫自己,回送应答报文;
   而从站2~5号接收比较后,发现不是呼叫自己,不予理睬。
4. 0号主站接收完1号从站的回送应答报文后,接着发送从站2号的寻址命令,下面的作动反应如同步骤3,从站3~5号也依此类推。
5. 等0号主站接收完5号从站的回送应答报文后,代表完整收齐全部的资料,准备再一次新的广播发送。


让将流程符合您提供的解决方法,但是经过测试之后,仍然会有同样的问题,让整个无线系统停住:

1. 主站0号已发送了某个从站模块的寻址命令,但是对应的从站模块却没收到?或是没回送应答报文?
2. 从站模块已回送了应答报文,主站PC机却没有收到,导致无法接着发送下一个从站模块的寻址命令。


仍在思考和不解原因为何?
烦请指导,谢谢您!

ba_wang_mao 发表于 2011-2-17 09:02:18

1. 主站0号 (PC机) 发送广播,从站模块1~5号等待接收。

    主站不发送广播。
    a.那么主站主何如何知道总线上有多少块从站呢?解决方法如下:

            主站依次发送1 ---30 ,

          (1)、呼叫从站1,从站1号接收比较后,发现是呼叫自己,回送应答报文(其它从站比对地址,发现不是呼叫自己,不予理睬),主站收到从站1的应答报文后,主站就知道总线上有从站1了。

          (2)、呼叫从站2,从站2号接收比较后,发现是呼叫自己,回送应答报文(其它从站比对地址,发现不是呼叫自己,不予理睬),主站收到从站2的应答报文后,主站就知道总线上有从站1了。
   b.如何解决模块的热插拔

         运行过程中,如果用户从总线上去掉了一块模块,主站如何知道总线上还有哪些模块呢?
          解决方法:依照TCP/IP协议中,ARP协议采用的办法,建立一个高速缓存表,记录下当前总线上在线的模块,在这段时间内,主站只访问高速缓存表中记录下来的模块。

          然后主站每隔1分钟重新在依次呼叫总线上的所有模块(1-30),然后将在线的从站号填写到高速缓存表中。平时主站只访问高速缓存表中记录下来的从站,高速缓存表中没有记录的从站不予访问。

ba_wang_mao 发表于 2011-2-17 09:14:39

但是经过测试之后,仍然会有同样的问题,让整个无线系统停住:

1. 主站0号已发送了某个从站模块的寻址命令,但是对应的从站模块却没收到?或是没回送应答报文?
2. 从站模块已回送了应答报文,主站PC机却没有收到,导致无法接着发送下一个从站模块的寻址命令。


   上述两个现象,应该是你的中断服务程序出现了问题。

   1、假定定时中断服务程序周期是50ms,定时中断服务程序中代码的执行时间是20ms,在执行定时中断服务程序这段时间内,PC机给从站发送数据,由于在20ms内,都无法进入串口中断,由于串口硬件缓存有限,导致后面来的字节挤掉了前面的字节,因此会出现上述现象。


      记住一条原则:

            (1)、所有的中断服务程序必须尽可能的短。
            (2)、定时中断中只有跟时间非常严格要求的代码才放在定时中断程序中,否则应该在定时中断中置一个标志,然后通过该标志在主程序中执行。
            (3)、串口应该采用 中断+缓存的方式接收
                      串口中断应该尽量短,只需要将接收到的数据放到缓存中,既应该立即退出中断。
            (4)、串口分析包的过程应该放在主程序中完成

chiaming 发表于 2011-2-20 02:11:07

回复【6樓】ba_wang_mao
-----------------------------------------------------------------------

请问这样的中断服务程序还会很久吗?

因为是由PC机发送命令,而所有从机只需要判断是否为相应的命令即可,再根据相应的命令进入要作动的状态。

中断服务程序如下:

ISR(USART_RXC_vect)
{       
    char data;

    data = UDR;
       
    RX_Command = data;      //将资料存入缓存区

    switch(rxIndex)      //判断存入缓存的次数和命令
    {
      case 0:      //判断第一个缓存区的资料是否为同步字节:是的话,将下一个资料存入缓存       
            if(RX_Command != SYNCHRON)
            {
                rxIndex = 0;
            }
            else
            {
                rxIndex = 1;
            }
            break;

      case 1:      //判断第二个缓存区的数据是为启动命令还是请求ID命令:是的话,可进入相应的状态
            switch(RX_Command)
            {
                case START:                                       
                  State = ACTION;      //进入ADC状态
                  UCSRB &= ~_BV(RXEN);      //进入相应的状态后,就禁止接收
                  break;
                case ID:
                  State = TX;      //进入准备传送封包状态      
                  UCSRB &= ~_BV(RXEN);
                  break;
            }
            rxIndex = 0;      //判断完后,回归队列                                                                       
            break;       
    }
}


上面原本的写法:
是直接让 data = UDR; ,接着将判断RX_Command改为判断data就好。

后来我依您的建议硬是加入了一个缓存,但是,我只需按照PC机发送相应的命令,进入相应的状态就好,这样还会影响串口处理时间吗?

如有更好的写法,烦请指导,谢谢。

hubeilcsun3 发表于 2011-2-20 22:16:50

mark

ba_wang_mao 发表于 2011-2-21 10:35:20

1、确保主站(PC机)每隔100毫秒发送一次请求帧(从机能够及时响应)

    2、你程序中,其它中断服务程序必须尽可能的执行时间非常短(这是编写中断服务程序的基本原则)

    3、串口架构可以采用TCP/IP和MODBUS国际标准协议采用的方法
      (1)、modbus 规定,当接收的两个字节之间的间隔时间大于3.5字符的时间,就认为接收到一帧完整的数据包。
      (2)、因此开启一个定时器0,定时器0的周期=3.5字符静止时间。比如:设置定时器定时器0的溢出时间为5ms(3.5字符时间)。
      (3)、当进入串口接收中断后,立即开启定时器0.这样当定时器0溢出后,就认为已经接收到一帧完整的数据。

      中断服务程序只用于接收数据,具体的分析和判断放在主程序中处理。
         
    4、排查通讯错误时,请把所有和通讯无关的代码屏蔽,专心检查是否是由于通讯造成的上述现象。


#define OS_ENTER_CRITICAL()                                CLI()
#define OS_EXIT_CRITICAL()                                SEI()
#define MSCOMM_BUFFER_LENGTH                        (255)


//开启定时器0
#define TIMER0_START()        (TCCR0 = (0 << WGM00) | (0 << WGM01) | (1 << CS02) | (1 << CS01) | (0 << CS00))                // 256 分频

//停止定时器0
#define TIMER0_STOP()        (TCCR0 = 0x00)       




INT8U volatile USART0_receCount = 0;//接收指针
INT8U USART0_mscomm_buffer;//接收缓冲区
BOOL USART0_OK_mark = FALSE;//定时器0溢出,假定已经接收到一帧完整的数据包(只是假定,数据包是否正确还需要在主程序中判断)


//====================================================================================================
//TIMER0 initialize - prescale:256
// WGM: Normal
// desired value: 1.25mSec
// actual value:1.250mSec (0.0%)
// CTC 模式
// OCR0=11059200*1.25/256/1000-1=54-1=53=0x35
// 溢出模式
// TCNT0=256-11059200*1.25/256/1000=256-54==202=0xCA
//====================================================================================================
void TIMER0_Init(void)
{
        TCCR0 = 0x00;
        ASSR= 0x00;
        TCNT0 = 0xCA;
        OCR0 = 0x35;
        TIMSK |= (1 << TOIE0);
}


#pragma interrupt_handler timer0_ovf_isr:iv_TIM0_OVF
void timer0_ovf_isr(void)
{
    USART0_OK_mark = TRUE;//定时器0溢出,假定已经接收到一帧完整的数据包(只是假定,数据包是否正确还需要在主程序中判断)
        TIMER0_STOP();//
}




#pragma interrupt_handler USART0_RI_ISR:iv_USART0_RX
void USART0_RI_ISR(void)
{
        INT8U ch;


        ch = UDR0;   //必须将数据从硬件缓冲区中先取出来
        if (USART0_receCount < MSCOMM_BUFFER_LENGTH)
                USART0_mscomm_buffer = ch;

        TIMER0_STOP();//停止定时器0         
        TCNT0 = 0xCA;   //装载定时器0的初值(5ms)
        TIMER0_START();//开启定时器0          
}
      


void main(void)
{
        OS_ENTER_CRITICAL();
        PORT_Init();
         TIMER0_Init();
        USART0_Init();
        OS_EXIT_CRITICAL();

        while (1)
        {                 
              if (USART0_OK_mark)//;//定时器0溢出,假定已经接收到一帧完整的数据包(只是假定,数据包是否正确还需要在主程序中判断)
          {
                       USART0_OK_mark = FALSE;
             USART0_Modbus_Analyze();// 解析函数
//--------------------------------------------
// 说明: 执行完解析函数后,必须将接收指针“USART0_receCount ”清零。
//       (1).当前收到的包是正确的,“USART0_receCount ”必须清零
//       (2).当前收到的包是错误的,(例如:通讯线路干扰,PC机发送了8个字节,串口只收到7个字节),因此
//      必须放弃这一帧不完整的数据包(即:最后必须清除清除接收指针=0)。
//--------------------------------------------
                  USART0_receCount = 0;                // 清除接收指针=0
             }
          }

}


void USART0_Modbus_Analyze(void)
{
       
    if (USART0_receCount >= 8)            // 接收数据合法吗?(虽然USART0_OK_mark=TRUE,认为接收到一帧完整的数据,但是还必须再仔细分析该帧数据是否正确)
    {      
      switch (USART0_mscomm_buffer)
        {
                case 1:
                               break;
            }
    }

}

machao 发表于 2011-2-21 15:44:24

关于多机通信,我的书中有非常详细的介绍和例子,可以参考。

chiaming 发表于 2011-2-22 19:46:30

回复【9樓】ba_wang_mao
-----------------------------------------------------------------------

在问题解决测试期间,已先将通讯功能以外的其他功能全都关闭排除了。

经过许多方法测试之后(包括您建议提供的),仍然会有同样的问题,让整个无线系统停住:

1. 主站0号已发送了某个从站模块的寻址命令,但是对应的从站模块却没收到?或是没回送应答报文?
2. 从站模块已回送了应答报文,主站PC机却没有收到,导致无法接着发送下一个从站模块的寻址命令。

而对我使用的系统来说,只要通讯期间有停摆的情况,就要重新启动系统,但这样很耗时,故想找出解决方法和原因。

是否是因为使用无线通信(ZIG-100),所以一定会有收发失误率的产生?

烦请指导,谢谢你。

chiaming 发表于 2011-2-22 19:53:24

回复【10楼】machao
-----------------------------------------------------------------------

马老师您好,

原先测试的方式,就是参照您书上的多机通信的范例使用。

在中断服务程序内,先判断收到为同步字节,下一次判断收到为地址字节后,
就禁止接收始能,并在中断结束后,要求进入的作动状态。

在测试期间,已先将通讯功能以外的其他功能全都关闭排除了。

经过许多方法测试之后,仍然会有同样的问题,让整个无线系统停住:

1. 主站0号已发送了某个从站模块的寻址命令,但是对应的从站模块却没收到?或是没回送应答报文?
2. 从站模块已回送了应答报文,主站PC机却没有收到,导致无法接着发送下一个从站模块的寻址命令。

是否是因为使用无线通信(ZIG-100),所以一定会有收发失误率的产生?

烦请指导,谢谢您。

rfinchina2011 发表于 2011-2-22 20:07:58

点击此处下载 ourdev_612616Z6NMKL.pdf
(文件大小:716K) (原文件名:UTC-1212无线模块使用文档2.0.pdf)
点击此处下载 ourdev_612617SAH7NP.pdf
(文件大小:1.18M) (原文件名:UTC-2303使用手册2.0.pdf)
点击此处下载 ourdev_612030EKE7Y1.rar(文件大小:11K)
(原文件名:UTC1212-C51参考程序.rar)
点击此处下载 ourdev_612031XYUJW9.rar(文件大小:4.63M)
(原文件名:UTC2303驱动.rar)
点击此处下载 ourdev_612032HBJX03.rar(文件大小:311K)
(原文件名:UTC-121配置软件.rar)

chiaming 发表于 2011-2-22 20:57:19

回复【13樓】rfinchina2011
-----------------------------------------------------------------------

谢谢你,但目前未考虑要更换无线模块。

rfinchina2011 发表于 2011-2-23 07:58:45

ZIGBEE距离比较近,而且隔墙传输效果不好,楼主要慎重

ba_wang_mao 发表于 2011-2-23 08:55:31

【9楼】给你提供的代码,经过了严格测试,通讯次数达到2000000(2百万),接收全部正确,没有一次错误。


   会不会是你初始化串口时,使用了9位传输模式造成的?


   一般的MODBUS-rtu国际标准协议,都是采用8位传输模式,而且“从站地址”就包含在数据包中。不要使用AVR单片机中第9位来区分地址帧还是数据帧。

   建议你看一下modbus-rtu协议。将你的自定义协议,改造成国际标准协议。


#include <iom128v.h>
void USART1_Init(void)
{
        UCSR1B = 0x00;
        UCSR1A = 0x00;
        UCSR1C = (1 << UCSZ10) | (1 << UCSZ11);
        switch (STOP_NO)
        {
                case 0:
                        UCSR1C |= (0 << USBS1);
                        break;
                case 1:
                        UCSR1C |= (1 << USBS1);
                        break;
        }
        switch (UPM_NO)
        {
               case 0:
                        UCSR1C |= ((0 << UPM11) | ( 0 << UPM10));
                        break;               
                case 1:                        //        偶校验
                        UCSR1C |= ((1 << UPM11) | ( 0 << UPM10));
                        break;
                case 2:                        //        奇校验
                        UCSR1C |= ((1 << UPM11) | ( 1 << UPM10));
                        break;
        }
        switch (Baud_NO)
        {
                case 0:                                                                //        1200
                        UBRR1L = 0x3F;
                        UBRR1H = 0x02;
                        break;
                case 1:                                                                //        2400
                        UBRR1L = 0x1F;
                        UBRR1H = 0x01;
                        break;
                case 2:                                                                //        4800       
                        UBRR1L = 0x8F;
                       UBRR1H = 0x00;
                        break;
                case 3:                                                                //        9600       
                        UBRR1L = 0x47;
                       UBRR1H = 0x00;
                        break;
                case 4:                                                //        14400=(11059200/16/14400-1)%256
                        UBRR1L = 0x2F;
                       UBRR1H = 0x00;
                        break;
                case 5:                                                                //        19200       
                        UBRR1L = 0x23;
                       UBRR1H = 0x00;
                        break;
                case 6:                                                                //        38400
                        UBRR1L = 0x11;
                       UBRR1H = 0x00;
                        break;
                case 7:                                                                //        57600
                        UBRR1L = 0x0B;
                       UBRR1H = 0x00;
                        break;                  
                default:                                                        //        115200               
                        UBRR1L = 0x05;
                       UBRR1H = 0x00;
                        break;
        }
        UCSR1B = (1 << RXEN1) | (1 << RXCIE1) | (1 << TXEN1) | (1 << TXCIE1);
}

chiaming 发表于 2011-2-23 17:04:36

回复【16樓】ba_wang_mao
-----------------------------------------------------------------------

我自定义的协议的应该就是MODBUS-rtu国际标准协议了,

采用8位传输模式,1位停止位,「从站地址」包含在数据包中,

也没有使用AVR单片机中第9位来区分地址帧还是数据帧,

波特率也测试过从9600到115200,但仍然有问题。

烦请指导,谢谢你。

machao 发表于 2011-2-23 21:29:32

无线通信产生误码的情况可能行非常大。在考虑协议中,应该考虑出现误码的情况。

例如:
1。规定上位机发送命令给1号从机,从机必须在500ms给出回答。
2。上位机在规定的500m时间内接受不到正确的回答(或根本没有回答),则再次重发。
3。3次重发后还是没有正确的回答,那么就需要转入错误处理,比如提示通信故障。

这样就是模块坏了,程序也不会“死掉”

进入“死循环”的过程:上位机下发命令了,但传到1号从机为误码,1号从机不做回答,此时上位机在“死”等1号从机的回答。

======================================
在我编写的书中,P402、P409都讲到这个问题。书中有例子。由于篇幅,书中只有下位机代码,上位机是PC,用VB写的,代码在光盘中,你全面看过了?

在我的例子中,如果把通信线拔掉,上位机马上就给出通信故障的提示,程序不会“死”在那里。

ba_wang_mao 发表于 2011-2-24 11:00:15

建议:
         1、首先去掉复杂的通讯协议,上位机使用一个“串口调试精灵”软件(记录有发送次数)
             固定给某一个从站发送数据
         2、“串口调试精灵”软件每隔500ms发送一帧固定的数据(8个字节)
             例如:“1,2,3,4,5,6,7,8”
         3、然后在从站中,接收上述数据,看是否每次都接收正确。
            将正确接收次数显示到数码管上。

   上述正确后,再查找你程序的其它问题。

renkaikaiser 发表于 2011-2-24 11:53:38

弱弱的问一句,你这个跑的是zigbee协议栈吗?TI的?
从机的设备类型是什么?ZED or ZR?
好像没有提到自组网什么的。

stalker2 发表于 2011-11-11 16:05:01

mark

century99 发表于 2011-11-22 15:57:57

ZigBee 无线信号强度比较弱,一般它的发射功率都小于20mW,肯定很容易被干扰。我想楼主的问题应该是信号受到干扰,造成数据接收错误。
我对这块也很感兴趣,以后常来这里看看。

intentydh 发表于 2011-12-4 10:17:43

mark

ZSHDZ 发表于 2013-3-18 10:42:05

技术贴,给了很多思路,多谢
页: [1]
查看完整版本: 请问马老师:有关“ UART (USART)口的多机通信 ─ 利用 ZigBee 无线模块相互通信“的