STM32之定时器(二)

1. 输入捕获

1.1 输入捕获的基本原理

• 输入捕获的功能:可以测量 输入信号 的参数,比如可以测量输入信号的周期,占空比,脉宽等等。

• 怎么测量?

• 其实就是捕获外部输入信号 的 信号变化,比如捕获上升沿或者下降沿。

• 捕获到信号变化的时候,会触发一个ccx拍照事件这个事件会拍下当前CNT寄存器里面的值,然后放到对应CCR寄存器里面

• 拍照保存CNT计数器里面的值放到CCR寄存器里面,可以知道信号变化的那一刻时间

• 例子,假设要测量这个脉宽。如图:

• 设置一个CH1通道捕获上升沿,然后拍照把当前CNT计数器的值放到CCR1寄存器里面。

• 设置一个CH2通道捕获下降沿,然后拍照把当前CNT计数器的值放到CCR2寄存器里面。

• 然后,将两个寄存器里面的值相减得到脉宽的时间。

1.2 输入捕获的内部结构

• 输入捕获的内部结构图,如图所示。

• 输入捕获通道主要分为四个部分:输入滤波,边沿检测,信号选择,分频器。

1.2.1 输入滤波,如图:

• 输入滤波可以把杂乱的波形过滤成整齐的波形。

• 作用:去噪声的影响,滤除波形上的毛刺。

1.2.2 边缘检测,如结构图

• 边沿检测的是输入信号的变化,可以检测信号上升沿或者信号下降沿。

1.2.3 信号选择

• 选择输入信号,这里的信号分为三种可以选择:

• 直接:上图通道1和通道2是同一对的,通道3和通道4也是同一对的,根据结构图也就是说,同一个通道的值 测出来的值 可以放到不同的寄存器。

• 比如,通道1测到的值得可以放到CCR1和CCR2里面,但是一定要同一组才行。这样做的好处就是能够过节省引脚。

• 直接的意思,从原本的通道测出的得,放到对应的寄存器。比如,通道1测出的值放到CCR1,这就是直接。

​​​​​• 间接:从原本的通道测出的得,放到对侧的寄存器,比如,通道1测出的值放到CCR2,这就是间接。

• TRC:这个从模式输入的。

1.2.4 分频器

​​​​​• 作用:分频的系数 会影响CCx事件的触发,例子,如图:

​​​​​• 如果,系数设置为1,就说明 需要 测出一次上升沿就触发事件,如果是2的话。这就需要测出两次上升沿才触发事件,以此类推。

​​​​​• 输入捕获节点的名字,如图:

​​​​​• 解析:这里以TI1FP1为例子,TI1表示的时定时器 输入 1通道,F时滤波过后的信号,P是选择了极性的(选择了上升沿或者下降沿),1是被那个通道使用,这里是被通道1使用。

1.3 输入捕获实操

• 一些编程接口,如图:

1.3.1 GPIO配置,如图:

• 其实,最好是给一个默认低电压,这样就可以就算是断开的时候,也可以是有默认值。

1.4 代码

cpp 复制代码
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"

void usart1_init();
void hcsr04_init();

int main(void)
{
    usart1_init();
    hcsr04_init();
	while(1)
	{
        //1.清0CNT里面的值
        TIM_SetCounter(TIM1,0);
        //2.清除cc1和cc2标志位
        TIM_ClearFlag(TIM1,TIM_FLAG_CC1);
        TIM_ClearFlag(TIM1,TIM_FLAG_CC2);
        //3.开始定时器
        TIM_Cmd(TIM1,ENABLE);
        //4.向TRIG引脚发至少10us高电平,让hcsr工作
        GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
        DelayUs(10);
        GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
        //5.等待cc1和cc2标志位为1,就说两个通道都采集相应的信号
        while(TIM_GetFlagStatus(TIM1,TIM_FLAG_CC1) == RESET);
        while(TIM_GetFlagStatus(TIM1,TIM_FLAG_CC2) == RESET);
        //7.关闭定时器
        TIM_Cmd(TIM1,DISABLE);
        //8.计算,这里面都是us级别
        uint16_t ccr1 = TIM_GetCapture1(TIM1);
        uint16_t ccr2 = TIM_GetCapture2(TIM1);
        
        float distance = ((ccr2 - ccr1) * 1.0e-6f * 340.0f) / 2;
        My_USART_Printf(USART1,"dis : %.4f",distance);
        Delay(100);
        
        
	}
}

