STM32笔记17--串口通信实验讲解/STM32笔记18--PWM输出实验

1、串口配置的一般步骤(回顾上一讲)

2、串口通信代码讲解

本节参考资料:《STM32开发指南--库函数版本》5.3、USART串口文件夹介绍;9、串口实验

1.1、复习串口配置中常用的函数

1.2、串口配置的一般步骤

注意: 第一步,由于串口属于STM32的外设,因此需要使能时钟,并且由于串口一用到的引脚PA9和PA10,因此也要使能。

第三步初始化GPIO,设置GPIO的输入输出模式,这里需要对应参考手册并结合实际情况来设置。(具体见中文参考手册第八章--8.1.11的表格)如下所示:

 第四部初始化相关串口的参数,包括:波特率,停止位,传输字节长度等等;

需要注意的是,串口通信一般都需要设置中断,这里就需要设置中断优先级,中断分组等内容;

2、串口通信实验讲解(与上一节有一些不同,总体类似) 

3、PWM输出实验

什么是PWM?PWM是脉冲调制,是一种微控制器的数字输出对模拟电路进行控制的有效手段,广泛应用在控制和变换的领域。

什么是PWM频率?指的是信号在1秒内(或者说一个周期内)从高电平到低电平再到高电平的次数。

什么是占空比?指的是在一个周期的时间内,高电平所占时间 / 周期总时间

3.1、单片机PWM

以STM32单片机为例,由于GPIO口只能输出数字信号,也就是只能输出高电平和低电平。如果需要控制IO口输出模拟电压,就需要用到PWM改变IO口的方波占空比,从而将IO口输出的数字信号模拟成模拟信号。

PS:假设单片机IO口输出的高电平为5V,低电平为0V。如果说IO口输出高电平相当于与直流电源连接,IO口输出低电平相当于是与直流电源断开。因此,只需要通过控制IO口与电源连接和断开的时间就可以控制IO口输出任意的电压值(0~5V),如下三个例子:

假设控制高电平时间(也就是上文所说的IO口与直流电源连接的时间)占50%,低电平时间(也就是上文所说IO口与直流电源断开的时间)占50%———换言之,占空比为50%,这时理论上IO口就会输出2.5V的模拟电压。

假设控制高电平时间为75%,也就是占空比为75%,这时理论上IO口就会输出3.75V的模拟电压。

假设控制高电平时间为20%,也就是占空比为20%,这时理论上IO口就会输出1V的模拟电压。

总结:单片机的PWM就是通过对占空比的调节来实现对IO口模拟电压数值的控制,这也是单片机的D/A转换。

3.2、单片机中PWM的经典应用——呼吸灯

根据CSDN的数据,当LED灯在1秒内,0.5秒高电平,0.5秒低电平,就会使人眼感到闪烁。

当LED灯在10毫秒内,5毫秒高电平,5毫秒低电平,就会使得LED灯还没有完全亮就熄灭了,会在人眼产生呼吸灯的效果。(频率为100HZ)

也就是说,要想产生呼吸灯的效果,首先要求频率够高(最好不低于100HZ),其次,需要利用PWM控制占空比。

要想实现这一效果,需要用到STM32中的通用定时器功能。

4、通用定时器原理

首先,需要了解的背景知识是:STM32有三种定时器,分别是:通用定时器,高级定时器,基本定时器,其中,无论是高级定时器还是基本定时器,其基本原理与通用定时器都很类似,这里优先学习通用定时器。

参考资料:《STM32开发指南--库函数版本》13、14、15章 或者,《STM32中文参考手册》第14章、通用定时器;

4.1、三种定时器的区别

 在STM32F103ZET6中:TIME1和TIME8为高级定时器,TIME2~TIME5为通用定时器,TIME和TIME7为基本定时器。三种定时器区别如下所示:

4.2、通用定时器的特点(TIME2~TIME5)

(1)通用定时器位于低速的APB1总线;

(2)16位向上、向下、中心对齐的计数方式,自动装载计数器(可以自动重新装载);

(3)16位可编程(可实时编程修改的)预分频器,计数器时钟频率分频的系数在1~65535之间的           任意数值;

(4)4个独立通道:TIMx_CH1~4,可以用作:输入比较、输出捕获、PWM生成、单脉冲模式输             出;

(5)可以用外部信号控制定时器与定时器互连,用一个定时器控制另一个定时器;

PS:以下情况可能产生中断:

通用定时器在STM32中除了最基本的定时和计数外,主要应用在两个方面:一是测量输入的脉冲长度,二是产生PWM波形; 

另外,每个定时器是相互独立的,不共享任何资源。

