当前位置: 首页 > news >正文

STM32内部时钟输出比较OC(学习笔记)

  声明:资料来自B站江协科技

   之前学习了内部时钟的定时功能以及外部GPIO电平触发中断(即定时功能模式2),模式1还没有学习。这篇定时器输出捕获依然继续学习相关寄存器的控制。

      首先分享一下自己复写程序时遇到的问题。

       第一个是呼吸灯的代码,把里面的延时程序换成了定时中断。因为都是用的TIM2定时的,所以其实相当于重复定义了时基单元。

       即Timer.c和PWM.c里都用到了Tim2的时基单元,因此它只能使能其中一个,另一个被覆盖了,如果不用示波器或者逻辑分析仪去查看PWM的波形还真发现不了这个问题。最后统一模块里的参数

      第二个是舵机程序写完发现不工作,因为刚学很多库函数不熟悉加上有点灯下黑,没有找到问题,最后是DEBUG源码和自己的代码,看寄存器CCR的值位置不一样。最后发现一个用的setcompare2自己用的setcompare1.最后改掉了。看来懂点寄存器还是不错的。

      第三个在PWM控制的时候打算用光耦来触发刹车,逻辑上很简单按理应该没什么问题的,就是在外部触发中断中写上Speed = 0;应该就可以了,用开关这么设置可以触发。结果光耦没有,找了应该有2个小时的BUG,结果发现是光耦模块的工作触发方式自己设想的不一样,属于是进入中断但是没进入判断语句,最后发现问题的时候相当无语,因为之前找问题的时候想了很多错误的可能。后面想想光耦模块不是我自己写的,在功能逻辑上没有理解到位,结果吃到苦头了。

    第4个就是写“1”清0,之前只知道写0清0,现在知道挂起寄存器是写“1”清0,

    在STM32的硬件中,EXTI_PR寄存器的设计是:

  • 写入0:无操作(位值保持原样)。
  • 写入1:将对应位清零(Write-1-to-Clear)。

  因此,虽然代码看起来是“写1”,但硬件实际会将其解释为“清零”。


以下来自DeepSeek

在STM32中,外部触发产生中断产生事件是两种不同的响应机制,其核心区别在于触发后的处理路径和硬件行为。以下是详细对比:

一、核心区别

特性中断(Interrupt)事件(Event)
触发对象处理器(CPU)其他硬件模块(如DMA、ADC、定时器等)
处理路径通过中断服务程序(ISR)由CPU处理直接触发硬件操作,无需CPU干预
挂起位(Pending Bit)有(需软件清除)无(自动生成脉冲,无需状态记录)
延迟较高(需上下文切换)极低(硬件直接响应)
应用场景需要复杂逻辑处理(如数据处理、状态更新)快速硬件协作(如DMA启动、定时器捕获)

二、配置流程对比

1. 中断配置
  • 步骤
    1. 配置中断线(EXTI Line)的触发边沿(上升沿/下降沿)。
    2. 使能中断屏蔽寄存器(EXTI_IMR)对应位(写1)。
    3. 配置NVIC(嵌套向量中断控制器)使能中断通道。
  • 触发后行为
    • 挂起位(EXTI_PR)被硬件置1
    • CPU跳转执行中断服务程序(ISR)。
    • 需在ISR中手动清除挂起位(写1清零)。
// 示例:配置PA0引脚上升沿中断GPIO_Init(GPIOA, &GPIO_InitStructure); EXTI_InitStructure.EXTI_Line = EXTI_Line0;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_Init(&EXTI_InitStructure);NVIC_EnableIRQ(EXTI0_IRQn); // 使能NVIC中断通道
2. 事件配置
  • 步骤
    1. 配置事件线(EXTI Line)的触发边沿(同中断)。
    2. 使能事件屏蔽寄存器(EXTI_EMR)对应位(写1)。
  • 触发后行为
    • 生成一个硬件事件脉冲(电平信号)。
    • 无挂起位,无需软件清除。
    • 直接触发其他外设(如启动DMA传输)。
