04_CC2530+UART串口通信
串口通信基本概念
- 串行通信: 数据字节一位位地依次传送的通信方式, 串行通信的速度慢, 但用的传输线条数少, 成本低,适用于远距离的数据传送
- 并行通信: 数据字节的各位同事传送的通信方式, 优点是数据传送速度快, 缺点是占用的传输线条数多, 适用于近距离通信, 远距离通信的成本较高
-
异步通信: 一次通信传送一个字符帧, 发送的字符之间的时间间隔可以是任意的, 优点是通信设备简单、价格低廉, 但因为具有起始位和停止位, 传输效率较低
-
同步通信: 进行通信前先建立同步, 发送频率和接受方的接受频率要同步。在发送信息时, 将多个字符加上同步字符组成一个信息帧, 有一个统一的时钟控制发送端的发送, 接收端识别到同步字符后, 就认为开始一个信息帧, 此后位数作为实际传输信息处理。优点: 传输速度较快, 可用于点对多点 缺点: 需要使用专用的时钟控制线实现同步, 对于长距离通信成本较高, 通信速率也会降低。一般用于同一PCB上芯片级之间的通信
USART和UART的区别
-
UART(universal asynchronous receiver and transmitter) 通用异步收/发器
-
USART(universal synchronous asynchronous receiver and transmitter) 通用同步/异步收/发器
-
USART是UART的增强型
-
CC2530芯片有USART0和USART1串行通信接口, 它们能够分别运行于异步UART模式或者同步SPI 模式,两个USART具有同样的功能
UART串口参数及时序(串行异步通讯)
- 波特率: 串口通信的速率,通讯双方的波特率需要一致
- 起始位: 标志一个数据帧的开始, 固定为低电平
- 数据位: 数据帧的有效载荷, 1为高电平, 0为低电平, 低位先行
- 校验位: 用于数据验证, 根据数据位计算得来
- 停止位: 用于数据帧间隔, 固定为高电平
硬件电路与CH340 USB转串口芯片
- 简单双向串口通信有两根通信线(发送端TX和接受端RX)
- TX与RX需要交叉连接
- 当只需单向的数据传输时, 可以只接一根通信线
- 当电平标准不一致时, 需要加电平转换芯片
- 由于单片机UART外设与电脑USB接口采用的电平标准不同,通常需要通过CH340芯片进行电平转换,且电脑上需要安装相应的CH340驱动程序
- Note: 若开发板上有电平转换芯片,一般只需用数据线直接将电脑和开发板上USB接口连接。若开发板上没有电平转换芯片,则需找到对应串口的引脚, 通过外置的USB转TTL模块与电脑相连接
CC2530 UART通信
CC2530有USART0和USART1两个串行通信接口,它们能够分别运行于异步UART模式或者同步SPI 模式。两个USART具有同样的功能,可以设置在单独的 I/O 引脚。
UART模式
UART模式提供异步串行接口。在UART模式中,接口使用2线或者含有引脚RXD、TXD、可选RTS和CTS的4线。UART模式具有以下特点:
- 8位或者9位负载数据
- 奇校验、偶校验或者无奇偶校验
- 配置起始位和停止位电平(默认起始位低电平,停止位高电平)
- 配置LSB或者MSB首先传送
- 独立收发中断
- 独立收发DMA触发
- 奇偶校验和帧校验出错状态
UART发送
- 当往UxBUF(USART 收/发数据缓冲器寄存器) 写入数据时,该字节发送到输出引脚 TXDx。
- 当字节传送开始时,UxCSR.ACTIVE位变为高电平,而当字节传送结束时为低。当传送结束时, UxCSR.TX_BYTE位设置为 1
UART接收
- 当1写入 UxCSR.RE 位时,在UART上数据接收就开始了。(开启接收,需要配置完UART所有参数后)
- UART会在输入引脚RXDx中寻找有效起始位,当检测出有效起始位时,收到的字节就传入到接收寄存器,并将UxCSR.RX_BYTE位设置为 1,接收完一个字节后会产生一个中断
- 通过寄存器UxBUF提供收到的数据字节。当UxBUF读出时,UxCSR.RX_BYTE位由硬件清 0
USART中断
每个USART都有两个中断:URXx(接收完成中断)和UTXx(发送完成中断),USART0相关中断使能和中断标志如下:
- USART0 RX:IEN0.URX0IE
- USART0 TX:IEN2.UTX0IE
- USART0 RX:TCON.URX0IF
- USART0 TX:IRCON2.UTX0IF
波特率的产生
由寄存器 **UxBAUD.BAUD_M[7:0]**和 UxGCR.BAUD_E[4:0]定义波特率。公式如下:
波特率
=
(
256
+
B
A
U
D
_
M
)
∗
2
B
A
U
D
_
E
2
28
∗
F
\begin{aligned} 波特率=\frac{(256+BAUD\_M)*2^{BAUD\_E}}{2^{28}}*F\\ \end{aligned}
波特率=228(256+BAUD_M)∗2BAUD_E∗F
其中: F是系统时钟频率
UART通信相关寄存器
端口配置相关寄存器
由于CC2530的USART0有两个可选位置(如下图所示),因此需要配置相关寄存器,来选择USART0所用的引脚。
需要配置的寄存器有:
- PERCFG:外设I/O控制寄存器
- P2DIR:端口2方向和端口0外设优先级控制
- P0SEL:端口0功能选择
UART串口参数配置相关寄存器
在使用UART通讯前,要将USART配置成UART模式,并设置通讯时的串口参数(见UART串口参数及时序小节)。需要配置的寄存器有:
- U0CSR:USART0控制和状态寄存器
- U0UCR:USART0 UART控制寄存器
- U0BAUD:USART0波特率控制
- U0GCR:USART0通用控制
USART中断配置相关寄存器
一般只用到接收中断,用于接收信息。无特殊情况,不需要用到发送中断。与USART0接收中断相关寄存器有:
-
URX0IF:USART0接收中断标志
-
URX0IE:USART0接收中断使能
-
IEN0.EA: 全局中断使能
配置完上述寄存器后,还需重写
putchar
函数,即可用printf
函数将信息从串口中发送出去。在接收中断服务函数中,完成信息的接受,并做出相应的回答。
具体代码讲解
项目结构
Serial.c
用于实现UART通信,System
和Timer
中的源文件,主要用于实现Delay_ms
函数,下面将重点讲解serial.c
中的代码。Delay_ms
函数相关代码在基础篇03中有详细介绍,不在赘述。
Serial.c
#include <ioCC2530.h>
#include "serial.h"
#include <stdio.h>
unsigned char RxBuf; // 接收缓存
unsigned char RxData[RXLEN]; // 数据保存
unsigned char RxState = 0; // 接收状态 0 等待包头'@' 1 接收数据
/**
* @brief 初始化串口 UART0
*/
void Serial_Init(void)
{
// 端口配置
PERCFG = 0x00; // 选用UART0默认位置 P0_2 Rx P0_3 Tx
P2DIR &= 0x3f; // 选用UART0作为第一优先级指派给端口0
P0SEL |= 0x0C; // P0端口 2、3引脚设置为外设功能
// UART 配置
U0CSR |= 0x80; // 选择UART模式
U0UCR |= 0x02; // 8bit 无校验 1bit停止位
// 波特率设置 Baud = (256 + BAUD_M)*2^(BAUD_E) / 2^28 * F F为系统时钟频率 32MHz
// 若波特率要设置成9600 则BAUD_M = 44 BAUD_E = 8
U0BAUD |= 0x2C; // 44
U0GCR |= 0x08; // 8
// 配置接收中断
URX0IF = 0; // 清除接收中断标志
URX0IE = 1; // 使能接受中断
EA = 1; // 开启全局中断
U0CSR |= 0x40; // 接收器使能 需要先配置完USART所有参数
}
/**
* @brief 重写 putchar函数 UART0 printf函数底层调用的就是该函数 重写过后,调用printf会通过串口发送信息
*/
__ATTRIBUTES int putchar(int c)
{
UTX0IF = 0; // 清除发送标志位
U0DBUF = (char)c;
while(UTX0IF == 0); // 等待发送成功 发送成功时 发送标志位复位
return(c);
}
/**
* @brief UART0 接收中断服务函数
*/
#pragma vector = URX0_VECTOR
__interrupt void USAT0_ISR(void)
{
static unsigned int idx = 0; // 接收数组下标 static关键字只初始化一次
if(U0CSR&0x04 != 0) // 接收到数据
{
RxBuf = U0DBUF; // 从UART寄存器中读取数据
if(RxState == 0) // 等待包头
{
if(RxBuf == '@')
{
RxState = 1; // 切换状态为接收信息
idx = 0; // 重置下标
}
}
else if(RxState == 1) // 读取数据
{
if(idx < RXLEN && (RxBuf != '\n')) // 接收数据
{
RxData[idx++] = RxBuf; //从接收缓存区中读取信息,放到接收数组中
}
else // 数据超过限制 或者 收到包尾
{
idx = 0; // 重置下标
RxState = 0; // 切换状态为等待包头
printf("Get Info:%s\n", RxData); // 回复消息
}
}
}
URX0IF = 0; // 清除中断标志
}
- UART0接收中断服务函数:实现接收包头为
'@'
的消息,并在接收到包尾\n
时,发送接收数组RxData
中的数据作为回复。 - 具体实现看代码,非常详细
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#define RXLEN 256 // 接收数据最大长度
void Serial_Init(void);
#endif
main.c
#include <ioCC2530.h>
#include "delay.h"
#include <stdio.h>
#include "serial.h"
void main(void)
{
Delay_Init(); // 初始化延时函数, 基于定时器3实现
Serial_Init(); // 初始化串口
while(1)
{
printf("%s\n", "hello zigbee!\n"); // 每过五秒向上位机发送消息
Delay_ms(5000);
}
}
- Note:
Delay_ms()
相关代码见基础篇03
实验现象
发送消息
大约每隔5s上位机收到从单片机串口发来的消息
接收消息
根据回复内容,可以判断单片机正确接受到上位机的消息
参考资料
STM32入门笔记10_USART串口通信+案例:上位机控制LED亮灭(USART串口通信、TIM定时器、EXTI综合案例)-CSDN博客
CC2530中文数据手册完全版