PS:定时器三种计数方式的各自特点(向上、向下、中心对齐)

所谓的中心对齐,简单来说就是从0到自动装载值,再从自动装载值回到0,周而复始,如下所示:

4.3、通用定时器工作过程(重点)

如下是定时器的整体控制框图,较为复杂,分段理解比较好;

 (1)下图中红框部分为时钟发生部分。作用就是产生时钟送至预分频处理:

根据上图可知,时钟产生部分的时钟源来自于内部时钟,也就是来自于APB1总线时钟的一个倍频。  第二种时钟来源是ETR外部引脚的输入(这里的ETR可以在数据手册查看对应的具体是哪个引脚,另外ETR作为时钟源主要是指:TIM2、TIM3、TIM4三个定时器,至于TIM5是没有这个功能的)  第三种时钟来源是ITR0~ITR3,这里指的是其他定时器的级联;

总结:在实际应用中,最为重要也是最为常见的还是来源于APB1总线倍频的时钟源,这种时钟源既简单,又准确;同时在上图中的触发控制器里可以配置定时器的计数方式是向上还是向下或者中心对齐。

(2)下图中为分频部分:

 这一部分也被称为时基单元,在时基单元中,从时钟产生部分的时钟先经过预分频器的分频之后产生的才是定时器的计数时钟,如下图箭头处所示:

 (3)下图为输入捕获部分:

 输入捕获的具体工作流程(简单地举个例子):假设将输入捕获引脚TMx_CH1设置为上升沿捕获,那么在捕获到上升沿,也可以理解为被信号的上升沿触发,那么就会记一次计数器的值,每触发一次就记一次数,就可以计算出信号脉冲的宽度。每一个定时器都有4个独立的捕获通道。这四个通道,要么是输入捕获,要么是输出比较,只能二选一。(上图是为了方便表述)

(4)下图是输出比较部分

 输出比较部分的应用场景和工作流程举例说明为:假设在CNT计数器以向下的计数方式从初值100计数到0,此时在捕获/比较计数器设置比较数值为50,当计数器的值>50,那么就控制相应的输出比较引脚输出高电平,如果计数器的值<50,就控制相应的输出比较引脚输出低电平,由于计数器在不停地计数,那么输出比较引脚输出的就是脉冲波形,通过改变比较计数器的数值就可以改变输出波形的占空比;而调整脉冲的周期则是调整自动装载值。

5、定时器中断实验

参考资料:《STM32开发指南--库函数版》第13章、定时器中断实验

《STM32中文参考手册》第14章、通用定时器

5.1、通用定时器回顾

以通用定时器工作流程为例,首先是选择APB1总线时钟作为定时器的时钟产生部分,其次是设置时基单元部分的预分频、计数器的值和自动装载值并且需要设置计数方式。

那么APB1时钟的时钟源是从何而来,又是怎样成为计数器的时钟源呢?如下图所示:

 

 具体流程是:AHB时钟经过预分频(除以某个系数)得到APB1总线时钟,APB1总线时钟要么*1要么*2得到CK_INT时钟,那么具体是乘以1还是乘以2如下图所示:

 其中,上图CK_INT、CK_PSC、CK_CNT的关系如下图所示(也就是通用定时器的总体框图):

5.2、定时器实验的相关寄存器

(1)计数器的值---计数器当前值寄存器CNT

 (2)预分频寄存器

(3)自动重装载值寄存器

(4)控制寄存器1

 控制寄存器1中位4用于设置计数方式,位0用于使能计数器

 (5)DMA中断使能寄存器

5.3、常用的库函数

为操作在5.2节中提及的常见寄存器,需要学习常用的库函数

接下来,先学习定时器中断的步骤:

(1)使能定时器时钟;

(2)初始化定时器,配置ARR、PSC;

(3)开启定时器中断,配置NVIC;

(4) 使能定时器;

(5)编写中断服务函数;

5.4、编写简单的定时器实验

定时器中断实验的要求:通过定时器中断配置,实现每500ms中断一次,并且中断服务控制函数控制LED实现状态取反(即LED灯闪烁)

PS:溢出时间的计算:

Tout(溢出时间)=(ARR+1){(PSC+1)/Tclk}

上式中,TCLK为APB1总线的时钟,PSC为预分频器,因此(PSC+1)/Tclk 就是定时器一个周期的时间长度,ARR则是自动重装载寄存器,为实现500ms一次的中断,应设置ARR+1=5000,也就是ARR=4999;

这次所编写的简单的定时器实验,建立在跑马灯实验的基础上,因此在跑马灯实验的HARDWARE文件夹下新建文件TIMER(名字自定义)

