1. 程式人生 > 其它 >CC2530串列埠通訊中如何接收上位機的一串字串

CC2530串列埠通訊中如何接收上位機的一串字串

這幾天苦於學習zigbee,也才是剛入門.在掌握8051的基礎下,首先得調通2530的串列埠,不然怎麼對得起這晶片呢?

說真的,學習zstack真的是讓人崩潰的一件事情,當你對著資料連續看了兩天也找不到下手的地方的時候,真的是有一種欲哭無聲,有氣生不出來的感覺.

我是想實現這樣一種功能:

zigbee節點通過串列埠連線GPRS DTU向伺服器定時(1min)傳送請求,請求一個json字串,這個字串中包含了一個配肥料的資訊.zigbee節點通過json中的引數配置肥料,完成後向伺服器傳送confirm請求,完成一個訂單.     然後,又開始間隔向伺服器請求別的訂單.

是的,這樣一個程式,我本打算用串列埠小程式先模擬出來,但是遇到一個問題:串列埠傳送請求後,伺服器響應json給zigbee節點,節點總會無緣無故抽風,要麼收不到資料,要麼收的資料亂碼,要麼收到的資料解析的時候沒有結果,傳送null給伺服器.

就這樣的問題,我除錯了N久,基礎程式碼調不通,就悶頭回來學習zstack,學習一兩個小時,程式碼看懂了就是無從下手,又返回去看基礎程式碼.....

來來回回,我的生命就這樣一點點被消耗...

然後我又重新設計程式流程圖,重新寫程式碼,,,,,,,又來來回回,除錯了幾遍,還是沒有結果.最後一遍,我靜下心來,一點一點地思考,一點一點地修改.終於除錯通了.

事後,我總結了一下自己的問題:

1.基礎程式碼還沒完全摸透,zigbee硬體的特性還沒有完全熟悉,就去摸zstack,肯定行不通,而且zstack這玩意兒就算你是天才也不是一兩天就摸得透的.

2.串列埠通訊過程中,串列埠傳送接收的特性,串列埠中斷函式的執行方式等,如果這兩個特性摸不透,根本看不懂別人寫的串列埠程式碼的邏輯.

我分析了以下串列埠通訊的原理:

首先是裝置上電,初始化串列埠,將裝置和電腦通過串列埠相連線(電腦先模擬GPRS,除錯).

電腦傳送abcde給串列埠.

串列埠的特性是按位按序列接收資料.比如a的序列是11000110(比喻),串列埠首先按位接收這個字元,收到後將這個序列存到U0DBUF(用串列埠0)中,即緩衝區,然後產生中斷,執行中斷函式,中斷執行完畢後,將URX0IF中斷暫存器設定為0,關閉中斷,然後返回執行main.這個時候又會接收第二個b字元,又產生中斷,直到接收資料完成.才不產生其它中斷了.

中斷函式大概是這樣的:

/**********************/
/***串列埠中斷函式    ***/
#pragma vector = URX0_VECTOR
__interrupt void UART0_ISR(void)
{
  URX0IF = 0;
  recFlag = U0DBUF;
  //從U0DBUF獲取資料
  RecData[recIndex++] = U0DBUF;
}

其中RecData是陣列,作為資料緩衝區,recIndex是陣列索引,都是全域性變數,沒產生一次中斷,就會把中斷的一個byte存到陣列中,最後就可以產生一個完整的字串.

最後,要學會串列埠,還需要弄明白兩個函式:

1.串列埠初始化函式

2.串列埠傳送字串的函式

初始化函式:

/****************/
/***初始化串列埠 **/
/****************/
void initUART0(void)
{
  CLKCONCMD &= ~0x40;   //設定時鐘源,32MHz晶振
  while(CLKCONSTA & 0x40);    //等待晶振穩定工作
  CLKCONCMD &= ~0x47; //設定系統主時鐘頻率 ,32MHz
  
  PERCFG = 0x00; //位置1 P0口
  P0SEL = 0x0c;   //P0用於串列埠
  P2DIR &= ~0XC0;                             //P0優先作為UART0  
  
  U0CSR |= 0x80;				//串列埠設定為UART方式
  U0GCR |= 8;				
  U0BAUD |= 59;				//波特率設為9600
  UTX0IF = 1;     //UART0 TX中斷標誌初始置位1 
  URX0IF = 0;
  
  U0CSR |= 0X40;				//允許接收
  IEN0 |= 0x84;				//開總中斷,接收中斷
}
/****************/
/***初始化LED  **/
/****************/
void initLED(void)
{
  //P1控制led
  P1DIR = 0x02;
  LED = 0;  //點亮LED;
}
/****************/
/***延時函式ms   **/
/****************/
void Delay(uint n)
{
  int i,j;
  for(i=0; i<n; i++)
  {
    for(j=0; j<560; j++)
        ;
  }
}

