TIM的OC和IC(非PWMI模式)
tim其它...(高级的特性?)
PWM 频率与占空比
$$PWM_f = TIM_f/((PSC+1)*(ARR+1))$$
$$Duty = CCR/(ARR+1)$$
// 所以一般普遍来说 改PSC和CCR会好一些
// void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode); // TIM_PSCReloadMode can be TIM_PSCReloadMode_Update or TIM_PSCReloadMode_Immediate
// uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx); // PSC
// void TIM_SetComparek(TIM_TypeDef* TIMx, uint16_t Comparek); (where x can be 1-4)
// uint16_t TIM_GetCapturek(TIM_TypeDef* TIMx); (where x can be 1-4) // CCR
外部时钟源
// 外部时钟源?
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
孩子们市电当作外部时钟源有没有懂得
$$f=1/Ts \quad duty=Ton/Ts$$
输出比较 / PWM
/* 输出比较/PWM配置:决定一个通道怎样输出波形 */
typedef struct
{
uint16_t TIM_OCMode;
// 输出比较模式,如 PWM1、PWM2、翻转模式等
// TIM_OCMode_PWM1, TIM_OCMode_PWM2
uint16_t TIM_OutputState;
// 是否使能普通输出通道
// TIM_OutputState_Enable
uint16_t TIM_OutputNState;
// 是否使能互补输出 CHxN,仅 TIM1/TIM8 使用
uint16_t TIM_Pulse;
// 比较值 CCRx
// PWM模式下主要决定占空比:Duty ≈ CCR / (ARR + 1)
// void TIM_SetComparek(TIM_TypeDef* TIMx, uint16_t Comparek)
// uint16_t TIM_GetCapturek(TIM_TypeDef* TIMx);
uint16_t TIM_OCPolarity;
// 普通通道输出极性:高有效或低有效
// TIM_OCPolarity_High, TIM_OCPolarity_Low
uint16_t TIM_OCNPolarity;
// 互补通道输出极性,仅 TIM1/TIM8 使用
uint16_t TIM_OCIdleState;
// 普通通道空闲时的电平,仅 TIM1/TIM8 使用
uint16_t TIM_OCNIdleState;
// 互补通道空闲时的电平,仅 TIM1/TIM8 使用
} TIM_OCInitTypeDef;
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
TIM输出PWM: TIM_CounterMode && TIM_OCMode && TIM_OCPolarity
TIM_OCMode_PWM1 TIM_OCPolarity_High
输出比较TIM初始化:
RCC(有GPIO_PinRemapConfig时就有AFIO) -> GPIO初始化
-> (GPIO_PinRemapConfig 注意 GPIO_PartialRemap_TIMx和GPIO_FullRemap_TIMx)
-> ClockConfig -> TimeBaseInit(注意不开中断无NVIC以及注意**TIM_Period, TIM_Prescaler**)
-> TIM_OCInit -> TIM_Cmd
Kconfig与设备树
$$Freq = CK_PSC/((PSC+1)*(ARR+1))$$
$$Duty = CCR/(ARR+1)$$
然后是 IC...
AD 本身会产生大量均匀到来的中断,这对于 CPU 来说非常恶劣,
因为每次进出中断都必须保护所有寄存器,频繁的中断将导致大量不必要的开销。
因此,通过 DMA 在每个 AD 结果产生时自动移动到内存中,相当于空间换时间。
将大量小中断集中起来统一处理,这本身就能降低 CPU 的工作量。
/* 输入捕获配置:决定在哪个输入边沿保存 CNT */
typedef struct
{
uint16_t TIM_Channel;
// 选择捕获通道 CH1~CH4,捕获结果保存在对应CCRx
// uint16_t TIM_GetCapturek(TIM_TypeDef* TIMx);
// void TIM_SetComparek(TIM_TypeDef* TIMx, uint16_t Comparek); // k:1-4
uint16_t TIM_ICPolarity;
// 捕获极性:上升沿或下降沿(检测)
// TIM_ICPolarity_Rising, TIM_ICPolarity_Falling, TIM_ICPolarity_BothEdge
uint16_t TIM_ICSelection;
// 输入映射:
// TIM_ICSelection_DirectTI = 通道直接连接对应输入
// TIM_ICSelection_IndirectTI = 通道交叉连接另一路输入
// 孩子们这里是真的感觉有一点绕迷糊了
// 直连就是将通道1捕获的值,直接传到捕获寄存器1;交叉是将通道1的值,交叉传到捕获寄存器2。并且只能1和2交叉.3和4交叉
uint16_t TIM_ICPrescaler;
// 捕获边沿分频:每1、2、4或8个有效边沿捕获一次,这个相比时基单元的那个要重要一些了
// 注意:它不改变CNT的计数速度
uint16_t TIM_ICFilter;
// 输入数字滤波,0表示不滤波,0xF通常滤波最强(但是强硬件滤波会代表着高延时)
} TIM_ICInitTypeDef;
输入捕获: (边沿检测)上升/下降沿 看是否是对应CHx分直接/间接
定时器的输入捕获交叉引用(Cross-connection / Interconnection)功能,
允许一个物理引脚的输入信号同时路由到多个捕获通道,或者让一个通道捕获另一个引脚的信号。
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
感觉测频法会好一些(f高时);测周法(f比较小时)
TI1FP1, TI2FP2(CH1, CH2) ??? TRGO TRGI ???
不赖
TIxFPy:定时器Channelx的信号(CHx),...,连接到捕获/比较通道y(和TIM_ICSelection有关)
每一个捕获/比较通道都是围绕着一个捕获/比较寄存器(包含影子寄存器),包括捕获的输入部分
(数字滤波、多路复用和预分频器),和输出部分(比较器和输出控制)。
好像现在我看不懂原理但是会写代码了(x)
1.捕获信号路由 2.TIxFPy TRGI TRGO
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
手册摘录(14.3.x 捕获/比较)
// 不管了先把手册搬过来,有些地方还是写的不错的
14.3.4 捕获/比较通道
每一个捕获/比较通道都是围绕着一个捕获/比较寄存器(包含影子寄存器),包括捕获的输入部分
(数字滤波、多路复用和预分频器),和输出部分(比较器和输出控制)。
下面几张图是一个捕获/比较通道概览。
输入部分对相应的TIx输入信号采样,并产生一个滤波后的信号TIxF。然后,一个带极性选择的
边缘检测器产生一个信号(TIxFPx),它可以作为从模式控制器的输入触发或者作为捕获控制。该
信号通过预分频进入捕获寄存器(ICxPS)。捕获/比较模块由一个预装载寄存器和一个影子寄存器组成。读写过程仅操作预装载寄存器。
通用定时器(TIMx)
在捕获模式下,捕获发生在影子寄存器上,然后再复制到预装载寄存器中。
在比较模式下,预装载寄存器的内容被复制到影子寄存器中,然后影子寄存器的内容和计数器
进行比较。14.3.5 输入捕获模式
在输入捕获模式下,当检测到ICx信号上相应的边沿后,计数器的当前值被锁存到捕获/比较寄存
器(TIMx_CCRx)中。当捕获事件发生时,相应的CCxIF标志(TIMx_SR寄存器)被置’1’,如果使能
了中断或者DMA操作,则将产生中断或者DMA操作。如果捕获事件发生时CCxIF标志已经为
高,那么重复捕获标志CCxOF(TIMx_SR寄存器)被置’1’。写CCxIF=0可清除CCxIF,或读取存
储在TIMx_CCRx寄存器中的捕获数据也可清除CCxIF。写CCxOF=0可清除CCxOF。
以下例子说明如何在TI1输入的上升沿时捕获计数器的值到TIMx_CCR1寄存器中,步骤如下:
● 选择有效输入端:TIMx_CCR1必须连接到TI1输入,所以写入TIMx_CCR1寄存器中的
CC1S=01,只要CC1S不为’00’,通道被配置为输入,并且TM1_CCR1寄存器变为只读。
● 根据输入信号的特点,配置输入滤波器为所需的带宽(即输入为TIx时,输入滤波器控制位是
TIMx_CCMRx寄存器中的ICxF位)。假设输入信号在最多5个内部时钟周期的时间内抖动,
我们须配置滤波器的带宽长于5个时钟周期。因此我们可以(以fDTS频率)连续采样8次,以确
认在TI1上一次真实的边沿变换,即在TIMx_CCMR1寄存器中写入IC1F=0011。
● 选择TI1通道的有效转换边沿,在TIMx_CCER寄存器中写入CC1P=0(上升沿)。
● 配置输入预分频器。在本例中,我们希望捕获发生在每一个有效的电平转换时刻,因此预
分频器被禁止(写TIMx_CCMR1寄存器的IC1PS=00)。
● 设置TIMx_CCER寄存器的CC1E=1,允许捕获计数器的值到捕获寄存器中。
● 如果需要,通过设置TIMx_DIER寄存器中的CC1IE位允许相关中断请求,通过设置
TIMx_DIER寄存器中的CC1DE位允许DMA请求。
当发生一个输入捕获时:
● 产生有效的电平转换时,计数器的值被传送到TIMx_CCR1寄存器。
● CC1IF标志被设置(中断标志)。当发生至少2个连续的捕获时,而CC1IF未曾被清除,
CC1OF也被置’1’。
● 如设置了CC1IE位,则会产生一个中断。
● 如设置了CC1DE位,则还会产生一个DMA请求。
为了处理捕获溢出,建议在读出捕获溢出标志之前读取数据,这是为了避免丢失在读出捕获溢
出标志之后和读取数据之前可能产生的捕获溢出信息。
注: 设置TIMx_EGR寄存器中相应的CCxG位,可以通过软件产生输入捕获中断和/或DMA请求。14.3.6 PWM输入模式
该模式是输入捕获模式的一个特例,除下列区别外,操作与输入捕获模式相同:
● 两个ICx信号被映射至同一个TIx输入。
● 这2个ICx信号为边沿有效,但是极性相反。
● 其中一个TIxFP信号被作为触发输入信号,而从模式控制器被配置成复位模式。
例如,你需要测量输入到TI1上的PWM信号的长度(TIMx_CCR1寄存器)和占空比(TIMx_CCR2
寄存器),具体步骤如下(取决于CK_INT的频率和预分频器的值)
● 选择TIMx_CCR1的有效输入:置TIMx_CCMR1寄存器的CC1S=01(选择TI1)。
● 选择TI1FP1的有效极性(用来捕获数据到TIMx_CCR1中和清除计数器):置CC1P=0(上升沿
有效)。
● 选择TIMx_CCR2的有效输入:置TIMx_CCMR1寄存器的CC2S=10(选择TI1)。
● 选择TI1FP2的有效极性(捕获数据到TIMx_CCR2):置CC2P=1(下降沿有效)。
● 选择有效的触发输入信号:置TIMx_SMCR寄存器中的TS=101(选择TI1FP1)。通用定时器(TIMx)
● 配置从模式控制器为复位模式:置TIMx_SMCR中的SMS=100。
● 使能捕获:置TIMx_CCER寄存器中CC1E=1且CC2E=1。
由于只有TI1FP1和TI2FP2连到了从模式控制器,所以PWM输入模式只能使用TIMx_CH1/TIMx_CH2信号。
PWMI模式和主从触发模式本身都不依赖中断,它们是定时器硬件自动完成的。
CH3/CH4测PWM不依赖中断,但依赖软件作差;中断只是为了及时保存捕获结果,避免CCRx被覆盖。
ti1fp1是从模式的其中一个输入信号,从模式的作用是收到信号就做相应的操作,如清零cnt,
如果没有这个,那就只能用中断来完成这个任务,太浪费,所以就有了这个硬件电路,通过PWMI模式来完成这些频率高且简单的任务
信号路由:OC / IC / TRGO / TRGI
解释:
IC的PWMI模式需要TIMxCH1/CH2,其他两个不能自动计算
OC流程:TIMxCHy ---> TIxF -> TIxFPx -> ICx -> CCR
(TIM_ICFilter)
如果要OC -> IC的话...(内部触发捕获:)
TIMx -> TRGO -> ITRm -> TRGI -> TIMy
( OC ) ( IC )
TRGO → ITRm → TRGI/TRC → ICx
使用 TRC
外部引脚捕获:
CHx引脚 → TIx → ICx
使用 TIM_ICSelection_DirectTI
// 应该一般也是使用外部的emm
#define TIM_ICSelection_DirectTI ((uint16_t)0x0001) /*!< TIM Input 1, 2, 3 or 4 is selected to be
connected to IC1, IC2, IC3 or IC4, respectively */
#define TIM_ICSelection_IndirectTI ((uint16_t)0x0002) /*!< TIM Input 1, 2, 3 or 4 is selected to be
connected to IC2, IC1, IC4 or IC3, respectively. */
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
TIM_InputTriggerSource:
TS[2:0]:触发选择 (Trigger selection) 位6:4
这3位选择用于同步计数器的触发输入。
000:内部触发0(ITR0),TIM1 100:TI1的边沿检测器(TI1F_ED)
001:内部触发1(ITR1),TIM2 101:滤波后的定时器输入1(TI1FP1)
010:内部触发2(ITR2),TIM3 110:滤波后的定时器输入2(TI2FP2)
011:内部触发3(ITR3),TIM4 111:外部触发输入(ETRF)
关于每个定时器中ITRx的细节,参见表78。
注:这些位只能在未用到(如SMS=000)时被改变,以避免在改变时产生错误的边沿检测。
GPIO上的OC→IC用于测量真实外部波形;
内部TRGO→TRGI主要用于定时器同步、级联和硬件控制,不是专门为了输入捕获。
OC → IC 初始化流程
所以。。(还要注意PWMI模式只能CH1, CH2用)
OC -> IC(普通):
OC侧:RCC -> GPIO_init -> ClockConfig -> TimeBaseInit -> (无中断) -> OCInit(OCMode, OCPolarity) -> TIM_Cmd
IC侧: RCC -> GPIO_init -> ClockConfig -> TimeBaseInit -> ITConfig(TIM_IT_CCx) -> ICInit(ICPolarity, TIM_ICSelection) -> NVIC_Init -> TIM_Cmd
OC -> IC(内部通道PWMI模式):
OC侧一样,IC侧在TIM_Cmd之前要配置TRGO(-> SelectInputTrigger -> SelectSlaveMode )....这个就测量占空比很方便了
纯 IC 测 Duty 和 Freq
哎,仅凭我口还是不能很好的记述出来
纯IC测Duty和Freq...xxx
以TIM4CH3(RisingDirect, FallingIndirect)为例:
那好了直接记公式好吧(TIM4为IC,所以只出现了TIM4的数据)
// 孩子们我不用PWMI直接用普通的IC模式测量占空比
// 我服了好像更加复杂了https://www.cnblogs.com/helesheng/p/14107012.html
// Duty = CCR/(ARR+1) -> xxx
// Freq = CK_PSC/((PSC+1)*(ARR+1)) -> (72MHz/TIM_GetPrescaler(TIM4))/(TIM_GetCapture4(TIM4)+1)
void TIM4_IRQHandler() { // IC -> TIM4CH3:PB8(RisingDirect), TIM4CH4:PB9(FallingIndirect)
// 模仿PWMI模式...不过是软件方式2333
if (TIM_GetITStatus(TIM4, TIM_IT_CC3) == SET) {
TIM_ClearITPendingBit(TIM4, TIM_IT_CC3);
// TIM_GetCounter(TIM4)
TIM4->CCR3 = TIM_GetCounter(TIM4);
TIM4->CNT = 0;
}
if (TIM_GetITStatus(TIM4, TIM_IT_CC4) == SET) {
TIM_ClearITPendingBit(TIM4, TIM_IT_CC4);
TIM4->CCR4 = TIM_GetCounter(TIM4);
}
}

浙公网安备 33010602011771号