深度理解STM32的串口实验(寄存器)【保姆级教程】

这篇具有很好参考价值的文章主要介绍了深度理解STM32的串口实验(寄存器)【保姆级教程】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

USART—通用同步异步收发接收器,是一个串行通信设备,可以和外部设备进行灵活的全双工数据交换,有别于USART还有一个UART(在原来的基础上裁剪掉了同步通信功能(时钟同步)),串行通信一般是以帧格式传输数据,一帧一帧的传。

协议层: 串口通信的一个数据包包含从起始信号开始,直到停止信号的结束

起始信号:一个逻辑0数据位表示。

停止信号:0.5,1,1.5或2个逻辑1的数据位表示。

0.5个停止位:智能卡模式下的接收数据时使用。

1个停止位:停止位的默认数值

1.5个停止位:智能卡模式下的手法数据和接收数据时使用

2个停止位:常规USART模式,单线模式以及调制解调器的模式。

有效数据的基本长度被约定为5,6,7,8.

奇偶检验(设置USART-CR1 的PS位)

偶检验:数据=00110101,里面数据1的个数位为偶数位,检验位置“0”,当数据检验和偶数相同的时候,证明没有出错,反之则错误

奇检验:数据 = 01110101,里面数据1的个数为奇检位,检验位置“1”,当数据检验和奇数相同,则证明没有出错,反之错误。

当然也会存在同时两个位一块出现错误,导致无法判断是否位奇偶检验的错误,但发生的概率很低。

下面这张图需要重点理解

深度理解STM32的串口实验(寄存器)【保姆级教程】

 下面是对代码的理解:

深度理解STM32的串口实验(寄存器)【保姆级教程】

 首先先看这个图,,可以看出USART_RX_STA类似与一个16位的寄存器,前14位存储的是数据,后面两个分别检测0X0D和0X0A。

接下里分析:

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
}

temp=(float)(pclk2*1000000)/(bound*16);这是一个计算公式,因为使能的是串口1,而串口1是在APB2ENR寄存器里面(其余串口均在寄存器APB1ENR里面),因为APB2的频率一般位72M,而APB1的频率一般位36M。

 所以这里的pclk2为72M,而bound是你需要设置的波特率。

USARTX-BRR:

深度理解STM32的串口实验(寄存器)【保姆级教程】

前四位为小数部分 ,后12位是整数部分,假设算出来的mantissa = 39.5,小数部分相当于把1分成了16份,所以相当于把0.5*16转化为二进制存入。

mantissa = temp的作用仅仅是:为了接下来将小数部分求出来

    fraction=(temp-mantissa)*16; //得到小数部分	 
    mantissa<<=4;

这两行代码是为将十进制的整数部分和小数部分,分别转化为16进制。然后存入到波特率寄存器里面。紧接着使能串口1和PORTA时钟(串口一对应的IO口是PA9,PA10,需要拿跳帽连接在一起).

然后将IO口置零,然后分别进行设置成一个输入一个输出,

USART1->CR1|=0X200C;     设置成使能串口8个字长1个停止位(USART_CR2中[13:12]默认为“0”)

MY_NVIC_Init(3,3,USART1_IRQn,2)

将其分在组2里面,此时的抢占优先级:响应优先级为 = 2:2,即(00-11)四种情况,而3:3的安排选择了组2优先级最小的一种情况。这样可以先执行上面的波特率赋值,以及串口使能等等操作,最后再进行这行代码运行。

接下来看下一部分:

u16 USART_RX_STA=0;       //接收状态标记	  
  
void USART1_IRQHandler(void)
{
	u8 res;	
#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntEnter();    
#endif
	if(USART1->SR&(1<<5))	//接收到数据
	{	 
		res=USART1->DR; 
		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;//接收数据错误,重新开始接收	  
				}		 
			}
		}  		 									     
	}

起始阶段: USART_RX_STA=0,对接受状态的标记。

