一、補充基礎知識
在CC2530 中,USART0 和USART1 是串行通信接口,它們能夠分別運行於異步USART 模式或者同步SPI 模式。兩個USART 的功能是一樣的,並且各自有單獨的IO 引腳。USART里面的A指的就是asynchronous(異步),S指的是synchronous(同步)。這里我們使用異步通信方式。
UART模式特征:
·一次傳8或9個比特的數據
·奇校驗、偶校驗或者無校驗位
·配置起始位和停止位點平
·配置LSB或者MSB首先傳送
·獨立收發中斷
·獨立收發DMA觸發
UART模式下可以進行全雙工異步通信,UART發送的一個字節由一個起始位,8個數據位,第9個數據位或者奇偶校驗位,一或二個結束位組成。
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
起始位 |
數據位 |
數據位或奇偶校驗位 |
結束位 |
UART的控制和狀態寄存器:U0CSR、U1CSR
UART的控制寄存器:U0UCR、U1UCR
(0、1分別對應UART0、UART1)
把UxCSR.MODE置1即選中UART模式
UART0 P0_2——RX
P0_3——TX
UART1 P0_5——RX
P0_4——TX
二、實驗目的和步驟
1. 實驗目的:實現串口發送、接收、控制LED
2. 實驗步驟:
我把實驗分成三個小實驗,逐步實現
①實現串口發送數據
②實現串口發送和接收數據
③實現控制LED功能
三、USB轉串口部分原理圖
四、實驗1——串口發送數據
1. 寄存器及波特率計算
本次實驗串口相關的寄存器和標志位有:U0CSR、U0GCR、U0BAUD、U0DBUF、UTX0IF、PERCFG、P2DIR。
相關功能見下表
U0CSR (UART0控制和狀態寄存器) |
Bit7:MODE |
0:SPI模式 |
1:UART模式 |
||
Bit6:RE |
0:接收器禁止 |
|
1:接收器使能 |
||
Bit5:SLAVE |
0:SPI主模式 |
|
1:SPI從模式 |
||
Bit4:FE |
0:沒有檢測出幀錯誤 |
|
1:收到字節停止位電平出錯 |
||
Bit3:ERR |
0:沒有檢測出奇偶校驗出錯 |
|
1:收到字節奇偶校驗出錯 |
||
Bit2:RX_BYTE |
0:沒有收到字節 |
|
1:收到字節就緒 |
||
Bit1:TX_BYTE |
0:沒有發送字節 |
|
1:寫到數據緩沖區寄存器的最后字節已經發送 |
||
Bit0:ACTIVE |
0:UART空閑 |
|
1:UART忙 |
||
U0GCR (UART0通用控制寄存器) |
Bit7:CPOL |
0:SPI負時鍾極性 |
1:SPI正時鍾極性 |
||
Bit6:CPHA |
0:當來自CPOL的SCK反相之后又返回CPOL時,數據輸出到MOSI;當來自CPOL的SCK返回CPOL反相時,輸入數據采樣到MISO |
|
1:當來自CPOL的SCK返回CPOL反相時,數據輸出到MISO;當來自CPOL的SCK反相之后又返回CPOL時,輸入數據采樣到MOSI |
||
Bit5:ORDER |
0:LSB先傳送 |
|
1:MSB先傳送 |
||
Bit[4-0]: BAUD_E |
波特率指數值 BAUD_E和BAUD_M一起決定了UART的波特率 |
|
U0BAUD (UART0波特率控制寄存器) |
Bit[7-0]: BAUD_M
|
波特率尾數值 BAUD_E和BAUD_M一起決定了UART的波特率 |
U0DBUF(UART0收發數據緩沖區) |
|
串口發送/接收數據緩沖區 |
UTX0IF |
中斷標志5 IRCON2的Bit1 (UART0字節發送完成標志位) |
0:中斷未掛起 未發送完 |
1:中斷掛起 發送完畢 |
串口波特率公式:
32Mhz系統時鍾的常用波特率設置
波特率(bps) |
UxBaud.BAUD_M |
UxGCR.BAUD_E |
誤差(%) |
2400 |
59 |
6 |
0.14 |
4800 |
59 |
7 |
0.14 |
9600 |
59 |
8 |
0.14 |
14400 |
216 |
8 |
0.03 |
19200 |
59 |
9 |
0.14 |
28800 |
216 |
9 |
0.03 |
38400 |
59 |
10 |
0.14 |
57600 |
216 |
10 |
0.03 |
76800 |
59 |
11 |
0.14 |
115200 |
216 |
11 |
0.03 |
230400 |
216 |
12 |
0.03 |
端口 |
Bit位 |
名稱 |
初始化 |
讀/寫 |
描述 |
PERCFG 外設控制寄存器 |
7 |
--- |
0 |
R0 |
未使用 |
6 |
T1CFG |
0 |
R/W |
計時器1的I/O位置: 0:選擇到位置1(Alt.1) 1:選擇到位置2(Alt.2) |
|
5 |
T3CFG |
0 |
R/W |
計時器3的I/O位置: 0:選擇到位置1(Alt.1) 1:選擇到位置2(Alt.2) |
|
4 |
T4CFG |
0 |
R/W |
計時器4的I/O位置: 0:選擇到位置1(Alt.1) 1:選擇到位置2(Alt.2) |
|
3:2 |
--- |
00 |
R/W |
未使用 |
|
1 |
U1CFG |
0 |
R/W |
USART 1的I/O位置: 0:選擇到位置1(P0_4、P0_5) 1:選擇到位置2(P1_6、P1_7) |
|
0 |
U0CFG |
0 |
R/W |
USART 0的I/O位置: 0:選擇到位置1(P0_2、P0_3) 1:選擇到位置2(P1_4、P1_5) |
P2DIR |
7:6 |
PRIP0[1:0] |
00 |
R/W |
端口0外設優先級控制,當PERCFG分配給一些外設相同引腳的時候,這些位將確定優先級。優先級從前到后如下: 00:USART 0,USART 1,Timer 1 01:USART 1,USART 0,Timer 1 10:Timer 1 channels 0-1,USART 1,USART 0,Timer 1 channels 2-3 11:Timer 1 channels 2-3, USART 0,USART 1,Timer 1 channels 0-1 |
|
5 |
--- |
0 |
R0 |
未使用 |
|
4:0 |
DIRP2_[4:0] |
00000 |
R/W |
P2.4—P2.0的方向 (0:輸入 1:輸出) |
2.串口初始化代碼
void uartInit() { //先設置晶振 CLKCONCMD &= ~0x40; //設置系統時鍾源為32MHz晶振 while(CLKCONSTA & 0x40); //等待晶振穩定為32M CLKCONCMD &= ~0x47; //設置系統主時鍾頻率為32MHz //先把串口通信相應引腳初始化下 PERCFG &= ~0x01; //位置1,即P0口 P0SEL |= 0x0c; //P0_2、P0_3用作串口 P2DIR &= ~0xc0; //P0優先作為UART0 //再配置串口 U0CSR |= 0x80; //設置為UART方式 U0GCR |= 11; //設置波特率115200 U0BAUD |= 216; //| UTX0IF = 0; //UART0 TX中斷標志初始位 }
3. 串口發送代碼
void uartSend(char *Data, int len) { int j; for(j=0; j<len; j++) //發送設定長度個字節 { U0DBUF = *Data++; //當前字節放入緩存並指向下一個字節 while(UTX0IF == 0); //等待標志位置1,表示當前字節發送完了 UTX0IF = 0; //手動置0,准備發送下一個字節 } }
4. 完整代碼
#include <ioCC2530.h> #include <string.h> #define uchar unsigned char #define uint unsigned int //引腳定義 #define led1 P1_0 //函數聲明 void delayms(uint ms); //延時函數 void ledInit(); //led初始化 void uartInit(); //串口初始化 void uartSend(char *Data, int len); //串口發送 //變量聲明 char Txdata[14]; /************************************* 延時函數 *************************************/ void delayms(uint ms) { uint i, j; for(i=ms; i>0; i--) for(j=1774; j>0; j--); } /************************************* led初始化 *************************************/ void ledInit() { P1SEL &= ~0x01; P1DIR |= 0x01; P1INP &= ~0x01; led1 = 1; } /************************************* 串口初始化 *************************************/ void uartInit() { //先設置晶振 CLKCONCMD &= ~0x40; //設置系統時鍾源為32MHz晶振 while(CLKCONSTA & 0x40); //等待晶振穩定為32M CLKCONCMD &= ~0x47; //設置系統主時鍾頻率為32MHz //先把串口通信相應引腳初始化下 PERCFG &= ~0x01; //位置1,即P0口 P0SEL |= 0x0c; //P0_2、P0_3用作串口 P2DIR &= ~0xc0; //P0優先作為UART0 //再配置串口 U0CSR |= 0x80; //設置為UART方式 U0GCR |= 11; //設置波特率115200 U0BAUD |= 216; //| UTX0IF = 0; //UART0 TX中斷標志初始位 } /************************************* 串口發送 *************************************/ void uartSend(char *Data, int len) { int j; for(j=0; j<len; j++) //發送設定長度個字節 { U0DBUF = *Data++; //當前字節放入緩存並指向下一個字節 while(UTX0IF == 0); //等待標志位置1,表示當前字節發送完了 UTX0IF = 0; //手動置0,准備發送下一個字節 } } /************************************* 主函數 *************************************/ void main() { ledInit(); uartInit(); strcpy(Txdata, "I'm Donut! "); //將發送內容復制到Txdata while(1) { uartSend(Txdata, sizeof("I'm Donut! ")); //串口發送數據 delayms(1000); //延時 led1 = ~led1; } }
5. 實驗結果
注意波特率、數據位之類的不要設置錯!不然會出現亂碼!
五、實驗二——串口收發數據
1. 實驗目的
PC端通過串口發送數據給硬件端(數據長度不超過50,終止符為#),硬件端收到數據后發送回PC端
2. 再多學一個寄存器和標志位:IEN0、URX0IF
IEN0(置1為中斷)
位 |
位名 |
復位值 |
操作性 |
功能描述 |
7 |
EA |
0 |
讀/寫 |
中斷總開關 |
6 |
|
0 |
讀/寫 |
未用 |
5 |
STIE |
0 |
讀/寫 |
睡眠定時器中斷使能 |
4 |
ENCIE |
0 |
讀/寫 |
AES加/解密,完成中斷使能 |
3 |
URX1IE/I2SRXIE |
0 |
讀/寫 |
USART1/I^2S接受中斷 |
2 |
URX0IE |
0 |
讀/寫 |
USART0接受中斷 |
1 |
ADCIE |
0 |
讀/寫 |
A/D轉換完成中斷 |
0 |
RFTXRXIE |
0 |
讀/寫 |
RF收發完成中斷 |
URX0IF |
中斷標志1 TCON的Bit3 |
0:USART0接受中斷使能 |
1:保留,但必須置1 |
這個URX0IF我的理解就是有數據傳送過來就自動置1,產生中斷,然后需要手動清0
3. 程序流程
①初始化(LED、串口)
②接收狀態(沒有收到數據則一直等待、收到數據放到指定數組里、數組放滿或收到終止符轉到發送狀態)
③發送狀態(關閉數據接收中斷、發送數據直至發完、打開中斷、轉到接收狀態)
④接收狀態里面的數據是怎么收到的?用串口中斷!接收到的數據會放到緩沖區,中斷程序的任務就是把緩沖區的數據讀出來!
4. 完整代碼
#include <ioCC2530.h> #include <string.h> #define uchar unsigned char #define uint unsigned int //引腳定義 #define led1 P1_0 #define led2 P1_1 //函數聲明 void delayms(uint ms); //延時函數 void ledInit(); //led初始化 void uartInit(); //串口初始化 void uartSend(char *Data, int len); //串口發送函數 //變量聲明 uchar RXTXflag = 1; //選擇標志位,決定接收數據還是發送數據 char temp; //存放接收到的數據 uchar datanumber = 0; //累計一次接收的數據個數 char Rxdata[50]; //一次最多接收50個字符 /*********************************** 延時函數 ***********************************/ void delayms(uint ms) { int i, j; for(i=ms; i>0; i--) for(j=1156; j>0; j--); } /*********************************** led初始化 ***********************************/ void ledInit() { P1SEL &= ~0x03; P1DIR |= 0x03; P1INP &= ~0x03; led1 = 0; led2 = 0; } /*********************************** 串口初始化 ***********************************/ void uartInit() { //設置晶振 CLKCONCMD &= ~0x40; //設置系統時鍾源為32MHz晶振 while(CLKCONSTA & 0x40); //等待晶振穩定為32M CLKCONCMD &= ~0x47; //設置系統主時鍾頻率為32MHz //串口引腳初始化 PERCFG &= ~0x01; //位置1,即P0口 P0SEL |= 0x0c; //P0_2、P0_3用作串口 P2DIR &= ~0xc0; //P0優先作為UART0 //設置串口寄存器 U0CSR |= 0x80; //設置為UART方式 U0GCR |= 11; //設置波特率115200 U0BAUD |= 216; //| UTX0IF = 0; //UART0 TX中斷標志初始位 U0CSR |= 0x40; //設置UART0允許接收數據 IEN0 |= 0x84; //開總中斷、UART0接收中斷 } /*********************************** 串口發送函數 ***********************************/ void uartSend(char *Data, int len) { int j; for(j=0; j<len; j++) //發送設定長度個字節 { U0DBUF = *Data++; //當前字節放入緩存並指向下一個字節 while(UTX0IF == 0); //等待標志位置1,表示當前字節發送完了 UTX0IF = 0; //手動置0,准備發送下一個字節 } } /*********************************** 主函數 ***********************************/ void main() { //初始化 ledInit(); uartInit(); //循環 while(1) { if(RXTXflag == 1) //接收狀態 { led1 = 1; if(temp != 0) { if((temp!='#')&&(datanumber<50)) //如果沒有收到終止符且字符數小於50 Rxdata[datanumber++] = temp; //把收到的字符存入數組 else //否則進入發送狀態 { RXTXflag = 3; led1 = 0; } temp = 0; } } if(RXTXflag == 3) //發送狀態 { led2 = 1; U0CSR &= ~0x40; //禁止接收 uartSend(Rxdata, datanumber); //發送已記錄的字符串 RXTXflag = 1; //恢復到接收狀態 datanumber = 0; //長度重新置0 led2 = 0; U0CSR |= 0x40; //允許接收 } } } /*********************************** UART0接收中斷 ***********************************/ #pragma vector = URX0_VECTOR __interrupt void UART0_ISR(void) { URX0IF = 0; //清中斷標志 temp = U0DBUF; //讀取緩沖中的數據 }
六、實驗三——串口控制LED
1. 實驗目的
發送“L1#”,燈L1改變狀態
發送“L2#”,燈L2改變狀態
2. 實驗代碼
其實主要思想和實驗二是一樣的,只是對收到的數據進行處理的方式不同
實驗二是把數據發送出去,該實驗則是分析數據並作出響應
#include <ioCC2530.h> #include <string.h> #define uchar unsigned char #define uint unsigned int //引腳定義 #define led1 P1_0 #define led2 P1_1 //函數聲明 void delayms(uint ms); //延時函數 void ledInit(); //led初始化 void uartInit(); //串口初始化 void uartSend(char *Data, int len); //串口發送函數 //變量聲明 uchar RXTXflag = 1; //選擇標志位,決定接收數據還是發送數據 char temp; //存放接收到的數據 uchar datanumber = 0; //累計一次接收的數據個數 char Rxdata[50]; //一次最多接收50個字符 /*********************************** 延時函數 ***********************************/ void delayms(uint ms) { int i, j; for(i=ms; i>0; i--) for(j=1156; j>0; j--); } /*********************************** led初始化 ***********************************/ void ledInit() { P1SEL &= ~0x03; P1DIR |= 0x03; P1INP &= ~0x03; led1 = 1; led2 = 1; } /*********************************** 串口初始化 ***********************************/ void uartInit() { //設置晶振 CLKCONCMD &= ~0x40; //設置系統時鍾源為32MHz晶振 while(CLKCONSTA & 0x40); //等待晶振穩定為32M CLKCONCMD &= ~0x47; //設置系統主時鍾頻率為32MHz //串口引腳初始化 PERCFG &= ~0x01; //位置1,即P0口 P0SEL |= 0x0c; //P0_2、P0_3用作串口 P2DIR &= ~0xc0; //P0優先作為UART0 //設置串口寄存器 U0CSR |= 0x80; //設置為UART方式 U0GCR |= 11; //設置波特率115200 U0BAUD |= 216; //| UTX0IF = 0; //UART0 TX中斷標志初始位 U0CSR |= 0x40; //設置UART0允許接收數據 IEN0 |= 0x84; //開總中斷、UART0接收中斷 } /*********************************** 串口發送函數 ***********************************/ void uartSend(char *Data, int len) { int j; for(j=0; j<len; j++) //發送設定長度個字節 { U0DBUF = *Data++; //當前字節放入緩存並指向下一個字節 while(UTX0IF == 0); //等待標志位置1,表示當前字節發送完了 UTX0IF = 0; //手動置0,准備發送下一個字節 } } /*********************************** 主函數 ***********************************/ void main() { //初始化 ledInit(); uartInit(); //循環 while(1) { if(RXTXflag == 1) //接收狀態 { if(temp != 0) { if((temp!='#')&&(datanumber<50)) //如果沒有收到終止符且字符數小於50 Rxdata[datanumber++] = temp; //把收到的字符存入數組 else //否則進入發送狀態 RXTXflag = 3; temp = 0; } } if(RXTXflag == 3) //發送狀態 { U0CSR &= ~0x40; //禁止接收 if(Rxdata[0] == 'L') { if(Rxdata[1] == '1') led1 = ~led1; else if(Rxdata[1] == '2') led2 = ~led2; } RXTXflag = 1; //恢復到接收狀態 datanumber = 0; //長度重新置0 U0CSR |= 0x40; //允許接收 } } } /*********************************** UART0接收中斷 ***********************************/ #pragma vector = URX0_VECTOR __interrupt void UART0_ISR(void) { URX0IF = 0; //清中斷標志 temp = U0DBUF; //讀取緩沖中的數據 }