目录
通用定时器中断配置(Basic timers):
通用定时器/高级定时器(General-purpose timers/Advanced-control timers):
PWM配置:
PWM互补输出:
输出比较实现PWM:Out Compare模式
输入捕获模式:
1.普通捕获:
2.PWM输入模式(输入捕获的一种特殊形式):
可以在库中判断具体挂在哪个总线上:(stm32g4xx_hal_rcc.h)
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE(); //可以根据这个直接go to 定义,就在stm32g4xx_hal_rcc.h中
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
我认为TIM6是挂在APB1上面的,所以是80MHz。 这里对TIM6进行配置:
定时器的频率为80M/(79+1)/(999+1)=1KHz,即1ms一次中断。
这里对于这个Trigger Event Selection不懂。
基本定时器的功能函数(轮询、中断、DMA)
我先学习中断处理:
1.在main函数中开启中断:
HAL_TIM_Base_Start_IT(&htim6);
2.重新定义回调函数并添加自己想要实现的功能(我只用LED闪烁简单测试):
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
}
或者在stm32G431RB数据手册中寻找 block diagram(框图)
TIM15挂在APB2上:
同样开启中断、代码相似。自动生成tim.h和tim.c文件:
#include "tim.h"
TIM_HandleTypeDef htim6;
TIM_HandleTypeDef htim15;
在main函数中:
HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim15);
自己编写对应的函数中断:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM15)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
}
else if(htim->Instance==TIM6)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9);
}
}
if中也可以使用htim->Instance == htim1.Instance 例如这里:if(htim->Instance==htim15.Instance)
对于instance在TIM的定义中可以看到:(可以区分进入中断的不同定时器)
void MX_TIM15_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim15.Instance = TIM15; //不同的定时器的instance不同
htim15.Init.Prescaler = 7999;
htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
htim15.Init.Period = 9999;
htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim15.Init.RepetitionCounter = 0;
htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim15) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
图片来自B站UP:PetraKing
频率的计算是一样的,pwm占空比的设置是Pulse/Counter Period计算的。
初始化只需要在main函数中添加:
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
或者:(IT:interruption中断)要对应打开嵌套向量中断控制器
HAL_TIM_PWM_Start_IT(&htim3,TIM_CHANNEL_1);
这里对优先级不配置:有需要的可以配置 。
更改PWM的频率和占空比:
通过以上可知,PWM的占空比改变的是CCR寄存器的值(不同通道对应不同的CCR),频率的改变对应的是ARR寄存器的值,也就是下图
至于CCR寄存器,我们可以看到初始化中,在sConfigOC.Pulse赋值给我们想要的值。
而在HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1)的定义中,我们能找到 TIM_OC1_SetConfig(htim->Instance, sConfig);类似的函数,而这个函数中的定义中,
就能找到实际把Pulse的值给了TIM的CCR寄存器。
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,100);//占空比
__HAL_TIM_SET_AUTORELOAD(&htim3,9999);//周期
所以就可以通过这两个来改变占空比和周期(频率)。
用不到,先暂时不学: 互补输出配置示例
图片来自B站UP:PetraKing
左边是通用定时器,只有一个自动重装载(ARR),但是有多个CCR,所以同一个定时器下面的通道只有一个频率,但是可以有多个占空比。
上图为详细的原理,讲的很清楚,如果看不懂的话建议找一下这个up的视频看一看。
具体配置:
这里大概说一下自己的看法:中断是肯定要打开的,因为要不断改CCR的值,这里的预分配和你想写的pulse决定了频率,所以这个ARR是没有用的,至于自动重装载还是打开吧,不然会有什么bug吧,我这里预分频后是80M/8000=10Khz,我想配置的500个的高电平和300个低电平,那么总共是800个,实际的频率就是(800/10k)hz。
具体程序:
HAL_TIM_OC_Start_IT(&htim16,TIM_CHANNEL_1);
__HAL_TIM_GET_COUNTER也是一个宏定义。
在stm32f4xx_hal_tim.h文件中可以找到。其作用是获取定时器某一通道的捕获/比较寄存器值
等价于 : HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==htim16.Instance)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
{
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12)==GPIO_PIN_RESET)
{
__HAL_TIM_SET_COMPARE(&htim16,TIM_CHANNEL_1,__HAL_TIM_GET_COUNTER(&htim16)+300);
}
else
{
__HAL_TIM_SET_COMPARE(&htim16,TIM_CHANNEL_1,__HAL_TIM_GET_COUNTER(&htim16)+500);
}
}
}
}
如果不清楚这if怎么写,可以去定义里面找:
去找TIM_HandleTypeDef的定义:
再去找HAL_TIM_ActiveChannel的定义:(就找到了)(枚举)
没有示波器,只用万用表简单测试了一下,感觉电压差不多,然后再去找示波器看一下吧,主要是STM32G431RB好像KEIL不支持它的虚拟示波器,我也没逻辑测试仪,然后看玩再放图吧。
使用示波器看过了,程序没有问题,注意使用的是__HAL_TIM_GET_COUNTER,不是__HAL_TIM_GET_COMPARE。
同时如果只是简单的配置不同频率的50%pwm,不需要判断高低电平了,这杨相当各200的高电平和低电平
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
__HAL_TIM_SET_COMPARE(htim,TIM_CHANNEL_1,(__HAL_TIM_GetCounter(htim)+200));//5Khz
}
}
输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,常用的有测量输入信号的脉
宽和测量 PWM 输入信号的频率和占空比这两种。
main函数中:
HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_1);
输入捕获的中断回调函数:
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==htim4.Instance)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
{
PWM_count=__HAL_TIM_GET_COUNTER(&htim4);
__HAL_TIM_SET_COUNTER(&htim4,0);
frequency=1000000/PWM_count;
}
}
}
读出当前计数值,清空,频率就是之前配置的80M/(79+1)/__HAL_TIM_GET_COUNTER()
在main函数中:
HAL_TIM_IC_Start_IT( &htim2, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT( &htim2, TIM_CHANNEL_2);
在中断回调函数中:
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==htim2.Instance)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
{
PWM_count1=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1)+1;
}
else if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2)
{
PWM_count2=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2)+1;
}
}
}
由此读出来PWM_count1是总周期数,PWM_count2是低电平数 。
我认为input capture direct mode是读取整个周期的,把他设置为上升沿就会在高电平来到时触发,input capture indirect mode则也同时开始,然后再下降沿到来时停止计数,而前一个则一直计算到下一个上升沿。(最后结果都需要加1).
剩下还有很多不会的不理解的,以后慢慢加深学习吧。
对输入捕获的理解,当我们配置了某个定时器的某个通道为输入捕获时,对一个未知的pwm进行采集,比如设置在高电平时触发的话,在高电平来到后,记录此时该定时器的CNT寄存器,即该定时器在预分频下采集到的计数值,然后记得每次采集完清空该CNT,否则就会累加。
例如:预分频设置为79,而该时钟是80M的,比如CNT的值是1000,那么这个未知的pwm的频率就是80M/(79+1)/1000