STM32F407HAL库-11.定时器

定时器(Timer)最基本的功能就是定时了,比如定时发送 USART 数据、定时采集 AD 数据等等。如果把定时器与 GPIO 结合起来使用的话可以实现非常丰富的功能,可以测量输入信号的脉冲宽度,可以生产输出波形。定时器生产 PWM 控制电机状态是工业控制普遍方法,这方面知识非常有必要深入了解。

STM32F4xx系列控制器有2个高级控制定时器、10个通用定时器和2个基本定时器,还有两个看门狗定时器。看门狗定时器会在后面实验讲解。控制器上所有定时器都是彼此独立的,不共享任何资源。各个定时器特性请参考下表:

 

实际上,就功能上来说通用定时器包含所有基本定时器功能,而高级控制定时器包含通用定时器所有功能。

高级控制定时器时基单元包括一个16位自动重载计数器ARR,一个16位的计数器CNT,可向上/下计数,一个16位可编程预分频器PSC,预分频器时钟源有多种可选,有内部时钟和外部时钟,还有一个8位的重复计数器RCR。

高级控制定时器功能框图包含了高级控制定时器最核心内容,掌握了功能框图,对高级控制定时器就有一个整体的把握,在编程时思路就非常清晰,如下图:

 1、①时钟源

高级控制定时器有4个时钟源可选:

  • A、内部时钟源CK_INT
  • B、外部时钟模式1:外部输入引脚TIx(x=1,2,3,4)
  • C、外部时钟模式2:外部触发输入ETR
  • D、内部触发输入

A:内部时钟源

内部时钟CK_INT即来自于芯片内部,等于168M,一般情况下,我们都是使用内部时钟。当从模式控制寄存器 TIMx_SMCR 的 SMS 位等于 000 时,则使用内部时钟。

B:外部时钟模式1

①:时钟信号输入引脚

当使用外部时钟模式1 的时候,时钟信号来自于定时器的输入通道,总共有4个,分别为 TI1/2/3/4,即TIMx_CH1/2/3/4。具体使用哪一路信号,由 TIM_CCMx 的位CCxS[1:0] 配置,其中 CCM1 控制 TI1/2,CCM2 控制 TI3/4。

②:滤波器

如果来自外部的时钟信号的频率过高或者混杂有高频干扰信号的话,我们就需要使用滤波器对 ETRP 信号重新采样,来达到降频或者去除高频干扰的目的,具体的由 TIMx_CCMx 的位 ICxF[3:0]配置。

③:边沿检测

边沿检测的信号来自于滤波器的输出,在成为触发信号之前,需要进行边沿检测,决定是上升沿有效还是下降沿有效,具体的由 TIMx_CCER 的位 CCxP 和 CCxNP 配置。

④:从模式选择

选定了触发源信号后,最后我们需把信号连接到 TRGI 引脚,让触发信号成为外部时钟模式 1 的输入,最终等于 CK_PSC,然后驱动计数器 CNT 计数。具体的配置 TIMx_SMCR 的位 SMS[2:0]为 000 即可选择外部时钟模式 1。

⑤:使能计数器

经过上面的 5个步骤之后,最后我们只需使能计数器开始计数,外部时钟模式 1 的配置就算完成。使能计数器由 TIMx_CR1 的位 CEN 配置。

C:外部时钟模式2

 ①:时钟信号输入引脚

当使用外部时钟模式2的时候,时钟信号来自于定时器的特定输入通道 TIMx_ETR,只有 1 个。

②:外部触发极性

来自 ETR 引脚输入的信号可以选择为上升沿或者下降沿有效,具体的由 TIMx_SMCR 的位 ETP 配置。

③:外部触发预分频器

由于 ETRP 的信号的频率不能超过 TIMx_CLK(180M)的1/4,当触发信号的频率很高的情况下,就必须使用分频器来降频,具体的由 TIMx_SMCR 的位ETPS[1:0]配置。

④:滤波器

如果 ETRP 的信号的频率过高或者混杂有高频干扰信号的话,我们就需要使用滤波器对 ETRP 信号重新采样,来达到降频或者去除高频干扰的目的。具体的由TIMx_SMCR 的位 ETF[3:0]配置,其中的fDTS是由内部时钟 CK_INT分频得到,具体的由TIMx_CR1的位 CKD[1:0]配置。

⑤:从模式选择

