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:定时器的寄存器定义

cpp 复制代码
#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中断服务程序

cpp 复制代码
//-------------------------------------------------------------------------------------------------
// 初始化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 = 0
    TIM_CR1(TIM6) = 0;
    TIM_CR1(TIM6) |= TIM_CR1_ARPE;

    // 定时器时钟 = 输入时钟 / (PSC+1) = 36.864MHz / 18 = 2.048 MHz
    TIM_PSC(TIM6) = 17;

    // PA0 = 1
    DEV_GPIO_BitSet(GPIOA, GPIO_PIN_0);

    // 定时2.5ms的计数值 = 2500 us * 2.048 MHz = 5120
    TIM_ARR(TIM6) = 5120;

    // 产生更新事件,将当前TIM_ARR内的数据写入影子寄存器,同时将TIM_CNT清0。
    TIM_EGR(TIM6) |= TIM_EGR_UG;

    // 定时7.5ms的计数值 = 7500 us * 2.048 MHz = 15360
    TIM_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);
    }   
}

运行结果:

相关推荐
guangshui5163 小时前
18006.STM32通过SPI读取LAN9253数据
stm32·单片机·嵌入式硬件
小莞尔5 小时前
【51单片机】【protues仿真】基于51单片机全自动洗衣机系统
c语言·单片机·嵌入式硬件·物联网·51单片机
hazy1k6 小时前
51单片机基础-静态数码管显示
stm32·单片机·嵌入式硬件·51单片机
bu_shuo7 小时前
单片机中经常定义的结构体解读
单片机·嵌入式硬件
00后程序员张7 小时前
Windows 安全分割利器:strtok_s () 详解
windows·单片机·安全
小莞尔7 小时前
【51单片机】【protues仿真】基于51单片机矩阵电子琴系统
单片机·嵌入式硬件
蜀黍@猿8 小时前
【GD32】软、硬件I2C对比
单片机·嵌入式硬件·mcu
@小张要努力8 小时前
STM32学习记录-0.1 STM32外设
stm32·嵌入式硬件·学习