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

}
相关推荐
鸿喵小仙女31 分钟前
C# WPF读写STM32/GD32单片机Flash数据
stm32·单片机·c#·wpf
lucy153027510791 小时前
MCU 功耗基准测试
科技·单片机·嵌入式硬件·智能家居·信号处理·工控主板
m0_748240912 小时前
OpenMV与STM32通信全面指南
stm32·单片机·嵌入式硬件
Cchengzu4 小时前
阿里巴巴2017实习生笔试题(二)
stm32·单片机·嵌入式硬件
重生之我是数学王子7 小时前
单片机 STM32入门
stm32·单片机·嵌入式硬件
qq_4597300311 小时前
4-3 MCU中ARM存储器的作用
arm开发·单片机·嵌入式硬件
嵌入式科普13 小时前
嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
c语言·stm32·can·spi·全双工·ra6m5
重生之我是数学王子14 小时前
点亮核心板小灯 STM32U575
stm32·单片机·嵌入式硬件
end_SJ14 小时前
初学stm32 --- 定时器中断
stm32·单片机·嵌入式硬件
南城花随雪。14 小时前
单片机:实现数码管动态显示(0~99999999)74hc138驱动(附带源码)
单片机·嵌入式硬件