先通过状态寄存器SR的RXNE是否为1,是1则接收到了数据,反之则没有。紧接这定义一个res变量来接收从数据寄存器的一个字节,然后此时USART_RX_STA为0,与0X8000进行&运算,结果为0,则未接受到,接着继续进行判断,0X4000进行与运算,看是否为0,也是判断是否接受道路0X0D,如果没有接受到,则将这个res变量存放在数组里面,此时的USART_RX_STA为 0 与0X3fff进行&运算,大家算算会发现,因为他的前14位是数据位,所以你会发现第一个变量就会存放在BUF[0]里面,大概逻辑是这样的:

深度理解STM32的串口实验(寄存器)【保姆级教程】

所以每个字节都会被存放到具体的数组位上 。

if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收

当数组越界的时候,则会重新开始。

接下来就会一直循环,当数据位存满后,接下来res里面接受的就是0X0D,先和上面一样判断USART_RX_STA是否接受到了0X0A和0X0D。

接着执行:

if(res==0x0d)USART_RX_STA|=0x4000;

USART_RX_STA的第十五位变为1,,接下来进行下一次循环,这一次res接受到的值为0X0A,

然后进行判断进入到

       if(USART_RX_STA&0x4000)//接收到了0x0d
			{
				if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
			}

所以执行USART_RX_STA|=0x8000,使得USART_RX_STA的第十六位变为1。

接下来看主函数部分:

int main(void)
{			
	u8 t;
	u8 len;	
	u16 times=0;  
	Stm32_Clock_Init(9); //系统时钟设置
	delay_init(72);	     //延时初始化 
	uart_init(72,9600);	 //串口初始化为9600 
	LED_Init();		  	 //初始化与LED连接的硬件接口    
	while(1)
	{
		if(USART_RX_STA&0x8000)
		{					   
			len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
			printf("\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\nALIENTEK MiniSTM32开发板 串口实验\r\n");
				printf("正点原子@ALIENTEK\r\n\r\n\r\n");
			}
			if(times%200==0)printf("请输入数据,以回车键结束\r\n");  
			if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
			delay_ms(10);   
		}
	}	 
}

if(USART_RX_STA&0x8000)                 判断是否接收到了0X0A

len=USART_RX_STA&0x3fff;举个简单的例子此时USART_RX_STA为1100000000000011和0X3fff进行&运算,得到的结果是3,自然就表示了当前数组的大小。

最后阶段,重点理解以下两行代码:

USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待发送结束

分析如下:将每个组内的信息存入到数据寄存器,此时数据寄存器将数据给TDR,发送信息的时候,是一位一位发送的,每一数据帧都有起始位,数据位,以及停止位,当检测到数据寄存器的细信息发送完了(完全给了TDR),此时状态寄存器的TXE便变为1,当检测到TXE为1后,TC也会变为1(系统自动进行)。所以第二行才会检测这个状态寄存器的第6位是否为1来判断是否发送成功了这个字节

由此推出,直接判断TXE也可以判断发送是否完成

所以代码可以改为:文章来源地址https://www.toymoban.com/news/detail-414559.html

        for(t=0;t<len;t++)
			{
				USART1->DR=USART_RX_BUF[t];
				while((USART1->SR&0X80)==0);//等待发送结束
			}