经过滤波器滤波的信号连接到 ETRF引脚后,触发信号成为外部时钟模式 2 的输入,最终等于 CK_PSC,然后驱动计数器 CNT 计数。具体的配置 TIMx_SMCR 的位 ECE 为 1 即可选择外部时钟模式 2。

⑥:使能计数器

经过上面的5个步骤之后,最后我们只需使能计数器开始计数,外部时钟模式 2 的配置就算完成。使能计数器由 TIMx_CR1 的位 CEN 配置。

D:内部触发输入

内部触发输入是使用一个定时器作为另一个定时器的预分频器。硬件上高级控制定时器和通用定时器在内部连接在一起,可以实现定时器同步或级联。主模式的定时器可以对从模式定时器执行复位、启动、停止或提供时钟。高级控制定时器和部分通用定时器 (TIM2至 TIM5)可以设置为主模式或从模式,TIM9 和 TIM10 可设置为从模式。

下图为主模式定时器(TIM1)为从模式定时器(TIM2)提供时钟,即TIM1用作TIM2的预分频器。

 2、②控制器

高级控制定时器控制器部分包括触发控制器、从模式控制器以及编码器接口。触发控制器用来针对片内外设输出触发信号,比如为其它定时器提供时钟和触发 DAC/ADC 转换。编码器接口专门针对编码器计数而设计。从模式控制器可以控制计数器复位、启动、递增/递减、计数。

3、③时基单元

 ①:预分频器PSC

预分频器 PSC,有一个输入时钟 CK_PSC 和一个输出时钟 CK_CNT。输入时钟 CK_PSC 就是上面时钟源的输出,输出CK_CNT 则用来驱动计数器 CNT 计数。通过设置预分频器 PSC 的值可以得到不同的 CK_CNT,实际计算为:fCK_CNT等于 fCK_PSC/(PSC[15:0]+1),可以实现 1 至 65536 分频。

②:计数器CNT

高级控制定时器的计数器有三种计数模式,分别为递增计数模式、递减计数模式和递增/递减(中心对齐)计数模式。

  • 递增计数模式下,计数器从0开始计数,每来一个CK_CNT 脉冲计数器就增加1,直到计数器的值与自动重载寄存器 ARR 值相等,然后计数器又从 0 开始计数并生成计数器上溢事件,计数器总是如此循环计数。如果禁用重复计数器,在计数器生成上溢事件就马上生成更新事件(UEV);如果使能重复计数器,每生成一次上溢事件重复计数器内容就减 1,直到重复计数器内容为 0 时才会生成更新事件。
  • 递减计数模式下,计数器从自动重载寄存器 ARR 值开始计数,每来一个CK_CNT 脉 冲计数器就减 1,直到计数器值为 0,然后计数器又从自动重载寄存器 ARR 值开始递减计数并生成计数器下溢事件,计数器总是如此循环计数。如果禁用重复计数器,在计数器生成下溢事件就马上生成更新事件;如果使能重复计数器,每生成一次下溢事件重复计数器内容就减 1,直到重复计数器内容为 0 时才会生成更新事件。
  • 中心对齐模式下,计数器从 0 开始递增计数,直到计数值等于(ARR-1)值生成计数器上溢事件,然后从 ARR 值开始递减计数直到 1 生成计数器下溢事件。然后又从 0 开始计数,如此循环。每次发生计数器上溢和下溢事件都会生成更新事件。

③:自动重载寄存器ARR

自动重载寄存器 ARR 用来存放与计数器 CNT 比较的值,如果两个值相等就递减重复计数器。可以通过 TIMx_CR1 寄存器的 ARPE 位控制自动重载影子寄存器功能,如果 ARPE 位置 1,自动重载影子寄存器有效,在事件更新时才把 TIMx_ARR 值赋给影子寄存器。如果 ARPE 位为 0,则将TIMx_ARR 值永久的赋给影子寄存器。

④:重复计数器RCR

在基本/通用定时器发生上/下溢事件时直接就生成更新事件,但对于高级控制定时器却不是这样,高级控制定时器在硬件结构上多出了重复计数器,在定时器发生上溢或下溢事件是递减重复计数器的值,只有当重复计数器为 0 时才会生成更新事件。在发生 N+1 个上溢或下溢事件(N 为 RCR 的值)时产生更新事件。

4、④输入捕获

 

输入捕获可以对信号的上升沿,下降沿或者双边沿进行捕获,常用的有测量输入信号的脉宽和测量PWM输入信号的频率和占空比这两种。

