• 【STM32】TIM2的PWM:脉冲宽度调制


    注意点:

    TIM_Period---->指要进行比较的值Compare

    TIM_Prescaler----> 指要进行分频的值【分频值/原始时钟值】

    PWM是一种周期固定,脉宽可 调整的输出波形。

    ​​​​​​https://www.cnblogs.com/brianblog/p/7117896.html

    0.通用寄存器输出

    1.捕获/比较通道1的主电路--中间部分

    1)在程序员写入CCR1(比较值)的时候,值是不会传输到影子寄存器中的

    2)影子寄存器百年直接被访问

    2.捕获/比较通道的输出部分--输出

    有8种输出模式

    PWM输出有两种模式:PWM1和PWM2

    3.通用定时器输出PWM原理

    PWM波周期或者频率由ARR(就是要进递增/递减的值)决定,PWM波占空比由CRRx决定。

    4.PWM模式图解

    1.定时器的PWM输出功能介绍

    使用PWM波形,就可以在数字系统等效输出模拟量

     

    1)通过定时器的中断,在isr中将一个GPIO引脚电平反转,可以实现PWM输出功能【麻烦,设置多】

    2)定时器附带专用的PWM输出功能,定时器那边和某一个引脚绑定,然后定时器设置好了之后内部开始+1或者-1,然后时间到了之后不是产生中断,而是直接将绑定的引脚电平反转产生PWM输出。【CPU不参与,效率高】

    1.占空比:脉宽(高电平)占总周期的比例

    1)可以用来调制脉冲宽度--》脉冲宽度调制

    2)占空比的调节,是通过比较值与计数器的大小差距,当两者的关系改变的时,会进行电平反转。

    3)占空比越大,那等效的模拟电压就越趋近于低电平​​​​​​​。占空比越小,等效的模拟电压降越趋近于高电平。

    2.PWM频率

    频率越大,切换速度越快,时间段越短

    3.PWM占空比和周期

    4.PWM1  VS  PWM2

    5.PWM参数计算

    6.通用定时器-->输出比较通道

    7.输出比较模式

    1.冻结:相当于CNT和CCR是无效的,当我们想要在输出PWM的时候先暂停一下,就使用这个模式。

    2.匹配时电平翻转:可以输出占空比50%的PWM波

    3.PWM1和PWM2模式:都可以输出可调的PWM波形

     8.高级定时器-->输出比较通道

    2.专用PWM输出的实现原理

    1.比较功能

    1)所谓的比较原理,设计3个计数有关的寄存器:CMP(比较),CNT(计数器),ARR(存放计数原始值)

    定时器有4个输出通道,每一个通道都有一个捕获/比较寄存器,将寄存器值(ARR)和计数器值(CNT)进行比较,通过比较结果输出高低电平,实现PWM信号输出。

    高低电平的1和0可以进行设置

    2)在输入捕获/输出比较功能中--都要使用同一个外部引脚

    3)每一个定时器只有一个计数器,但是每一个通道都有自己的捕获/比较寄存器,因此对于一个定时器来说,4路输出的PWM频率(周期)都是相同的,而不同通道的占空比可以不同。

    2.相关寄存器

    1.TIMx_CNT(计数器),TIMx_ARR(自动重装载寄存器),TIMx_CCRn(捕获/比较寄存器)

    TIMx_CCRn:是来选择哪一条通道

    2.CCMR1,CCMR2,CCER:捕获/比较模式寄存器的基本配置

    CCMR1:处理了通道1和通道2

    CCMR2:处理了通道3和通道4

    CCER:配置要什么电平才是有效的

    3.CR1,CR2,PSC

    CR1,CR2:使能,开关

    PSC:分频功能

    3.标准库中相关的API

    1.TIM_TimeBaseInit

    定时器的基本初始化,包括要进行分频的频率,计数个数

    1. void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
    2. {
    3. uint16_t tmpcr1 = 0;
    4. /* Check the parameters */
    5. assert_param(IS_TIM_ALL_PERIPH(TIMx));
    6. assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
    7. assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));
    8. tmpcr1 = TIMx->CR1;
    9. if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
    10. (TIMx == TIM4) || (TIMx == TIM5))
    11. {
    12. /* Select the Counter Mode */
    13. tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
    14. tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
    15. }
    16. if((TIMx != TIM6) && (TIMx != TIM7))
    17. {
    18. /* Set the clock division */
    19. tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
    20. tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
    21. }
    22. TIMx->CR1 = tmpcr1;
    23. /* Set the Autoreload value */
    24. //要计数的值
    25. TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
    26. /* Set the Prescaler value */
    27. //预分频参数
    28. TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
    29. if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))
    30. {
    31. /* Set the Repetition Counter value */
    32. TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
    33. }
    34. /* Generate an update event to reload the Prescaler and the Repetition counter
    35. values immediately */
    36. //预分频器参数的改变
    37. TIMx->EGR = TIM_PSCReloadMode_Immediate;
    38. }

    2.TIM_OC1Init(TIM_OCnInit)

    TIM_OCn--->指的使用了哪一个通道

    1. void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct)
    2. {
    3. uint16_t tmpccmrx = 0, tmpccer = 0, tmpcr2 = 0;
    4. /* Check the parameters */
    5. assert_param(IS_TIM_LIST8_PERIPH(TIMx));
    6. assert_param(IS_TIM_OC_MODE(TIM_OCInitStruct->TIM_OCMode));
    7. assert_param(IS_TIM_OUTPUT_STATE(TIM_OCInitStruct->TIM_OutputState));
    8. assert_param(IS_TIM_OC_POLARITY(TIM_OCInitStruct->TIM_OCPolarity));
    9. /* Disable the Channel 1: Reset the CC1E Bit */
    10. TIMx->CCER &= (uint16_t)(~(uint16_t)TIM_CCER_CC1E);
    11. /* Get the TIMx CCER register value */
    12. tmpccer = TIMx->CCER;
    13. /* Get the TIMx CR2 register value */
    14. tmpcr2 = TIMx->CR2;
    15. /* Get the TIMx CCMR1 register value */
    16. tmpccmrx = TIMx->CCMR1;
    17. /* Reset the Output Compare Mode Bits */
    18. tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_OC1M));
    19. tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR1_CC1S));
    20. /* Select the Output Compare Mode */
    21. tmpccmrx |= TIM_OCInitStruct->TIM_OCMode;
    22. /* Reset the Output Polarity level */
    23. tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1P));
    24. /* Set the Output Compare Polarity */
    25. tmpccer |= TIM_OCInitStruct->TIM_OCPolarity;
    26. /* Set the Output State */
    27. tmpccer |= TIM_OCInitStruct->TIM_OutputState;
    28. if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)||
    29. (TIMx == TIM16)|| (TIMx == TIM17))
    30. {
    31. assert_param(IS_TIM_OUTPUTN_STATE(TIM_OCInitStruct->TIM_OutputNState));
    32. assert_param(IS_TIM_OCN_POLARITY(TIM_OCInitStruct->TIM_OCNPolarity));
    33. assert_param(IS_TIM_OCNIDLE_STATE(TIM_OCInitStruct->TIM_OCNIdleState));
    34. assert_param(IS_TIM_OCIDLE_STATE(TIM_OCInitStruct->TIM_OCIdleState));
    35. /* Reset the Output N Polarity level */
    36. tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1NP));
    37. /* Set the Output N Polarity */
    38. tmpccer |= TIM_OCInitStruct->TIM_OCNPolarity;
    39. /* Reset the Output N State */
    40. tmpccer &= (uint16_t)(~((uint16_t)TIM_CCER_CC1NE));
    41. /* Set the Output N State */
    42. tmpccer |= TIM_OCInitStruct->TIM_OutputNState;
    43. /* Reset the Output Compare and Output Compare N IDLE State */
    44. tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS1));
    45. tmpcr2 &= (uint16_t)(~((uint16_t)TIM_CR2_OIS1N));
    46. /* Set the Output Idle state */
    47. tmpcr2 |= TIM_OCInitStruct->TIM_OCIdleState;
    48. /* Set the Output N Idle state */
    49. tmpcr2 |= TIM_OCInitStruct->TIM_OCNIdleState;
    50. }
    51. /* Write to TIMx CR2 */
    52. TIMx->CR2 = tmpcr2;
    53. /* Write to TIMx CCMR1 */
    54. TIMx->CCMR1 = tmpccmrx;
    55. /* Set the Capture Compare Register value */
    56. TIMx->CCR1 = TIM_OCInitStruct->TIM_Pulse;
    57. /* Write to TIMx CCER */
    58. TIMx->CCER = tmpccer;
    59. }

    3.TIM_OCInitTypeDef:OC的结构体

    1. typedef struct
    2. {
    3. //选择TIM的模式:PWM1或者PWM2
    4. uint16_t TIM_OCMode; /*!< Specifies the TIM mode.
    5. This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
    6. //选择TIM的输出状态:向上/向下
    7. uint16_t TIM_OutputState; /*!< Specifies the TIM Output Compare state.
    8. This parameter can be a value of @ref TIM_Output_Compare_state */
    9. uint16_t TIM_OutputNState; /*!< Specifies the TIM complementary Output Compare state.
    10. This parameter can be a value of @ref TIM_Output_Compare_N_state
    11. @note This parameter is valid only for TIM1 and TIM8. */
    12. //要进行比较的值:Compare值
    13. uint16_t TIM_Pulse; /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
    14. This parameter can be a number between 0x0000 and 0xFFFF */
    15. //输出的极性
    16. uint16_t TIM_OCPolarity; /*!< Specifies the output polarity.
    17. This parameter can be a value of @ref TIM_Output_Compare_Polarity */
    18. uint16_t TIM_OCNPolarity; /*!< Specifies the complementary output polarity.
    19. This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
    20. @note This parameter is valid only for TIM1 and TIM8. */
    21. uint16_t TIM_OCIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state.
    22. This parameter can be a value of @ref TIM_Output_Compare_Idle_State
    23. @note This parameter is valid only for TIM1 and TIM8. */
    24. uint16_t TIM_OCNIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state.
    25. This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
    26. @note This parameter is valid only for TIM1 and TIM8. */
    27. } TIM_OCInitTypeDef;

    1.TIM_OCMode:选择TIM的模式

    选择PWM1或者PWM2

    2.TIM_OutputState:选择输出状态

    选择输出的状态:enable/able

    3.TIM_Pulse:输入要进行比较的值(Compare)

    4.TIM_OCPolarity:设置输出极性

    4.TIM_OC1PreloadConfig

    作用:TIM_CCMRx寄存器OCxPE位使能相应的预装在寄存器【这个预装载的值是比较值】

    1. void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload)
    2. {
    3. uint16_t tmpccmr1 = 0;
    4. /* Check the parameters */
    5. assert_param(IS_TIM_LIST8_PERIPH(TIMx));
    6. assert_param(IS_TIM_OCPRELOAD_STATE(TIM_OCPreload));
    7. tmpccmr1 = TIMx->CCMR1;
    8. /* Reset the OC1PE Bit */
    9. tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC1PE);
    10. /* Enable or Disable the Output Compare Preload feature */
    11. tmpccmr1 |= TIM_OCPreload;
    12. /* Write to TIMx CCMR1 register */
    13. TIMx->CCMR1 = tmpccmr1;
    14. }

    6.TIM_ClearOC1Ref

    1. void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear)
    2. {
    3. uint16_t tmpccmr1 = 0;
    4. /* Check the parameters */
    5. assert_param(IS_TIM_LIST3_PERIPH(TIMx));
    6. assert_param(IS_TIM_OCCLEAR_STATE(TIM_OCClear));
    7. tmpccmr1 = TIMx->CCMR1;
    8. /* Reset the OC1CE Bit */
    9. tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC1CE);
    10. /* Enable or Disable the Output Compare Clear Bit */
    11. tmpccmr1 |= TIM_OCClear;
    12. /* Write to TIMx CCMR1 register */
    13. TIMx->CCMR1 = tmpccmr1;
    14. }

    7.TIM_OC1PolarityConfig

    1. void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity)
    2. {
    3. uint16_t tmpccer = 0;
    4. /* Check the parameters */
    5. assert_param(IS_TIM_LIST8_PERIPH(TIMx));
    6. assert_param(IS_TIM_OC_POLARITY(TIM_OCPolarity));
    7. tmpccer = TIMx->CCER;
    8. /* Set or Reset the CC1P Bit */
    9. tmpccer &= (uint16_t)~((uint16_t)TIM_CCER_CC1P);
    10. tmpccer |= TIM_OCPolarity;
    11. /* Write to TIMx CCER register */
    12. TIMx->CCER = tmpccer;
    13. }

    8.TIM_CtrlPWMOutputs

    高级定时器专用的

    9.TIM_SetComparex

    设置TIMx Capture Compare1寄存器值-->通过设置参数值可以修改PWM的占空比

    10.TIM_OCStructInit

    用默认值填充每个TIM_OCInitStruct成员。因为我们给定时器初始化的时候,并不是整个都进行赋值,后面可能会出现错误,所以我们需要在设置我们定时器需要的值之前先初始化全部参数。

    4.GPIO引脚和PWM的对应关系

    STM32F103中文教程及参考手册.pdf · 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com)

    在AFIO中进行查找

    没有重映像:表示默认接入的io口

    完全重映像:如果使用这个则要调用函数进行声明【GPIO_PinRemapConfig】

    5.TIM2的专用PWM输出编程实践

    1.官方示例代码

    我们使用的是TIM3,因为我们复用了GPIOA,所以要去AFIO中去查找TIM3对应的关系

    1. #include "pwm.h"
    2. #include "led.h"
    3. //PWM输出初始化
    4. //arr:自动重装值
    5. //psc:时钟预分频数
    6. void TIM1_PWM_Init(u16 arr,u16 psc)
    7. {
    8. GPIO_InitTypeDef GPIO_InitStructure;
    9. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    10. TIM_OCInitTypeDef TIM_OCInitStructure;
    11. RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //
    12. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能
    13. //设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
    14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1
    15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
    16. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    17. GPIO_Init(GPIOA, &GPIO_InitStructure);
    18. TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
    19. TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
    20. TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    21. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    22. TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    23. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
    24. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    25. TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
    26. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
    27. TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    28. TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能
    29. TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能
    30. TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
    31. TIM_Cmd(TIM1, ENABLE); //使能TIM1
    32. }

    2.代码移植

    我们先去查看我们进行操作的TIM2对应应该复用哪一个AFIO引脚

    可知TIM2的通道1对于的没有重映像是PA0

    1. #include "stm32f10x.h" // Device header
    2. /**
    3. 使用TIM2的Channel1,无重映射时对应PA0引脚,在原理图上对应P1.0
    4. */
    5. void pwm_init(void);
    6. int main(){
    7. pwm_init(); //频率是2Kh
    8. return 0;
    9. }
    10. void pwm_init(void)
    11. {
    12. GPIO_InitTypeDef GPIO_InitStructure; //声明一个结构体变量,用来初始化GPIO
    13. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//声明一个结构体变量,用来初始化定时器
    14. TIM_OCInitTypeDef TIM_OCInitStructure;//根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    15. /* 开启时钟 */
    16. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
    17. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    18. /* 配置GPIO的模式和IO口 */
    19. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
    20. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    21. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
    22. GPIO_Init(GPIOA,&GPIO_InitStructure); // GPA15,
    23. // time = CNT/fHz = 9000/72000000s
    24. // Fpwm = 1/T = 72000000/9000Hz = 8000Hz = 8KHz
    25. //TIM3定时器初始化
    26. TIM_TimeBaseInitStructure.TIM_Period = 9000 - 1; //不分频,PWM 频率=72000/900=8Khz//设置自动重装载寄存器周期的值
    27. TIM_TimeBaseInitStructure.TIM_Prescaler = 0;//设置用来作为TIMx时钟频率预分频值,100Khz计数频率
    28. TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;//设置时钟分割:TDTS = Tck_tim
    29. TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    30. TIM_TimeBaseInit(TIM2, & TIM_TimeBaseInitStructure);
    31. // 将TIM2的输出引脚进行fll remap到PA15,也就是P3.7
    32. //GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);
    33. //PWM初始化 //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
    34. TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
    35. TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//PWM输出使能
    36. //TIM_OCInitStructure.TIM_Pulse = 4500 - 1;
    37. //TIM_Pulse:设置占空比【占了1/3==3000/9000】
    38. TIM_OCInitStructure.TIM_Pulse = 3000 - 1;
    39. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    40. TIM_OC1Init(TIM2,&TIM_OCInitStructure);
    41. TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
    42. TIM_Cmd(TIM2,ENABLE);//使能或者失能TIMx外设
    43. }

    6.通用定时器PWM输出实验配置步骤

    1.具体步骤

    1.初始化时钟

    2.初始化定时器

    3.配置Oc部分

    4.使能定时器

    2.相关HAL库函数介绍

     

    3.条件分析

    1.查看相关的原理分析引脚的连接

    1)如果相关的开发版原理图上有led对应的引脚与定时器的通道相对应则直接使用(无论是复用GPIO还是重映射)

    例如下面这种情况,就是开发板上led对应PB5,而PB5的重映射功能上有定时器的相关通道

    2)如果相关的开发板上没有对应的,则直接找一个相关的定时器接口(比如stm32f103C8T5对应的

    7.PWM驱动LED呼吸灯

    1.硬件接线

    2.代码编写

    1.初始化注意点

    1)注意点:我们在对定时器结构体赋值的时候,并不是给全部变量进行赋值,所以这个值是局部变量,所以如果我们初始化没有全部赋值,则需要在最前面对结构体初始一个默认值

    2)决定输出PWM的周期和占空比(ARR【Period】,PSC【Prescaler】,Pulse【CCR】)

    3)我们的PWM需要通过一个GPIO引脚输出出去,所以我们需要对应引脚定义表,查看该定时器,对应是哪一个GPIO引脚。

    4)为什么GPIO引脚使用复用推挽输出?

    我们使用的GPIO设置高低电平(占空比)的大小,所以是输出模式。

    如果使用定时器则需要使用复用开漏/推挽输出模式。输出控制权将转移给片上外设,这里的片上外设引脚连接的就是TIM2的CH1通道,所以只有将GPIO设置为复用推挽输出,引脚的控制器才可以交给片上外设,PWM波形才可以通过引脚输出,引脚的控制器才能交给片上外设,PWM波形才能通过引脚输出。

    2.初始化函数

    1. /**
    2. * 函 数:PWM初始化
    3. * 参 数:无
    4. * 返 回 值:无
    5. */
    6. void PWM_Init(void)
    7. {
    8. /*开启时钟*/
    9. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
    10. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
    11. /*GPIO初始化*/
    12. GPIO_InitTypeDef GPIO_InitStructure;
    13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15;
    15. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    16. GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为复用推挽输出
    17. //受外设控制的引脚,均需要配置为复用模式
    18. /*配置时钟源*/
    19. TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
    20. /*时基单元初始化*/
    21. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
    22. TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    23. TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
    24. TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //计数周期,即ARR的值
    25. TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //预分频器,即PSC的值
    26. TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
    27. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
    28. /*输出比较初始化*/
    29. TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量
    30. TIM_OCStructInit(&TIM_OCInitStructure); //结构体初始化,若结构体没有完整赋值
    31. //则最好执行此函数,给结构体所有成员都赋一个默认值
    32. //避免结构体初值不确定的问题
    33. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1
    34. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性,选择为高,若选择极性为低,则输出高低电平取反
    35. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
    36. TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值
    37. TIM_OC1Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
    38. /*TIM使能*/
    39. TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
    40. }

    3.PSC,ARR,CCR值的计算

    我们想要产生一个频率为1KHZ,占空比为50%,分辨率为1%的PWM

    1. TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //计数周期,即ARR的值
    2. TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //预分频器,即PSC的值
    3. TIM_OCInitStructure.TIM_Pulse = 50; //初始的CCR值

    4.修改CCR的值(修改占空比)

    1. /**
    2. * 函 数:PWM设置CCR
    3. * 参 数:Compare 要写入的CCR的值,范围:0~100
    4. * 返 回 值:无
    5. * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
    6. * 占空比Duty = CCR / (ARR + 1)
    7. */
    8. void PWM_SetCompare1(uint16_t Compare)
    9. {
    10. TIM_SetCompare1(TIM2, Compare); //设置CCR1的值
    11. }

    5.main

    1. uint8_t i; //定义for循环的变量
    2. int main(void)
    3. {
    4. /*模块初始化*/
    5. OLED_Init(); //OLED初始化
    6. PWM_Init(); //PWM初始化
    7. while (1)
    8. {
    9. for (i = 0; i <= 100; i++)
    10. {
    11. PWM_SetCompare1(i); //依次将定时器的CCR寄存器设置为0~100,PWM占空比逐渐增大,LED逐渐变亮
    12. Delay_ms(10); //延时10ms
    13. }
    14. for (i = 0; i <= 100; i++)
    15. {
    16. PWM_SetCompare1(100 - i); //依次将定时器的CCR寄存器设置为100~0,PWM占空比逐渐减小,LED逐渐变暗
    17. Delay_ms(10); //延时10ms
    18. }
    19. }
    20. }

    6.AFIO使用

    1. /*GPIO重映射*/
    2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,重映射必须先开启AFIO的时钟
    3. GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
    4. GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //将JTAG引脚失能,作为普通GPIO引脚使用

    8.同一个定时器使用多个通道输出多道PWM通道

    1)频率相同:对于同一个定时器的不同通道输出的PWM,因为不同通道是共用一个计数器,所以频率应该是相同的。

    2)占空比不同:占空比由各自CCR决定

    3)相位相同:由于计数器更新,所有PWM同时跳变,所以相位是同步的。

    4)如果驱动多个舵机或者直流电机,使用同一个定时器不同通道的PWM即可。

  • 相关阅读:
    【Yocto1】构建嵌入式Linux系统
    用shell批量改变打错标签的名字(打标签是一生之敌)
    go|一道算法题引发的思考|slice底层剖析
    Spring底层原理(一)
    打造“共富果园” 广东乳源推动茶油全产业链高质量发展
    【AI】机器学习——朴素贝叶斯
    mybatis plus框架的@TableField注解不生效问题总结
    【JDK 8-集合框架进阶】6.1 parallelStream 并行流
    Python ElementTree 导出 xml 缺少 开头声明&【Pymssql】使用cursor.fetchall()获取执行结果时中文乱码
    1204. 最后一个能进入电梯的人
  • 原文地址:https://blog.csdn.net/m0_63077733/article/details/134260001