前言
学习zigbee中用到串口,关于CC2530串口程序的一篇不错的博客,学习一下。原文链接在结尾处。
文章目录
一、补充基础知识
在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; //读取缓冲中的数据
}
用餐愉快!!!
链接:「ZigBee模块」基础实验(5)串口通讯—甜小圈
END!