STM32F103学习笔记三 串口通信

STM32F103学习笔记三 串口通信

本文参照正点原子STM32F1xx官方资料:《STM32中文参考手册V10》-第25章通用同步异步收发器(USART) 及
【STM32】串口通信基本原理(超基础、详细版)
单片机入门学习十 STM32单片机学习七 串口通讯

1、串口通信背景知识

1.1、 设备之间的通信方式

并行通信 串行通信
传输原理 数据各个位同时传输 数据按位顺序传输
优点 速度快 占用引脚资源少
缺点 占用引脚资源多 速度相对较慢

1.2、 串行通信分类

1.2.1、按照通信方式,分为:

同步通信:带时钟同步信号传输。比如:SPI,I²C通信接口
异步通信:不带时钟同步信号。比如:UART(通用异步收发器),单总线

1.2.2、按照数据传送方向,分为:

单工:数据传输只支持数据在一个方向上传输;
半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口。
全双工:允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端。
STM32F103学习笔记三 串口通信_第1张图片

1.3、常见的串行通信接口

STM32F103学习笔记三 串口通信_第2张图片

2、STM32的串口通信基础

2.1、STM32的串口通信接口有两种,分别是:

UART(通用异步收发器)、
USART(通用同步异步收发器)、

对于大容量STM32F10x系列芯片,分别有3个USART和2个UART。

2.2、UART引脚连接方法

① 单片机连接单片机

RXD:数据输入引脚,数据接受
TXD"数据发送引脚,数据发送

对于两个芯片之间的连接,两个芯片GND共地,同时TXD和RXD交叉连接。这里的交叉连接的意思就是,芯片1的RxD连接芯片2的TXD,芯片2的RXD连接芯片1的TXD。这样,两个芯片之间就可以进行TTL电平通信了
STM32F103学习笔记三 串口通信_第3张图片
② 单片机连接PC

若是芯片与PC机(或上位机)相连,除了共地之外,就不能这样直接交叉连接了。尽管PC机和芯片都有TXD和RXD引脚,但是通常PC机(或上位机)通常使用的都是RS232接口,因此不能直接交叉连接。RS232接口是9针(或引脚),通常是TxD和RxD经过电平转换得到的。而单片机采用的是 TTL电平,所以需要 连接一个RS232转换器 将TTL电平转换成 PC可以识别的RS232电平,再交叉连接。
STM32F103学习笔记三 串口通信_第4张图片经过电平转换后,芯片串口和rs232的电平标准是不一样的:

单片机的电平标准(TTL电平):+5V表示1,0V表示0;
Rs232的电平标准:+15/+13 V表示1,-15/-13表示0。

RS-232通讯协议标准串口的设备间通讯结构图如下:
STM32F103学习笔记三 串口通信_第5张图片
所以单片机串口与PC串口通信就应该遵循下面的连接方式:
在单片机串口与上位机给出的rs232口之间,通过电平转换电路(如下面图中的Max232芯片) 实现TTL电平与RS232电平之间的转换。
STM32F103学习笔记三 串口通信_第6张图片

2.3、UART异步通信特点

  • 全双工异步通信;
  • 分数波特率发生器系统,提供精确的波特率。发送和接受共用的可编程波特率,最高可达4.5Mbits/s;
  • 可编程的数据字长度(8位或者9位);
  • 可配置的停止位(支持1或者2位停止位);
  • 可配置的使用DMA多缓冲器通信;
  • 单独的发送器和接收器使能位;
  • 检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志;
  • 多个带标志的中断源,触发中断;
  • 其他:校验控制,四个错误检测标志。

2.4、STM32串口异步通信需要定义的参数

STM32异步通信参数:
①起始位
②数据位(8位或者9位)
③奇偶校验位(第9位)
④停止位(1,15,2位)
⑤波特率设置