输入捕获的大概原理就是,当捕获到信号的跳边沿的时候,把计数器CNT的值锁存到捕获寄存器CCR中,把前后两次捕获到的CCR寄存器中的值相减,就可以算出脉宽或者频率。如果捕获的脉宽的时间长度超过你的捕获定时器的周期,就会发生溢出,这个我们需要做额外的处理。

①:输入通道

需要被测量的信号从定时器的外部引脚 TIMx_CH1/2/3/4 进入,通常叫 TI1/2/3/4,在后 面的捕获讲解中对于要被测量的信号我们都以 TIx 为标准叫法。

②:输入滤波器和边沿检测器

当输入的信号存在高频干扰的时候,我们需要对输入信号进行滤波,即进行重新采样, 根据采样定律,采样的频率必须大于等于两倍的输入信号。比如输入的信号为 1M,又存在高频的信号干扰,那么此时就很有必要进行滤波,我们可以设置采样频率为 2M,这样可以在保证采样到有效信号的基础上把高于 2M 的高频干扰信号过滤掉。

滤波器的配置由 CR1 寄存器的位 CKD[1:0]和 CCMR1/2 的位 ICxF[3:0]控制。从 ICxF 位的描述可知,采样频率 fSAMPLE可以由 fCK_INT和 fDTS分频后的时钟提供,其中是 fCK_INT内部时钟,fDTS 是 fCK_INT 经过分频后得到的频率,分频因子由 CKD[1:0]决定,可以是不分频, 2 分频或者是 4 分频。

边沿检测器用来设置信号在捕获的时候是什么边沿有效,可以是上升沿,下降沿,或者是双边沿,具体的由 CCER 寄存器的位 CCxP 和 CCxNP 决定。

③:捕获通道

捕获通道就是图中的 IC1/2/3/4,每个捕获通道都有相对应的捕获寄存器 CCR1/2/3/4,当发生捕获的时候,计数器 CNT的值就会被锁存到捕获寄存器中。

这里我们要搞清楚输入通道和捕获通道的区别,输入通道是用来输入信号的,捕获通道是用来捕获输入信号的通道,一个输入通道的信号可以同时输入给两个捕获通道。比如输入通道 TI1 的信号经过滤波边沿检测器之后的 TI1FP1 和 TI1FP2 可以进入到捕获通道 IC1 和 IC2,其实这就是我们后面要讲的 PWM 输入捕获,只有一路输入信号(TI1)却占用了两个捕获通道(IC1 和 IC2)。当只需要测量输入信号的脉宽时候,用一个捕获通道即可。输入通道和捕获通道的映射关系具体由寄存器 CCMRx 的位 CCxS[1:0]配置。

④:预分频器

ICx的输出信号会经过一个预分频器,用于决定发生多少个事件时进行一次捕获。具体的由寄存器 CCMRx 的位 ICxPSC 配置,如果希望捕获信号的每一个边沿,则不分频。

⑤:捕获寄存器

经过预分频器的信号 ICxPS 是最终被捕获的信号,当发生捕获时(第一次),计数器 CNT 的值会被锁存到捕获寄存器 CCR 中,还会产生 CCxI 中断,相应的中断位 CCxIF(在 SR 寄存器中)会被置位,通过软件或者读取 CCR 中的值可以将 CCxIF 清 0。如果发生第二次捕获(即重复捕获:CCR 寄存器中已捕获到计数器值且 CCxIF 标志已置 1),则捕获溢出标志位 CCxOF(在 SR 寄存器中)会被置位,CCxOF 只能通过软件清零。

5、⑤输出比较

 输出比较就是通过定时器的外部引脚对外输出控制信号,有冻结、将通过X(x=1,2,3,4)设置为匹配时输出有效电平、将通道X设置为匹配时输出无效电平、翻转、强制变为无效电平、强制变为有效电平、PWM1和PWM2这八种模式,具体使用哪种模式由寄存器CCMRx的位OCxM[2:0]配置。其中PWM模式是输出比较中的特例,使用的也最多。

①:比较寄存器

当计数器 CNT 的值跟比较寄存器 CCR 的值相等的时候,输出参考信号 OCxREF 的信号的极性就会改变,其中OCxREF=1(高电平)称之为有效电平,OCxREF=0(低电平)称之为无效电平,并且会产生比较中断 CCxI,相应的标志位 CCxIF(SR 寄存器中)会置位。然后 OCxREF 再经过一系列的控制之后就成为真正的输出信号 OCx/OCxN。

