STM32单片机:基本定时器应用:PWM 生成(STM32L4xx)
STM32的基本定时器的功能比较简单,主要就是提供一个定时器的功能。
本文讨论基本定时器是不是可以实现PWM的功能呢?
其实基本定时器里面是没有PWM功能的,也没有对应的输出引脚。但通过对基本定时器一些寄存器的操作,是可以模拟PWM功能的,只是这个PWM是有软件参与,所以时效性不如硬件上自带的PWM来的高。不过可以通过这个实验来更加深刻理解基本定时器的工作原理。
所谓PWM就是脉宽调制,也就是可以根据需要调整波形的周期和占空比。
为了实现这个功能,需要充分利用自动重新加载寄存器(TIMx_ARR)的预加载功能,也就是通过设置控制寄存器(TIMx_CR1)中的自动重新加载预加载启用位(ARPE=1)来采用预加载方式。
预加载功能的实现过程:就是将(TIMx_ARR)的值加载到影子寄存器后,定时器的计数值(TIMx_CNT)和影子寄存器比较,在此期间可以将新的待加载的值存入(TIMx_ARR),新存入的值不影响当前影子寄存器里面的值,当(TIMx_CNT)计数值达到影子寄存器存储的数值时,会产生更新中断,且自动将刚才存储在(TIMx_ARR)里面待加载的数据加载到影子寄存器,以及将(TIMx_CNT)清0,并重新计数和影子寄存器里面的数据做比较。在此期间可以再次将新的待加载的值存入(TIMx_ARR),如此循环往复。
任务:设计一个波形周期为10ms,高电平时间为2.5ms,低电平时间为7.5ms,通过PA0输出此波形。
使用基本定时器TIM6实现此功能的具体实施步骤如下:
1、 使能TIM6的时钟。
2、 TIM6_CR1.ARPE = 1,TIM6_CR1.URS = 0,TIM6_CR1.UDIS = 0 :允许TIM6_ARR预加载,允许更新,且更新请求的事件源为:定时器溢出,置位TIM6_EGR.UG,通过从模式控制器产生更新。
3、 设置定时器输入时钟的分频系数。
4、 输出高电平:PA0=1
5、 根据定时器时钟频率,计算出维持高电平输出的2.5ms对应的计数值(此处用名称High_CNT表示),并将此值写入(TIM6_ARR),此时这个计数值并没有写入到真正参与比较的影子寄存器,只是缓存在(TIM6_ARR)中,下一步将此值加载到影子寄存器。
6、 TIM6_EGR.UG = 1,将(TIM6_ARR)中的数据加载到影子寄存器,同时将TIM6_CNT清0。
7、 根据定时器时钟频率,计算出维持低电平输出的7.5ms对应的计数值(此处用名称Low_CNT表示),并将此值写入(TIM6_ARR),这个值不影响影子寄存器的值,等到维持高电平的2.5ms时间到达时,产生更新事件,自动将此值(维持低电平的时间的计数值)加载到影子寄存器。
8、 TIM6_SR.TIM_SR_UIF = 0:清除更新中断标志, TIM6_DIER.TIM_DIER_UIE = 1:允许更新中断
9、 TIM6_CR1.CEN = 1: 启动定时器TIM6
10、 NVIC_EnableIRQ(TIM6_DAC_IRQn):开启TIM6更新中断
11、 TIM6中断服务程序:
如果当前PA0=1,表示高电平输出时间结束,因为此前已经将维持低电平输出时间的计数值(Low_CNT)存储到(TIM6_ARR)中了,所以会自动将此值(Low_CNT)加载到影子寄存器(这个无需软件干预,是硬件自动完成的),然后我们可以将维持高电平输出时间的计数值(High_CNT)存入(TIM6_ARR),当低电平输出时间结束后,自动载入维持高电平输出时间的计数值(High_CNT)到影子寄存器。
如果当前PA0=0,表示低电平输出时间结束,因为此前已经将维持高电平输出时间的计数值(High_CNT)存储到(TIM6_ARR)中了,所以会自动将此值(High_CNT)加载到影子寄存器(这个无需软件干预,是硬件自动完成的),然后我们可以将维持低电平输出时间的计数值(Low_CNT)存入(TIM6_ARR),当高电平输出时间结束后,自动载入维持低电平输出时间的计数值(Low_CNT)到影子寄存器。
实现代码:
STM32L4xx_timer.h:定时器的寄存器定义
#define TIM1 ((uint32_t)0x40012C00) // Advanced-control timer 1
#define TIM8 ((uint32_t)0x40013400) // Advanced-control timer 8#define TIM2 ((uint32_t)0x40000000) // General-purpose timer 2
#define TIM3 ((uint32_t)0x40000400) // General-purpose timer 3
#define TIM4 ((uint32_t)0x40000800) // General-purpose timer 4
#define TIM5 ((uint32_t)0x40000C00) // General-purpose timer 5#define TIM15 ((uint32_t)0x40014000) // General-purpose timer 15
#define TIM16 ((uint32_t)0x40014400) // General-purpose timer 16
#define TIM17 ((uint32_t)0x40014800) // General-purpose timer 17#define TIM6 ((uint32_t)0x40001000) // Basic timer 6
#define TIM7 ((uint32_t)0x40001400) // Basic timer 7#define TIM_CR1(timx) REG32(timx + 0x00000000) // TIMx control register 1
#define TIM_CR2(timx) REG32(timx + 0x00000004) // TIMx control register 2
#define TIM_SMCR(timx) REG32(timx + 0x00000008) // TIMx slave mode control register
#define TIM_DIER(timx) REG32(timx + 0x0000000C) // TIMx DMA/Interrupt enable register
#define TIM_SR(timx) REG32(timx + 0x00000010) // TIMx status register
#define TIM_EGR(timx) REG32(timx + 0x00000014) // TIMx event generation register
#define TIM_CCMR1(timx) REG32(timx + 0x00000018) // TIMx capture/compare mode register 1
#define TIM_CCMR2(timx) REG32(timx + 0x0000001C) // TIMx capture/compare mode register 2
#define TIM_CCER(timx) REG32(timx + 0x00000020) // TIMx capture/compare enable register
#define TIM_CNT(timx) REG32(timx + 0x00000024) // TIMx counter
#define TIM_PSC(timx) REG32(timx + 0x00000028) // TIMx prescaler
#define TIM_ARR(timx) REG32(timx + 0x0000002C) // TIMx auto-reload register
#define TIM_RCR(timx) REG32(timx + 0x00000030) // TIMx repetition counter register
#define TIM_CCR1(timx) REG32(timx + 0x00000034) // TIMx capture/compare register 1
#define TIM_CCR2(timx) REG32(timx + 0x00000038) // TIMx capture/compare register 2
#define TIM_CCR3(timx) REG32(timx + 0x0000003C) // TIMx capture/compare register 3
#define TIM_CCR4(timx) REG32(timx + 0x00000040) // TIMx capture/compare register 4
#define TIM_BDTR(timx) REG32(timx + 0x00000044) // TIMx break and dead-time register
#define TIM_DCR(timx) REG32(timx + 0x00000048) // TIMx DMA control register
#define TIM_DMAR(timx) REG32(timx + 0x0000004C) // TIMx DMA address for full transfer
#define TIM_OR1(timx) REG32(timx + 0x00000050) // TIMx option register 1
#define TIM_CCMR3(timx) REG32(timx + 0x00000054) // TIMx capture/compare mode register 3
#define TIM_CCR5(timx) REG32(timx + 0x00000058) // TIMx capture/compare register 5
#define TIM_CCR6(timx) REG32(timx + 0x0000005C) // TIMx capture/compare register 6
#define TIM_OR2(timx) REG32(timx + 0x00000060) // TIMx option register 2
#define TIM_OR3(timx) REG32(timx + 0x00000064) // TIMx option register 3
dev_tim6.c: 定时器TIM6初始化,以及TIM6中断服务程序
//-------------------------------------------------------------------------------------------------
// 初始化TIM6: 基本定时器, 产生PWM波形(高电平时间为2.5ms,低电平时间为7.5ms), 通过PA0输出此波形
//
// 时钟源: APB1-PCLK1 = 18.432MHz【80 MHz Max】
//
// RCC_CFGR_PPRE1 的分频系数 = 1 时 CLK = APB1-PCLK1
// RCC_CFGR_PPRE1 的分频系数 != 1 时 CLK = APB1-PCLK1 * 2
// 因为: 本系统 RCC_CFGR_PPRE1 的分频系数 = RCC_CFGR_PPRE1_DIV4 , 不等于1
// 所以: CLK = APB1-PCLK1 * 2 = 18.432MHz * 2 = 36.864MHz
//-------------------------------------------------------------------------------------------------
void DEV_TIM6_Init(void)
{// 允许TIM6时钟RCC_APB1ENR1 |= RCC_APB1ENR1_TIM6EN;// TIM_CR1.ARPE = 1, TIM_CR1.URS = 0, TIM_CR1.UDIS = 0TIM_CR1(TIM6) = 0;TIM_CR1(TIM6) |= TIM_CR1_ARPE;// 定时器时钟 = 输入时钟 / (PSC+1) = 36.864MHz / 18 = 2.048 MHzTIM_PSC(TIM6) = 17;// PA0 = 1DEV_GPIO_BitSet(GPIOA, GPIO_PIN_0);// 定时2.5ms的计数值 = 2500 us * 2.048 MHz = 5120TIM_ARR(TIM6) = 5120;// 产生更新事件,将当前TIM_ARR内的数据写入影子寄存器,同时将TIM_CNT清0。TIM_EGR(TIM6) |= TIM_EGR_UG;// 定时7.5ms的计数值 = 7500 us * 2.048 MHz = 15360TIM_ARR(TIM6) = 15360;// 清除更新中断标志TIM_SR(TIM6) = 0;// 允许更新中断TIM_DIER(TIM6) = 0;TIM_DIER(TIM6) |= TIM_DIER_UIE;// 启动TIM6计数器TIM_CR1(TIM6) |= TIM_CR1_CEN;// 开TIM6中断NVIC_EnableIRQ(TIM6_DAC_IRQn);
}//-----------------------------------------------------------------------------
// TIM6中断服务程序
//-----------------------------------------------------------------------------
void TIM6_DAC_IRQHandler(void)
{// 清除更新中断标志TIM_SR(TIM6) = 0;if (GPIO_IDR(GPIOA) & BIT0){TIM_ARR(TIM6) = 5120; DEV_GPIO_BitClr(GPIOA, GPIO_PIN_0);} else{TIM_ARR(TIM6) = 15360; DEV_GPIO_BitSet(GPIOA, GPIO_PIN_0);}
}
运行结果: