STM32主从定时器输出个数、频率可调的脉冲

STM32中发出脉冲一般有两种方式:

1)利用定时中断输出脉冲,但是间隔的延时会影响其他主程序的进程,当控制多个电机的时候就非常不可取;

2)利用PWM脉宽调制,并通过主从定时器进行设定,好处是不占用主程序时钟,且能精准控制;

下面以定时器1为主,定时器2为从进行配置:

PWM.c文件

复制代码
#include "stm32f10x.h"   // Device header

/***定时器1主模式,定时器2从模式***/
void TIM1_config(u16 Cycle)
{
    GPIO_InitTypeDef GPIO_InitStructure; //GPIO设置,创建结构体
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;//定时器设置结构体
    TIM_OCInitTypeDef  TIM_OCInitStructure; //pwm波对应设置结构体
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_TIM1 , ENABLE); //开启时钟
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    TIM_TimeBaseStructure.TIM_Period = Cycle-1;                                                   
    TIM_TimeBaseStructure.TIM_Prescaler =71;                    //设置用来作为TIMx时钟频率除数的预分频值                                                     
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //设置时钟分割:TDTS= Tck_tim            
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;            //重复计数,一定要=0!!!
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);               //装载                        
 
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;          //选择定时器模式:TIM脉冲宽度调制模式1       
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    TIM_OCInitStructure.TIM_Pulse = Cycle/2-1;                    //设置待装入捕获寄存器的脉冲值                                   
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;      //输出极性       
 
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);     //装载通道1,PA8 

 
    TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);
    //设置或者重置 TIMx 主/从模式
    //TIMx: x 可以是 2, 3 或者 4,来选择 TIM 外设
    //TIM_MasterSlaveMode:定时器主/从模式,TIM 主/从模式使能
    
    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
    //选择 TIMx 触发输出模式
    //TIMx: x 可以是 2, 3 或者 4,来选择 TIM 外设
    //TIM_TRGOSource:触发输出模式
    //TIM_TRGOSource_Update:使用更新事件作为触发输出(TRGO)
    
 
//    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  //使能或者失能 TIMx 在 CCR3 上的预装载寄存器

    TIM_ARRPreloadConfig(TIM1, ENABLE);  // 使能或者失能 TIMx 在 ARR 上的预装载寄存器 
    //允许或禁止在定时器工作时向ARR的缓冲器中写入新值,以便在更新事件发生时载入覆盖以前的值
}
 
/***定时器2从模式***/
void TIM2_config(u16 PulseNum)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure; //对应结构体声明
    NVIC_InitTypeDef NVIC_InitStructure;  //NVIC 对应结构体声明
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
 
    TIM_TimeBaseStructure.TIM_Period = PulseNum-1;   
    TIM_TimeBaseStructure.TIM_Prescaler =0;    
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);  
 
    TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0);//选择 TIMx 输入触发源,TIM 内部触发 0      
    TIM_SelectSlaveMode(TIM2,TIM_SlaveMode_External1 );// 等同 TIM2->SMCR|=0x07 //设置从模式寄存器 
    //   TIM2->SMCR|=0x07;                                  //设置从模式寄存器       
    TIM_ITConfig(TIM2,TIM_IT_Update,DISABLE); //
 
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;        
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;     
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure);
}
 
//入口设定函数
void Pulse_output(u16 Cycle,u16 PulseNum)
{
    TIM1_config(Cycle); //装载   
    TIM_Cmd(TIM1, ENABLE);//使能
    TIM_CtrlPWMOutputs(TIM1, ENABLE);   //高级定时器一定要加上,主输出使能

    TIM2_config(PulseNum);//装载
    TIM_Cmd(TIM2, ENABLE);//使能
    TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除TIMx 的中断待处理位
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能或者失能指定的 TIMx 中断
}
  
//中断处理函数
void TIM2_IRQHandler(void) 
{ 
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)     // TIM_IT_CC1
    { 
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志位 
        TIM_CtrlPWMOutputs(TIM1, DISABLE);  //主输出使能
        TIM_Cmd(TIM1, DISABLE); // 关闭定时器 
        TIM_Cmd(TIM2, DISABLE); // 关闭定时器 
        TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);         
    } 
} 

PWM.h文件

复制代码
#ifndef __PWM_H
#define __PWM_H

void Pulse_output(u16 Cycle,u16 PulseNum);//入口函数
 