打开Keil5后,首先是添加源文件到分组,如下所示:

随后,点击魔术棒添加路径,如下所示:

 接着,需要将库里的源文件添加到FWLIB,如下:

 

 至此,需要添加的源文件和路径都已完毕,在编写源文件和主程序之前,再回顾一下所要求的现象:每隔500ms让LED1灯翻转一次,

(1)timer.h的程序:

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h" //sys.h中包含有顶层头文件,直接引用sys.h就可以


void TIM3_Int_Init(u16 arr,u16 psc); //定义一个函数,命名为“定时器3中断初始化函数”,其中包括arr和psc两个16位参数

#endif  //预编译,具体原理在跑马灯章节中有解释;

(2)timer.c中的程序:

#include "timer.h"
#include "led.h"
void TIM3_Int_Init(u16 arr,u16 psc) //在timer.h中具体定义该函数
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; //定义结构体变量,并将变量地址赋给下面的函数
  NVIC_InitTypeDef NVIC_InitStructure; //定义中断优先级的结构体变量
 
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //使能定时器3的时钟
 //配置结构体中各变量
  TIM_TimeBaseInitStrue.TIM_Period=arr; //由系统自动装载,因此是arr
  TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //分频系数由psc预分频器设置,因此是psc
  TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //设置为向上计数模式
  TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //与本次实验无关,随便选择一个有效值

  TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStrue); //初始化定时器

  TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //使能定时器3的更新中断
 //中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器

  TIM_Cmd(TIM3,ENABLE); //使能定时器3

}

void TIM3_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //判断是否是由定时器3产生的更新中断
 {
  LED1=!LED1; //如果是由定时器3产生的更新中断,就设置LED1灯状态翻转
  TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //手动清除标志位
 }


}

(3)主程序:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"

 int main(void)
 {	
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组为2
	 delay_init(); //初始化延时函数
	 LED_Init(); //初始化LED函数
   TIM3_Int_Init(4999,7199); //注意我们自己定义的函数格式void TIM3_Int_Init(u16 arr,u16 psc)此时为500ms的溢出时间
   while(1); // while死循环
 }

6、PWM输出实验

参考资料:《STM32开发指南--库函数》第14章、PWM输出实验

5.1、通用定时器PWM概述

(2)STM32中的PWM工作过程:假设以向上计数方式为例,计数器从0开始计数,自动重装载值ARR设为100,也就是计数器从0计数到100后产生中断,再循环计数,此时,设置比较寄存器CCR为50,那么当计数器计数的值CNT小于50时,IO口输出低电平,当计数器值大于50时,IO口输出高电平,由于计数器在一直计数,那么就可以循环往复地产生高低电平,在这个例子中占空比为50%,假设最高电平为5V,那么通过PWM的配置,可以模拟产生2.5V的模拟电压,也就是实现了数模转换。

此时,脉宽调制信号的周期是由ARR决定的,当然也和定时器的时钟有关;调制信号的占空比是由CCR决定的;如图所示:

 PS:PWM模式1和PWM模式2的区别:PWM模式1下,无论是向上计数还是向下计数方式,当计数器的值CNT<捕获比较寄存器的值ARR,则输出有效电平(只是有效电平,并没有说是高电平有效还是低电平有效至于是高电平有效还是低电平有效,需要在CCER:CC1P寄存器中设置,该寄存器设0,则高电平有效,该寄存器设1,则低电平有效)

另外,在设置PWM中,还需要使能ARPE(自动重装载的预装载寄存器),ARPE的作用是,在计数器计数的过程中,如果我们更新了ARR的值,当ARPE使能时,ARR寄存器在这个比较周期立即生效,如果ARPE不使能,则ARR寄存器在下个比较周期生效.

(2)通用定时器3的输出通道引脚(TIM3)

5.2、PWM输出库函数的概述(下图只需要看红色部分)

上图中各函数初始化或者使能的格式如下:

5.3、PWM实验配置呼吸灯

要求:使用定时器3的PWM功能,输出占空比可变的PWM波,用于驱动LED灯,实现LED灯由暗变亮,由亮变暗的循环过程。 

配置步骤:

 首先在头文件中定义函数

void TIM3_PWM_Init(u16 arr,u16 psc); //定义PWM的初始化函数 

接着在源文件中写出这个函数