// 示例:配置PA0引脚上升沿事件
GPIO_Init(GPIOA, &GPIO_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;  // 事件模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure);

三、关键机制解析

1. 中断的挂起位(Pending Bit)
  • 作用:标志中断请求是否已发生,供CPU查询。
  • 清除方式:向挂起位写1(硬件设计为写1清零)。
  • 必要性:确保中断不会被重复误触发。
2. 事件的脉冲特性
  • 行为:事件触发时,EXTI会生成一个单周期的高/低电平脉冲。
  • 直接驱动硬件:例如:
    • 触发ADC开始采样。
    • 启动DMA传输。
    • 复位定时器计数器。
  • 无需软件干预:硬件自动完成操作。

四、应用场景示例

1. 中断的典型应用
  • 按键检测:按下按键后,触发中断执行去抖和状态更新。
  • 传感器数据就绪:传感器数据准备好后,中断通知CPU读取。
2. 事件的典型应用
  • DMA传输同步:外部信号触发DMA从内存搬运数据到外设。
  • 定时器捕获:外部边沿事件触发定时器记录当前计数值。
  • 低功耗唤醒:事件唤醒处于睡眠模式的CPU(无需进入ISR)。

五、软件触发中断/事件

通过软件中断/事件寄存器(EXTI_SWIER,可以手动生成中断或事件:

  • 写入1:模拟硬件触发,行为与外部触发一致。
  • 用途:调试时模拟信号,或强制启动硬件操作。
// 手动触发PA0的中断
EXTI_GenerateSWInterrupt(EXTI_Line0);// 手动触发PA0的事件
EXTI_GenerateSWEvent(EXTI_Line0);


六、总结

  • 中断:面向CPU,需软件处理,适合复杂逻辑。
  • 事件:面向硬件协作,无CPU参与,适合高效操作。
  • 设计意义:STM32通过分离中断和事件路径,兼顾灵活性与实时性。

1. void EXTI_ClearFlag(uint32_t EXTI_Line)

功能

直接清除EXTI线的挂起标志位(Pending Bit),无论该中断是否被使能。

  • 操作对象:EXTI挂起寄存器(EXTI_PR)。
  • 硬件行为:向指定中断线(EXTI_Line)对应的位写1,将该位清零。
  • 特点
    • 不检查中断是否被使能(不依赖中断屏蔽寄存器EXTI_IMR)。
    • 即使中断未使能,也能强制清除挂起标志。
典型用途
  • 在不需要中断服务程序干预的情况下,手动清除某个事件触发的挂起标志。
  • 初始化时清理残留的中断标志。
// 清除EXTI线0的挂起标志
EXTI_ClearFlag(EXTI_Line0);

2. ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)

功能

检查指定EXTI线的中断状态是否有效,即同时满足以下两个条件:

  1. 中断使能:该中断线在中断屏蔽寄存器(EXTI_IMR)中被使能(对应位为1)。
  2. 挂起标志置位:该中断线在挂起寄存器(EXTI_PR)中的挂起位为1
  • 返回值
    • SET:中断有效(已使能且挂起位为1)。
    • RESET:中断无效(未使能或挂起位为0)。
典型用途
  • 在中断服务程序(ISR)中确认中断来源是否合法,避免误触发。
示例
// 在中断服务程序中检查EXTI线0的中断状态
if (EXTI_GetITStatus(EXTI_Line0) == SET) {// 处理中断// ...// 清除挂起位EXTI_ClearITPendingBit(EXTI_Line0);
}

3. void EXTI_ClearITPendingBit(uint32_t EXTI_Line)

功能

安全清除EXTI线的挂起标志位(Pending Bit),通常用于中断服务程序末尾。

  • 操作对象:EXTI挂起寄存器(EXTI_PR)。
  • 硬件行为:向指定中断线对应的位写1,将该位清零。
  • EXTI_ClearFlag的区别
    • 功能上两者等价,均直接操作EXTI_PR
    • 语义差异EXTI_ClearITPendingBit专为中断服务程序设计,强调“中断挂起位”的清除;而EXTI_ClearFlag更通用,可能用于非中断场景。
典型用途

函数操作目标是否检查中断使能典型场景
EXTI_GetITStatus检查状态中断服务程序开头
EXTI_ClearITPendingBit清除挂起位中断服务程序末尾
EXTI_ClearFlag清除挂起位非中断场景的强制清除
  • 在中断服务程序(ISR)结束时清除挂起位,防止重复进入中断。
  • void EXTI0_IRQHandler(void) {if (EXTI_GetITStatus(EXTI_Line0) == SET) {// 处理中断逻辑// ...// 清除挂起位EXTI_ClearITPendingBit(EXTI_Line0);}
    }
    

    三者的关系与使用流程

  • 中断触发:外部信号触发EXTI线,硬件自动置位EXTI_PR对应位。
  • 状态检查:在ISR中使用EXTI_GetITStatus确认中断有效性。
  • 清除标志:处理完中断后,使用EXTI_ClearITPendingBitEXTI_ClearFlag清除挂起位。

PWM设置过程(OC输出捕获比较)

void PWM_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA2引脚初始化为复用推挽输出	//受外设控制的引脚,均需要配置为复用模式/*配置时钟源*/TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟/*时基单元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;                 //计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;               //预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元/*输出比较初始化*/ TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量TIM_OCStructInit(&TIM_OCInitStructure);                         //结构体初始化,若结构体没有完整赋值//则最好执行此函数,给结构体所有成员都赋一个默认值//避免结构体初值不确定的问题TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               //输出比较模式,选择PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       //输出极性,选择为高,若选择极性为低,则输出高低电平取反TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //输出使能TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值TIM_OC3Init(TIM2, &TIM_OCInitStructure);                        //将结构体变量交给TIM_OC3Init,配置TIM2的输出比较通道3/*TIM使能*/TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}
void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare3(TIM2, Compare);		//设置CCR3的值
}

OC结构体设置:

PA2是输出捕获3通道

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //输出使能

                                             

 /* Reset the Output Compare mode and Capture/Compare selection Bits */
  tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR2_OC3M));
  tmpccmrx &= (uint16_t)(~((uint16_t)TIM_CCMR2_CC3S));  

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 