void hcsr04_init(){
    //配置时基
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
    
    TIM_TimeBaseInitTypeDef tim_initstruct = {0};
    
    tim_initstruct.TIM_CounterMode = TIM_CounterMode_Up;
    tim_initstruct.TIM_Period = 65536 - 1;
    tim_initstruct.TIM_Prescaler = 72 - 1;
    tim_initstruct.TIM_RepetitionCounter = 0;
    
    TIM_TimeBaseInit(TIM1,&tim_initstruct);
    
    //配置ECHO引脚
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef gpio_initstruct = {0};
    
    //配置PA9 TX
    gpio_initstruct.GPIO_Mode = GPIO_Mode_IPD;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_8;
    //gpio_initstruct.GPIO_Speed = GPIO_Speed_2MHz;
    
    GPIO_Init(GPIOA,&gpio_initstruct);
    
    //配置捕获通道,需要配置两个通道,一个捕获上升沿和一个捕获下降沿
    
    TIM_ICInitTypeDef tim_icinitstruct = {0};
    
    tim_icinitstruct.TIM_Channel = TIM_Channel_1;
    tim_icinitstruct.TIM_ICFilter = 0;
    tim_icinitstruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
    tim_icinitstruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    tim_icinitstruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
    
    TIM_ICInit(TIM1,&tim_icinitstruct);
    
    
    tim_icinitstruct.TIM_Channel = TIM_Channel_2;
    tim_icinitstruct.TIM_ICFilter = 0;
    tim_icinitstruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
    tim_icinitstruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    tim_icinitstruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
    
    TIM_ICInit(TIM1,&xtim_icinitstruct);
    
    
    //配置TRIG引脚
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_0;
    gpio_initstruct.GPIO_Speed = GPIO_Speed_2MHz;
    
    GPIO_Init(GPIOA,&gpio_initstruct);
}

void usart1_init(){
    
    //配置GPIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef gpio_initstruct = {0};
    
    //配置PA9 TX
    gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_9;
    gpio_initstruct.GPIO_Speed = GPIO_Speed_2MHz;
    
    GPIO_Init(GPIOA,&gpio_initstruct);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    //配置A10 RX
    
    gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_10;
    //gpio_initstruct.GPIO_Speed = GPIO_Speed_2MHz;
    
    GPIO_Init(GPIOA,&gpio_initstruct);
    
    
    //配置USART1
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    
    USART_InitTypeDef usart_initstruct = {0};
    
    usart_initstruct.USART_BaudRate = 115200;
    usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    usart_initstruct.USART_Parity = USART_Parity_No;
    usart_initstruct.USART_StopBits = USART_StopBits_1;
    usart_initstruct.USART_WordLength = USART_WordLength_8b;
    
    USART_Init(USART1,&usart_initstruct);
    
    USART_Cmd(USART1,ENABLE);
}

2. 从模式控制器

• 从模式控制器是定时器中一个允许定时器被外部信号控制的功能。简单说就是让定时器"听命于"其他信号,而不是自顾自地运行。

2.1 什么是从模式控制器

• 这是定时器从模式的框图:

• 可以看到定时器从模式控制器,有三个箭头

• 其实,最重要就是这三个箭头

• TRGI:触发输入的作用,相当于接收命令或者信号,控制里面的时基单元做指定的动作,如断开开关,设置分频系数,清零CNT等等。

• TRGO:触发输出的作用,相当于向外发出命令或者信号来控制其他定时器或者其他的片上模块,如触发ADC的采集等。

• 双向箭头:从模式控制器与时基单元的相互作用。

2.2 8种从模式,如图:

• 从模式:相当于被控对象,TRGI输入的信号是什么,那么该时基单元就需要根据 从模式的要求,做什么的动作。

2.3 8种主模式,如图:

• 根据配置,时基单元的信号 通过 双向箭头经过从模式控制器通过TRGO发出。

2.4 定时器级联的例子,如图:

• 将外界的时钟信号(1kHz)经过分频(999 + 1)后得到1hz,分辨率就为1s,然后ARR的周期为59 + 1,并且重复计数器设置为0,所以产生一次更新事件的时间为1分钟。

• 所以一分钟之后,信号通过TRGO发出去 给 另外一个定时器的TRGI接收。

• TIM2需要接收 60分钟 之后才产生更新事件,才能通过TRGO发给TIM3。

2.5 定时器同时启停的例子,如图:

