STM32——串口通信

举报
万里羊 发表于 2021/08/26 00:58:48 2021/08/26
【摘要】 STM32串口通信 1.STM32串口简介: STM32 的串口资源相当丰富的,功能也相当强劲。 ALIENTEK 精英 STM32 开发板所使用的 STM32F103ZET6 最多可提供 5 路串口...

STM32串口通信

1.STM32串口简介:

STM32 的串口资源相当丰富的,功能也相当强劲。 ALIENTEK 精英 STM32 开发板所使用的 STM32F103ZET6 最多可提供 5 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、 支持调制解调器操作、 智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA等。
串口最基本的设置,就是波特率的设置。 STM32 的串口使用起来还是蛮简单的,只要你开启了串口时钟,并设置相应 IO 口的模式,然后配置一下波特率,数据位长度,奇偶校验位等信息,就可以使用了。下面,我们就简单介绍下这几个与串口基本配置直接相关的寄存器。
1,串口时钟使能
串口作为 STM32 的一个外设,其时钟由外设时钟使能寄存器控制,这里我们使用的串口 1是在 APB2ENR寄存器的第 14位。只是说明一点,就是除了串口 1 的时钟使能在 APB2ENR 寄存器,其他串口的时钟使能位都在 APB1ENR 寄存器。
2,串口复位
当外设出现异常的时候可以通过复位寄存器里面的对应位设置,实现该外设的复位,然后重新配置这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。串口 1 的复位是通过配置 APB2RSTR 寄存器的第 14 位来实现的。 APB2RSTR 寄存器的各位描述如图 9.1.1 所示
在这里插入图片描述
从图 9.1.1 可知串口 1 的复位设置位在 APB2RSTR 的第 14 位。通过向该位写 1 复位串口 1,写 0 结束复位。其他串口的复位位在 APB1RSTR 里面。
3,串口波特率设置
每个串口都有一个自己独立的波特率寄存器 USART_BRR,通过设置该寄存器就可以达到配置不同波特率的目的。具体实现方法。
4,串口控制
STM32 的每个串口都有 3 个控制寄存器 USART_CR1~3,串口的很多配置都是通过这 3 个寄存器来设置的。这里我们只要用到 USART_CR1 就可以实现我们的功能了,该寄存器的各位描述如图 9.1.2 所示
在这里插入图片描述
该寄存器的高 18 位没有用到,低 14 位用于串口的功能设置。 UE 为串口使能位通过该位置 1,以使能串口。 M 为字长选择位,当该位为 0 的时候设置串口为 8 个字长外加 n 个停止位,停止位的个数(n)是根据 USART_CR2 的[13:12]位设置来决定的,默认为 0。 PCE 为校验使能位,设置为 0,则禁止校验,否则使能校验。 PS 为校验位选择,设置为 0 则为偶校验,否则为奇校验。 TXIE 为发送缓冲区空中断使能位,设置该位为 1,当 USART_SR 中的 TXE 位为1 时,将产生串口中断。 TCIE 为发送完成中断使能位,设置该位为 1,当 USART_SR 中的 TC位为 1 时,将产生串口中断。RXNEIE 为接收缓冲区非空中断使能,设置该位为 1,当 USART_SR中的 ORE 或者 RXNE 位为 1 时,将产生串口中断。 TE 为发送使能位,设置为 1,将开启串口的发送功能。 RE 为接收使能位,用法同 TE。其他位的设置,这里就不一一列出来了,《STM32 中文参考手册》有详细介绍,在这里我们就不列出来了。
5,数据发送与接收
STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDRRDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。该寄存器的各位描述如图 9.1.3 所示:
在这里插入图片描述
可以看出,虽然是一个 32 位寄存器,但是只用了低 9 位(DR[8: 0]),其他都是保留。DR[8: 0]为串口数据,包含了发送或接收的数据。由于它是由两个寄存器组成的,一个给发送用(TDR),一个给接收用(RDR),该寄存器兼具读和写的功能。 TDR 寄存器提供了内部总线和输出移位寄存器之间的并行接口。 RDR 寄存器提供了输入移位寄存器和内部总线之间的并行接口。当使能校验(USART_CR1 中 PCE 位被置位)进行发送时,写到 MSB 的值(根据数据的长度不同, MSB 是第 7 位或者第 8 位)会被后来的校验位取代。当使能校验位进行接收时,读到的 MSB 位是接收到的校验位。
6,串口状态
串口的状态可以通过状态寄存器 USART_SR 读取。 USART_SR 的各位描述如图 9.1.4 所示:
在这里插入图片描述
这里我们关注一下两个位,第 5、 6 位 RXNE 和 TC。RXNE(读数据寄存器非空),当该位被置 1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将
该位清零,也可以向该位写 0,直接清除。
TC(发送完成),当该位被置位的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式: 1)读 USART_SR,写USART_DR。 2)直接向该位写 0。

