STM32串口通信

数据通信的基础概念

在单片机的应用中,数据通信是必不可少的一部分,比如:单片机和上位机、单片机和外 围器件之间,它们都有数据通信的需求。由于设备之间的电气特性、传输速率、可靠性要求各 不相同,于是就有了各种通信类型、通信协议,我们最常的有: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位。

  1. 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)	
	{
	
	}

}
相关推荐
智商偏低1 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen3 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森5 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白5 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D5 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术8 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt9 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘9 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang9 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n11 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件