②:死区发生器

在生成的参考波形 OCxREF 的基础上,可以插入死区时间,用于生成两路互补的输出信号 OCx 和 OCxN,死区时间的大小具体由 BDTR 寄存器的位 DTG[7:0]配置。死区时间的大小必须根据与输出信号相连接的器件及其特性来调整。下图显示了死区时间的输出信号与参考信号OCxREF之间的关系。

③:输出控制

 

在输出比较的输出控制中,参考信号OCxREF 在经过死区发生器之后会产生两路带死区的互补信号 OCx_DT 和 OCxN_DT(通道 1~3 才有互补信号,通道 4 没有,其余跟通道 1~3 一样),这两路带死区的互补信号然后就进入输出控制电路,如果没有加入死区控制, 那么进入输出控制电路的信号就直接是 OCxREF。

进入输出控制电路的信号会被分成两路,一路是原始信号,一路是被反向的信号,具体的由寄存器 CCER 的位 CCxP 和 CCxNP 控制。经过极性选择的信号是否由 OCx 引脚输出到外部引脚 CHx/CHxN 则由寄存器 CCER 的位 CxE/CxNE 配置。

④:输出引脚

输出比较的输出信号最终是通过定时器的外部 IO 来输出的,分别为 CH1/2/3/4,其中前面三个通道还有互补的输出通道 CH1/2/3N。更加详细的 IO 说明还请查阅相关的数据手册。

6、⑥短路功能

断路功能就是电机控制的刹车功能,使能断路功能时,根据相关控制位状态修改输出信

号电平。在任何情况下,OCx 和 OCxN 输出都不能同时为有效电平,这关系到电机控制常用的 H 桥电路结构原因。

断路源可以是时钟故障事件,由内部复位时钟控制器中的时钟安全系统(CSS)生成,也 可以是外部断路输入 IO,两者是或运算关系。

系统复位启动都默认关闭断路功能,将断路和死区寄存器(TIMx_BDTR)的 BKE 为置 1, 使能断路功能。可通过TIMx_BDTR 寄存器的 BKP 位设置设置断路输入引脚的有效电平, 设置为 1 时输入 BRK 为高电平有效,否则低电平有效。

发送断路时,将产生以下效果:

  • TIMx_BDTR寄存器中主输出模式使能(MOE)位被清零,输出处于无效、空闲或复位状态;
  •  根据相关控制位状态控制输出通道引脚电平;当使能通道互补输出时,会根据情况自动控制输出通道电平;
  •  将 TIMx_SR 寄存器中的 BIF 位置 1,并可产生中断和 DMA 传输请求。
  • 如果 TIMx_BDTR 寄存器中的自动输出使能(AOE)位置 1,则 MOE位会在发生下一个 UEV 事件时自动再次置 1。

下面介绍一下本实验使用到的寄存器(寄存器详细描述请参考STM32F407参考手册):

  • 控制寄存器1(TIMx_CR1):

  •  预分频寄存器(TIMx_PSC):

  • 自动重装载寄存器(TIMx_ARR)

 该寄存器在物理上实际对应着两个寄存器,一个是可以直接操作的,另一个是看不到的,这个看不到的寄存器在STM32F4参考手册叫做影子寄存器,事实上真正起作用的是影子寄存器,根据 TIMx_CR1 寄存器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影子寄存器,此时 2 者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装载寄存器(ARR)的内容传送到影子寄存器。

  • 状态寄存器(TIMx_SR)

 该寄存器用来标记当前与定时器相关的各种事件/中断是否发生。

定时器bsp_tim.c程序如下:

TIM_HandleTypeDef TIM1_Handler; // 定义定时器初始化结构体参数
// 定时器1初始化函数
void TIM1_Init(void)
{
	    TIM1_Handler.Channel = HAL_TIM_ACTIVE_CHANNEL_1; // 选择定时器通道
	    TIM1_Handler.Instance = TIM1; // 选择定时器1
	    TIM1_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 定时器时钟分频 不分频
	    TIM1_Handler.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1; // 中心对齐模式向上/向下计数
	    TIM1_Handler.Init.Period = 10000; // 自动重装载值
	    TIM1_Handler.Init.Prescaler = 0xFFFF; // 预分频
	    TIM1_Handler.Init.RepetitionCounter = 0xFF; // 定义重复次数
	    HAL_TIM_Base_Init(&TIM1_Handler); // 初始化定时器
	
	    HAL_TIM_Base_Start(&TIM1_Handler); // 启动定时器
} 