void TIM1_config(u16 Cycle);//1为主模式
void TIM2_config(u16 PulseNum);//2为从模式

#endif 

主函数调用:

复制代码
Pulse_output(1000,8000);//1KHZ,8000个脉冲

其中/TIM_SelectInputTrigger(TIM1, TIM_TS_ITR2);中的参数需要注意,参考手册查对应的可用连接,如下:

问题:

ARR的值最高只能是65535,所以这种方法最多只能输出65535个脉冲。有待改进。

解决方法:(2024.10.26)

将需要发送的脉冲数拆分为高16位和低16位,没进入中断前将低16位装载到从定时器的ARR,第一次进入中断再将高16位装载到从定时器ARR,第二次进入中断关闭脉冲输出和定时器。

完整代码:

复制代码
#include "stm32f10x.h"   // 包含STM32F10x系列的设备头文件

// 定义全局变量
volatile u16 PulseNum_High = 0;  // 高16位脉冲计数
volatile u16 PulseNum_Low = 0;   // 低16位脉冲计数
volatile int pulseStage = 0;     // 脉冲阶段标志

/**
 * @brief 配置TIM1定时器
 */
void TIM1_config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;  // 定义GPIO初始化结构体
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;  // 定义TIM1时间基础初始化结构体
    TIM_OCInitTypeDef  TIM_OCInitStructure;  // 定义TIM1输出比较初始化结构体

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);  // 使能TIM1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能GPIOA时钟

    // 配置GPIOA的Pin8为复用推挽输出模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置TIM1时间基础
    TIM_TimeBaseStructure.TIM_Period = 100-1;  // 定时周期为99,即100个计数周期
    TIM_TimeBaseStructure.TIM_Prescaler = 720-1;  // 预分频器为719,即720分频
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频为1
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数模式
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;  // 重复计数器为0
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);  // 初始化TIM1时间基础

    // 配置TIM1输出比较模式
    TIM_OCStructInit(&TIM_OCInitStructure);  // 初始化输出比较结构体
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  // PWM模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  // 输出使能
    TIM_OCInitStructure.TIM_Pulse = 50;  // 比较值为50
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  // 输出极性为高
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);  // 初始化TIM1通道1的输出比较模式

    TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);  // 使能主从模式
    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);  // 选择触发输出源为更新事件
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  // 使能通道1的CCR预装载

    TIM_ARRPreloadConfig(TIM1, ENABLE);  // 使能自动重装载寄存器预装载

    TIM_CtrlPWMOutputs(TIM1, DISABLE);  // 关闭TIM1的PWM输出
    TIM_Cmd(TIM1, DISABLE);  // 关闭TIM1
}

/**
 * @brief 配置TIM2定时器
 */
void TIM2_config(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;  // 定义TIM2时间基础初始化结构体
    NVIC_InitTypeDef NVIC_InitStructure;  // 定义NVIC初始化结构体

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  // 使能TIM2时钟

    // 配置TIM2时间基础
    TIM_TimeBaseStructure.TIM_Period = 65535;  // 定时周期为65535
    TIM_TimeBaseStructure.TIM_Prescaler = 0;  // 预分频器为0
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频为1
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数模式
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);  // 初始化TIM2时间基础

    TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0);  // 选择输入触发源为内部触发器0
    TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_External1);  // 选择从模式为外部触发模式1

    TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);  // 禁用TIM2更新中断
    TIM_ARRPreloadConfig(TIM2, ENABLE);  // 使能自动重装载寄存器预装载

    // 配置NVIC
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  // 配置优先级分组为2
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  // 中断通道为TIM2
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  // 抢占优先级为0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  // 子优先级为0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  // 使能中断通道
    NVIC_Init(&NVIC_InitStructure);  // 初始化NVIC

    TIM_Cmd(TIM2, DISABLE);  // 关闭TIM2
}

/**
 * @brief 输出指定周期和数量的脉冲
 * @param Cycle 周期值,对应TIM1的ARR
 * @param PulseNum 脉冲数量,最大为65535
 */
