stm32之输出比较OC和输入捕获IC
目录
- 1.输出比较OC
- 1.1 简介
- 1.2 PWM简介
- 1.3 输出比较通道
- 1.3.1 通用定时器
- 1.3.2 高级定时器
- 1.4 PWM基本结构
- 1.6 舵机
- 1.7 直流电机
- 1.7.1 引入:MX1508 芯片
- 1.7.2 TB6612芯片
- 1.8 结构体和API
- 1.8.1 结构体
- 1.8.2 API
- 1. `TIM_OC1Init` / `TIM_OC2Init` / `TIM_OC3Init` / `TIM_OC4Init`
- 2. `TIM_OCStructInit`
- 3. `TIM_SetCompare1` / `TIM_SetCompare2` / `TIM_SetCompare3` / `TIM_SetCompare4`
- 4.示例
- 1.9 实验
- 1.9.1 呼吸灯
- 1. `PWM_Init` 函数
- 2. `PWM_SetCompare1` 函数
- 3.使用示例
- 1.9.2 舵机
- 1.9.3 直流电机
- 1.10 混淆
- 1.10.1 中断频率
- 1.10.2 PWM 输出频率
- 1.10.3 占空比
- 1.10.4示例理解
- 1.10.5 PWM波形和时钟电平
- 2.输入捕获IC
- 2.1 简介
- 2.2 输入的频率测量
- 2.3 输入比较通道
- 2.4 基本结构
- 2.5 相关api和结构体
- 2.5.1 结构体
- 2.5.2 api
- 2.6 实验
- 2.6.1 输入捕获模式测频率
- 2.6.2 PWMI模式测频率占空比
- 3.TIM编码器接口
- 3.1 简介
- 3.2 正交编码器
- 3.3 基本结构
- 3.4 工作模式
- 3.5 实例
- 3.5 实验
1.输出比较OC
1.1 简介
OC(Output Compare)输出比较
通用定时器的输出比较单元支持四个输出通道(OC1、OC2、OC3、OC4),用于比较 CNT 计数器和预设的比较值 (CCRx),主要用于产生 PWM 信号、输出定时事件等。
输出比较单元通过比较 CNT 计数器和指定的比较值 (CCRx) 来控制输出行为,支持多种模式:
- PWM 模式:通过设置比较值实现 PWM 输出,用于调节输出信号的占空比。
- 定时模式:在计数器值等于比较值时生成输出事件,适合用于定时控制。
- 单脉冲模式:产生单个脉冲信号,可以用于控制单一事件。
每个高级定时器和通用定时器都拥有4个输出比较通道,高级定时器的前3个通道额外拥有死区生成和互补输出的功能,这些在上一篇文章已经讲解过,下面是通用定时器中输出比较寄存器的位置:
1.2 PWM简介
PWM(Pulse Width Modulation)脉冲宽度调制。
PWM参数: 频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距
PWM波形是一种数字信号,也是由高低电平组成。在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域。
按理说数字信号控制LED只有完全亮和完全灭两种状态,但是通过PWM可以实现呼吸灯的效果,也就是渐变的亮度,而PWM也是数字信号,这怎么实现的???其实就是通过改变数字信号高低电平波形的的时间比例(Ton和Toff)来实现LED呈现不同亮度的级别,只要我闪的够快,就察觉不到我是只有完全灭和亮。
其实说白了PWM就是通过对数字信号的高低电平时间占比的调制,来实现类似于模拟信号的效果:占空比越大,就越趋近于模拟电压的高电平
但需要注意的是有个前提:在具有惯性的系统中。比如LED由亮转灭,这个过程是有时间的,并不是一瞬间的事情;对于电机,转动的时候想要停下来,也是需要时间的。这些都是惯性。
1.3 输出比较通道
1.3.1 通用定时器
当CNT>CCR1或CNT=CCR1的时候,输出模式控制器就会该改变它输出的co1ref(Output Compare 1 reference 输出捕获参考信号1)。接下来这个信号可以通往主模式控制器,也就是映射到TRGO引脚输出出去。不过oc1ref主要是去往下面的一路,可以到了有个0和1的极性选择,就是CC1P写0的时候,信号不翻转,写1的时候信号翻转。
那么什么时候给oc1reg高电平,什么时候给oc1ref低电平,这就要去看输出模式控制器的输出比较模式了,通过配置TIMx_CCMR1寄存器来灵活地控制器ocref的输出
模式 | 描述 |
---|---|
冻结 | CNT=CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CCR时,REF置有效电平 |
匹配时置无效电平 | CNT=CCR时,REF置无效电平 |
匹配时电平翻转 | CNT=CCR时,REF电平翻转 |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
PWM模式1 | 向上计数:CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平向下计数:CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平 |
PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平向下计数:CNT>CCR时,REF置有效电平,CNT≤CCR时,REF置无效电平 |
1.3.2 高级定时器
通常外部电路接的是两个MOS管(大功率电子开关),高电平导通,低电平断开,也就是最基本的推挽电路,中间是输出。上管导通,下管断开就是输出高电平;反之则输出低电平。因此上管和下管是互补的,因此内部中就设置了OC1和OC1N来和个字节的MOS管对应,分别控制MOS的导通和关闭。
如果上管导通的瞬间,下管立刻打开,可能会因为期间的不理想,上管还没有完全打开下管就已经导通了,出现了短暂的上下导通事件,会导致功功率损耗,引起器件发热。因为为了这种现象就有了死区生成电路,起着MOS关闭的时候延迟一小段时间。
1.4 PWM基本结构
对于图中的折线图,黄色线是ARR,也就是中断发生所需要达到的计数次数;蓝色折现则是CNT计数次数,当CNT==ARR就清0,申请中断。
在这个过程中添加一个CCR,也就是红色线,当CNT<CCR,REF就值为有效电平(高电平),也就是图中的绿色波形信号图。
这样PWM的占空比就受到CCR和ARR影响(无效电平占比,ARR越高,CCR越高,占空比就越大)
- PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
- PWM占空比: Duty = CCR / (ARR + 1)
- PWM分辨率: Reso = 1 / (ARR + 1)
需要牢记的是PWM波形靠的是ARR、CCR、CNT来进行转换成电平的,因此得借助到时钟电平,因为CNT的计数是和时钟电平挂钩的,所以千万不要认识转换后的REF的PWM波形是和时钟电平是一回事。
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频,不分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // 计数周期 ARR,设置为 99
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 预分频 PSC,设置为 719
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器(仅高级定时器用),设置为 0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 配置 TIM2 的时基单元
- TIM_ClockDivision:设为 TIM_CKD_DIV1,不分频。
- TIM_CounterMode:设为 TIM_CounterMode_Up,表示计数器采用向上计数模式。
- TIM_Period:设为 99 (100 - 1),ARR 决定计数的上限。ARR 的值加上预分频器值决定 PWM 的频率。
- TIM_Prescaler:设为 719,定时器时钟频率会被分频为 72MHz / 720 = 100KHz。
- TIM_RepetitionCounter:设为 0,仅在高级定时器(如 TIM1)中生效。
以上配置将时钟频率设置为 100KHz,计数器每达到 99 时溢出,所以 PWM 频率为 72MHz /720Hz/100Hz = 1KHz。(1kHz表示每秒有1000个周期。因此,1个周期的时间是1秒除以1000,即0.001秒,也就是一次PWM波形的输出占比的时间是1毫秒,其中又分为了高电平和低电平的占比。当输出了99次PWM波形后CNT就清0从从新开始)
注意是不要和中断混淆,在中断中(假设是向上计数),CNT每加1(上升沿+1)的周期时间是1/(CK_PSC/(PSC+1)),CNT达到ARR就申请中断,也就是申请中断的时间为1/(CK_PSC/(PSC+1)) ✖ ARR,这个时间内有多个完整的高低电平。
在PWM中,PWM频率则是指一次完整的PWM周期波形所所占的时间:1/Freq,Freq = CK_PSC / (PSC + 1) / (ARR + 1)。比如上图就是1ms;占空比的比例是指在PWM波形周期计数CCR➗重装载计数ARR+1,其实就可以等价于高电平在一个PWM波形中所占的时间比例。
1.6 舵机
舵机是一种根据输入PWM信号占空比来控制输出角度的装置输入
PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
1.7 直流电机
1.7.1 引入:MX1508 芯片
GPIO 驱动能力有限,要在引脚和电机之间加入驱动电路,也就是驱动芯片。
MX1508 芯片采用 H 桥电路结构设计,采用高可靠性功率管工艺,特别适合驱动线圈、马达等感性负载,内置两路驱动电路,可同时驱动两个线圈马达,工作电压范围覆盖 2V 到 9.6V。电路设计有芯片级温度检测电路,实时监控芯片内部发热,当芯片内部温度超过设定值时(典型值 150℃),启动保护功能,芯片内置的温度迟滞电路,确保电路恢复到安全温度后,才允许重新对功率管进行控制。
待机模式
- 在待机模式下,INAx=INBx=L。包括驱动功率管在内的所有内部电路都处于关断状态。电路消耗极低极低的电流。此时马达输出端 OUTAx 和 OUTBx 都为高阻状态(记为 Z)。
正转模式
- 正转模式的定义为:INAx=H,INBx=L,此时马达驱动端 OUTAx 输出高电平,马达驱动端 OUTBx 输出低电平时,马达驱动电流从 OUTAx 流入马达,从OUTBx 流到地端,此时马达的转动定义为正转模式。
反转模式
- 反转模式的定义为:INAx=L,INBx=H,此时马达驱动端 OUTBx 输出高电平,马达驱动端 OUTAx 输出低电平时,马达驱动电流从 OUTBx 流入马达,从OUTAx 流到地端,此时马达的转动定义为反转模式。
刹车模式
- 刹车模式的定义为:INAx=H,INBx=H,此时马达驱动端 OUTAx 以及 OUTBx 都输出低电平,马达内存储的能量将通过 OUTAx 端 NMOS 管或者 OUTBx 端NMOS 快速释放,马达在短时间内就会停止转动。注意在刹车模式下电路将消耗静态功耗。
PWM 模式 A
- 当输入信号 INAx 为 PWM 信号,INBx=0 或者 INAx=0,INBx 为 PWM 信号时,马达的转动速度将受到 PWM 信号占空比的控制。在这个模式下,马达驱动电路是在导通和待机模式之间切换,在待机模式下,所有功率管都处于关断状态,马达内部储存的能量只能通过功率 MOSFET 的体二极管缓慢释放。
- 当输入信号 INAx 为 PWM 信号,INBx=1 或者 INAx=1,INBx 为 PWM 信号时,马达的转动速度将受到 PWM 信号占空比的控制。在这个模式下,马达驱动电路输出在导通和刹车模式之间,在刹车模式下马达存储的能量通过低边的NMOS 管快速释放。
1.7.2 TB6612芯片
直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向
1.8 结构体和API
1.8.1 结构体
这个 TIM_OCInitTypeDef
结构体用于配置定时器的输出比较(Output Compare)和 PWM 模式的参数,适用于 STM32F101 系列。以下是每个成员的详细说明,包括其可能的取值及含义:
TIM_OCMode
:指定定时器的输出比较模式或 PWM 模式。
-
- 可选值包括:
-
-
TIM_OCMode_Timing
:仅用于定时,不产生任何输出。TIM_OCMode_Active
:当计数器达到捕获比较寄存器(CCR)值时,将输出引脚置为高电平。TIM_OCMode_Inactive
:当计数器达到 CCR 值时,将输出引脚置为低电平。TIM_OCMode_Toggle
:当计数器达到 CCR 值时,输出引脚电平翻转。TIM_OCMode_PWM1
:标准 PWM 模式 1。TIM_OCMode_PWM2
:标准 PWM 模式 2。
-
-
TIM_OutputState
:配置输出比较状态。
-
- 可选值包括:
-
-
TIM_OutputState_Disable
:禁用输出比较。TIM_OutputState_Enable
:启用输出比较,使能输出信号。
-
TIM_OutputNState
:配置互补输出比较状态,仅适用于高级定时器 TIM1 和 TIM8。
-
- 可选值包括:
-
-
TIM_OutputNState_Disable
:禁用互补输出。TIM_OutputNState_Enable
:启用互补输出。
-
TIM_Pulse
:指定加载到捕获比较寄存器(CCR)中的脉冲值。
-
- 值范围为
0x0000
至0xFFFF
。在 PWM 模式下,该值用于控制 PWM 的占空比。
- 值范围为
TIM_OCPolarity
:配置输出比较信号的极性。
-
- 可选值包括:
-
-
TIM_OCPolarity_High
:输出高电平有效。TIM_OCPolarity_Low
:输出低电平有效。
-
TIM_OCNPolarity
:配置互补输出的极性,仅适用于 TIM1 和 TIM8。
-
- 可选值包括:
-
-
TIM_OCNPolarity_High
:互补输出高电平有效。TIM_OCNPolarity_Low
:互补输出低电平有效。
-
TIM_OCIdleState
:配置空闲状态下(即计数器停止时)输出比较引脚的状态,仅适用于 TIM1 和 TIM8。
-
- 可选值包括:
-
-
TIM_OCIdleState_Set
:空闲状态下输出引脚为高电平。TIM_OCIdleState_Reset
:空闲状态下输出引脚为低电平。
-
TIM_OCNIdleState
:配置空闲状态下互补输出引脚的状态,仅适用于 TIM1 和 TIM8。
-
- 可选值包括:
-
-
TIM_OCNIdleState_Set
:空闲状态下互补输出引脚为高电平。TIM_OCNIdleState_Reset
:空闲状态下互补输出引脚为低电平。
-
下面是一个使用 TIM_OCInitTypeDef
的示例代码,配置定时器通道 1 为 PWM 模式 1,输出高电平有效,空闲状态低电平:
TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable; // 仅用于 TIM1 和 TIM8,其他定时器可忽略
TIM_OCInitStructure.TIM_Pulse = 1000; // 占空比设置,决定 PWM 高电平的时间
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; // 仅用于 TIM1 和 TIM8
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; // 仅用于 TIM1 和 TIM8
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; // 仅用于 TIM1 和 TIM8// 使用配置初始化定时器通道1的输出比较功能
TIM_OC1Init(TIMx, &TIM_OCInitStructure);
以上配置在计数器到达 CCR 值时切换输出状态,以产生 PWM 信号,适合用于控制占空比的应用。
1.8.2 API
与输出比较(Output Compare)功能相关的函数如下:
TIM_OC1Init
TIM_OC2Init
TIM_OC3Init
TIM_OC4Init
TIM_OCStructInit
TIM_SetCompare1
TIM_SetCompare2
TIM_SetCompare3
TIM_SetCompare4
其余的函数看固件库函数手册就行了。
1. TIM_OC1Init
/ TIM_OC2Init
/ TIM_OC3Init
/ TIM_OC4Init
这些函数用于初始化定时器的输出比较通道。不同的函数针对不同的输出比较通道(OC1、OC2、OC3 和 OC4)进行初始化。
- 函数原型:
void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
其中 x
可以是 1 到 4,分别对应 OC1 到 OC4 通道。
-
参数:
-
TIMx
:指定要配置的定时器,例如TIM1
、TIM2
等。TIM_OCInitStruct
:指向TIM_OCInitTypeDef
结构体的指针,该结构体包含输出比较相关的配置信息,如比较模式、极性、脉冲值等。
-
使用示例:
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1000;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;// 初始化 TIM1 的 OC1 通道
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
2. TIM_OCStructInit
该函数用于初始化 TIM_OCInitTypeDef
结构体,使其成员变量设定为默认值。
- 函数原型:
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
- 使用示例:
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
3. TIM_SetCompare1
/ TIM_SetCompare2
/ TIM_SetCompare3
/ TIM_SetCompare4
这些函数用于设置定时器的比较寄存器值,即指定定时器计数器达到比较值时的输出状态。
- 函数原型:
void TIM_SetComparex(TIM_TypeDef* TIMx, uint16_t Comparex);
其中 x
可以是 1 到 4,分别对应 OC1 到 OC4 比较寄存器。
-
参数:
-
TIMx
:指定要配置的定时器,例如TIM1
、TIM2
等。Comparex
:指定比较值。当定时器计数器达到该值时,会触发输出比较事件。
-
使用示例:
// 设置 TIM1 的 OC1 通道的比较值为 2000
TIM_SetCompare1(TIM1, 2000);
4.示例
以下示例演示如何使用这些函数来配置一个定时器的输出比较通道,实现当计数器到达指定的比较值时,输出引脚的状态会发生切换(Toggle 模式)。
#include "stm32f10x.h"// 初始化输出比较
void TIM_OC_ExampleInit(void) {TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;// 1. 启用定时器时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// 2. 配置定时器的基础时间TIM_TimeBaseStructure.TIM_Period = 4000 - 1; // 自动重装载值TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 预分频器TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 不分频TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);// 3. 配置 OC1 通道为 Toggle 模式TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; // 切换模式TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出TIM_OCInitStructure.TIM_Pulse = 2000; // 设置比较值TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高电平极性TIM_OC1Init(TIM2, &TIM_OCInitStructure);// 4. 使能定时器TIM_Cmd(TIM2, ENABLE);
}int main(void) {// 初始化输出比较TIM_OC_ExampleInit();while (1) {// 主循环}
}
- 定时器配置:使用
TIM_TimeBaseInit
配置定时器基础参数,设置自动重装载值为 4000 和预分频值为 7200,以调整定时器的计数频率。 - 输出比较配置:配置 OC1 通道为 Toggle 模式,当计数器达到设定的脉冲值(2000)时会切换输出状态。
- 开启定时器:调用
TIM_Cmd(TIM2, ENABLE);
启动定时器。
在该示例中,定时器每当计数器达到 TIM_OCInitStructure.TIM_Pulse
的值(2000)时,输出状态(例如引脚电平)将发生切换,从而实现一个方波输出或其它周期性信号的生成。
1.9 实验
1.9.1 呼吸灯
📎6-3 PWM驱动LED呼吸灯.zip
注:这里是负极接地,正极接IO口,也就是说这个LED是高电平驱动亮的。
User:
- 📎main.c
Hardware:
- 📎PWM.h📎OLED.c📎OLED.h📎OLED_Font.h📎PWM.c
#include "stm32f10x.h" // Device header/*** 函 数:PWM初始化* 参 数:无* 返 回 值:无*/
void PWM_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO重映射*/
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,重映射必须先开启AFIO的时钟
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //将JTAG引脚失能,作为普通GPIO引脚使用/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为复用推挽输出 //受外设控制的引脚,均需要配置为复用模式 /*配置时钟源*/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_OC1Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}/*** 函 数:PWM设置CCR* 参 数:Compare 要写入的CCR的值,范围:0~100* 返 回 值:无* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比* 占空比Duty = CCR / (ARR + 1)*/
void PWM_SetCompare1(uint16_t Compare)
{TIM_SetCompare1(TIM2, Compare); //设置CCR1的值
}
1. PWM_Init
函数
PWM_Init
函数用于初始化 PWM 的配置,包括开启定时器的时钟、设置 GPIO 引脚、配置时基单元、输出比较模式等。
时钟与 GPIO 配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 开启 TIM2 的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 开启 GPIOA 的时钟
首先开启了 TIM2 和 GPIOA 的时钟,以确保后续可以对这两个外设进行配置。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 设置引脚模式为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 选择 PA0 引脚作为 PWM 输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚的速度为 50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化 PA0 引脚
配置了 PA0 引脚为复用推挽输出模式,速度为 50MHz,用于输出 PWM 信号。使用定时器来控制引脚,需要使用复用功能输出,因为此时通过数据寄存器输出的引脚是断开的。
定时器内部时钟配置
TIM_InternalClockConfig(TIM2); // 选择 TIM2 为内部时钟
将 TIM2 的时钟源设置为内部时钟,默认为内部时钟,所以可以不写。
时基单元初始化
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频,不分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // 计数周期 ARR,设置为 99
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 预分频 PSC,设置为 719
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器(仅高级定时器用),设置为 0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 配置 TIM2 的时基单元
- TIM_ClockDivision:设为 TIM_CKD_DIV1,不分频。
- TIM_CounterMode:设为 TIM_CounterMode_Up,表示计数器采用向上计数模式。
- TIM_Period:设为 99 (100 - 1),ARR 决定计数的上限。ARR 的值加上预分频器值决定 PWM 的频率。
- TIM_Prescaler:设为 719,定时器时钟频率会被分频为 72MHz / 720 = 100KHz。
- TIM_RepetitionCounter:设为 0,仅在高级定时器(如 TIM1)中生效。
以上配置将时钟频率设置为 100KHz,计数器每达到 99 时溢出,所以 PWM 频率为 72MHz /720Hz/100Hz = 1KHz。(1kHz表示每秒有1000个周期。因此,1个周期的时间是1秒除以1000,即0.001秒,也就是一次PWM波形的输出占比的时间是1毫秒,其中又分为了高电平和低电平的占比。)
输出比较初始化
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 设置 PWM 模式为 PWM1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 设置输出极性为高
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
TIM_OCInitStructure.TIM_Pulse = 0; // 初始的占空比为 0%
TIM_OC1Init(TIM2, &TIM_OCInitStructure); // 初始化 TIM2 通道 1
- TIM_OCMode:设置为 TIM_OCMode_PWM1,表示 PWM 模式 1,当计数器值小于
CCR
值时输出高电平,大于等于时输出低电平。 - TIM_OCPolarity:设置为高极性,即输出高电平。
- TIM_OutputState:使能输出。
- TIM_Pulse:设为 0,这里控制了 CCR 寄存器的初始值,占空比公式为
CCR / (ARR + 1)
,设为 0 表示初始占空比为 0。可以理解为1个周期内(1KHz的一个周期时间为1ms)高电平所占的时间
使能定时器
TIM_Cmd(TIM2, ENABLE); // 使能 TIM2
使能 TIM2,开始计数,输出 PWM 信号。
2. PWM_SetCompare1
函数
void PWM_SetCompare1(uint16_t Compare)
{TIM_SetCompare1(TIM2, Compare); // 设置 CCR1 的值
}
- Compare:占空比设定值,范围为 0 ~ 100。
- TIM_SetCompare1:将
Compare
值写入 TIM2 的 CCR1 寄存器,控制输出占空比。
例如,Compare
设为 50 时,占空比为 50%。
3.使用示例
int main(void)
{PWM_Init(); // 初始化 PWMwhile (1){for (uint16_t i = 0; i <= 100; i++){PWM_SetCompare1(i); // 逐步增加占空比Delay_ms(10); // 延时,形成呼吸效果}for (uint16_t i = 100; i > 0; i--){PWM_SetCompare1(i); // 逐步减小占空比Delay_ms(10);}}
}
上述代码在 main
函数中,通过循环逐步增加和减少占空比,实现 LED 灯的“呼吸”效果。占空比越大,LED越亮。
1.9.2 舵机
📎6-4 PWM驱动舵机.zip
User:
- 📎main.c
Hardware:
- 📎Key.c
- 📎Key.h
- 📎OLED.c
- 📎OLED.h
- 📎OLED_Font.h
- 📎PWM.c
- 📎PWM.h
- 📎Servo.c
- 📎Servo.h
舵机的PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms。也就是Freq = 50Hz
#include "stm32f10x.h" // Device header/*** 函 数:PWM初始化* 参 数:无* 返 回 值:无*/
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_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA1引脚初始化为复用推挽输出 //受外设控制的引脚,均需要配置为复用模式/*配置时钟源*/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 = 20000 - 1; //计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 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_OC2Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC2Init,配置TIM2的输出比较通道2/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}/*** 函 数:PWM设置CCR* 参 数:Compare 要写入的CCR的值,范围:0~100* 返 回 值:无* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比* 占空比Duty = CCR / (ARR + 1)*/
void PWM_SetCompare2(uint16_t Compare)
{TIM_SetCompare2(TIM2, Compare); //设置CCR2的值
}
/*** 函 数:舵机设置角度* 参 数:Angle 要设置的舵机角度,范围:0~180* 返 回 值:无*/
void Servo_SetAngle(float Angle)
{PWM_SetCompare2(Angle / 180 * 2000 + 500); //设置占空比//将角度线性变换,对应到舵机要求的占空比范围上
}
系统内部时钟CK_PSC为72MHz,那么PSC可以设置为72Hz,ARR设置为20K,Freq就为50Hz(一个周期0.02s = 20ms)这个是PWM一个波形输出的周期时间
那么占空比就可以设置为Angle / 180 * 2000 + 500,Angle是角度
舵机的控制脉宽范围为 0.5ms 到 2.5ms 对应 -90° 到 90°,也就是2ms的时间刚好丢应一个180°。
脉宽的最大增量 = (2.5ms - 0.5ms) = 2ms。而这个2ms需要转换为CNT的计数次数才能被识别。根据代码设置的PSC+1为72,那么分频后的系统时钟频率为:1,000,000,也就是1s(1000ms)内有1,000,000个电平周期(或者说是1s内CNT寄存器会计数1,000,000次),那么换算一下就可以知道1ms有1,000个周期,所以2ms是2000个周期。
而ARR设置为了20000个计数,那么就是说一个PWM脉冲波形持续20000/1000 = 20ms
也就是说走一个180°,CNT需要计数2000个,占空比为2000/20000=1/10 — CNT/(ARR+1),也就是高电平的时间为1/10 ✖ 20ms = 2ms,这不就对上了。那1°的话,CNT就需要计数1/180° ✖2000 次。而加上这个舵机默认是从-90°开始的,未转动的起始脉冲就占了0.5ms,所以得加上偏移量500
1.9.3 直流电机
📎6-5 PWM驱动直流电机.zip
User:
- 📎main.c
Hardware:
- 📎Motor.c📎Motor.h📎OLED.c📎OLED.h📎OLED_Font.h📎PWM.c📎PWM.h📎Key.h📎Key.c
#include "stm32f10x.h" // Device header/*** 函 数:PWM初始化* 参 数:无* 返 回 值:无*/
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 = 36 - 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,定时器开始运行
}/*** 函 数:PWM设置CCR* 参 数:Compare 要写入的CCR的值,范围:0~100* 返 回 值:无* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比* 占空比Duty = CCR / (ARR + 1)*/
void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare3(TIM2, Compare); //设置CCR3的值
}
PWM脉冲一个周期的电平持续多长时间???和舵机的差不多:
设置了PSC为36,那么分频后的时钟频率为2MHz(72/32),也就是1s内时钟输出的电平周期有2,000,000个,即1s内CNT计数器能计数2,000,000,而设置了ARR+1为100,也就是计数到了100,CNT就清0。
那么这时候就可以知道CNT计数一次的时间了,也就是一个时钟电平周期的时间为0.0005ms
即一个PWM波形电平周期时间为0.0005ms ✖ (ARR+1) = 0.05ms,其高电平持续时间为CNT/(ARR+1) ✖0.05 ms。
1.10 混淆
需要牢记的是PWM波形靠的是ARR、CCR、CNT来进行转换成电平的,因此得借助到时钟电平,因为CNT的计数是和时钟电平挂钩的,所以千万不要认识转换后的REF的PWM波形是和时钟电平是一回事。
1.10.1 中断频率
在定时器中断模式下,可以设置定时器达到 ARR
值(即自动重装载值)时触发中断。假设定时器计数器是向上计数模式,那么每当 CNT
增加 1 时经过的时间为:
即每次计数的时间间隔是时钟频率 CK_PSC 经过预分频器 PSC
分频之后的结果(这是分频后的时钟,一次内部完整的高低电平周期时间)。计数器 CNT
达到 ARR
值时就会触发一次中断,因此中断触发周期(即中断的间隔时间)为:
在这个时间间隔内,计数器 CNT
从 0 增加到 ARR
,即会经过 ARR + 1
个计数周期。
1.10.2 PWM 输出频率
在 PWM 模式下,PWM 的频率是指一个完整的 PWM 周期,即一个完整的高电平和低电平所需的时间。PWM 频率可以通过预分频器 PSC
和自动重装载寄存器 ARR
来设置。PWM 的频率公式如下:
也就是说,PWM 的周期 T
为:
这个周期就是一个完整的 PWM 波形(一个高电平和一个低电平的组合)的时间。
1.10.3 占空比
在 PWM 中,占空比定义为在一个 PWM 周期内,高电平所占的时间比例。可以用 CCR
值与 ARR
值的比值来表示占空比:
例如,当 CCR
为 ARR
的一半时,占空比为 50%,表示高电平时间是低电平时间的 1 倍。如果 ARR
为 99,那么占空比为 50 / 100 = 50%
。
1.10.4示例理解
假设时钟 CK_PSC 为 72MHz,设置 PSC = 719
,ARR = 99
,那么:
- 中断触发频率:每当
CNT
从 0 增加到ARR
时触发中断。
-
- 预分频后时钟频率为:
- 预分频后时钟频率为:
-
-
- 这表示分频后的时钟一次完整的高低电平的时间为0.01ms,也就是一次计数的时间为0.01ms
-
-
- 每次中断的周期为:
- 每次中断的周期为:
-
这表示每 1 毫秒触发一次计数清0。
-
PWM 输出频率:
-
- PWM 频率为:
- PWM 频率为:
-
表示 PWM 信号的频率是 1kHz,即每个 PWM 周期的时间为 1 毫秒。
-
占空比:
-
- 若
CCR = 50
,则占空比为
- 若
-
表示高电平持续时间为 0.5 毫秒,低电平持续时间也为 0.5 毫秒。
1.10.5 PWM波形和时钟电平
PWM 波形的生成确实依赖于 ARR
、CCR
和 CNT
,它们共同作用将定时器的计数(由时钟驱动)转换为我们所需要的 PWM 输出信号。但 PWM 波形本身和时钟电平并不是一回事,虽然 PWM 信号的产生需要依赖时钟信号的计数过程。
- 计数原理:
-
CNT
是一个计数器,它的计数是基于内部时钟(CK_PSC)驱动的。每当CNT
增加 1,实际是内部时钟经过了若干周期(取决于预分频器PSC
的配置)。CNT
从 0 开始计数到ARR
,在达到ARR
后重置为 0,形成一个周期。
- PWM 信号生成:
-
- 在每个 PWM 周期内,当
CNT
<CCR
时,输出为高电平;当CNT
≥CCR
时,输出为低电平。 - 通过设置
CCR
的值,可以改变在一个周期内高电平所占的时间(即占空比)。但是,CCR
的大小不会影响计数器CNT
的计数速度,也不会影响 PWM 的周期(周期由ARR
决定)。
- 在每个 PWM 周期内,当
- 时钟与 PWM 波形的关系:
-
- 时钟信号的频率和预分频器
PSC
设置决定了CNT
增加的速度,即定时器的计数速度。 - 然而,PWM 波形的频率和占空比是由
ARR
和CCR
控制的,是CNT
的计数结果被映射到输出引脚生成的波形。也就是说,PWM 波形只是利用了时钟信号计数的结果,并不是直接等同于时钟电平。
- 时钟信号的频率和预分频器
因此,时钟电平是定时器计数的基础,而 PWM 波形是基于 ARR
、CCR
和 CNT
生成的输出结果。
2.输入捕获IC
2.1 简介
IC(Input Capture)输入捕获
输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道,可配置为PWMI模式,同时测量频率和占空比可配合主从触发模式,实现硬件全自动测量
-
通用定时器支持四路输入捕获通道(TI1、TI2、TI3、TI4),用于捕获外部输入信号的事件,主要用于频率测量、信号周期或占空比测量等。并且从图中可以可以看出,一个引脚的输入是可以有两种路线可以走的,也就是可以隐射到两个捕获单元,这是PWMI模式的经典结构
-
每个输入捕获通道具有以下功能组件:
-
- 输入滤波器:对输入信号进行去抖和滤波处理,以减少噪声的干扰。
- 边沿检测器:可以选择高电平触发还是低电平触发,当出现指定电平时,就会触发后续电路执行工作
- 预分频器 (ICxPS):可以将输入信号分频后再捕获,以支持不同频率信号的测量。
- 捕获比较寄存器 (CCRx):当发生输入事件时,捕获当前的 CNT 值并存储在对应的 CCR 寄存器中。
2.2 输入的频率测量
测频法:在闸门时间T内,对上升沿计次,得到N,则频率
- 就是指定时间内出现了多少个周期电平信号,符合频率的基本定义
- 这个适合测高频信号
测周法:两个上升沿内,以标准频率fc计次,得到N ,则频率
- 周期的倒数就是频率,如果能知道一个周期的时间,就能知道其频率。通过捕获一个周期电平信号的时间(两个电平的上升沿之间),获得其时间,就能知道输入的信号的频率。
- 但是一般我们没有那么精确的时间测试来对输入的信号频率进行测试,用的还是内部的时钟,而时钟本质还是计数,我们是可以知道时钟频率为fc,也就是计数一次的时间为1/fc,那么只要从输入的电平的第一个上升沿开始计次到第二个上升沿结束,得到其计次数N,就可以知道输入的信号周期时间为:1/fc ✖ N,取倒数就可以知道频率为fc/N
- 这个适合测低频信号
中界频率:测频法与测周法误差相等的频率点
- 这个主要是解决上面两种方法正负1误差的,根据情况决定选择上面的哪两种方法。
- 当我使用的是测频法,阀门时间T到了,而最后一个几次刚好就对应在了最后的电平的中间位置,就会产生误差,要么只能舍弃它要么就只能算成1个周期。同理测周法也一样会有这种类似的情况
- 所以一般计次N都需要尽量大一点,来减少些误差。
2.3 输入比较通道
上面也提到过,输入的信号经过处理后是可以进入不同的捕获通道的,也就是上面的TI1FP1和TI2FP1,前者来自自己的通道,后者是来自通道2的。通过数据选择寄存器CC1S来对TI1FP1和TI2FP1进行选择;而ICPS则是对分频器进行选择分频;CCE1寄存器则是使能或失能输出,如果使能那么输入的信号就可以转换为IC1PS,让CNT寄存器的值转运到CCR寄存器(捕获/比较寄存器)中。
另外每捕获一次CNT的值就要将CNT清0以便下次捕获,这里硬件电路就可以实现在捕获后自动实行清0工作。
- 意思就是比如测周法,获取一个输入信号周期的计数,当CNT达到CCR的值时就清0
- 对于TI1FP1和TI1F_ED信号都可以通向从模式控制器,比如TI1FP1通向捕获通道的同时还可以通向从模型中的电路,这些内部电路可以自动完成CNT的清0。
主模式可以将定时器内部的信号映射到TRGO引脚,用于触发别的外设。
从模式则用于接收其它外设或者自身外设的一些信号用于控制自身定时器的运行,也就是被别的信号控制。
触发源选择用于选择一个指定的信号,得到TRGI,去触发从模式,从模式可以在自身的列表中选择一个操作去执行。
比如刚才提到了CNT清0,就可以通过输入TI1D_ED,对于到从模式的Reset信号,去触发CNT的自动清0
具体的信号自行去看参考手册中TIMx寄存器的介绍。
2.4 基本结构
输入捕获基本结构:
PWMI基本结构:
使用了两个通道去同时捕获一个输入引脚。
TF1FP1信号去设置上升沿触发,去触发捕获周期和清0CNT;TF2FP1信号设置为下升沿触发,去触发通道2的捕获单元。
这样可以实现CCR1去获取一整个电平周期的计数值,而CCR2则可以去获取一个周期内高电平信号的计数值,这样不就可以计算出该输入信号的占空比。
2.5 相关api和结构体
2.5.1 结构体
typedef struct
{uint16_t TIM_Channel; /*!< Specifies the TIM channel.This parameter can be a value of @ref TIM_Channel */uint16_t TIM_ICPolarity; /*!< Specifies the active edge of the input signal.This parameter can be a value of @ref TIM_Input_Capture_Polarity */uint16_t TIM_ICSelection; /*!< Specifies the input.This parameter can be a value of @ref TIM_Input_Capture_Selection */uint16_t TIM_ICPrescaler; /*!< Specifies the Input Capture Prescaler.This parameter can be a value of @ref TIM_Input_Capture_Prescaler */uint16_t TIM_ICFilter; /*!< Specifies the input capture filter.This parameter can be a number between 0x0 and 0xF */
} TIM_ICInitTypeDef;
TIM_ICInitTypeDef
结构体用于配置 STM32 的定时器输入捕获功能中的各个参数。
\1. TIM_Channel
-
作用:指定输入捕获通道。
-
可能取值:
-
TIM_Channel_1
:定时器通道 1。TIM_Channel_2
:定时器通道 2。TIM_Channel_3
:定时器通道 3。TIM_Channel_4
:定时器通道 4。
-
含义:选择定时器的特定通道用于输入捕获功能。STM32 的每个通道可以被配置为输入捕获模式,以捕捉外部信号的边沿事件。
\2. TIM_ICPolarity
-
作用:指定输入信号的活动边沿(即上升沿或下降沿)。
-
可能取值:
-
TIM_ICPolarity_Rising
:捕获上升沿。TIM_ICPolarity_Falling
:捕获下降沿。TIM_ICPolarity_BothEdge
:捕获上升沿和下降沿(如果硬件支持)。
-
含义:定义捕获事件触发的条件,即在信号的哪个边沿发生时捕获定时器的计数值。这有助于检测输入信号的周期或占空比。
\3. TIM_ICSelection
-
作用:指定输入源。
-
可能取值:
-
TIM_ICSelection_DirectTI
:直接连接到定时器输入。TIM_ICSelection_IndirectTI
:连接到另一个通道的反相输入。TIM_ICSelection_TRC
:选择触发输入(由TIMx_TRC
信号生成)。
-
含义:设置输入捕获单元的信号源,可以将输入通道连接为直接输入、间接输入或选择触发输入。这对于某些应用场景(如 PWM 输入测量)是有用的。
\4. TIM_ICPrescaler
-
作用:指定输入捕获的预分频器(即对输入信号的频率进行分频)。
-
可能取值:
-
TIM_ICPSC_DIV1
:无分频,捕获每一个事件。TIM_ICPSC_DIV2
:2 倍分频,捕获每 2 个事件。TIM_ICPSC_DIV4
:4 倍分频,捕获每 4 个事件。TIM_ICPSC_DIV8
:8 倍分频,捕获每 8 个事件。
-
含义:对输入捕获事件进行分频,可用于减少输入信号频率较高时的处理次数,从而降低系统负荷。
\5. TIM_ICFilter
-
作用:指定输入捕获的数字滤波器,用于滤除噪声。
-
可能取值:可以是
0x0
到0xF
(即 0 到 15 之间的数值)。 -
- 数值越高,滤波器的时间常数越长,抗干扰能力越强,但响应时间越慢。
-
含义:设置输入信号的采样滤波窗口,通过调整滤波器值可以忽略短暂的干扰信号,适用于捕捉带有噪声的信号。
2.5.2 api
以下几个函数主要用于输入捕获单元(Input Capture)的操作:
TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)
-
- 作用:配置定时器的输入捕获功能。
- 参数:
-
-
TIMx
:选择目标定时器。TIM_ICInitStruct
:指向TIM_ICInitTypeDef
结构体的指针,用于配置输入捕获参数。
-
-
- 用法:在使用输入捕获功能时,初始化结构体变量
TIM_ICInitTypeDef
,设置输入捕获通道、输入极性、预分频、滤波等参数,然后调用该函数将配置应用到目标定时器的指定输入捕获通道。
- 用法:在使用输入捕获功能时,初始化结构体变量
TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct)
-
- 作用:初始化
TIM_ICInitTypeDef
结构体的默认值。 - 参数:
- 作用:初始化
-
-
TIM_ICInitStruct
:指向TIM_ICInitTypeDef
结构体的指针。
-
-
- 用法:在对
TIM_ICInitTypeDef
结构体进行自定义配置前,可以使用该函数将结构体初始化为默认值,以避免未初始化的字段出现不确定值。
- 用法:在对
TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)
-
- 作用:配置 PWM 输入模式,用于测量输入信号的频率和占空比。
- 参数:
-
-
TIMx
:选择目标定时器。TIM_ICInitStruct
:指向TIM_ICInitTypeDef
结构体的指针,用于配置 PWM 输入模式参数。
-
-
- 用法:该函数用于配置 PWM 输入捕获,以便测量输入信号的周期和高电平时长。可以用于捕获 PWM 信号的频率和占空比,特别适用于需要分析 PWM 波形的应用。
TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
、TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
、TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
、TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
-
- 作用:设置输入捕获通道的分频器。
- 参数:
-
-
TIMx
:目标定时器。TIM_ICPSC
:指定输入捕获通道的分频比率。
-
-
- 用法:可以对输入信号的频率进行分频,以减缓输入频率较高时的计数速度,从而优化捕获效果。
TIM_GetCapture1(TIM_TypeDef* TIMx)
、TIM_GetCapture2(TIM_TypeDef* TIMx)
、TIM_GetCapture3(TIM_TypeDef* TIMx)
、TIM_GetCapture4(TIM_TypeDef* TIMx)
-
- 作用:获取捕获寄存器的值,即从指定输入捕获通道读取捕获的时间计数。
- 参数:
-
-
TIMx
:选择目标定时器。
-
-
- 用法:这些函数在捕获到输入信号的上升沿或下降沿后,读取捕获寄存器的值,用于计算时间差来确定信号的周期或脉宽。
TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)
TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource)
TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode)
2.6 实验
2.6.1 输入捕获模式测频率
📎6-6 输入捕获模式测频率.zip
User:
- 📎main.c
Hardware:
- 📎OLED.c
- 📎OLED.h
- 📎OLED_Font.h
- 📎PWM.c
- 📎PWM.h
- 📎IC.h
- 📎IC.c
2.6.2 PWMI模式测频率占空比
📎6-7 PWMI模式测频率占空比.zip
User:
- 📎main.c
Hardware:
- 📎OLED.c
- 📎OLED.h
- 📎OLED_Font.h
- 📎PWM.c
- 📎PWM.h
- 📎IC.h
- 📎IC.c
注意:
- 需要注意,对于stm32的内部计数器CNT,最多只能计数到65535次。所以在测量输入的PWMI波形的一个周期电平的频率的时候,不能超过CK_PSC/(PSC+1)/65535。
- 以这个例子,分频后的时钟频率是1MHz,那么1MH/65535,也就是大概1Hz,也就是PWMI的输入的周期电平不能超过1Hz,即时间不能超过1s
3.TIM编码器接口
采用的是仍然是旋转编码器计次的实验,只不过这个代码是通过定时器的编码器接口,来自动计次。之前的实验是通过外部中断来触发中断,在中断函数中进行手动计次的,这样的坏处是会浪费软件资源。
通过编码器接口可以完成一些需要频繁计次而完成的只是简单的任务的情况下去使用。比如使用电机高速旋转需要知道其旋转圈数时,编码器不得产生非常多的脉冲,程序就得频繁中断,而中断只是简单的进行加1,造成严重的软件资源浪费,通过编码器接口就可以解决这种情况。
3.1 简介
Encoder Interface 编码器接口
编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
每个高级定时器和通用定时器都拥有1个编码器接口
两个输入引脚借用了输入捕获的通道1和通道2
3.2 正交编码器
为什么不是使用单独定义一个引脚来表示正向和反向,而是使用这种提前或滞后的正交信号来表示方向???
- 正交信号的精度更高,因为A、B相都可以计次,就相当于计次频率提高了一倍。
- 正交信号可以抗噪声,因为两个信号的是交替跳变的,可以设计一个抗噪电路,如果一个信号不变,而另一个信号在连续跳变,也就是产生了噪声,CNT就可以选择不计次。
3.3 基本结构
3.4 工作模式
3.5 实例
均不反相:
TI1反相:
3.5 实验
📎6-8 编码器接口测速.zip
User:
- 📎main.c
Hardware:
- 📎OLED.c
- 📎OLED.h
- 📎OLED_Font.h
- 📎Encoder.h
- 📎Encoder.c
相关文章:
stm32之输出比较OC和输入捕获IC
目录 1.输出比较OC1.1 简介1.2 PWM简介1.3 输出比较通道1.3.1 通用定时器1.3.2 高级定时器 1.4 PWM基本结构1.6 舵机1.7 直流电机1.7.1 引入:MX1508 芯片1.7.2 TB6612芯片 1.8 结构体和API1.8.1 结构体1.8.2 API1. TIM_OC1Init / TIM_OC2Init / TIM_OC3Init / TIM_O…...
为什么Transformer推理需要做KV缓存
一、我们先来回忆一下在transformer中KV在哪里出现过,都有什么作用? α的计算过程: 这里引入三个向量: 图中的q为Query,用来匹配key值 图中的k为key,用来被Query匹配 图中的Value,是用来被进行加权平均的 由…...
Stream和Collections工具类
Stream流 Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象。这种风格将要处理的元素集合看作一种流,流在管道中传输&am…...
结合Hutool 突增突降检测的算法
在 Hutool 中虽然没有直接提供“突增突降检测”的算法,但可以通过其提供的工具类(如 CollUtil、ArrayDeque、MathUtil 等)结合滑动窗口、差分分析等方法,快速实现突增突降检测逻辑。以下是基于 Hutool 的实现思路和示例代码&#…...
java springboot deepseek流式对话集成示例
1.直接上代码-后端: RestController CrossOrigin(origins "*") public class DeepSeekController {private static final String API_URL "https://api.deepseek.com/v1/chat/completions";private final ObjectMapper objectMapper new Ob…...
技术对暴力的削弱
信息时代的大政治分析:效率对暴力的颠覆 一、工业时代勒索逻辑的终结 工厂罢工的消亡 1930年代通用汽车罢工依赖工厂的物理集中、高资本投入和流水线脆弱性,通过暴力瘫痪生产实现勒索。 信息时代企业分散化、资产虚拟化(如软件公司可携带代码…...
RAG框架搭建(基于Langchain+Ollama生成级RAG 聊天机器人)
目录 一 Ollama安装 Windows 系统安装 验证安装 二 Langchain安装 2.1 先创建一个虚拟环境 2.2 安装最新版 langchain 三 基于 Langchain私有模型,构建一个生成级RAG 聊天机器人 3.1 初始化LLM 3.2 增强生成 3.3生成嵌入 3.4 生成并存储嵌入 一 Ol…...
spring cloud gateway(网关)简介
Spring Cloud Gateway 是一个基于 Spring WebFlux 构建的强大且广泛使用的 API 网关。它负责处理所有进入的请求,并将它们路由到相应的后端服务。 Gateway 的主要作用: 统一的入口点 (Single Entry Point): 它为所有的客户端请求提供了一个…...
webrtc 视频直播
webrtc 是一种开源的音视频通信技术,可以不借助中间媒介建立浏览器点对点(peer-to-peer)连接,实现音视频以及其他数据的传输。webrtc具有平台兼容性,低延迟与高实时的优点。今天主要记录一下webrtc的使用记录ÿ…...
【Elastsearch】如何获取已创建的api keys
在Elasticsearch中,可以通过API获取已创建的API密钥(API keys)。以下是具体步骤和示例: 1.使用GET请求获取API密钥 Elasticsearch提供了GETAPI,用于列出当前用户可以访问的所有API密钥。 请求格式 plaintext GET /_se…...
AI Agent开发第57课-AI用在销售归因分析场景中-用随机森林从0构建自己的“小模型”
开篇 在前一篇《机器学习的基础-线性回归如何应用在商业场景中》里,我们说到了如果我们只是简单的分析和预测一下投入广告费用和销售额增长是否存在必然关系,我们用了线性回归法得到了分析,得到的分析结果极其精准,以及提到了:如果当销售因素是非线性的并且有着额外一些如…...
Elasticsearch知识汇总之ElasticSearch部署
五 ElasticSearch部署 部署Elasticsearch,可以在任何 Linux、MacOS 或 Windows 机器上运行 Elasticsearch。在Docker 容器 中运行 Elasticsearch 。使用Elastic Cloud on Kubernetes 设置和管理 Elasticsearch、Kibana、Elastic Agent 以及 Kubernetes 上的 Elasti…...
高等数学第五章---定积分(§5.4反常积分)
5.4 反常积分 前面我们学习了定积分 ∫ a b f ( x ) d x \int_a^b f(x) d x ∫abf(x)dx,其中积分区间 [ a , b ] [a, b] [a,b] 是有限区间,且被积函数 f ( x ) f(x) f(x) 在 [ a , b ] [a, b] [a,b] 上是连续的(或至多有有限个第一类间…...
UE5 ML机械学习肌肉反应与布料反应
在查找Ai过渡动画的过程中,通过米哈游鹿鸣的展示,了解到的机械学习技术 https://dev.epicgames.com/documentation/zh-cn/unreal-engine/using-the-machine-learning-deformer-in-unreal-engine#%E5%85%88%E5%86%B3%E6%9D%A1%E4%BB%B6 https://dev.epicgames.com/documentati…...
UE5 诺伊腾动捕使用笔记
AxisStudio使用说明 诺伊腾动捕有两个软件,分别是AxisStudio和Axis Post 打开软件后选择"工程" 分为两种工程,一种是PN Studio和PN3,这两个工程对于不同的骨骼方式(也可以修改) 以PNStudio的2.0-Carwheel举例 右侧的数据为你的目标骨骼的尺寸,例如我现在是Metahuma…...
【测试开发】概念篇 - 从理解需求到认识常见开发、测试模型
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
【2025年】基于电脑的jdk1.8通过idea创建springboot2.x版本(非常简洁快速)
【2025年】基于电脑的jdk1.8通过idea创建springboot2.x版本 提示:帮帮志会陆续更新非常多的IT技术知识,希望分享的内容对您有用。本章分享的是springboot的使用。前后每一小节的内容是存在的有:学习and理解的关联性。【帮帮志系列文章】&…...
在sheel中运行Spark
RDD基本概念 Resilient Distributed Dataset 叫做弹性分布式数据集,是Spark中最基本的数据抽象,是分布式计算的实现载体,代表一个不可变,可分区,里面的元素并行计算的集合。 Dataset: 一个数据集合…...
如何从windows中的cursor打开windows里面的wsl中的项目
解决方法: ✅ 步骤 1:在 Windows 中安装 Cursor 首先,确保你已在 Windows 上安装了 Cursor 编辑器。 安装完成后,打开 Cursor 编辑器。 ✅ 步骤 2:安装并配置 WSL 扩展 为了让 Cursor 与 WSL 集成,需…...
UE5 C++项目实现单例
在 UE5 中,要实现“全局只有一个实例”的单例模式,主要有两种思路:一种是传统 C++ 静态单例,另一种是利用 UE5 提供的Subsystem体系(如 UGameInstanceSubsystem、UWorldSubsystem 等)。下面先给出核心示例代码及对比,随后讨论典型使用场景、优缺点,对常见问题作出诊断并…...
信息论04:从信息熵到互信息——信息共享的数学度量
从信息熵到互信息:信息共享的数学度量 1. 信息论基础概念 1.1 信息熵(Information Entropy) 定义:信息熵由香农提出,用于量化随机变量的不确定性。对于离散随机变量X,其熵定义为: H ( X ) …...
MYSQL的DDL语言和单表查询
MYSQL的DDL语言和单表查询 Mysql介绍 SQL(Structured Query Language)是一种专门用于管理和操作关系型数据库的标准化语言,通过定义、查询、更新和控制数据,为应用程序提供一致且高效的持久化存储方式。它包含数据定义语言&…...
奇瑞依托汽车产业链,实现服务机器人万台下线
近日,奇瑞集团旗下墨甲机器人(MOJA)全球批量交付的消息得到官方确认。这一重大进展不仅标志着奇瑞在服务机器人领域的商业化落地迈出关键一步,更成为国产智能装备进军全球市场的重要里程碑。 墨甲机器人简介 产品定位 墨甲是奇…...
Python Bug 修复案例分析:函数参数传递引发的逻辑错误修复
在 Python 编程学习的过程中,各种意想不到的 Bug 常常会阻碍我们编写的程序的正常运行。这次,我们将围绕一个因函数参数传递导致逻辑错误的案例,深入剖析 Bug 的修复全过程,帮助初学者掌握处理这类问题的方法。 案例背景 最近编写…...
论文阅读笔记——ROBOGROUND: Robotic Manipulation with Grounded Vision-Language Priors
RoboGround 论文 一类中间表征是语言指令,但对于空间位置描述过于模糊(“把杯子放桌上”但不知道放桌上哪里);另一类是目标图像或点流,但是开销大;由此 GeoDEX 提出一种兼具二者的掩码。 相比于 GR-1&#…...
deeplabv3+街景图片语义分割,无需训练模型,看不懂也没有影响,直接使用,cityscapes数据集_23
目录 0、简介1、下载链接1.1、CSDN链接,含权重文件直接使用,建议直接下这个,还不限速。1.2 Github链接: 2、下载代码,下载预训练好的权重3、预测代码4、像素提取,或者说类别提取5、文档部分内容截图6、其他…...
JavaScript性能优化实战:深入探讨性能瓶颈与优化技巧
JavaScript性能优化实战:深入探讨性能瓶颈与优化技巧 引言 在当今快速发展的Web世界中,性能已经成为衡量应用质量的关键指标。随着Web应用复杂度的不断提升,JavaScript作为前端开发的核心语言,其性能优化变得尤为重要。本文旨在全面深入地探讨JavaScript性能优化的各个方…...
第2章——springboot核心机制
一、为何以继承方式引入SpringBoot 1.提出疑问 以前我们在开发项目时,需要什么,引入对应的依赖就行,比如我们需要连接mysql数据,则引入mysql驱动的依赖,如下: <dependency><groupId>com.mys…...
huggingface 热门开源TTS模型Dia-1.6B,支持多人对话生成、情感控制~
简介 Dia-1.6B 是一款由 Nari Labs 开发的开源文本转语音(TTS)模型,专注于生成自然对话。其项目背景和模型架构基于近期可用的网络信息进行了详细分析,以下是全面的报告。 项目背景概述 Dia-1.6B 的开发始于 Nari Labsÿ…...
深入理解West:介绍、使用及与Repo的对比
目录 引言 West简介 West的由来 West的核心功能 West的架构与工作流程 West安装与使用 环境准备与安装 Manifest 文件结构解析 常用命令详解与进阶用法 Tip与Troubleshoot 实践案例:基于West的Zephyr项目管理 初始化与同步 构建与闪存 插件示例:自定义命令 Repo简介 Repo的背…...
力扣-hot100 (矩阵置零)
73. 矩阵置零 中等 给定一个 *m* x *n* 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1: 输入:matrix [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]] 示…...
OpenKylin安装Elastic Search8
一、环境准备 Java安装 安装过程此处不做赘述,使用以下命令检查是否安装成功。 java -version 注意:Elasticsearch 自 7.0 版本起内置了 OpenJDK,无需单独安装。但如需自定义 JDK,可设置 JAVA_HOME。 二、安装Elasticsearch …...
【JVM】从零开始深度解析JVM
本篇博客给大家带来的是JVM的知识点, 重点在类加载和垃圾回收机制上. 🐎文章专栏: JavaEE初阶 🚀若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅🚀 …...
制造企业PLM系统成本基准:2025年预算分配与资源成本率的5种优化模型
在 2025 年制造业数字化转型的浪潮中,PLM(产品生命周期管理)系统已成为制造企业提升核心竞争力的关键工具。然而,PLM 系统的实施和运营成本较高,如何有效控制成本、优化预算分配和资源成本率,成为企业关注的…...
【Python】一键提取视频音频并生成MP3的完整指南 by `MoviePy`
摘要 昨天, 我在让一个小朋友给我整理一次培训的视频的时候,我看到他把视频文件放到剪映里面处理。 我以为他要干什么呢, 还很期待,结果他只是为了导出音频而已。 于是就有了今天的这篇博客。 作为音视频处理领域的常用需求&…...
Golang领域Beego框架的中间件开发实战
在Golang的Beego框架中,中间件(Middleware)是一种强大的机制,用于在请求处理的不同阶段插入自定义逻辑。 中间件可以用于处理日志记录、身份验证、错误处理、请求/响应修改等任务。 Beego框架中间件开发的实战指南: …...
Elasticsearch:我们如何在全球范围内实现支付基础设施的现代化?
作者:来自 Elastic Kelly Manrique SWIFT 和 Elastic 如何应对基础设施复杂性、误报问题以及日益增长的合规要求。 金融服务公司在全球范围内管理实时支付方面面临前所未有的挑战。SWIFT(Society for Worldwide Interbank Financial Telecommunication -…...
【LLIE专题】基于 CLIP 的无监督背光增强算法
CLIP-LIT: Iterative Prompt Learning for Unsupervised Backlit Image Enhancement(2023,ICCV) 专题介绍一、研究背景二、CLIP-LIT方法三、实验结果四、总结五、思考 本文将对 CLIP-LIT: Iterative Prompt Learning for Unsupervised Backl…...
深入了解酒店一次性牙刷:材质选择与设计考量全解析
酒店的一次性牙刷是我们住酒店时常见的用品,它方便了很多旅客出行,虽小巧,却对人们口腔清洁有一定作用,扬州卓韵酒店用品在这个领域表现优秀,下面我们就深入了解酒店一次性牙刷。 一次性牙刷的材质相当重要。常见的有…...
[人机交互]理解用户
一.解释什么是认知,以及它对交互设计的重要性 1.1什么是认知 认知是指与knowing相关的能力,行为和过程(考填空) -如何感知物理刺激?如注意、知觉等 -如何认识自我、他人以及环境?如意识、记忆等 -如何…...
css3伸缩盒模型第二章(侧轴相关)
css3伸缩盒模型第二章(侧轴相关) 侧轴对齐方式 侧轴对齐我们需要分两种情况,一种是多行,一种是单行,两种设置方式不同 属性:align-items 单行属性: align-content 多行 单行 align-items flex-start: 侧轴的起点对…...
【WPS】怎么解决“word的复制表格”粘贴到“excel的单元格”变多行单元格的问题
把 word文档复制表格到这个excel表格上面的话,会出现由单个单元格变成多行单元格的情况。 现在,就这个问题怎么解决,提出了一个方案,就是先查找是什么导致了这个换行,然后再将换行的这个字符进行一个整体的替换&#x…...
股指期货深度贴水是什么意思?
如果贴水的幅度特别大,比如股票指数是3000点,但股指期货的价格只有2800点,贴水了200点,这就叫“深度贴水”。简单来说,股指期货贴水就是指股指期货的价格低于其对应的现货指数价格。当这种贴水程度较大时,就…...
GCC编译器安装详细说明(举例arm-2013q3)
比如在官网GNU Arm Embedded Toolchain project files : GNU Arm Embedded Toolchain 下载了一个gcc-arm-none-eabi-4_7-2013q3-20130916-linux.tar.bz2 1 sudo tar -xvf gcc-arm-none-eabi-4_7-2013q3-20130916-linux.tar.bz2 解决了解压 在部署环境,在安装2…...
第十一届蓝桥杯 2020 C/C++组 蛇形填数
目录 题目: 题目描述: 题目链接: 思路: 思路详解: 代码: 代码详解: 题目: 题目描述: 题目链接: 蛇形填数 - 蓝桥云课 思路: 思路详解: 看图找规律…...
https://juejin.cn/editor/drafts/7262346366541070395
.Net Core从零学习搭建权限管理系统教程 推荐一组WPF自定义控件开源项目。 项目简介 这是基于WPF开发的,为开发人员提供了一组方便使用自定义组件,并提供了各种常用的示例。 包含组件:数据表格、属性列表、树形列表、选色器、单选框列表、…...
STL?string!!!
一、引言 在之前的文章中,我们一同学习了有关类和对象、模板、动态内存管理的相关知识,那么接下来一段时间我们将要趁热打铁,一起来手撕C库中最重要的一个库----STL中的一些容器,在手撕它们之前,我将先介绍一下对应的容…...
CentOS 7 安装指定版本 Docker 及镜像加速/配置优化攻略
摘要 本文详述 CentOS 7 系统下安装指定版本 Docker ,涵盖镜像加速配置(实测最快)、存储位置优化、日志轮转等核心配置。 文章目录 一、安装指定版本Docker1.1 卸载旧版本(如有)1.2 安装依赖包1.3 添加Docker仓库&…...
域名别名(CNAME)解析及域名注册操作步骤
以虚拟主机为例,大多网站空间无独立ip,域名打开以别名解析为主,那域名别名(CNAME)如何解析呢?以下以新网为例,别名解析操作步骤: 1.登录域名管理界面,点击管理解析记录; …...
JVM内存模型深度解剖:分代策略、元空间与GC调优实战
堆 堆是Java虚拟机(JVM)内存管理的核心区域,其物理存储可能分散于不同内存页,但逻辑上被视为连续的线性空间。作为JVM启动时创建的第一个内存区域,堆承载着几乎所有的对象实例和数组对象(极少数通过逃逸分…...