2、代码实现

//初始化 IO 串口 1
//pclk2:PCLK2 时钟频率(Mhz)
//bound:波特率
void uart_init(u32 pclk2,u32 bound)
{
	float temp;
	u16 mantissa;
	u16 fraction;
	temp=(float)(pclk2*1000000)/(bound*16);//得到 USARTDIV
	mantissa=temp;                         //得到整数部分
	fraction=(temp-mantissa)*16;           //得到小数部分
	mantissa<<=4;
	mantissa+=fraction;
	RCC->APB2ENR|=1<<2;                   //使能 PORTA 口时钟
	RCC->APB2ENR|=1<<14;                  //使能串口时钟
	GPIOA->CRH&=0XFFFFF00F;               //IO 状态设置
	GPIOA->CRH|=0X000008B0;               //IO 状态设置
	RCC->APB2RSTR|=1<<14;                 //复位串口 1
	RCC->APB2RSTR&=~(1<<14);              //停止复位
	//波特率设置
	USART1->BRR=mantissa;                 // 波特率设置
	USART1->CR1|=0X200C;                  //1 位停止,无校验位.
	#if EN_USART1_RX                      //如果使能了接收
	//使能接收中断
	USART1->CR1|=1<<5;                    //接收缓冲区非空中断使能
	MY_NVIC_Init(3,3,USART1_IRQn,2);      //组 2,最低优先级
	#endif
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

从该代码可以看出,其初始化串口的过程,和我们前面介绍的一致。 先计算得到
USART1->BRR 的内容。然后开始初始化串口引脚,然之后设置波特率和奇偶校验等。
这里需要注意一点,因为我们使用到了串口的中断接收,必须在 usart.h 里面设置EN_USART1_RX 为 1 (默认设置就是 1 的)。该函数才会配置中断使能,以及开启串口 1 的 NVIC
中断。这里我们把串口 1 中断放在组 2,优先级设置为组 2 里面的最低。串口 1 的中断服务函数 USART1_IRQHandler。
介绍完了这两个函数,我们回到 test.c,在 test.c 里面编写如下代码:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
int main(void)
{
	u16 t; u16 len; u16 times=0;
	Stm32_Clock_Init(9); //系统时钟设置
	uart_init(72,115200); //串口初始化为 115200
	delay_init(72); //延时初始化
	LED_Init(); //初始化与 LED 连接的硬件接口
	while(1)
	{
		if(USART_RX_STA&0x8000)
		{
			len=USART_RX_STA&0x3FFF;//得到此次接收到的数据长度
			printf("\r\n 您发送的消息为:\r\n\r\n");
			for(t=0;t<len;t++)
			{
				USART1->DR=USART_RX_BUF[t];
				while((USART1->SR&0X40)==0);//等待发送结束
			}
			printf("\r\n\r\n");//插入换行
			USART_RX_STA=0;
		}
		else
		{
			times++;
			if(times%5000==0)
			{
				printf("\r\n 精英 STM32 开发板 串口实验\r\n");
				printf("正点原子@ALIENTEK\r\n\r\n");
			}
		if(times%200==0)printf("请输入数据,以回车键结束\r\n");
		if(times%30==0)LED0=!LED0;//闪烁 LED,提示系统正在运行.
		delay_ms(10);
		}
	}
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

这段代码比较简单,重点看下以下两句:
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待发送结束
第一句,其实就是发送一个字节到串口,通过直接操作寄存器来实现的。第二句呢,就是我们在写了一个字节在 USART1->DR 之后,要检测这个数据是否已经被发送完成了,通过检测USART1->SR 的第 6 位,是否为 1 来决定是否可以开始第二个字节的发送。

文章来源: wlybsy.blog.csdn.net,作者:万里羊,版权归原作者所有,如需转载,请联系作者。

原文链接:wlybsy.blog.csdn.net/article/details/97624418

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200