到了这里,关于深度理解STM32的串口实验(寄存器)【保姆级教程】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32寄存器点亮LED灯

     这个灯是 PB5引脚 看原理图可以看出 让GPIOB5输出低电平 就能点亮 那么我们得让 打开控制GPIOB5的时钟 让GPIOB5 输出模式 让GPIOB5低电平 时钟就相当于一个阀门开关,或者理解成水龙头,你打开了 才有水出来,也就是你打开才能工作。 看中文参考手册 系统架构 找到你的GPIO

    2024年02月07日
    浏览(48)
  • STM32 入门 —— 寄存器与 GPIO

    STM32 总线构图: 什么是寄存器 根据百度百科介绍,寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。简单来说,寄存器就是存放东西的东西,存放的东西是指令、数据或地址 存放数据的寄存器最容易理解,不同

    2024年02月04日
    浏览(47)
  • STM32 寄存器配置笔记——GPIO配置输出

           本文主要介绍GPIO 作为输出时的寄存器配置。包括时钟配置,输出模式配置。以STM32F10xxx系列为例,配置PA8、PD2端口作为输出,输出高/低电平。         1)GPIO外设时钟          通过查找STM32F10xxx中文参考手册得知,GPIO PORT口的时钟配置在RCC_APB2ENR寄存器的第2~6位,

    2024年01月24日
    浏览(67)
  • STM32存储器映射以及寄存器映射

    目录 1.STM32的寻址范围? 2.存储器映射 3.存储器功能划分(以STM32F1为例) 4.寄存器映射 1.32位单片机有32根地址线,每根地址线有两种状态,导通或者不导通。 2.单片机内存地址访问的存储单元是按字节编址的,而不是bit。 比如我们要访问0x01这个地址,访问的其实是一个字节的空

    2024年04月23日
    浏览(46)
  • STM32的GPIO操作(寄存器&HAL)

    (注:此为乐某学习记录,若有出错的地方欢迎各位指出!) 本人使用的开发板的芯片是STM32H743XIH6,所以外设也是根据此芯片来介绍。 在使用HAL库配置的时候,需要安装STM32CubeMX。此软件可在ST官网下载,剩下的安装步骤比较简单,不会单独出篇进行讲解。 STM32CubeMX下载网址

    2024年02月04日
    浏览(50)
  • 【STM32调试】寄存器调试不良问题记录持续版

    记录一些stm32调试过程中:不易被理解、存在使用误区、不清不楚、是坑、使用常识等方面的一些记录。本记录只包含stm32的内核以及外设等寄存器的调试,不包含业务方面。 使用低功耗功能前是否需要打开PWR时钟? 是需要先打开PWR时钟的,否则无法唤醒。 睡眠模式、停机模

    2024年01月18日
    浏览(56)
  • keil5 查看stm32 寄存器的值

    1 查看芯片内部寄存器的值,首先是在仿真状态下,首先仿真,程序运行。 2 点击菜单栏的 View - System viewer ,右侧便会出现芯片的所有寄存器(如果没有,需要添加),点击要查看的寄存器,便会出现在侧边栏上 可以查看值,也可以直接设置寄存器的值。 3 如果没有出现,如下

    2024年01月24日
    浏览(49)
  • STM32微机系统框架、内存、存储器、寄存器

    大家好,我是 杰哥嵌入式开发 最近在出定时器系列, 但是线下班有一些学生在学习完C语言之后, 在51接触各种寄存器和对软件代码各种操作是如何在单片机系统中起到作用的感到非常的不解, 经过我的初步分析,是对嵌入式微机系统的大概雏形系统框架不熟悉导致。 所以

    2024年02月06日
    浏览(55)
  • STM32 寄存器操作 GPIO 与下降沿中断

    寄存器本质就是一个开关,当我们把芯片寄存器配置指定的状态时即可使用芯片的硬件能力。 寄存器映射表则是开关的地址说明。对于我们希望点亮 GPIO_B 的一个灯来说,需要关注以下的两个寄存器:   对于我们实现希望点亮一个灯的需求来说,不仅需要配置配置 GPIO_B 的时

    2024年02月20日
    浏览(51)
  • 【STM32 CubeMX】HAL库的本质读写寄存器

    在嵌入式系统开发中,HAL(Hardware Abstraction Layer)库是一个重要的概念,它提供了一个抽象层,使开发者可以更容易地编写可移植的代码,而不必担心底层硬件的细节。STM32CubeMX是一款由STMicroelectronics提供的工具,用于生成STM32微控制器的初始化代码,其中包括了HAL库的使用。

    2024年02月20日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包