OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 746|回复: 1

《M144Z-M3最小系统板使用指南——STM32F103版》第二十一章 高级定时器输出指定个数PWM实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4667
金钱
4667
注册时间
2019-5-8
在线时间
1224 小时
发表于 2024-4-10 14:21:24 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2024-4-9 16:24 编辑

第二十一章 高级定时器输出指定个数PWM实验

1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板

2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boar ... _mini_sysboard.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子STM32技术交流QQ群:725095144

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

本章将介绍使用STM32F103高级定时器输出指定个数的PWM。通过本章的学习,读者将学习到高级定时器重复计数器的使用。
本章分为如下几个小节:
21.1 硬件设计
21.2 程序设计
21.3 下载验证

21.1 硬件设计
21.1.1 例程功能
1. 按下KEY0按键,PC6引脚输出5个频率为2Hz的PWM
2. LED0闪烁,提示程序正在运行

21.1.2硬件资源
1. LED
       LED0 - PB5
2. 按键
       KEY0- PE4
3. TIM8
       CH1 - PC6

21.1.3 原理图
本章实验使用的TIM8为STM32F103的片上资源,因此没有对应的连接原理图。

21.2 程序设计
21.2.1 HAL库的TIM驱动
本章实验将使用KEY0按键控制TIM8通过通道1(PC6引脚)输出指定个数的PWM,其具体的配置步骤如下:
①:初始化定时器PWM
②:配置定时器PWM输出通道
③:开始定时器PWM输出
在HAL库中对应的驱动函数如下:
①:初始化定时器PWM输出
请见第18.2.1小节中初始化定时器PWM的相关内容。
②:配置定时器PWM输出通道
请见第18.2.1小节中配置定时器PWM输出通道的相关内容。
③:开始定时器PWM输出
请见第18.2.1小节中开始定时器PWM输出的相关内容。

21.2.2 高级定时器驱动
本章实验的高级定时器驱动主要负责向应用层提供高级定时器的初始化和输出指定个数PWM的函数,并实现高级定时器的中断回调函数。本章实验中,高级定时器的驱动代码包括atim.c和atim.h两个文件。
高级定时器驱动中,对TIM、GPIO的相关宏定义,如下所示:
  1. #define ATIM_TIMX_NPWM             TIM8
  2. #define ATIM_TIMX_NPWM_IRQn        TIM8_UP_TIM13_IRQn
  3. #define ATIM_TIMX_NPWM_IRQHandler  TIM8_UP_TIM13_IRQHandler
  4. #define ATIM_TIMX_NPWM_CLK_ENABLE()    \
  5.     do {                                \
  6.          __HAL_RCC_TIM8_CLK_ENABLE();   \
  7.     }while (0)
  8. #define ATIM_TIMX_NPWM_CHY             TIM_CHANNEL_1
  9. #define ATIM_TIMX_NPWM_CHY_GPIO_PORT   GPIOC
  10. #define ATIM_TIMX_NPWM_CHY_GPIO_PIN    GPIO_PIN_6
  11. #define ATIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE()   \
  12.     do {                                        \
  13.          __HAL_RCC_GPIOC_CLK_ENABLE();          \
  14.     }while (0)
