stm32g431rb单片机中,共10个定时器
- 2个基本定时器(TIM6和TIM7)
- 3个通用定时器(TIM2~TIM4):全功能定时器
- 3个通用定时器(TIM15~TIM17):只有1个或者2个通道
- 2个高级控制寄存器(TIM1和TIM8)
定时器功能比较
功能:
- ADC和DAC开始转换触发
- 输入捕获:脉冲计数、上升沿或下降沿时间检测、PWM输入检测
- 输出比较:脉冲输出、电机控制
- 脉冲宽度调节PWM:电压输出控制、直流减速电机控制、直流无刷电机控制
- 单脉冲模式输出
- 编码接口、霍尔传感器接口
定时器的计数方式:
中心对齐模式分为3种子模式:
模式1:下溢事件产生更新事件
模式2:上溢事件产生更新事件
模式3:上溢和下溢都产生更新事件
基本定时器基本原理:
计数过程:
每来一个CK_CNT脉冲,TIMx_CNT值+1,当TIMx_CNT值与TIMx_ARR设定的值相等时就自动生成更新事件(也可以产生DMA请求、中断信号或触发DAC同步电路),并且TIMx_CNT自动清零,然后重新开始计数,不断重复上述过程。因此,我们只要设定TIMx_PSC和TIMx_ARR这两个寄存器的值就可以控制事件生成时间,对应的就是程序中定时器预分频设置和定时周期。
以定时器6为例,用定时器以中断方式控制LED灯1S闪烁
然后就是编程
按照这个方式找到中断回调函数,也就是在main.c里重写虚函数
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6)
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_All);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
// HAL_TIM_Base_Start_IT(&htim6);
}
/* USER CODE END 0 */
当然最后还要在main函数里头启动定时器
高级定时器
也就是要把这张图给看透了
先从上半部分看起,大致分为3大块(时钟源,控制器,时基单元)
Slave controller mode:定时器从模式除了用于接收内部时钟外的信号,该信号用于完成如下控制:
- 复位Reset模式:使用内部时钟作为时钟源,TI1/2外部有效信号复位计数器。eg:CH1触发输入上升沿有效时,计数器复位到默认值0 .
- 门控enable模式:使用内部时钟作为时钟源,根据TI1/2外部电平情况运行计数。eg:CH1输入为高电平时,定时器正常计数;CH1输入为低电平时,停止计数。
- 触发enable模式:使用内部时钟作为触发源,TI1/2外部触发启动定时器。eg:CH1触发输入上升沿时,定时器启动运行。
- 外部时钟模式2+触发模式:使用外部ETR作为时钟源,配合其它(1,2,3)的触发启动模式。
外部时钟输入模式1
外部时钟输入模式2
其它定时器输出的触发信号
作用:
- 使用一个定时器作为另一个定时器的预分频器
- 使用一个定时器使能另一个定时器
- 使用一个定时器去启动另一个定时器
- 使用一个外部触发同步的启动2个定时器
下半张图,分为两部分
可以看出有重叠的部分,所以同一时刻,捕获和比较只能进行一个。
输入捕获原理
模式1:普通输入捕获模式
模式2:PWM输入捕获模式
模式1:
输入捕获两大核心功能:
1、捕获定时器的数值
2、产生中断,类似于外中断(上升沿产生中断)
模式2:
pwm输入模式时序图
1.PWM信号由TI1进入,配置TI1FP1为触发信号,上升沿捕获。
2.当上升沿的时候IC1和IC2同时捕获,计数器CNT清零。
3.到了下降沿的时候,IC2捕获,此时计数器CNT的值被锁存到捕获寄存器CCR2中。
4.到了下一个上升沿的时候,IC1捕获,计数器CNT的值被锁存到捕获寄存器CCR1中。
5.其中CCR2测量的是脉宽,CCR1测量的是周期。
输出比较
output control 控制模式输出(由OC1M[3:0]第三位控制 0000 ~ 0111)
- 1:(冻结模式)冻结模式。定时器作为普通定时器使用,不使用输出比较功能。
- 2/3:(比较输出模式1)匹配时输出有效/无效电平模式。如递增计数器,比较寄存器内部数值提前设定好,当计数器相等或者大于比较值时,匹配,产生/不产生有效信号输出。当计数器值小于比较值时,不产生/产生有效信号输出。
- 4:(比较输出模式2)电平翻转模式。当匹配时,引脚状态翻转。步进电机控制常用的模式。
- 5/6:(强制输出模式)强置为无效/有效电平模式。不管比较寄存器和计数器数值,强制设置比较寄存器的输出。
- 7/8:(PWM模式) PWM1和PWM2模式。(重点)
输出模式1~6:
PWM模式:
1、产生参考电平
pwm模式可以产生一个由TIMx_ARR寄存器确定频率(周期)、由TIMx_CRR寄存器确定占空比的pwm信号。
stm32的pwm模式有两种,根据TIMx_CRR寄存器中的OCxM位来确定
- 110:PWM模式1,在递增计数时,TIMx_CNT<TIMx_CCRx时OCxREF为有效电平,否则为无效电平;在递减计数时,TIMx_CNT>TIMx_CCRx时OCxREF为无效电平,否则为有效电平。
- 111:PWM模式2,在递增计数时,TIMx_CNT<TIMx_CCRx时OCxREF为无效电平,否则为有效电平; 在递减计数时,TIMx_CNT>TIMx_CCRx时OCxREF为有效电平,否则为无效电平。
2、产生死区和最终输出
实际应用:电机调速,使用数字信号达到一个模拟信号的效果。
每个桥的上半桥臂和下半桥臂是是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有及时关断,造成功率元件烧毁。死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。这段延迟时间就是死区。
带死区的互补输出
开始敲代码
然后咱们板子上有两个pwm输入通道
在手册上找到这两个引脚所对应的定时器功能
编程实现测量这两路的PWM频率和占空比
cubemx关于定时器的配置
然后打开MDK开始敲代码了
首先还是得找到定时器输入捕获的中断回显函数,然后在main.c里面重写
参考代码:
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3) //PB4
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
pwm1_period = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1)+1;
pwm1_duty = (float)pwm1_high/pwm1_period;
}
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
pwm1_high = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_2)+1;
}
}
if(htim->Instance == TIM2) //PA15
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
pwm2_period = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1)+1;
pwm2_duty = (float)pwm2_high/pwm2_period;
}
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
pwm2_high = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2)+1;
}
}
}
void LCD_Proc()
{
if(uwTick - lcd_uwTick < 100)
return;
lcd_uwTick = uwTick;
LCD_DisplayStringLine(Line0,(uint8_t *)"--------------------");
LCD_DisplayStringLine(Line1,(uint8_t *)"---test pwm_duty----");
sprintf((char *)lcd_string,"pwm1_freq: %06d",1000000/pwm1_period);
LCD_DisplayStringLine(Line2,lcd_string);
sprintf((char *)lcd_string,"pwm2_freq: %06d",1000000/pwm2_period);
LCD_DisplayStringLine(Line3,lcd_string);
sprintf((char *)lcd_string,"pwm1_period: %06d",pwm1_period);
LCD_DisplayStringLine(Line4,lcd_string);
sprintf((char *)lcd_string,"pwm2_period: %06d",pwm2_period);
LCD_DisplayStringLine(Line5,lcd_string);
sprintf((char *)lcd_string,"pwm1_high: %06d",pwm1_high);
LCD_DisplayStringLine(Line6,lcd_string);
sprintf((char *)lcd_string,"pwm2_high: %06d",pwm2_high);
LCD_DisplayStringLine(Line7,lcd_string);
sprintf((char *)lcd_string,"pwm1_duty: %4.2f%%",pwm1_duty*100);
LCD_DisplayStringLine(Line8,lcd_string);
sprintf((char *)lcd_string,"pwm2_duty: %4.2f%%",pwm2_duty*100);
LCD_DisplayStringLine(Line9,lcd_string);
}
/* USER CODE END 0 */
写完之后肯定要打开定时器(在main()里面启动相关定时器和定时器通道输入捕获通道)
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
/* 启动定时器*/
HAL_TIM_Base_Start_IT(&htim3);
/* 启动定时器通道输入捕获并开启中断*/
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);
/* 启动定时器*/
HAL_TIM_Base_Start_IT(&htim2);
/* 启动定时器通道输入捕获并开启中断*/
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
LCD_Proc();
}
/* USER CODE END 3 */
}
这些函数在.h文件中找比较方便
然后就是输出比较了
编程用一个定时器生成两路占空比不等方波或PWM波
输出方波
cubemx配置
中断给打开
这里的输出速度让它高一点
咱这也要注意一下引脚的配置,不要忽略了。。
然后就可以生成代码了。
找到中断回显函数,在main.c里重写
//方波输出中断
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) + 100)); //5kHZ
}
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
__HAL_TIM_SET_COMPARE(htim,TIM_CHANNEL_2,(__HAL_TIM_GetCounter(htim) + 500)); //1kHZ
}
}
}
然后还要在main函数里头启动比较
/* 启动CH1比较输出 */
HAL_TIM_OC_Start_IT(&htim4,TIM_CHANNEL_1);
HAL_TIM_OC_Start_IT(&htim4,TIM_CHANNEL_2);
pwm波
这里生成代码,pwm配置好初始化他就可以直接输出了
/* PWM输出两路占空比不相等的PWM波*/
HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_2);