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更新中断
        }
    }
}
相关推荐
Java Fans26 分钟前
嵌入式软件在电子烟开发中的应用
嵌入式硬件
环能jvav大师2 小时前
使用Ubuntu系统+VS Code开发STC51单片机
linux·c语言·开发语言·单片机·嵌入式硬件·ubuntu
q4725994513 小时前
UART通过DMA接收和发送,使用环形缓冲区,状态机的使用
stm32·单片机·嵌入式硬件
亿道电子4 小时前
【ARM】MDK-Functions界面设置
arm开发·stm32·单片机
Gui林7 小时前
【GL08】STM32--ADC/DAC
stm32·单片机·嵌入式硬件
人生若只如初见6457 小时前
STM32-Cube定时器TIM
stm32·st-cube
sukalot8 小时前
windows 驱动实例分析系列: NDIS 6.0的Filter 驱动改造(四)
windows·单片机·嵌入式硬件
RIGOL小普8 小时前
如何用李萨如图形测正弦信号的频率?若不使用李萨如图形,如何用示波器测交流信号频率?
单片机·嵌入式硬件·fpga开发·硬件工程·射频工程
charlie11451419113 小时前
STM32 从0开始系统学习2
stm32·嵌入式硬件·c·1024程序员节·arm架构·寄存器映射