复制代码
高级定时器驱动中TIM8的初始化函数,如下所示:
  1. /**
  2. *@brief   初始化高级定时器输出指定个数PWM
  3. *@param   arr: 自动重装载值
  4. *@param   psc: 预分频系数
  5. *@retval  无
  6. */
  7. voidatim_timx_chy_npwm_init(uint16_t arr, uint16_t psc)
  8. {
  9.    GPIO_InitTypeDef gpio_init_struct;
  10.    TIM_OC_InitTypeDef timx_oc_init_struct = {0};
  11.    
  12.     /* 使能相关时钟 */
  13.    ATIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE();
  14.    ATIM_TIMX_NPWM_CLK_ENABLE();
  15.    
  16.     /* 配置PWM输出引脚 */
  17.    gpio_init_struct.Pin = ATIM_TIMX_NPWM_CHY_GPIO_PIN;
  18.    gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  19.    gpio_init_struct.Pull = GPIO_PULLUP;
  20.    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  21.    HAL_GPIO_Init(ATIM_TIMX_NPWM_CHY_GPIO_PORT, &gpio_init_struct);
  22.    
  23.     /* 配置中断优先级并使能中断 */
  24.    HAL_NVIC_SetPriority(ATIM_TIMX_NPWM_IRQn, 0, 0);
  25.    HAL_NVIC_EnableIRQ(ATIM_TIMX_NPWM_IRQn);
  26.    
  27.     /* 初始化定时器PWM */
  28.    g_timx_npwm_handle.Instance = ATIM_TIMX_NPWM;
  29.    g_timx_npwm_handle.Init.Prescaler = psc;
  30.    g_timx_npwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
  31.    g_timx_npwm_handle.Init.Period = arr;
  32.    g_timx_npwm_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  33.    g_timx_npwm_handle.Init.RepetitionCounter = 0;
  34.    HAL_TIM_PWM_Init(&g_timx_npwm_handle);
  35.    
  36.     /* 配置定时器PWM输出通道 */
  37.    timx_oc_init_struct.OCMode = TIM_OCMODE_PWM1;
  38.    timx_oc_init_struct.Pulse = (arr + 1) >> 1;
  39.    timx_oc_init_struct.OCPolarity = TIM_OCPOLARITY_HIGH;
  40.    HAL_TIM_PWM_ConfigChannel(  &g_timx_npwm_handle,
  41.                                  &timx_oc_init_struct,
  42.                                  ATIM_TIMX_NPWM_CHY);
  43.    
  44.     /* 开始定时器PWM输出 */
  45.    __HAL_TIM_ENABLE_IT(&g_timx_npwm_handle, TIM_IT_UPDATE);
  46.    HAL_TIM_PWM_Start(&g_timx_npwm_handle, ATIM_TIMX_NPWM_CHY);
  47. }
复制代码
从TIM8的初始化代码中可以看到,初始化函数中初始化了TIM8的PWM并配置了TIM8的PWM输出通道1,最后开始TIM8的PWM输出。
高级定时器驱动中,开启高级定时器输出指定个数PWM的函数,如下所示:
  1. /**
  2. *@brief   设置高级定时器输出指定个数PWM
  3. *@paramn  pwm: 指定PWM个数
  4. *@retval  无
  5. */
  6. voidatim_timx_chy_npwm_set(uint32_t npwm)
  7. {
  8.     if (npwm == 0)
  9.     {
  10.          return;
  11.     }
  12.    
  13.     /* 产生更新事件来输出指定个数PWM */
  14.    g_timx_chy_npwm_remain = npwm;
  15.    HAL_TIM_GenerateEvent(&g_timx_npwm_handle, TIM_EVENTSOURCE_UPDATE);
  16.    __HAL_TIM_ENABLE(&g_timx_npwm_handle);
  17. }