(奇偶校验位分为奇校验和偶校验两种,是一种简单的数据误码校验方法。奇校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为奇数;偶校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为偶数。
校验方法除了奇校验(odd)、偶校验(even)之外,还可以有:0 校验(space)、1 校验(mark)以及无校验(noparity)。 0/1校验:不管有效数据中的内容是什么,校验位总为0或者1)
UART串口通信的数据包以帧为单位,常用的帧结构为:1位起始位+8位数据位+1位奇偶校验位(可选)+1位停止位。如下图所示
STM32F103学习笔记三 串口通信_第7张图片

2.5、 UART(USART)框图

任何USART双向通信至少需要两个脚:接收数据输入(RX)和发送数据输出(TX)。
RX:接收数据串行输。通过过采样技术来区别数据和噪音,从而恢复数据。
TX:发送数据输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处于高电平。在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收。
● 总线在发送或接收前应处于空闲状态
● 一个起始位
● 一个数据字(8或9位),最低有效位在前
● 0.5,1.5,2个的停止位,由此表明数据帧的结束
● 使用分数波特率发生器 —— 12位整数和4位小数的表示方法。
● 一个状态寄存器(USART_SR)
● 数据寄存器(USART_DR)
● 一个波特率寄存器(USART_BRR),12位的整数和4位小数
● 一个智能卡模式下的保护时间寄存器(USART_GTPR)
STM32F103学习笔记三 串口通信_第8张图片

2.6、 串口通信过程

① 数据接收过程
外部设备将数据发送到 串行输入移位寄存器,串行输入移位寄存器在将数据传送到输入数据缓冲器,MCU在从输入数据缓冲器中读出数据
在这里插入图片描述② 数据发送过程
MCU将要发送的数据写入输出数据缓冲器,输出数据缓冲器在将数据写入串行输出移位寄存器,串行移位寄存器在将数据输出到外部设备
在这里插入图片描述

3、 串行通信的编程

3.1、串口通信的相关寄存器

3.1.1、 USART_SR状态寄存器

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
STM32F103学习笔记三 串口通信_第9张图片

3.1.2、 USART_DR数据寄存器
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

STM32F103学习笔记三 串口通信_第10张图片

3.1.3、 USART_BRR波特率寄存器
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

STM32F103学习笔记三 串口通信_第11张图片
波特率计算方法:

STM32F103学习笔记三 串口通信_第12张图片

3.2、 串口操作相关库函数(省略入口函数):

**void USART_Init();** //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
**void USART_Cmd();**//使能串口
**void USART_ITConfig();**//使能相关中断

**void USART_SendData();**//发送数据到串口,DR
**uint16_t USART_ReceiveData();**//接受数据,从DR读取接受到的数据

**FlagStatus USART_GetFlagStatus();**//获取状态标志位
**void USART_ClearFlag();**//清除状态标志位
**ITStatus USART_GetITStatus();**//获取中断状态标志位
**void USART_ClearITPendingBit();**//清除中断状态标志位


3.3、 串口配置一般步骤:

  1. 串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
  2. 串口复位:USART_DeInit(); 这一步不是必须的
  3. GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
  4. 串口参数初始化:USART_Init();
  5. 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤) NVIC_Init();
    USART_ITConfig();
  6. 使能串口:USART_Cmd();
  7. 编写中断处理函数:USARTx_IRQHandler();
  8. 串口数据收发:
    void USART_SendData();//发送数据到串口,DR
    uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
  9. 串口传输状态获取:
    FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
    void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

4、 串口编程

(参照3.2、3.3的章节,其中有关于代码配置的步骤)
usart.c

//串口1中断服务程序
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记	  
  
void uart_init(u32 bound)
{
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
	 
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
 
	//USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置

	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

  USART_Init(USART1, &USART_InitStructure); //初始化串口1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;
    #if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntEnter();    
    #endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 

main.c

int main(void)
 {		
 	u16 t;  
	u16 len;	
	u16 times=0;
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化
	KEY_Init();          //初始化与按键连接的硬件接口
 	while(1)
	{
		if(USART_RX_STA&0x8000)
		{					   
			len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
			printf("\r\n您发送的消息为:\r\n\r\n");
			for(t=0;t

关于代码编写的总结及注意点,以后有空总结

你可能感兴趣的:(STM学习笔记)