• TIM1连接着TIM2和TIM3:

• TIM1的主模式是使能模式,也就是说开关闭合TRGO = 1,断开时TRGO = 0。

• TIM2和TIM3的从模式都是门模式,也就说TRGI = 1时开关闭合。TRGI = 0时开关断开。

2.6 TRGI的来源,如图,这是从模式控制器的结构简图

• 可以看出TRGI由这四路来,TI1FP1,TI2FP2,TRC,ETR这四个当中选择。

• ETR:是由 外部参考信号 输入,是定时器ETR引脚输入。

• TRC主要是以ITR输入,从其他定时器的TRGO输入的(级联和其他定时器)。

• 如图,以上面例子来说

• TIM2是和TIM1级联,信号是来自TIM1的TRGO,看上面的表可以指定选择的是iTR0

• 注意,不同的定时器的ITR是不一样的。

2.7 PWM参数测量原理,如图:

• 将外部输入的PWM信号通过CH1输入,使用从模式和输入捕获去测量PWM的参数,例如占空比,周期等

• 其实,可以就使用输入捕获就可以去测量,但是太复杂,就单单CNT这个可能会溢出。

• 从模式的TRGI使用的是复位模式,也就说测量到上升沿就清零CNT。

• 如图:

• 测量的图

• 如图,测量到上升沿就会清零CNT,然后开始计数,当出现下降沿就捕获,并且将此刻CNT的值放到CCR2里面,然后再出现上升沿的时候把值放到CCR1里面,然后清零。

• 这样就可以知道一个周期是多少,通过计算也可以指定占空比是多少。

2.8 代码

cpp 复制代码
#include "stm32f10x.h"
#include "delay.h"
#include "math.h"
#include "usart.h"

void usart1_init();
void tim3_pwm_init();
void tim1_pwm_init();

int main(void)
{
    usart1_init();
    tim3_pwm_init();
    tim1_pwm_init();
    TIM_SetCompare1(TIM3,200);
    //My_USART_SendString(USART1,"122345");
	while(1)
	{
        //先清除trigger标志位的值,测量之前
        TIM_ClearFlag(TIM1,TIM_FLAG_Trigger);
        //等待标志位由0变成1,就说明由上升沿的捕获到,也就说一次测量就结束
        while(TIM_GetFlagStatus(TIM1,TIM_FLAG_Trigger) == RESET);
        //获取值
        uint16_t crr1 = TIM_GetCapture1(TIM1);
        uint16_t crr2 = TIM_GetCapture2(TIM1);
        
        float period = crr1 * 1.0e-3f;//换成ms
        float duty = ((float) crr2) / crr1 * 100.0f;
        
        My_USART_Printf(USART1,"周期是=%.3fms,占空比=%.2f%%\r\n",period,duty);
        Delay(100);
       
//        float t = GetTick() * 1.0e-3f;//变成s级别
//        float duty = 0.5 * (sin(2 * 3.14 * t) + 1);//占空比
//        uint16_t ccr1 = duty * 1000;
//        TIM_SetCompare1(TIM3,ccr1);
	}
}

void tim1_pwm_init(){
    //配置时基tim1初始化
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
    
    TIM_TimeBaseInitTypeDef tim_timbaseinitstruct = {0};
    
    tim_timbaseinitstruct.TIM_CounterMode = TIM_CounterMode_Up;
    tim_timbaseinitstruct.TIM_Period = 65536 - 1;//防溢出
    tim_timbaseinitstruct.TIM_Prescaler = 72 - 1;
    tim_timbaseinitstruct.TIM_RepetitionCounter = 0;
    
    TIM_TimeBaseInit(TIM1,&tim_timbaseinitstruct);
    
    //开启ARR预加载
    TIM_ARRPreloadConfig(TIM1,ENABLE);
    
    //闭合开关
    TIM_Cmd(TIM1,ENABLE);
    
    
    //初始化对应的GPIO
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef gpio_initstruct = {0};
    
    gpio_initstruct.GPIO_Mode = GPIO_Mode_IPD;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_8;
    //gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
    
    GPIO_Init(GPIOA,&gpio_initstruct);
    
    //配置捕获通道
    
    TIM_ICInitTypeDef tim_icinitstruct = {0};
    
    tim_icinitstruct.TIM_Channel = TIM_Channel_1;
    tim_icinitstruct.TIM_ICFilter = 0;
    tim_icinitstruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
    tim_icinitstruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    tim_icinitstruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
    
    TIM_ICInit(TIM1,&tim_icinitstruct);
    
    tim_icinitstruct.TIM_Channel = TIM_Channel_2;
    tim_icinitstruct.TIM_ICFilter = 0;
    tim_icinitstruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
    tim_icinitstruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    tim_icinitstruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
    
    TIM_ICInit(TIM1,&tim_icinitstruct);
    
    //设置从模式控制器
    
    TIM_SelectInputTrigger(TIM1,TIM_TS_TI1FP1);
    
    TIM_SelectSlaveMode(TIM1,TIM_SlaveMode_Reset);
    
}

