谷动谷力

 找回密码
 立即注册
谷动谷力 门户 查看主题

STM32F103RCT6开发板M3单片机教程07--TIMER1CH1输出 PWM做LED呼吸灯

发布者: sunsili | 发布时间: 2021-12-30 23:36| 查看数: 5443| 评论数: 2|帖子模式

本帖最后由 sunsili 于 2024-2-20 16:21 编辑

STM32F103RCT6开发板M3单片机教程07-TIMER1CH1输出 PWM做LED呼吸灯


本教程使用是(光明谷SUN_STM32mini开发板
212247mr8ksgib4e2u4uhy.jpg

免费开发板

谷动谷力社区注册用户,打卡,发帖求助都可以获取积分,当然最主要是发原创应用文档奖励更多积分.
(可用积分换取,真的不用钱,开发板免费玩):STM32F103RCT6开发板M3单片机核芯小系统板学习板 ...
已经购买用的,可以注册用户,打卡,发帖求助都可以获取积分,当然最主要是发原创应用文档奖励更多积分.用积分换钱,返还您的买开发板钱


书接上回:
上回我们用Timer2做了一个定时中断,让LED灯闪烁(STM32F103RCT6开发板M3单片机教程06--定时器中断 .. )

这回我们用Timer1的CH1 输出PWM做呼吸灯参看原理图(详细到 STM32F103RCT6_SDK开发资料应用手册下载
183023fookomoc15tf4kf6.png

何为呼吸灯?
慢慢变亮又慢慢变暗到灭,如此往复循环灯为呼吸灯也。

得知PA8连接LED阴极,低电平点亮。
参考STM32F103RCT6数据手册, 发现PA8是Timer1的CH1,可以输出PWM

STM32F103RCT6开发板M3单片机教程

STM32F103RCT6开发板M3单片机教程


上回有云:
大容量的STM32F103xx增强型系列产品包含最多2个高级控制定时器、 4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器。
下表比较了高级控制定时器、普通定时器和基本定时器的功能:
233105pn39kwae9eexqwde.png


高级控制定时器(TIM1和TIM8)
两个高级控制定时器(TIM1TIM8)可以被看成是分配到6个通道的三相PWM发生器,它具有带死区插入的互补PWM输出,还可以被当成完整的通用定时器。四个独立的通道可以用于:
● 输入捕获
● 输出比较
● 产生
PWM(边缘或中心对齐模式)
● 单脉冲输出
配置为
16位标准定时器时,它与TIMx定时器具有相同的功能。配置为16PWM发生器时,它具有全调制能力(0~100%)
在调试模式下,计数器可以被冻结,同时
PWM输出被禁止,从而切断由这些输出所控制的开关。
很多功能都与标准的
TIM定时器相同,内部结构也相同,因此高级控制定时器可以通过定时器链接功能与TIM定时器协同操作,提供同步或事件链接功能。


程序流程:

1、系统初始化(RCC, GPIO)
2、设定初始LED亮度为0,亮度递增
3、高度到100%(全亮时),亮度递减
返回第2步

开发码码

复制上节的工程源,更改文件夹名
修改BSP下Led.c, LED_GPIO_Config函数
更改主GPIO输出模式
原://        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //设置引脚工作模式为通用推挽输出
改:GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //PWMout


  1. /*******************************************************************************
  2. * 函数名  : LED_GPIO_Config
  3. * 描述    : LED IO配置
  4. * 输入    : 无
  5. * 输出    : 无
  6. * 返回    : 无
  7. * 说明    : LED(1~4)的IO口分别是:PB5,PB6,PB7,PB8
  8. *******************************************************************************/
  9. void LED_GPIO_Config(void)
  10. {
  11.         GPIO_InitTypeDef  GPIO_InitStructure;                                //定义一个GPIO_InitTypeDef类型的GPIO初始化结构体
  12.         
  13.         RCC_APB2PeriphClockCmd(LED_RCC, ENABLE);                        //使能GPIOB的外设时钟        
  14.         
  15.         GPIO_InitStructure.GPIO_Pin = LED_ALL;                                //选择要初始化的GPIOB引脚(PA5,PA6,PA7,PA8)
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //PWMout
  17. //        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //设置引脚工作模式为通用推挽输出                 
  18.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //设置引脚输出最大速率为50MHz
  19.         GPIO_Init(LED_PORT, &GPIO_InitStructure);                        //调用库函数中的GPIO初始化函数,初始化GPIOB中的PA5,PA6,PA7,PA8引脚
  20.    

  21.         LED_ALL_OFF();                                                                                //关闭ALL_LED                                          
  22. }
复制代码


修改main.c,  原Timer2 定时函数, 修改通用Timer_Init_Config定时中断配置函数(这个也可以不修改不影响功能,为了让源码更接近实际项目代码,提高代码可用性,可移植性,所以这样修改)一般来说TimerPeriod越大越好越定时越精确,但不能超过16bits 最大数65535。

  1. /*******************************************************************************
  2. * 函数名  : Timer_Init_Config
  3. * 描述    : Timer初始化配置
  4. * 输入    : TIM_TypeDef* TIMx 定时器名 TIM1~8
  5.             uint16_t fre_hz 定时器 中断 频率
  6. * 输出    : 无
  7. * 返回    : 无
  8. * 说明    : 无
  9. *******************************************************************************/
  10. void Timer_Init_Config(TIM_TypeDef* TIMx, uint16_t fre_hz)
  11. {
  12.         TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  13.         NVIC_InitTypeDef NVIC_InitStructure;
  14.         uint16_t TimerPeriod = 0;
  15.     uint16_t TimerPrescaler = 71; //1Mhz Timer Conter frequency
  16.    
  17.     /* Compute the value to be set in ARR regiter to generate signal frequency at Fre_hz */
  18.         TimerPeriod = (SystemCoreClock / fre_hz /( TimerPrescaler +1)) - 1;
  19.         TIM_TimeBaseStructure.TIM_Period = TimerPeriod; //4999;                                        //设置在下一个更新事件装入活动的自动重装载寄存器周期的值(计数到5000为500ms)
  20.         TIM_TimeBaseStructure.TIM_Prescaler = TimerPrescaler; //7199;                                        //设置用来作为TIMx时钟频率除数的预分频值(10KHz的计数频率) 71
  21.         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                //设置时钟分割:TDTS = TIM_CKD_DIV1
  22.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;        //TIM向上计数模式
  23.         TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);                                //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

  24.         /*中断优先级NVIC设置*/
  25.         NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;                                //TIM2中断
  26.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;        //先占优先级1级
  27.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;                        //从优先级1级
  28.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                //使能IRQ通道
  29.         NVIC_Init(&NVIC_InitStructure);                                                         //初始化NVIC寄存器
  30.          
  31.         TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE );                                 //使能TIMx指定的中断
  32.         
  33.         TIM_Cmd(TIMx, ENABLE);                                                                          //使能TIMx外设
  34. }         