void TIM3_PWM_Init(u16 arr,u16 psc) 
{
  GPIO_InitTypeDef  GPIO_InitStructure; //定义结构体变量
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure; //定义定时器初始化的结构体
	TIM_OCInitTypeDef TIM_OCInitTypeStructure; //定义结构体变量
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE); //使能GPIOB时钟和AFIO复用时钟
  
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB.5 端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 		 //推挽复用输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5

	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //定时器3的部分重映射,此时通道1对应PB4,通道2对应PB5
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
  
	TIM_OCInitTypeStructure.TIM_OCMode= TIM_OCMode_PWM2; //设置PWM模式2
	TIM_OCInitTypeStructure.TIM_OCPolarity= TIM_OCPolarity_High; //设置极性为高
	TIM_OCInitTypeStructure.TIM_OutputState= TIM_OutputState_Enable;
  TIM_OC2Init(TIM3,&TIM_OCInitTypeStructure);

  TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能预装载寄存器
  TIM_Cmd(TIM3,ENABLE); //使能定时器3

}

主程序代码

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"

 
int main(void)
 {		
 	u16 led0pwmval=0;
	u8 dir=1;	
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化
 	TIM3_PWM_Init(899,0);	 //不分频。PWM频率=72000000/900=80Khz
   	while(1)
	{
 		delay_ms(10);	 
		if(dir)led0pwmval++;
		else led0pwmval--;

 		if(led0pwmval>300)dir=0;
		if(led0pwmval==0)dir=1;										 
		TIM_SetCompare2(TIM3,led0pwmval);		   
	}	 

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32是由意法半导体(STMicroelectronics)公司推出的基于ARM Cortex-M内核的高性能、低功耗、高性价比32位微控制器系列。自其面世以来,STM32凭借其广泛的适用性和卓越的特性,已成为嵌入式系统设计领域的主流选择之一,广泛应用于工业控制、消费电子、物联网、汽车电子、医疗设备、智能家居等多个领域。 内核与架构 STM32产品线采用了不同版本的ARM Cortex-M内核,包括M0、M0+、M3、M4、M7等,分别对应不同级别的性能需求。这些内核提供单周期乘法、硬件除法、DSP指令集、浮点单元(FPU)等功能,以满足不同应用场景中的计算密集型任务需求。处理器架构遵循哈佛结构,具有独立的指令总线和数据总线,确保高效的代码执行和数据访问。 丰富的外设与接口 STM32微控制器集成了丰富的外设资源,以适应各种复杂系统设计。这些外设包括但不限于: 通信接口:如USART、UART、SPI、I2C、CAN、USB(全速/高速)、Ethernet、无线连接模块(如BLE、Wi-Fi)等,用于实现设备间的串行通信和网络连接。 定时器:多种通用定时器、高级定时器、基本定时器以及PWM输出,支持定时、计数、脉冲捕获、电机控制等多种功能。 模拟外设:高精度ADC(模数转换器)、DAC(数模转换器)、比较器、温度传感器等,用于采集和处理模拟信号。 存储器:内置Flash和SRAM,容量从几KB到几MB不等,满足不同应用的数据存储和运行空间需求。部分型号还支持外部存储器接口(如FSMC、Octo-SPI)以扩展存储能力。 安全与保护机制:如加密加速器、安全单元、内存保护单元(MPU)、看门狗定时器、时钟安全系统(CSS)等,保障系统安全稳定运行。 开发环境与生态系统 STM32拥有强大的软件支持和生态系统,简化开发流程并加速产品上市时间: 开发工具:官方提供STM32CubeMX初始化配置工具,帮助开发者快速进行项目设置、外设配置及代码生成。此外,还有STM32CubeIDE集成开发环境,集成了编译器、调试器和仿真器支持。 软件库:STM32Cube软件包包含HAL(硬件抽象层)库和LL(低层)库,前者提供跨平台、跨系列的统一API接口,后者直接面向寄存器提供高效访问。同时,还提供各类外设驱动、中间件组件(如FreeRTOS、FatFS、LwIP等)以及特定应用框架(如STM32Cube.AI for AI推理)。 社区与资源:ST官方社区、论坛、博客、技术文档、培训材料、应用笔记、用户案例等资源丰富,为开发者提供全方位的技术支持和交流平台。 产品线与封装 STM32产品线按性能、功耗、外设组合等特性划分为多个子系列,如STM32F、STM32L、STM32G、STM32H等,每个子系列下又包含多种型号,以适应不同成本、性能、尺寸和功耗要求。封装形式多样,从小型QFN、LQFP到大型BGA,满足不同应用场景的封装密度和散热需求。 综上所述,STM32微控制器以其强大的内核性能、丰富的外设集成、完善的开发支持和广泛的市场应用,为嵌入式系统设计提供了高度灵活且极具竞争力的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值