复制代码
该函数记录下了需要产生的PWM个数,因为会在TIM8的更新中断中处理输出PWM,因此手动产生了一次更新事件,由于初始化函数中开启了TIM8的更新中断,因此随后会进入TIM的中断服务函数中。
高级定时器驱动中,TIM8的中断回调函数,如下所示:
  1. /**
  2. *@brief   输出指定个数PWM高级定时器中断回调函数
  3. *@param   无
  4. *@retval  无
  5. */
  6. voidATIM_TIMX_NPWM_IRQHandler(void)
  7. {
  8.     uint16_t npwm = 0;
  9.    
  10.     /* 定时器更新事件中断 */
  11.     if (__HAL_TIM_GET_FLAG(&g_timx_npwm_handle, TIM_FLAG_UPDATE) != RESET)
  12.     {
  13.          /* 每次最多输出256个PWM(TIMx_RCR只有[7:0]有效) */
  14.          if (g_timx_chy_npwm_remain > 256)
  15.          {
  16.              g_timx_chy_npwm_remain -= 256;
  17.              npwm = 256;
  18.          }
  19.          else if ((g_timx_chy_npwm_remain % 256) != 0)
  20.          {
  21.              npwm = g_timx_chy_npwm_remain % 256;
  22.              g_timx_chy_npwm_remain = 0;
  23.          }
  24.          
  25.          if (npwm != 0)
  26.          {
  27.              /* 利用高级定时器的重复计数器来输出PWM */
  28.              ATIM_TIMX_NPWM->RCR = npwm - 1;
  29.              HAL_TIM_GenerateEvent(&g_timx_npwm_handle,TIM_EVENTSOURCE_UPDATE);
  30.              __HAL_TIM_ENABLE(&g_timx_npwm_handle);
  31.          }
  32.          else
  33.          {
  34.              ATIM_TIMX_NPWM->CR1 &= ~(TIM_CR1_CEN);
  35.          }
  36.          
  37.          /* 清除更新事件标志 */
  38.          __HAL_TIM_CLEAR_IT(&g_timx_npwm_handle, TIM_IT_UPDATE);
  39.     }
  40. }
复制代码
因为高级定时器的重复计数寄存器只有低八位有效,因此需要在需要产生大于255个PWM的情况下做特殊处理,随后将需要产生的PWM个数值减1写入TIM8的重复计数寄存器,并且由于写入重复计数寄存器的新值只有在下一次更新事件发生时才生效,因此需要手动产生一次更新事件,最后使能TIM8,这么一来,TIM8就会持续地输出PWM直到重复计数寄存器的数值为0后,产生一次更新中断,若此时已经输出完了指定个数的PWM,那么就会关闭高级定时器。

21.2.3 实验应用代码
本章实验的应用代码,如下所示:
  1. int main(void)
  2. {
  3.     uint8_t key;
  4.     uint8_t t = 0;
  5.    
  6.    HAL_Init();                                 /* 初始化HAL库 */
  7.    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 配置时钟,72MHz */
  8.    delay_init(72);                             /* 初始化延时 */
  9.    usart_init(115200);                         /* 初始化串口 */
  10.    led_init();                                 /* 初始化LED */
  11.    key_init();                                 /* 初始化按键 */
  12.    atim_timx_chy_npwm_init(5000-1, 7200-1);   /* 初始化高级定时器输出指定个数PWM */
  13.    
  14.     while (1)
  15.     {
  16.          key = key_scan(0);
  17.          if (key == KEY0_PRES)
  18.          {
  19.              /* 设置高级定时器输出5个PWM */
  20.              atim_timx_chy_npwm_set(5);
  21.          }
  22.          
  23.          if (++t == 20)
  24.          {
  25.              t = 0;
  26.              LED0_TOGGLE();
  27.          }
  28.          
  29.          delay_ms(10);
  30.     }
  31. }
复制代码
从上面的代码中可以看到,TIM8的自动重装载值配置为(5000-1),TIM8的预分频器数值配置为(7200-1),并且TIM8的时钟频率为72MHz,因此TIM8的计数频率为10KHz,且TIM8每计数5000次溢出一次,因此溢出频率为2Hz,也就是输出PWM的频率为2Hz。
初始化完TIM8后,就重复地扫描按键,若KEY0按键被按下,则输出5个PWM。

21.3 下载验证
在完成编译和烧录操作后,按下KEY0按键,此时可以通过示波器或外接LED的方式观察PC6引脚输出了5个频率为2Hz,占空比为50%的PWM。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

6

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2024-5-7
在线时间
10 小时
发表于 2024-5-20 10:30:12 | 显示全部楼层
本帖最后由 2772777 于 2024-5-20 10:31 编辑

你好,你的ATIM_TIMX_NPWM_IRQHandler函数第23行少了一个else{ npwm =0},    否则输入任意的脉冲数都会一直输出PWM。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 19:16

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表