ZigBee學習筆記(基礎篇)
前言
搬運個人部落格內容,還有幾天就到期了,這是年初寫的。而且那時候由於一起疫情無法返校,沒有實驗器材,在學習過程沒有實踐,所以對於具體使用還十分生疏,也沒有做相關專案。對於逐漸忘記的zigbee,我想在這裡記錄下,以便後續學習的需要。
基本概念
ZigBee,也稱紫蜂,是一種低速短距離傳輸的無線網上協議,底層是採用IEEE 802.15.4標準規範的媒體訪問層與物理層。主要特色有低速、低耗電、低成本、支援大量網上節點、支援多種網上拓撲、低複雜度、快速、可靠、安全。
Zigbee模組主要基於TI CC2530晶片實現,其中有5根與下載有關,構成下載電路。工作電壓為2~3.6V(最好用3.3V)。關於IO口,一共有21個通用IO口,又分為三組P0(8個引腳)、P1(8)、P2(5)。
關於IO設定
相關暫存器:PxSEL、PxDIR、PxINP。 Px(x分別對應0、1、2,用來控制對應的IO口組)3種暫存器。
PxSEL暫存器
PxSEL暫存器(x=0、1)相應的一個IO口,決定它是普通IO口還是片上外設,0表示普通IO口,1表示片上外設。(普通IO口與片上外設的區別:普通IO口又叫GPIO,可以任意設定這個IO口的高低狀態;而片上外設不能被任意控制,必須要通過控制器去控制)
P2SEL暫存器
同樣也是0表示普通IO、1表示片上外設 注意:其中P2組只有5個IO口,但是P2_1 P2_2是用於下載程式用的,所以,這兩個IO不需要配置,因此P2SEL的低3位分別對應P2_0 P2_3 、P2_4。
PxDIR暫存器
共8位,每一位對應具體的IO組中的相應的一個IO,決定它是輸入還是輸出,0表示輸入,1表示輸出。(P2只有五位,低五位照常,高五位不管)(圖片參考圖2、圖3)
PxINP暫存器
在輸入的模式時,8位,每一位對應具體的IO組中的相應的一個IO,決定它是上下拉模式還是三態,0表示上下拉模式,1表示三態。(在上下拉模式的時候,需要P2INP後面的3位決定,0表示上拉,1表示下拉)
以下為練習程式碼
#include<iocc2530.h>
void main()
{
/* P1SEL &=0xFE;// 1111 1110 讓P1_0處於普通IO,非偏上外設模式
P1DIR |=0x01;//0000 0001 讓P1_0處於輸出狀態非輸入狀態
P1_0=1;*/
P0SEL &=0xEF;// 1110 1111
P0DIR |=0x10;//0001 0000
P0SEL &=0xBF;// 1011 1111
P0DIR &=0xBF;
P0INP &=0xBF;//讓P0_6處於上下拉模式;
P2INP &=0xDF;//1101 1111 讓P0組處於上拉模式
while(1)
{
if(1==P0_6)
{//檢測到的是高電平
P0_4=0;
}
else
{//檢測到的P0_6外部是低電平
P0_4=1;
}
}
中斷
外部中斷配置
初始化IO口工作在普通IO上拉輸入狀態
開啟IO口組中斷
開啟組內對應的具體某IO口中斷
上升沿還是下降沿觸發
開總中斷EA=1
步驟2、3參考下圖
步驟4涉及暫存器PICTL,0代表上升沿,1代表下降沿。
中斷函式
參考程式碼如下:
#pragma vector = PxINT_VECTOR //PxIFG 和 PxIF清零
_unterrupt void anyname()
{
PxIFG=0;
PxIF=0;
}
練習程式碼如下:
#include<iocc2530.h>
#define uchar unsigned char
void delay(uchar u)
{
while(u--);
}
void main()
{
P0SEL &= 0xDF ;//1101 1111 P0_5
P0DIR &= 0xDF ;//1101 1111 P0_5
P0INP &= 0xDF ;//1101 1111 P0_5
P2INP &= 0xDF ;//1101 1111 P0_5
EA = 1;
P0IE = 1;// P1IE如果要設定為1,不能直接用PIE=1,IEN2 |= 0x10 ;// 0001 0000
// IEN |=0x01 ;
P0IEN |= 0x20 ;//0010 0000 設定三個試能標誌位,讓相應的中斷合上
PICTL |= 0x01;//下降沿觸發
P1DIR |= 0x01;
while(1);
}
#pragma vector= P0INT_VECTOR
__interrupt void timeduan ()
{
if(P0IFG &= 0x20)//P0組的5發生外部中斷
{
delay(1000);
if(0 == P0_5)
{
P1_0 ^=1 ;
}
}
P0IFG = 0;
P0IF = 0;
}
時鐘
基本概念:
- CC2530在正常執行的時候需要一個高頻時鐘訊號和一個低頻的時鐘訊號
高頻時鐘訊號,主要供給CPU,保證程式的執行。
低頻時鐘訊號,主要供給看門狗、睡眠定時器等偏上外設。 - 時鐘訊號的來源;
高頻訊號有2個,晶片內部的16M RC電路;外接的32M石英晶振
低頻訊號也有2個來源,晶片內部的32K RC電路,外接的32.768K石英晶振。 - CC2530晶片預設上電的時候,是內部的2個RC電路作為高頻和低頻的時鐘來源。
- 如果我們在用串列埠,特別是無線通訊的時候,必須要用32M的石英晶振作為高頻時鐘來源。
- 高頻時鐘源特點:2高頻時鐘源可以同時起振產生高頻時鐘訊號;而2個低頻時鐘源,某一時刻只能有1個起振,並且起振的這個時鐘源供給CC2530.
系統高頻時鐘源切換步驟:
讓2個高頻時鐘源起振;//讓SLEEPCMD的第2位為0;
等待目標時鐘源振盪穩定;// SLEEPSTA暫存器的第6位為1表示32M 時鐘源穩定
延時一小段時間63us;// 超過63微秒延時
不分頻輸出;//把暫存器CLKCONCMD的低3位 設定為000,表示不分頻輸出
選中目標高頻時鐘源作為系統主時鐘;//把暫存器CLKCONCMD的第6位 清0,設定32M作為系統主時鐘
確認一下當前工作的系統時鐘是不是所選的高頻時鐘;//如果讀CLKCONSTA這個暫存器的第6位為0,表示32M的時鐘源已經作為了當前的系統主時鐘,程式可以往下運行了。
練習程式碼3如下:
#include<iocc2530.h>
#include"74LS164_8LED.h"
void delayus()
{
char k=63;
while(k--);
}
void delay()
{
int i,j;
for(i=0;i<1000;i++)
for(j=0;j<800;j++);
}
void Init32M()
{
SLEEPCMD &=0xFB;//1111 1011 開啟2個高頻時鐘源
while(0==(SLEEPSTA & 0x40));// 0100 0000 等待32M穩定
delayus();
CLKCONCMD &=0xF8;//1111 1000 不分頻輸出
CLKCONCMD &=0XBF;//1011 1111 設定32M作為系統主時鐘
while(CLKCONSTA & 0x40); //0100 0000 等待32M成功成為當前系統主時鐘
}
void main()
{
char i;
LS164_Cfg();
Init32M();
while(1)
{
for(i=0;i<10;i++)
{
LS164_BYTE(i);
delay();
}
}
}
串列埠時鐘
具體步驟:
- 指定串列埠的IO位置
- 相應IO配置成片上外設功能
- 8個數據位、一個停止位、無流控、無校驗確立
- 波特率
- 開CPU中斷,對應串列埠接收中斷
串列埠中斷函式
樣例:
#pragma vector=URX0_VECTOR
__interrupt void anyname(void)
{
URX0IF=0;
ch=U0DBUF;
U0DBUF=ch;
while(UTX0IF==0);
UTX0IF=0;
}
波特率設定函式
//波特率設定參考上表
void Uart0Cfg()
{
PERCFG &=~0x01;
P0SEL |=0x0C;
U0CSR |=0Xc0;
U0GCR =8;
U0BAUD=59;
URX0IE=1;
EA=1;
}
練習程式碼如下:
#include<ioCC2530.h>
char ch;
void Cfg32M()
{
SLEEPCMD &=0xFB; //fB 0 00 讓2個時鐘源都起振
while(0==(SLEEPSTA & 0x40)); // 0100 0000 如果32M 晶振供電且穩定了,那麼程式往下執行
CLKCONCMD &=0xF8; //1111 1000 不分頻輸出
CLKCONCMD &=0xBF;//1011 1111 讓32M作為系統主時鐘供給CPU
while(1==(CLKCONSTA & 0x40));//如果32M確實供給CPU在工作,那麼程式往下執行
SLEEPCMD |=0x40;// 0000 0100
}
void UartCfg()
{//串列埠0的備用位置1配置成波特率9600
PERCFG &=0xFE;//1111 1110 選中串列埠0的的備用位置1
P0SEL |=0x0C; //0000 1100 P0_2 p0_3為偏上外設功能
U0CSR |=0Xc0;
U0GCR =8;
U0BAUD=59;
EA=1;
URX0IE=1;
}
void main()
{
Cfg32M();
UartCfg();
while(1);
}
#pragma vector=URX0_VECTOR
__interrupt void sdfs(void)
{
URX0IF=0;//串列埠0來資料的標誌位,硬體會置1,我們軟體要清0
ch=U0DBUF;//從接受暫存器裡取位元組存入變數ch
U0DBUF=ch;//把變數ch裡的值賦給串列埠0傳送資料暫存器
while(0==UTX0IF);
UTX0IF=0;
}