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,如有侵權,請聯絡刪除。