初始化串列埠的大致順序是:1首先設定外設功能,比如上述設定P0的P02 P03為外設功能,並通過P2設定P02 P03優先作為UART0,然後通過U0CSR暫存器設定串列埠為非同步模式UART.然後設定波特率,收發中斷標誌,以及收發允許等.最後一項為開總中斷,因為設定了串列埠中斷,不開中斷將不會產生任何串列埠收發的中斷.以上對應的暫存器在cc2530晶片手冊中都有詳細的描述.我就不贅述了.

傳送字串函式:

/****************/
/***傳送字串 **/
/****************/
void Send_To_Server(uchar *data,uint len)
{
  int j;
  for(j=0;j<len;j++)
  {
    U0DBUF = *data++;
    while(UTX0IF == 0);
    UTX0IF = 0;
  }
}

傳送資料也是一個字元一個字元地傳送,將資料寫入U0DBUF,資料將會自動被髮送到串列埠中去.這裡注意一下,有一個迴圈,迴圈是等UTX0IF為1才繼續執行,因為如果UTX0IF為1,說明產生了傳送字元的中斷函式,即字元確保被髮送出去了.然後關中斷,繼續傳送其它的.這裡還需要注意一下U0DBUF是一個雙向緩衝器,既可以用於傳送也可以用於接收.因此,傳送和接收必須要保證原子性,即傳送一個字串的時候不能同時產生接收的動作.因此傳送之前,務必使用暫存器保證這時不在接收字元,等傳送完畢後,在開啟接收功能.

最後附上我的基礎程式:

# include <iocc2530.h>
# include <string.h>

#define uint unsigned int
#define uchar unsigned char

//燈亮的埠
#define LED P1_1    //p11控制D2亮和滅


//宣告需要用到的全域性變數等
uchar token[17] = "machinetoken=123"; //令牌
uchar and[2] = "&";    //http引數連線字元
uchar oid_front[9] = "orderid=";  //http引數訂單號字首
uchar rs_front[8] = "result=";   //http引數結果字首

uchar txd[50];//即將傳送給伺服器的資料
uchar txFlag = 1;//傳送標誌,是否傳送請求

uchar RecData[110];
uint recIndex = 0;
uchar recFlag = 0;//中斷函式從伺服器拿到資料時,這個應該不為0

/***這些引數是oid,ferta,fertb,fertc等引數,應該為數字,是從伺服器傳送過來的json字串中解析出來的*/
uchar oid[5]; //總訂單數不可能超過10000吧?
uchar ferta[4];
uchar fertb[4];
uchar fertc[4];

/***這引數是終端裝置處理完成後生成的結果號*/
uchar rsid[2];

void Delay(uint n);  //延時函式
void initUART0(void);   //初始化串列埠
void initLED(void);  //初始化燈
void Send_To_Server(uchar *data,uint len);
void resolveOid(uchar *str);

void main(void)
{
  //首先延時2s
  //Delay(2000);
  //首先初始化串列埠,在初始化LED
  initUART0();
  initLED();
  //初始化完成進入迴圈
  while(1)
  {
    Delay(100);
    if(recFlag == '}')
    {
       //首先接收資料獲取訂單號,ferta,fertb,fertc等四個資料,此處只獲取一個數據即可
       resolveOid(RecData);
       //處理資料
       int i=0;
       for(;i<10;i++)
       {
         LED = !LED;
         Delay(500);
       }
       //.......
       rsid[0] = '1';
       //.......
       //生成處理結果字串
       strcat(txd,token);
       strcat(txd,and);
       strcat(txd,oid_front);
       strcat(txd,oid);
       strcat(txd,and);
       strcat(txd,rs_front);
       strcat(txd,rsid);
       strcat(txd,and);
       if(recIndex > 20)
       {
         //響應伺服器
         U0CSR &= ~0x40;                    //禁止接收 
         Delay(3000);
         Send_To_Server((char *)txd,50);
         Delay(3000);
         U0CSR |= 0x40;                     //允許接收 
         //清除字串
       }
         memset(txd,0,50);
         //進入傳送狀態
         //清除recFlag狀態
         recFlag = 0;

         //一次請求和回覆完成後,需要重置接收資料的快取.
         recFlag = 0;
         memset(RecData,0,110);
         recIndex = 0;
       
    }
    else
    {
      Delay(2000);  //消除時延
      if(recFlag == 0)
      {
         U0CSR &= ~0x40; 
         Send_To_Server((char *)token,17);
         U0CSR |= 0x40; 
      }
       
      
      memset(txd,0,50);
      //清除recFlag狀態
      recFlag = 0;

      //一次請求和回覆完成後,需要重置接收資料的快取.
      recFlag = 0;
      memset(RecData,0,110);
      recIndex = 0;
    }
    Delay(10);
  }    
}