TIM_OCInitStructure.TIM_Pulse = 0;	

这个值结合ARR的值一起计数PWM的频率。如果查看手册的话还涉及一些别的寄存器;


http://www.mrgr.cn/news/95861.html

相关文章:

  • 常用的离散时间傅里叶变换(DTFT)对
  • Langchain中的表格解析:RAG 和表格的爱恨情仇
  • 深入 SVG:矢量图形、滤镜与动态交互开发指南
  • Python进阶编程总结
  • 定长内存池原理及实现
  • 【Linux知识】RPM软件包安装命令行详细说明
  • MoManipVLA:将视觉-语言-动作模型迁移到通用移动操作
  • Rust从入门到精通之精通篇:21.高级内存管理
  • Tasklet_等待队列_工作队列
  • ngx_http_core_location
  • SVN常用命令
  • 团体协作项目总结Git
  • 基于Ebay拍卖网站成交价格的影响因素分析
  • python工厂模式
  • 2025前端面试题(vue、react、uniapp、微信小程序、JS、CSS、其他)
  • 吾爱出品,文件分类助手,高效管理您的 PC 资源库
  • 内核编程十二:打印task_struct中的数据
  • 单片机和微控制器知识汇总——《器件手册--单片机、数字信号处理器和可编程逻辑器件》
  • Mycat安装验证流程整理
  • 【Pandas】pandas Series to_csv