// 定时器硬件初始化
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
	    if(htim->Instance == TIM1)
	    {
		    GPIO_InitTypeDef GPIO_Handler;
		    __HAL_RCC_GPIOF_CLK_ENABLE(); // 使能GPIOF时钟
		    __TIM1_CLK_ENABLE();  // 使能定时器1时钟
		
		    GPIO_Handler.Pin = GPIO_PIN_7; // PF7
		    GPIO_Handler.Mode = GPIO_MODE_AF_PP; // 复用推挽模式
		    GPIO_Handler.Pull = GPIO_PULLUP; // 上拉电阻
		    GPIO_Handler.Speed = GPIO_SPEED_FREQ_MEDIUM; // 中速
		    GPIO_Handler.Alternate = GPIO_AF1_TIM1; // 复用为定时器
		    HAL_GPIO_Init(GPIOF, &GPIO_Handler); // 初始化GPIO
	    }
} 

主函数如下:

extern TIM_HandleTypeDef TIM1_Handler;
uint16_t Counter; // 定义存储计数变量

int main(void)
{
	    CLOCLK_Init(); // 初始化系统时钟
	    TIM1_Init(); // 初始化定时器1
	    UART_Init(); // 串口初始化
	
	    while(1)
	    {
		    Counter = __HAL_TIM_GET_COUNTER(&TIM1_Handler); // 获取定时器计数值
		    printf("Runtime is:%d\r\n", Counter); // 通过串口打印
		    HAL_Delay(100); // 延时100ms  
	    }
} 

实验现象:将程序下载到开发板,连接USB转TTL模块并打开串口调试助手,可以看到串口调试助手接收缓冲区中打印出了定时器的计数值。

参考文章链接:https://www.cnblogs.com/firege/p/5805886.html

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对你的问题,我可以给出以下步骤: 1. 初始化定时器3,使用HAL库函数`HAL_TIM_PWM_Init()`进行初始化,设置PWM模式为模式1,周期为20ms。 2. 配置定时器3的PWM输出通道,使用HAL库函数`HAL_TIM_PWM_ConfigChannel()`进行配置,设置通道号为TIM_CHANNEL_1、TIM_CHANNEL_2、TIM_CHANNEL_3,设置占空比为0。 3. 使能定时器3的PWM输出通道,使用HAL库函数`HAL_TIM_PWM_Start()`进行使能,这样PWM输出才能正常工作。 4. 控制sg90舵机的转动,由于sg90舵机的控制信号是一个周期为20ms的PWM信号,占空比在0.5ms到2.5ms之间,对应的脉宽为500us到2500us。因此,我们可以通过改变定时器3的PWM输出占空比来控制sg90舵机的转动。 下面是示例代码,可以参考一下: ```c #include "main.h" #include "stm32f4xx_hal.h" TIM_HandleTypeDef htim3; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM3_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM3_Init(); while (1) { // 控制sg90舵机转到0度 TIM3->CCR1 = 500; // 0.5ms TIM3->CCR2 = 500; // 0.5ms TIM3->CCR3 = 500; // 0.5ms HAL_Delay(1000); // 控制sg90舵机转到90度 TIM3->CCR1 = 1500; // 1.5ms TIM3->CCR2 = 1500; // 1.5ms TIM3->CCR3 = 1500; // 1.5ms HAL_Delay(1000); // 控制sg90舵机转到180度 TIM3->CCR1 = 2500; // 2.5ms TIM3->CCR2 = 2500; // 2.5ms TIM3->CCR3 = 2500; // 2.5ms HAL_Delay(1000); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** * Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** * Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** * Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } static void MX_TIM3_Init(void) { TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; htim3.Instance = TIM3; htim3.Init.Prescaler = 83; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 19999; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_PWM_Init(&htim3) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) { Error_Handler(); } HAL_TIM_MspPostInit(&htim3); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8, GPIO_PIN_RESET); /*Configure GPIO pins : PA6 PA7 PA8 */ GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void Error_Handler(void) { __disable_irq(); while (1) { } } ``` 在上述代码中,我们使用了定时器3的通道1、通道2、通道3来控制三个sg90舵机的转动,具体的占空比设置可以根据实际情况进行调整。另外,为了使定时器3能够输出PWM信号,我们还需要将PA6、PA7、PA8这三个引脚配置为复用功能,并设置复用功能为TIM3的PWM输出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值