/**從伺服器資料中解析出oid****/
void resolveOid(uchar *str)
{
  uchar tmp;
  int serverIndex = 10;//解析json字串的索引位置
  int index = 0;//字串索引
  for(; serverIndex<15; serverIndex++)    //獲取訂單號
  {
    tmp = str[serverIndex];
    if(tmp <= '9' && tmp >= '0')
    {
      oid[index++] = tmp;
    }
  }
}
/****************/
/***初始化串列埠 **/
/****************/
void initUART0(void)
{
  CLKCONCMD &= ~0x40;   //設定時鐘源,32MHz晶振
  while(CLKCONSTA & 0x40);    //等待晶振穩定工作
  CLKCONCMD &= ~0x47; //設定系統主時鐘頻率 ,32MHz
  
  PERCFG = 0x00; //位置1 P0口
  P0SEL = 0x0c;   //P0用於串列埠
  P2DIR &= ~0XC0;                             //P0優先作為UART0  
  
  U0CSR |= 0x80;				//串列埠設定為UART方式
  U0GCR |= 8;				
  U0BAUD |= 59;				//波特率設為9600
  UTX0IF = 1;     //UART0 TX中斷標誌初始置位1 
  URX0IF = 0;
  
  U0CSR |= 0X40;				//允許接收
  IEN0 |= 0x84;				//開總中斷,接收中斷
}
/****************/
/***初始化LED  **/
/****************/
void initLED(void)
{
  //P1控制led
  P1DIR = 0x02;
  LED = 0;  //點亮LED;
}
/****************/
/***延時函式ms   **/
/****************/
void Delay(uint n)
{
  int i,j;
  for(i=0; i<n; i++)
  {
    for(j=0; j<560; j++)
        ;
  }
}
/****************/
/***傳送字串 **/
/****************/
void Send_To_Server(uchar *data,uint len)
{
  int j;
  for(j=0;j<len;j++)
  {
    U0DBUF = *data++;
    while(UTX0IF == 0);
    UTX0IF = 0;
  }
}
/**********************/
/***串列埠中斷函式    ***/
#pragma vector = URX0_VECTOR
__interrupt void UART0_ISR(void)
{
  URX0IF = 0;
  recFlag = U0DBUF;
  //從U0DBUF獲取資料
  RecData[recIndex++] = U0DBUF;
}

非常簡單,大概就是不斷輪詢傳送資料給上位機.

一旦上位機發送資料給下位機

下位機便處理接收到的資料,在把處理完畢的資料傳送給上位機,然後又開始新的輪詢.

資料例項:

zb傳送:

machinetoken=123

server response:

{"orderId":60,"orderNumber":12233331,"creTime":"2017-11-13","fertA":2,"fertB":3,"fertC":4,"crePersonId":3}

zb處理後傳送:

machinetoken=123&orderid=60&result=1

server response:

{"result":"true"}

在上述過程中,如果上位機迴應給zb的資料是{"result":"false"},則zb節點不做任何迴應,開始新一輪的輪詢.輪詢的間隔時間是可以控制的.

最後說說我做這個幹嘛:

用手機發送一個配置肥料的訂單到伺服器,zb從伺服器獲取訂單,然後配置肥料.

當然這是一個簡單的demo,並不是真正的專案,目的是用於驗證專案的可行性,為畢業設計打好基礎.

轉載於:https://my.oschina.net/qkmc/blog/1575260

本文轉自 link,如有侵權,請聯絡刪除。