复制代码


修改main函数
  1. /*******************************************************************************
  2. * 函数名  : main
  3. * 描述    : 主函数,用户程序从main函数开始运行
  4. * 输入    : 无
  5. * 输出    : 无
  6. * 返回值  : int:返回值为一个16位整形数
  7. * 说明    : 无
  8. *******************************************************************************/
  9. int main(void)
  10. {
  11.     u8 keyVal;
  12.     RCC_Configuration();
  13.     SysTick_Init_Config();
  14.         USART1_Init_Config(115200);//USART1初始化配置
  15.     LED_GPIO_Config();
  16.     Key_GPIO_Config();
  17.    
  18.     printf ("*===================================================*\n");
  19.     printf ("*  *  Name: Sun STM32 mini Demo Code.    *************\n");
  20.     printf ("*  * (C) Sunshine Silicon Corporation    *************\n");
  21.     printf ("*  *  Website: http://www.sunsili.com    *************\n");
  22.     printf ("*  *   E-Mail : fan@sunsili.com          *************\n");
  23.     printf ("*===================================================*\n");
  24.     printf ("* Sun STM32 mini PWM  Demo code .*\n");
  25.    
  26.     lum = 0;    //Start luminance 0
  27.     flag = 1;   //Up count in
  28.     Timer_Ch1_pwm_init(TIM1, 10000, lum);
  29.     Timer_Init_Config(TIM2, 100);        //Timer2初始化配置 100Hz 中断频率 10ms中断一次
  30.    
  31.         while (1)
  32.         {
  33.         if(tim2_tick)
  34.         {
  35.             tim2_tick = 0;
  36.             if(flag)
  37.             {
  38.                if(++lum >= 100)
  39.                {
  40.                    flag = 0;    //Down count in
  41.                }
  42. //               LED4_ON();
  43.             }
  44.             else
  45.             {               
  46.                 if(!lum)
  47.                 {
  48.                     flag = 1;
  49.                 }
  50.                 else
  51.                 {
  52.                     lum--;
  53.                 }
  54. //                LED4_OFF();
  55.             }
  56.             TIM_OC1_set_Pulse(TIM1, 10000, 100-lum);
  57.         }
  58.         }
复制代码



增加:
  1. /*******************************************************************************
  2. * 函数名  : Timer_Ch1_pwminit
  3. * 描述    : 定时器 Ch1 pwm 初始化
  4. * 输入    : TIM_TypeDef* TIMx  定时器名 TIM1~8
  5.             uint16_t fre_hz PWM 频率
  6.             uint16_t dty 占空比 0-100
  7. * 输出    : 无
  8. * 返回    : 无
  9. * 说明    : 无
  10. *******************************************************************************/
  11. void Timer_Ch1_pwm_init(TIM_TypeDef* TIMx, uint16_t fre_hz, uint16_t dty)
  12. {
  13.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  14.     TIM_OCInitTypeDef  TIM_OCInitStructure;
  15.     uint16_t TimerPeriod = 0;
  16.     uint16_t Channel1Pulse = 0;
  17.    
  18.     /* TIM1 Configuration ---------------------------------------------------
  19.    Generate 7 PWM signals with 4 different duty cycles:
  20.    TIM1CLK = SystemCoreClock, Prescaler = 0, TIM1 counter clock = SystemCoreClock
  21.    SystemCoreClock is set to 72 MHz for Low-density, Medium-density, High-density
  22.    and Connectivity line devices and to 24 MHz for Low-Density Value line and
  23.    Medium-Density Value line devices
  24.    
  25.    The objective is to generate 7 PWM signal at 17.57 KHz:
  26.      - TIM1_Period = (SystemCoreClock / 17570) - 1
  27.    The channel 1 and channel 1N duty cycle is set to 50%
  28.    The channel 2 and channel 2N duty cycle is set to 37.5%
  29.    The channel 3 and channel 3N duty cycle is set to 25%
  30.    The channel 4 duty cycle is set to 12.5%
  31.    The Timer pulse is calculated as follows:
  32.      - ChannelxPulse = DutyCycle * (TIM1_Period - 1) / 100
  33.   ----------------------------------------------------------------------- */
  34.   /* Compute the value to be set in ARR regiter to generate signal frequency at Fre_hz */
  35.   TimerPeriod = (SystemCoreClock / fre_hz ) - 1;
  36.   /* Compute CCR1 value to generate a duty cycle at duty for channel 1 and 1N */
  37.   Channel1Pulse = (uint16_t) (((uint32_t) dty * (TimerPeriod - 1)) / 100);
  38.   /* Compute CCR2 value to generate a duty cycle at 37.5%  for channel 2 and 2N */
  39. //  Channel2Pulse = (uint16_t) (((uint32_t) 375 * (TimerPeriod - 1)) / 1000);
  40.   /* Compute CCR3 value to generate a duty cycle at 25%  for channel 3 and 3N */
  41. //  Channel3Pulse = (uint16_t) (((uint32_t) 25 * (TimerPeriod - 1)) / 100);
  42.   /* Compute CCR4 value to generate a duty cycle at 12.5%  for channel 4 */
  43. //  Channel4Pulse = (uint16_t) (((uint32_t) 125 * (TimerPeriod- 1)) / 1000);

  44.   /* Time Base configuration */
  45.   TIM_TimeBaseStructure.TIM_Prescaler = 0;
  46.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  47.   TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
  48.   TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  49.   TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

  50.   TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

  51.   /* Channel 1, 2,3 and 4 Configuration in PWM mode */
  52.   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
  53.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  54.   TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
  55.   TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;
  56.   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  57.   TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
  58.   TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
  59.   TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

  60.   TIM_OC1Init(TIMx, &TIM_OCInitStructure);
  61.   
  62.   /* TIM1 counter enable */
  63.   TIM_Cmd(TIMx, ENABLE);

  64.   /* TIM1 Main Output Enable */
  65.   TIM_CtrlPWMOutputs(TIMx, ENABLE);


  66. }


  67. /*******************************************************************************
  68. * 函数名  : TIM_OC1_set_Pulse
  69. * 描述    : 定时器 Ch1 pwm 脉冲相关设置
  70. * 输入    : TIM_TypeDef* TIMx  定时器名 TIM1~8
  71.             uint16_t prd  PWD周期
  72.             uint16_t dty 占空比 0-100
  73. * 输出    : 无
  74. * 返回    : 无
  75. * 说明    : 无
  76. *******************************************************************************/
  77. void TIM_OC1_set_Pulse(TIM_TypeDef* TIMx, uint16_t fre_hz, uint16_t dty)
  78. {
  79.     uint16_t Channel1Pulse = 0;
  80.     uint16_t TimerPeriod = 0;
  81.    
  82.      /* Compute the value to be set in ARR regiter to generate signal frequency at Fre_hz */
  83.     TimerPeriod = (SystemCoreClock / fre_hz ) - 1;
  84.     /* Compute CCR1 value to generate a duty cycle at duty for channel 1 and 1N */
  85.     Channel1Pulse = (uint16_t) (((uint32_t) dty * (TimerPeriod - 1)) / 100);
  86.     /* Set the Capture Compare Register value */
  87.     TIMx->CCR1 = Channel1Pulse;
  88. }
复制代码

修改:RCC_Configuration函数

  1. /*******************************************************************************
  2. * 函数名  : RCC_Configuration
  3. * 描述    : 设置系统时钟为72MHZ(这个可以根据需要改)
  4. * 输入    : 无
  5. * 输出    : 无
  6. * 返回值  : 无
  7. * 说明    : STM32F107x和STM32F105x系列MCU与STM32F103x系列MCU时钟配置有所不同
  8. *******************************************************************************/
  9. void RCC_Configuration(void)
  10. {
  11.   ErrorStatus HSEStartUpStatus;               //外部高速时钟(HSE)的工作状态变量
  12.   
  13.   RCC_DeInit();                               //将所有与时钟相关的寄存器设置为默认值
  14.   RCC_HSEConfig(RCC_HSE_ON);                  //启动外部高速时钟HSE
  15.   HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待外部高速时钟(HSE)稳定

  16.   if(SUCCESS == HSEStartUpStatus)             //如果外部高速时钟已经稳定
  17.   {
  18.     /* Enable Prefetch Buffer */
  19.     FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Flash设置
  20.     /* Flash 2 wait state */
  21.     FLASH_SetLatency(FLASH_Latency_2);
  22.    
  23.   
  24.     RCC_HCLKConfig(RCC_SYSCLK_Div1); //设置AHB时钟等于系统时钟(1分频)/72MHZ
  25.     RCC_PCLK2Config(RCC_HCLK_Div1);  //设置APB2时钟和HCLK时钟相等/72MHz(最大为72MHz)
  26.     RCC_PCLK1Config(RCC_HCLK_Div2);  //设置APB1时钟是HCLK时钟的2分频/36MHz(最大为36MHz)
  27.    
  28.     RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHz * 9 = 72 MHz


  29.     RCC_PLLCmd(ENABLE); //使能PLL
  30.     while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //等待PLL稳定

  31.     RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);          //设置系统时钟的时钟源为PLL

  32.     while(RCC_GetSYSCLKSource() != 0x08);               //检查系统的时钟源是否是PLL
  33.     RCC_ClockSecuritySystemCmd(ENABLE);                 //使能系统安全时钟
  34.    
  35.     /* TIM1 GPIOx clock enable */
  36.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);
  37.     /* TIM2 clock enable */
  38.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  39.   }
  40. }
