STM32的HAL库开发---高级定时器---输出比较模式实验
一、高级定时器输出比较模式实验原理
定时器的输出比较模式总共有8种,本文使用其中的翻转模式,当TIMXCCR1=TIMXCNT时,翻转OC1REF的电平,OC1REF为输出参考信号,高电平有效,OC1REF信号连接到0C1上面,然后控制CH1输出,CH1通过IO口复用功能,连接到IO口上面,最后输出到外部。
计时器工作在递增模式:
当CNT的值不断递增,递增到于输出比较寄存器CCR1的值相同时,IO电平翻转,然后CNT值继续递增,递增到ARR时,产生计数器溢出事件,计数器值从0开始重新递增,通过这种方式产生方波或者称为PWM波。
周期:IO口输出高电平和低电平总和为一个方波周期,从图上可以看出,一个方波周期为两次计数器溢出的事件,也就是2*(ARR + 1)*t,t为计时器计一个数所需时间。
占空比:再翻转模式下,占空比固定为50%,不可以改变。
总结:PWM波周期或频率由ARR决定,占空比固定50%,相位由CCRX决定。
二、高级定时器输出比较模式实验配置步骤
1、HAL_TIM_OC_Init()函数,配置定时器基础工作参数。
2、HAL_TIM_OC_Msplnit()函数,配置NVIC、CLOCK、GPIO等
3、HAL_TIM_OC_Configchannel()函数,配置输出比较模式。
4、__HAL_TIM_ENABLE_OCxPRELOAD()宏定义,使能通道预装载。
5、HAL_TIM_OC_Start()函数,使能输出、主输出、计数器。
6、__HAL_TIM_SET_COMPARE()宏定义,修改捕获/比较寄存器的值。
三、高级定时器输出比较模式实验
实验:通过定时器8通道1/2/3/4输出相位分别为25%、50%、75%、100%的PWM
1、寄存器版本
#include "./BSP/TIMER/atim.h"
//配置定时器8通道1 PC6、PC7、PC8、PC9为翻转模式输出
void Advanced_TIM_Init(void)
{
//开启TIM8时钟
RCC->APB2ENR |= (1 << 13);
//开启ARR寄存器缓冲功能
TIM8->CR1 |= (1 << 7);
//设置PSC预分频系数
TIM8->PSC = 71;
//设置重装载寄存器值 配置PWM方波为500HZ
//在翻转模式下 两个ARR溢出时间为PWM一个周期
TIM8->ARR = (1000000 / (500 * 2)) - 1;
/**************TIM8_CH1*****************/
//CC1S 设置捕获比较为输出模式
TIM8->CCMR1 &= ~(0x03 << 0);
//OC1PE 开启输出比较寄存器预装载功能
TIM8->CCMR1 |= (1 << 3);
//OC1M 设置为翻转模式
TIM8->CCMR1 |= (0X03 << 4);
TIM8->CCMR1 &= ~(1 << 6);
//设置CCR1捕获/比较寄存器值 25%相位
TIM8->CCR1 = 0.25 * (1000000 / (500 * 2)) - 1;
//TIM8->CCR1 =1;
//设置输出极性为高电平有效 CC1P
TIM8->CCER &= ~(1 << 1);
//使能输出比较 CC1E
TIM8->CCER |= (1 << 0);
/**************TIM8_CH2*****************/
//CC2S 设置捕获比较为输出模式
TIM8->CCMR1 &= ~(0x03 << 8);
//OC2PE 开启输出比较寄存器预装载功能
TIM8->CCMR1 |= (1 << 11);
//OC2M 设置为翻转模式
TIM8->CCMR1 |= (0X03 << 12);
TIM8->CCMR1 &= ~(1 << 14);
//设置CCR2捕获/比较寄存器值 50%相位
TIM8->CCR2 = 0.50 * (1000000 / (500 * 2)) - 1;
//设置输出极性为高电平有效 CC2P
TIM8->CCER &= ~(1 << 5);
//使能输出比较 CC2E
TIM8->CCER |= (1 << 4);
/**************TIM8_CH3*****************/
//CC3S 设置捕获比较为输出模式
TIM8->CCMR2 &= ~(0x03 << 0);
//OC3PE 开启输出比较寄存器预装载功能
TIM8->CCMR2 |= (1 << 3);
//OC3M 设置为翻转模式
TIM8->CCMR2 |= (0X03 << 4);
TIM8->CCMR2 &= ~(1 << 6);
//设置CCR3捕获/比较寄存器值 75%相位
TIM8->CCR3 = 0.75 * (1000000 / (500 * 2)) - 1;
//设置输出极性为高电平有效 CC3P
TIM8->CCER &= ~(1 << 9);
//使能输出比较 CC3E
TIM8->CCER |= (1 << 8);
/**************TIM8_CH4*****************/
//CC4S 设置捕获比较为输出模式
TIM8->CCMR2 &= ~(0x03 << 8);
//OC4PE 开启输出比较寄存器预装载功能
TIM8->CCMR2 |= (1 << 11);
//OC4M 设置为翻转模式
TIM8->CCMR2 |= (0X03 << 12);
TIM8->CCMR2 &= ~(1 << 14);
//设置CCR4捕获/比较寄存器值 100%相位
TIM8->CCR4 = 1 * (1000000 / (500 * 2)) - 1;
//设置输出极性为高电平有效 CC4P
TIM8->CCER &= ~(1 << 13);
//使能输出比较 CC4E
TIM8->CCER |= (1 << 12);
//软件更新事件 主要为将PSC的值转移到影子寄存器里边
TIM8->EGR |= (1 << 0);
//MOE 开启主输出
TIM8->BDTR |= (1 << 15);
//开启GPIOC时钟
RCC->APB2ENR |= (1 << 4);
//设置PC6为复用推挽输出
GPIOC->CRL |= (0X03 << 24);
GPIOC->CRL |= (1 << 27);
GPIOC->CRL &= ~(1 << 26);
//设置PC7为复用推挽输出
GPIOC->CRL |= (0X03 << 28);
GPIOC->CRL |= (1 << 31);
GPIOC->CRL &= ~(1 << 30);
//设置PC8为复用推挽输出
GPIOC->CRH |= (0X03 << 0);
GPIOC->CRH |= (1 << 3);
GPIOC->CRH &= ~(1 << 2);
//设置PC9为复用推挽输出
GPIOC->CRH |= (0X03 << 4);
GPIOC->CRH |= (1 << 7);
GPIOC->CRH &= ~(1 << 6);
//使能计数器
TIM8->CR1 |= (1 << 0);
}
2、库函数版本
atim.h头文件程序
#ifndef __ATIM_H
#define __ATIM_H
#include "stm32f1xx.h"
void Advanced_TIM_Init(void);
#endif
atim.c
#include "./BSP/TIMER/atim.h"
//配置定时器8通道1 PC6为翻转模式输出
TIM_HandleTypeDef htim;
void Advanced_TIM_Init(void)
{
htim.Instance = TIM8;
htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
htim.Init.Period = 1000000/(500 * 2) - 1;
htim.Init.Prescaler = 71;
HAL_TIM_OC_Init(&htim);
TIM_OC_InitTypeDef sConfig = {0};
sConfig.OCMode = TIM_OCMODE_TOGGLE;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.Pulse = 0.25 * (TIM8->ARR + 1) - 1;
//配置TIM8 CH1为翻转输出模式
HAL_TIM_OC_ConfigChannel(&htim,&sConfig,TIM_CHANNEL_1);
//配置TIM8 CH2为翻转输出模式
sConfig.Pulse = 0.50 * (TIM8->ARR + 1) - 1;
HAL_TIM_OC_ConfigChannel(&htim,&sConfig,TIM_CHANNEL_2);
//配置TIM8 CH3为翻转输出模式
sConfig.Pulse = 0.75 * (TIM8->ARR + 1) - 1;
HAL_TIM_OC_ConfigChannel(&htim,&sConfig,TIM_CHANNEL_3);
//配置TIM8 CH4为翻转输出模式
sConfig.Pulse = 1 * TIM8->ARR;
HAL_TIM_OC_ConfigChannel(&htim,&sConfig,TIM_CHANNEL_4);
//使能捕获/比较寄存器通道1预装载
__HAL_TIM_ENABLE_OCxPRELOAD(&htim,TIM_CHANNEL_1);
//使能捕获/比较寄存器通道2预装载
__HAL_TIM_ENABLE_OCxPRELOAD(&htim,TIM_CHANNEL_2);
//使能捕获/比较寄存器通道3预装载
__HAL_TIM_ENABLE_OCxPRELOAD(&htim,TIM_CHANNEL_3);
//使能捕获/比较寄存器通道4预装载
__HAL_TIM_ENABLE_OCxPRELOAD(&htim,TIM_CHANNEL_4);
TIM8->EGR |= (1 << 0);
//启动TIM8 CH1计数器 主输出 输出比较
HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1);
//启动TIM8 CH2计数器 主输出 输出比较
HAL_TIM_OC_Start(&htim, TIM_CHANNEL_2);
//启动TIM8 CH3计数器 主输出 输出比较
HAL_TIM_OC_Start(&htim, TIM_CHANNEL_3);
//启动TIM8 CH4计数器 主输出 输出比较
HAL_TIM_OC_Start(&htim, TIM_CHANNEL_4);
}
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
//开启定时器8时钟
__HAL_RCC_TIM8_CLK_ENABLE();
//开启GPIOC时钟
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_Init = {0};
GPIO_Init.Mode = GPIO_MODE_AF_PP;
GPIO_Init.Pin = GPIO_PIN_6;
//设置为输出模式时 这个没有用 可以不写
GPIO_Init.Pull = GPIO_NOPULL;
GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH;
//设置PC6为复用推挽输出
HAL_GPIO_Init(GPIOC, &GPIO_Init);
GPIO_Init.Pin = GPIO_PIN_7;
//设置PC7为复用推挽输出
HAL_GPIO_Init(GPIOC, &GPIO_Init);
GPIO_Init.Pin = GPIO_PIN_8;
//设置PC8为复用推挽输出
HAL_GPIO_Init(GPIOC, &GPIO_Init);
GPIO_Init.Pin = GPIO_PIN_9;
//设置PC9为复用推挽输出
HAL_GPIO_Init(GPIOC, &GPIO_Init);
}
main.c主函数程序
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/atim.h"
extern TIM_HandleTypeDef htim;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
led_Init(); /* LED初始化 */
Advanced_TIM_Init(); //高级定时器初始化
// __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_1, 0);
// __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_2, 500 - 1);
// __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_3, 750 - 1);
// __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_4, 1000 - 1);
while(1)
{
LED0(1);
LED1(0);
delay_ms(500);
LED0(0);
LED1(1);
delay_ms(500);
}
}
在配置过程中发现,如果捕获比较寄存器的值在初始化的时候设置成0,会导致相位错误,设置成1就不会。但是在初始化之后再将比较寄存器的值设置成0,相位正确。这个问题具体原因还没找到,如果有人有思路,可以私信我。