• STM32 学习(四)中断系统


    一、中断系统介绍

    众所周知,轮询是 CPU 通过不断地查询某个外部设备的状态,如果外部设备准备好,就可以向其发送数据或者读取数据,这种方式由于CPU不断查询总线,导致指令执行受到影响,效率非常低。

    而与之相对应的就是中断,正常情况 CPU 会处理其他的事情,如果设备有需要 CPU 处理的事情就产生一个中断,CPU 就会停下正在做的事情来处理中断。

    中断的执行流程如下:

    STM32 中断包含很多中断源(中断通道),并且使用 NVIC 统一管理中断,由左边的地址组成的表称为中断向量表,表中的内容为中断入口的地址: 

    NVIC嵌套向量中断控制器(Nested Vectored Interrupt Controller),在 STM32 中是用来统一分配中断优先级和管理中断的,它是一个内核外设

    STM32 的 NVIC 可以对优先级进行分组,抢占优先级(pre-emption priority)高的中断源可以打断当前中断,来执行自己的中断,而响应优先级(subpriority)高的中断源可以优先被 CPU 响应执行,在 STM32 官方的编程手册中有 NVIC 相关的介绍。 

    二、EXTI(外部中断) 

    中断系统是管理和执行中断的逻辑结构,外部中断(EXTI)是众多能产生中断的外设之一。在触发响应方式中,中断响应是正常的流程,引脚电平变化触发中断;而事件响应不会触发中断,而是触发别的外设操作,如 DMA 等。

    外部中断的基本结构如下图。相同的 Pin 不能同时触发中断,比如 pA0、pB0... 只有一个能接到后面的 16 个 GPIO_Pin 上,所以需要经过 AFIO 数据选择器。在经过 EXTI 边沿检测控制模块后,输出中断输出 EXTI0~15,还有 20 个引向其他外设,即事件响应。

    AFIO 结构,本质上是一系列数据选择器,可以用于引脚复用功能的选择和重定义,或者中断引脚的选择。 

    EXTI 内部框图如下: 

    需要使用外部中断的设备一般是外部驱动的很快的突发信号,比如旋转编码器:

    2.1 对射式红外传感器计数

    接线图:

    初始化函数 CountSensor_Init(),首先要把从左到又涉及到的外设的时钟打开;第二步把 GPIO 配置为输入模式;第三步配置 AFIO,将选择的 GPIO 连接到后面的 EXTI;第四步,配置 EXTI,选择边沿触发方式,如上升沿、下降沿和双边沿等,并且选择触发相应方式,可以选择中断响应或者事件响应;第五步配置 NVIC,给中断选择一个合适的优先级。这里涉及到的外设比较多,有 RCC、GPIO、AFIO、EXTI、NVIC 等。

    EXTI 相关函数: 

    1. /* stm32f10x_exti.h */
    2. /* 可以将EXTI的配置清除,恢复成上电默认的状态 */
    3. void EXTI_DeInit(void);
    4. /* 根据结构体初始化EXTI外设,和GPIO的差不多 */
    5. void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
    6. /* 可以给结构体赋予一个默认值 */
    7. void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
    8. /* 软件触发一次外部中断 */
    9. void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
    10. /* 获取指定的flag是否置1 */
    11. FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
    12. /* 对标志位进行清除 */
    13. void EXTI_ClearFlag(uint32_t EXTI_Line);
    14. /* 获取中断标志位是否被置1,在中断程序中使用 */
    15. ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
    16. void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

    NVIC 相关函数: 

    1. /* misc.h */
    2. /* 用来中断优先级分组,参数为中断分组的方式 */
    3. void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
    4. /* 根据指定结构体初始化NVIC */
    5. void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
    6. /* 设置中断向量表 */
    7. void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
    8. /* 系统低功耗配置 */
    9. void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
    10. /* 配置系统定时器的时钟源 */
    11. void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);

    编写中断函数 EXTI15_10_IRQHandler(在启动文件中可以找到):

    1. #include "stm32f10x.h"
    2. uint16_t count;
    3. void CountSensor_Init(void)
    4. {
    5. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    7. // 配置GPIO
    8. GPIO_InitTypeDef GPIO_InitStruct;
    9. GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD; // 上拉输入
    10. GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
    11. GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // 输入模式下该配置没用
    12. GPIO_Init(GPIOB, &GPIO_InitStruct);
    13. // 配置AFIO,选择GPIOB的14引脚,对应EXTI14
    14. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
    15. // 初始化EXTI
    16. EXTI_InitTypeDef EXTI_InitStructure;
    17. EXTI_InitStructure.EXTI_Line = EXTI_Line14; // 指定配置的中断线
    18. EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 配置EXTI的模式,选择中断模式,而不是事件模式
    19. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 选择下降沿触发
    20. EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 指定选择的中断线的新状态
    21. EXTI_Init(&EXTI_InitStructure);
    22. // 配置NVIC
    23. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置中断优先级分组
    24. NVIC_InitTypeDef NVIC_InitStruct;
    25. NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn; // 指定中断通道,External Line[15:10] Interrupts
    26. NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
    27. NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 响应优先级
    28. NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    29. NVIC_Init(&NVIC_InitStruct);
    30. }
    31. uint16_t GetCount(void)
    32. {
    33. return count;
    34. }
    35. // 中断处理函数
    36. void EXTI15_10_IRQHandler(void)
    37. {
    38. // 判断EXTI14是否发生中断
    39. if(EXTI_GetITStatus(EXTI_Line14) == SET)
    40. {
    41. count++;
    42. // 清除中断标志位,防止重复请求中断
    43. EXTI_ClearITPendingBit(EXTI_Line14);
    44. }
    45. }

    三、TIM 定时器中断

    基本定时器,内部时钟一般是 72MHz,到 PSC 预分频器会先对其进行分频,如果预分频器写 1,则为 2 分频(72MHz/2 = 36MHz) ,写 2 则为 3 分频。。。计数时钟 CK_CN 每来一个上升沿,则 CNT 计数器加一;当 CNT 计数值等于自动重装载寄存器(ARR)的值的时候,就相当于计时时间到来,产生一个中断信号,并且清零 CNT 计数器(下图的向上箭头 UI 即为更新中断,向下的箭头 U 为更新事件)。

    通用定时器,基本定时器只支持向上计数一种模式,而通用定时器和高级定时器支持向上计数、向下计数、中央计数三种模式。

    定时中断基本结构图, 其中粉红色部分的时基单元就是上面将的基本定时器框图中的结构;运行控制寄存器可以控制时基单元的工作,比如启动停止,向上计数或向下计数等;左边的部分能为时基单元提供时钟,可以选择 RCC 提供的内部时钟、ETR 引脚提供的外部时钟模式 2;右边部分输出中断信号会先在状态寄存器里置一个中断标志位,这个标志位会通过中断输出控制,到 NVIC 申请中断,而因为有很多地方都要申请中断,所以中间的中断输出控制模块可以控制中断的通断。

    预分频器时序: 

    计数器时序,更新中断标志位 UIF 置 1 后,就会去申请中断,中断响应后,就需要在中断程序中手动清 0。 

      

    计数器无预装时序,自动加载寄存器从 FF 更新为 36 后,计数器寄存器到 36 后自动更新。

    计数器有预装时序,自动加载寄存器从 F5 更新到 36 时,计数器寄存器还是会先计数到 F5 后再更新,此时自动加载寄存器的 36 才被更新到自动影子寄存器中,从而使得下一个计数周期 36 才生效。影子寄存器的目的是为了值的变化和更新事件同步,防止运行途中更改造成错误。

    3.1 定时器定时中断

    因为定时器模块都在 STM32 内部,所以无需外接其他模块,此部分现象为显示屏每秒计数加一: 

    时钟源选择(时基单元左边部分)要用到的一些接口: 

    1. /* stm32f10x_tim.h */
    2. /* 选择内部时钟 */
    3. void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
    4. /* 选择 ITRx 外部时钟 */
    5. void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
    6. /* 选择 TIx 外部时钟 */
    7. void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, uint16_t TIM_ICPolarity, uint16_t ICFilter);
    8. /* 选择 ETR 外部时钟模式 1 */
    9. void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
    10. /* 选择 ETR 外部时钟模式 2 */
    11. void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
    12. /* 配置 ETR 引脚的预分频器、极性、滤波器 */
    13. void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);

    时基单元部分要用到的一些接口: 

    1. /* stm32f10x_tim.h */
    2. /* 恢复定时器默认配置 */
    3. void TIM_DeInit(TIM_TypeDef* TIMx);
    4. /* 时基单元初始化 */
    5. void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
    6. /* 定时器运行控制 */
    7. void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

    中断输出控制(时基单元右边部分)需要用到的接口:

    1. /* 使能中断输出信号 */
    2. void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

    NVIC 相关函数: 

    1. /* misc.h */
    2. /* 用来中断优先级分组,参数为中断分组的方式 */
    3. void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
    4. /* 根据指定结构体初始化NVIC */
    5. void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
    6. /* 设置中断向量表 */
    7. void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
    8. /* 系统低功耗配置 */
    9. void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
    10. /* 配置系统定时器的时钟源 */
    11. void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);

    这里开启 TIM2 通用定时器时钟,可以从下图中看到 TIM2 属于 APB1: 

    Timer.c 代码如下: 

    1. #include "stm32f10x.h"
    2. extern uint16_t number;
    3. void Timer_Init(void)
    4. {
    5. // 开启 TIM2 通用定时器时钟使能,TIM2 属于 APB1 的外设
    6. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    7. // 使用内部时钟(这里不配置也可以,默认使用内部时钟)
    8. TIM_InternalClockConfig(TIM2);
    9. // 初始化时基单元,1s的间隔
    10. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    11. TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1; // PSC预分频器的值
    12. TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
    13. TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1; // ARR自动重装器的值
    14. TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 1分频
    15. TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; // 重复计数器的值
    16. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
    17. // 避免刚初始化就进入中断
    18. TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    19. // 中断输出控制
    20. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    21. // 配置NVIC
    22. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置中断优先级分组
    23. NVIC_InitTypeDef NVIC_InitStruct;
    24. NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; // 指定中断通道,External Line[15:10] Interrupts
    25. NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级
    26. NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 响应优先级
    27. NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    28. NVIC_Init(&NVIC_InitStruct);
    29. // 启动定时器TIM2
    30. TIM_Cmd(TIM2, ENABLE);
    31. }
    32. void TIM2_IRQHandler(void)
    33. {
    34. // 获取中断更新标志位
    35. if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    36. {
    37. number++;
    38. // 清除中断更新标志位
    39. TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    40. }
    41. }

    3.2 定时器外部时钟

    可以查看引脚图来看 ETR 引脚对应的哪个,用对射式红外传感器模拟外部时钟源,此部分现象为每次挡住对射式红外传感器计数值加一,并且计数 10 次后触发中断,number 加一。

    Timer.c 代码: 

    1. #include "stm32f10x.h"
    2. extern uint16_t number;
    3. void Timer_Init(void)
    4. {
    5. // 开启 TIM2 通用定时器时钟使能,TIM2 属于 APB1 的外设
    6. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    8. // 配置GPIO PA0
    9. GPIO_InitTypeDef GPIO_InitStruct;
    10. GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入
    11. GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    12. GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // 输入模式下该配置没用
    13. GPIO_Init(GPIOA, &GPIO_InitStruct);
    14. // 选择 ETR 外部时钟模式 2
    15. TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);
    16. // 初始化时基单元
    17. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    18. TIM_TimeBaseInitStruct.TIM_Prescaler = 1 - 1; // PSC预分频器的值
    19. TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
    20. TIM_TimeBaseInitStruct.TIM_Period = 10 - 1; // ARR自动重装器的值
    21. TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 1分频
    22. TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; // 重复计数器的值
    23. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
    24. // 避免刚初始化就进入中断
    25. TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    26. // 中断输出控制
    27. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    28. // 配置NVIC
    29. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置中断优先级分组
    30. NVIC_InitTypeDef NVIC_InitStruct;
    31. NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; // 指定中断通道,External Line[15:10] Interrupts
    32. NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级
    33. NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 响应优先级
    34. NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    35. NVIC_Init(&NVIC_InitStruct);
    36. // 启动定时器TIM2
    37. TIM_Cmd(TIM2, ENABLE);
    38. }
    39. // 获取计数器值
    40. uint16_t Timer_GetCounter(void)
    41. {
    42. return TIM_GetCounter(TIM2);
    43. }
    44. void TIM2_IRQHandler(void)
    45. {
    46. // 获取中断更新标志位
    47. if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    48. {
    49. number++;
    50. // 清除中断更新标志位
    51. TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    52. }
    53. }

    3.3 TIM 输出比较

    上图所说的 CCR 寄存器即为下图中的捕获/比较寄存器,通过比较 CNT 计数器和 CCR 寄存器的值,来对输出电平进行控制,可以用于输出 PWM 波形。输出比较功能只在通用定时器和高级定时器上拥有,在基本定时器上不存在该部分,所以无法在基本定时器上实现此功能。 

    分辨率就是占空比变化的细粒程度。

    通用定时器输出比较通道,当 CNT >= CCR1 时就会给输出模式控制器输出一个信号,然后输出模式控制器控制 oc1ref 参考信号,该信号会通过一个二路选择器,通过 CC1P 来控制二路选择来选择其输出的高低电平。

    通过 OC1M[2:0] 可以配置不同的输出比较模式:

    下图中,上面的波形图为计数器 CNT 向上计数的示意图,下面的波形图为通过 CCR 寄存器比较后的输出 PWM 波形。 

    各个参数的计算,PWM 的频率和定时器时钟频率相同,: 

    舵机硬件电路: 

    电机驱动模块硬件电路,通过 PWMA、AIN2、AIN1 可以控制 AO1、AO2 电机驱动输出端。STBY 可以控制电机的待机模式。

    3.3.1 PWM 驱动 LED 呼吸灯

    连接线路,使用 pA0 输出一个 PWM 波形来控制 LED 实现呼吸灯: 

    输出比较单元有四个,所以对应四个初始化函数:

    1. void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
    2. void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
    3. void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
    4. void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

    配置强制输出模式,即配置强制高电平或者低电平,用的不多

    1. void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
    2. void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
    3. void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
    4. void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);

    单独更改 CCR 寄存器值的函数,重要: 

    1. void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
    2. void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
    3. void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
    4. void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

    首先要初始化定时器,初始化方式和 3.1 节的定时器定时中断一样;然后初始化输出比较通道,这里我们使用 pA0 口,所以使用 TIM_OC1Init 函数。 pA0 上复用了很多引脚,其中就有 TIM2_CH1_ETR。

    假如想使用的引脚冲突了,可以使用 AFIO 来重映射引脚,如下图中的 USART2_TX 和 TIM2_CH3: 

    PWM.c 代码如下:

    1. #include "stm32f10x.h"
    2. // 频率为 1kHz,占空比为50%
    3. void PWM_Init(void)
    4. {
    5. // 开启 TIM2 通用定时器时钟使能,TIM2 属于 APB1 的外设
    6. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    8. /*GPIO重映射,GPIO_Pin_0 改成 GPIO_Pin_15*/
    9. //RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,重映射必须先开启AFIO的时钟
    10. //GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
    11. //GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //将JTAG引脚失能,作为普通GPIO引脚使用
    12. // 配置引脚;
    13. GPIO_InitTypeDef GPIO_InitStruct;
    14. GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出,将引脚的控制权交给片上外设,而不是寄存器
    15. GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    16. GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    17. GPIO_Init(GPIOA, &GPIO_InitStruct);
    18. // 使用内部时钟(这里不配置也可以,默认使用内部时钟)
    19. TIM_InternalClockConfig(TIM2);
    20. // 初始化时基单元,1s的间隔
    21. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    22. TIM_TimeBaseInitStruct.TIM_Prescaler = 720 - 1; // PSC预分频器的值
    23. TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
    24. TIM_TimeBaseInitStruct.TIM_Period = 100 - 1; // ARR自动重装器的值
    25. TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 1分频
    26. TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; // 重复计数器的值
    27. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
    28. // 初始化输出比较单元
    29. TIM_OCInitTypeDef TIM_OCInitStruct;
    30. TIM_OCStructInit(&TIM_OCInitStruct);
    31. TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // 设置输出比较模式
    32. TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // 设置输出比较使能
    33. TIM_OCInitStruct.TIM_Pulse = 0; // 设置CCR
    34. TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // 设置输出比较极性
    35. TIM_OC1Init(TIM2, &TIM_OCInitStruct);
    36. // 启动定时器TIM2
    37. TIM_Cmd(TIM2, ENABLE);
    38. }
    39. // 修改CCR的值
    40. void PWM_SetCompare1(uint16_t Compare1)
    41. {
    42. TIM_SetCompare1(TIM2, Compare1);
    43. }

    main 函数代码: 

    1. #include "stm32f10x.h"
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "PWM.h"
    5. uint16_t i;
    6. int main(void)
    7. {
    8. // 初始化OLED
    9. OLED_Init();
    10. PWM_Init();
    11. while(1)
    12. {
    13. for(i = 0; i <= 100; i++){
    14. PWM_SetCompare1(i);
    15. Delay_ms(10);
    16. }
    17. for(i = 0; i <= 100; i++){
    18. PWM_SetCompare1(100 - i);
    19. Delay_ms(10);
    20. }
    21. }
    22. }

    3.4 TIM 输入捕获

    3.5 TIM 编码器接口

  • 相关阅读:
    Java计算机毕业设计铜仁学院毕业就业管理系统源码+系统+数据库+lw文档
    计算机网络和因特网
    BGP的基本配置
    HBase 的安装与部署
    用《斗破苍穹》的视角打开C#委托2 委托链 / 泛型委托 / GetInvocationList
    atlas运维中遇到的问题
    艾美捷Immunochemistry MitoPT JC-1试剂盒
    TableUtilCache:针对CSV表格进行的缓存
    3.二叉树遍历序列还原
    2022年CCF推荐国际学术会议和期刊(人工智能领域)
  • 原文地址:https://blog.csdn.net/qq_51103378/article/details/135396175