复制代码


保存全部,编译, 下载。


运行结果:

STM32F103RCT6开发板M3单片机教程

STM32F103RCT6开发板M3单片机教程

STM32F103RCT6开发板M3单片机教程

STM32F103RCT6开发板M3单片机教程


总结课后作业
函数封装,原则:
1.main函数尽量少代码,简洁明了,体现主要让MCU干了哪些事。
2.中断服务函数尽量占用MCU运行时间,如果中断里占用MCU大量运行时间,会干扰其他任务执行
3.提高代码复用,提高代码效率,这样可以低配置的MCU, 干更多事,降低成本,重点涨工资
4.可移植性

zip.gif 07STM32F103RCT6_PWM.zip (318.95 KB, 下载次数: 1)

最新评论

sunsili 发表于 2021-12-31 00:00:08
本帖最后由 sunsili 于 2024-2-20 16:22 编辑

工程源码包里, maini.c 有个没用到的函数:void led_breath (void);
一起来精简一下main函数吧,知道做的小伙伴,做出来,有奖品




07STM32F103RCT6_PWM.zip

318.95 KB, 下载次数: 13

sunsili 发表于 2021-12-31 00:04:58
代码,还有几处不严谨的地方,全部找出来,也有奖品,
奖品统统是开发板

关注我们的公众号,把答案发到我们的公众号

谷动谷力公众号

谷动谷力公众号

QQ|Archiver|手机版|深圳市光明谷科技有限公司|光明谷商城|Sunshine Silicon Corpporation ( 粤ICP备14060730号|Sitemap

GMT+8, 2024-7-19 04:11 , Processed in 0.144183 second(s), 42 queries .

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

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