数据通信的基础概念
在单片机的应用中,数据通信是必不可少的一部分,比如:单片机和上位机、单片机和外 围器件之间,它们都有数据通信的需求。由于设备之间的电气特性、传输速率、可靠性要求各 不相同,于是就有了各种通信类型、通信协议,我们最常的有:USART、IIC、SPI、CAN、USB 等。
1. 数据通信方式
按数据通信方式分类,可分为串行通信和并行通信两种。串行和并行的对比如下图所示:
串行通信的基本特征是数据逐位顺序依次传输,优点是传输线少、布线成本低、灵活度高 等优点,一般用于近距离人机交互,特殊处理后也可以用于远距离,缺点就是传输速率低。
而并行通信是数据各位可以通过多条线同时传输,优点是传输速率高,缺点就是布线成本 高,抗干扰能力差因而适用于短距离、高速率的通信。
2. 数据传输方向
根据数据传输方向,通信又可分为全双工、半双工和单工通信。全双工、半双工和单工通 信的比较如下图所示:
单工是指数据传输仅能沿一个方向,不能实现反方向传输,如校园广播。半双工是指数据传输可以沿着两个方向,但是需要分时进行,如对讲机。
全双工是指数据可以同时进行双向传输,日常的打电话属于这种情形。
这里注意全双工和半双工通信的区别:半双工通信是共用一条线路实现双向通信,而全双 工是利用两条线路,一条用于发送数据,另一条用于接收数据。
3.同步通讯与异步通讯
根据通讯的数据同步方式,又分为同步和异步两种,可以根据通讯过程中是否有使用到时钟信号 进行简单的区分。
同步通信要求通信双方共用同一时钟信号,在总线上保持统一的时序和周期完成信息传输。 优点:可以实现高速率、大容量的数据传输,以及点对多点传输。缺点:要求发送时钟和接收 时钟保持严格同步,收发双方时钟允许的误差较小,同时硬件复杂。异步通信不需要时钟信号,而是在数据信号中加入开始位和停止位等一些同步信号,以便 使接收端能够正确地将每一个字符接收下来,某些通信中还需要双方约定传输速率。优点:没 有时钟信号硬件简单,双方时钟可允许一定误差。缺点:通信速率较低,只适用点对点传输。
4. 通信速率
衡量通讯性能的一个非常重要的参数就是通讯速率,通常以比特率(Bitrate)来表示,即每秒钟传 输的二进制位数,单位为比特每秒(bit/s)。容易与比特率混淆的概念是"波特率"(Baudrate),它 表示每秒钟传输了多少个码元。而码元是通讯信号调制的概念,通讯中常用时间间隔相同的符号 来表示一个二进制数字,这样的信号称为码元。如常见的通讯传输中,用0V表示数字0,5V表 示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位,此时 波特率的大小与比特率一致;如果在通讯传输中,有0V、2V、4V以及6V分别表示二进制数00、 01、10、11,那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特 位数的一半,这个时候的波特率为比特率的一半。
串口通信协议简介
通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter) 是一个串行通 信设备,可以灵活地与外部设备进行全双工数据交换。有别于USART还有一个UART(Universal Asynchronous Receiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能,只有异步 通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基 本都是UART。
USART 功能框图
① USART信号引脚
TX:发送数据输出引脚
RX:接收数据输入引脚
SCLK:发送器时钟输出,适用于同步传输
SW_RX:数据接收引脚,属于内部引脚,用于智能卡模式
IrDA_RDI:IrDA模式下的数据输入
IrDA_TDO:IrDA 模式下的数据输出
nRTS:发送请求,若是低电平,表示USART准备好接收数据
nCTS:清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送
② 数据寄存器USART_DR包含了已发送或接收到的数据。由于它本身就是两个寄存器组成的,一个专门 给发送用的(TDR),一个专门给接收用的(RDR),该寄存器具备读和写的功能。TDR寄存器 提供了内部总线和输出移位寄存器之间的并行接口。RDR寄存器提供了输入移位寄存器和内部 总线之间的并行接口。当进行数据发送操作时,往USART_DR中写入数据会自动存储在TDR 内;当进行读取操作时,向USART_DR读取数据会自动提去RDR数据。
USART数据寄存器(USART_DR)低9位数据有效,其他数据位保留。USART_DR的第 9 位数据是否有效跟USART_CR1的M位设置有关,当M位为0表示8位数据字长;当M位 为1时表示9位数据字长,一般使用8位数据字长。
当使能校验位(USART_CR1中PCE位被置位)进行发送时,写到 MSB 的值(根据数据的 长度不同,MSB 是第7位或者第8位)会被后来的校验位取代。
③ 控制器
USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。使用 USART 之前需要向USART_CR1寄存器的UE位置1使能USART,UE位用来开启供给给串口 的时钟。 发送或者接收数据字长可选8位或9位,由USART_CR1的M位控制。
发送器
在发送数据时,编程的时候有几个比较重要的标志位我们来总结下。
接收器
在接收数据时,编程的时候有几个比较重要的标志位我们来总结下。
④ 时钟与波特率
中断控制
USART 初始化结构体详解
1) USART_BaudRate:波特率设置。一般设置为2400、9600、19200、115200。标准库函数会根据 设定值计算得到USARTDIV值,从而设置USART_BRR寄存器值。
2) USART_WordLength:数据帧字长,可选8位或9位。它设定USART_CR1寄存器的M位的值。 如果没有使能奇偶校验控制,一般使用8数据位;如果使能了奇偶校验则一般设置为9数据位。
3) USART_StopBits:停止位设置,可选0.5个、1个、1.5个和2个停止位,它设定USART_CR2 寄存器的STOP[1:0]位的值,一般我们选择1个停止位。
4) USART_Parity:奇偶校验控制选择,可选USART_Parity_No(无校验)、USART_Parity_Even(偶 校验)以及USART_Parity_Odd(奇校验),它设定USART_CR1寄存器的PCE位和PS位的值。
5) USART_Mode:USART模式选择,有USART_Mode_Rx和USART_Mode_Tx,允许使用逻辑或 运算选择两个,它设定USART_CR1寄存器的RE位和TE位。
- USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式才有效,可选有使能 RTS、使能CTS、同时使能RTS和CTS、不使能硬件流。
Usart.c
cpp
#include "Usart.h"
void Usart_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
//打开串口GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//将USARTTx的GPIO配置为推挽复用模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
// 将 USART Rx 的 GPIO 配置为浮空输入模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
// 配置串口的工作参数
// 配置波特率
USART_InitStruct.USART_BaudRate = 115200;
// 配置硬件流控制
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 配置校验位
USART_InitStruct.USART_Parity = USART_Parity_No;
// 配置停止位
USART_InitStruct.USART_StopBits = USART_StopBits_1;
// 配置 针数据字长
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
// 完成串口的初始化配置
USART_Init(USART1,&USART_InitStruct);
// 串口中断优先级配置
NVIC_Configuration();
//配置指定的USART中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
// 使能串口
USART_Cmd(USART1,ENABLE);
}
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置 USART 为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
/*使能中断*/
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
/* 抢断优先级为 1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/*子优先级为2*/
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
/*初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
//发送一个字节
void Usart_SendByte(USART_TypeDef *pUSARTx, uint8_t ch)
{
/* 发送一个字节数据到 USART */
USART_SendData(pUSARTx,ch);
/* 等待发送数据寄存器为空 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE) == RESET);
}
//发送两个字节
void Usart_SendHalfWore(USART_TypeDef *pUSARTx,uint16_t data)
{
uint8_t temp_h,temp_l;
temp_h = (data&0xff00)>>8;
temp_l = data&0xff;
/* 发送一个字节数据到 USART */
USART_SendData(pUSARTx,temp_h);
/* 等待发送数据寄存器为空 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE) == RESET);
USART_SendData(pUSARTx,temp_l);
/* 等待发送数据寄存器为空 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE) == RESET);
}
/*发送8位数据的数组*/
void Usart_SendArray(USART_TypeDef *pUSARTx,uint8_t *arry,uint8_t num)
{
uint8_t i;
for(i=0;i<num;i++)
{
Usart_SendByte(pUSARTx,arry[i]);
}
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}
//发送一个字符串
void Usart_SendString(USART_TypeDef *pUSARTx,uint8_t *String)
{
uint8_t i = 0;
do
{
Usart_SendByte(pUSARTx,*(String+i));
i++;
}while(*(String+i) != '\0');
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}
//中断函数无返回值无参数
void USART1_IRQHandler(void)
{
uint8_t Temp;
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
{
Temp = USART_ReceiveData(USART1);
USART_SendData(USART1,Temp);
}
}
Usart.h
cpp
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h" // Device header
#include "stdio.h"
void Usart_Init(void);
static void NVIC_Configuration(void);
void Usart_SendByte(USART_TypeDef *pUSARTx, uint8_t ch);
void Usart_SendHalfWore(USART_TypeDef *pUSARTx,uint16_t data);
void Usart_SendArray(USART_TypeDef *pUSARTx,uint8_t *arry,uint8_t num);
void Usart_SendString(USART_TypeDef *pUSARTx,uint8_t *String);
#endif
main函数
cpp
#include "stm32f10x.h" // Device header
#include "Usart.h"
int main(void)
{
uint8_t arry[10] = {0,1,2,3,4,5,6,7,8,9};
uint8_t arr[] = "huang shi jiang\n";
/* 初始化 USART 配置模式为 115200 8-N-1,中断接收 */
Usart_Init();
//Usart_SendByte(USART1,'A');
//Usart_SendHalfWore(USART1,0xff55);
//Usart_SendArray(USART1,arry,10);
Usart_SendString(USART1,arr);
while(1)
{
}
}