void Pulse_output(u16 Cycle, u32 PulseNum)
{
    // 如果脉冲数量大于65535,则需要使用高位和低位计数
    if(PulseNum >= 65536)
    {
        PulseNum_High = (PulseNum >> 16) * 65536 - 1;  // 计算高16位计数值
        PulseNum_Low = (PulseNum & 0xFFFF) - 1;  // 计算低16位计数值
        pulseStage = 0;  // 设置脉冲阶段为高16位
    }
    else
    {
        PulseNum_High = 0;  // 高16位计数值为0
        PulseNum_Low = (PulseNum & 0xFFFF) - 1;  // 计算低16位计数值
        pulseStage = 1;  // 设置脉冲阶段为低16位
    }

    TIM1_config();  // 配置TIM1
    TIM2_config();  // 配置TIM2

    TIM_Cmd(TIM2, DISABLE);  // 关闭TIM2
    TIM_SetAutoreload(TIM2, PulseNum_Low);  // 设置TIM2的自动重装载寄存器为低16位计数值
    TIM_GenerateEvent(TIM2, TIM_EventSource_Update);  // 触发TIM2更新事件以更新ARR
    TIM_Cmd(TIM2, ENABLE);  // 开启TIM2
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  // 清除TIM2更新中断标志

    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  // 使能TIM2更新中断
    TIM_SetAutoreload(TIM1, Cycle);  // 设置TIM1的自动重装载寄存器为周期值
    TIM_GenerateEvent(TIM1, TIM_EventSource_Update);  // 触发TIM1更新事件以更新ARR
    TIM_SetCompare1(TIM1, Cycle / 2);  // 设置TIM1的比较值为周期值的一半,以生成50%占空比的PWM
    TIM_GenerateEvent(TIM1, TIM_EventSource_CC1);  // 触发TIM1捕获/比较事件
    TIM_CtrlPWMOutputs(TIM1, ENABLE);  // 开启TIM1的PWM输出
    TIM_Cmd(TIM1, ENABLE);  // 开启TIM1
}

/**
 * @brief TIM2中断服务函数
 */
void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)  // 检查是否为TIM2更新中断
    {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  // 清除TIM2更新中断标志

        if (pulseStage == 0)  // 如果处于高16位计数阶段
        {
            pulseStage = 1;  // 切换到低16位计数阶段
            TIM_Cmd(TIM2, DISABLE);  // 关闭TIM2
            TIM_SetAutoreload(TIM2, PulseNum_High);  // 设置TIM2的自动重装载寄存器为高16位计数值
            TIM_GenerateEvent(TIM2, TIM_EventSource_Update);  // 触发TIM2更新事件以更新ARR
            TIM_Cmd(TIM2, ENABLE);  // 开启TIM2
            TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  // 清除TIM2更新中断标志
            TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  // 使能TIM2更新中断
        }
        else  // 如果处于低16位计数阶段
        {
            // 脉冲输出完成,停止定时器
            TIM_CtrlPWMOutputs(TIM1, DISABLE);  // 关闭TIM1的PWM输出
            TIM_Cmd(TIM1, DISABLE);  // 关闭TIM1
            TIM_Cmd(TIM2, DISABLE);  // 关闭TIM2
            TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);  // 禁用TIM2更新中断
        }
    }
}
相关推荐
无畏jh25 分钟前
TLE5012B磁阻芯片解读
嵌入式硬件·汽车嵌入式·磁阻芯片
培林将军34 分钟前
Altium Designer 22的安装与汉化
嵌入式硬件·ad工具安装
idcardwang36 分钟前
xl9555-IO拓展芯片
stm32·单片机·嵌入式硬件
Y1rong39 分钟前
STM32之EXTI
stm32·单片机·嵌入式硬件
兆龙电子单片机设计44 分钟前
【STM32项目开源】STM32单片机智能语音家居控制系统
stm32·单片机·嵌入式硬件·物联网·开源·自动化
TaidL1 小时前
茂捷M1020电感式编码器芯片赋能工业智能升级,适用于工业及机器人等领域的各种应用场景
单片机·嵌入式硬件
意法半导体STM321 小时前
【官方原创】SAU对NSC分区的影响 LAT1578
stm32·单片机·嵌入式硬件·mcu·信息安全·trustzone·stm32开发
SmartRadio1 小时前
MK8000(UWB射频芯片)与DW1000的协议适配
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网·dw1000
LDR0061 小时前
芯片电路的引脚标识代表什么?
stm32·单片机·嵌入式硬件
恒锐丰小吕2 小时前
屹晶微 EG3116 600V高压、2A/2.5A驱动、双高有效输入逻辑的半桥栅极驱动芯片技术解析
嵌入式硬件·硬件工程