void tim3_pwm_init(){
    
    //配置时基tim3初始化
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    
    TIM_TimeBaseInitTypeDef tim_timbaseinitstruct = {0};
    
    tim_timbaseinitstruct.TIM_CounterMode = TIM_CounterMode_Up;
    tim_timbaseinitstruct.TIM_Period = 1000 - 1;
    tim_timbaseinitstruct.TIM_Prescaler = 72 - 1;
    tim_timbaseinitstruct.TIM_RepetitionCounter = 0;
    
    TIM_TimeBaseInit(TIM3,&tim_timbaseinitstruct);
    
    //开启ARR预加载
    TIM_ARRPreloadConfig(TIM3,ENABLE);
    
    //闭合开关
    TIM_Cmd(TIM3,ENABLE);
    
    //初始化对应的GPIO
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef gpio_initstruct = {0};
    
    gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_6;
    gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
    
    GPIO_Init(GPIOA,&gpio_initstruct);
    
    //配置输出比较通道
    TIM_OCInitTypeDef tim_ocinitstruct = {0};
    
    tim_ocinitstruct.TIM_OCMode = TIM_OCMode_PWM1;
    tim_ocinitstruct.TIM_OCPolarity = TIM_OCPolarity_High;
    tim_ocinitstruct.TIM_OutputState = TIM_OutputState_Enable;
    tim_ocinitstruct.TIM_Pulse = 0;
    
    TIM_OC1Init(TIM3,&tim_ocinitstruct);
    
    //使能MOE开关
    TIM_CtrlPWMOutputs(TIM3,ENABLE);
    
    //配置CCR预加载值
    TIM_CCPreloadControl(TIM3,ENABLE);
    
    
    
    
    
}

void usart1_init(){
    //初始化GPIO PA9 TX
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef gpio_initstruct = {0};
    
    gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_9;
    gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
    
    GPIO_Init(GPIOA,&gpio_initstruct);
    
    //初始化usart
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    
    USART_InitTypeDef usart_initstruct = {0};
    
    usart_initstruct.USART_BaudRate = 115200;
    usart_initstruct.USART_Mode = USART_Mode_Tx;
    usart_initstruct.USART_Parity = USART_Parity_No;
    usart_initstruct.USART_StopBits = USART_StopBits_1;
    usart_initstruct.USART_WordLength = USART_WordLength_8b;
    
    USART_Init(USART1,&usart_initstruct);
    
    USART_Cmd(USART1,ENABLE);//使能开关
    
}
相关推荐
不做无法实现的梦~2 小时前
使用stm32来解析航模遥控器协议
stm32·单片机·嵌入式硬件
记得多喝水o2 小时前
百度网盘偷偷给电脑“降频”?
stm32·单片机·电脑
田甲3 小时前
STM32L051实现内部EEPROM读写
stm32·单片机·嵌入式硬件·eeprom
灯琰13 小时前
STM32 HAL库配置DMA加空闲中断接收串口数据
stm32·单片机·嵌入式硬件
曾浩轩3 小时前
跟着江协科技学STM32之5-1EXTI外部中断
科技·stm32·嵌入式硬件
qq_401700413 小时前
MOS管开关电路上下拉电阻的作用
单片机·嵌入式硬件
南棱笑笑生3 小时前
20260113给飞凌OK3588-C开发板适配Rockchip原厂的Android14系统时点亮飞凌的7寸屏时适配CTP触摸屏FT5X06
单片机·嵌入式硬件·rockchip
独处东汉3 小时前
AI辅助Stm32l031项目开发基础准备
人工智能·stm32·嵌入式硬件
麒qiqi4 小时前
51单片机核心外设知识点总结:GPIO、按键、中断、定时器与PWM
单片机·嵌入式硬件·51单片机