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更新中断
        }
    }
}
相关推荐
清风66666614 分钟前
基于单片机的双档输出数字直流电压源设计
单片机·mongodb·毕业设计·nosql·课程设计
牛马大师兄36 分钟前
STM32独立看门狗IWDG与窗口看门狗WWDG知识梳理笔记
笔记·stm32·单片机·嵌入式硬件·嵌入式·看门狗
夜月yeyue1 小时前
STM32 Flash 访问加速器详解(ART Accelerator)
linux·单片机·嵌入式硬件·uboot·bootloard
A9better1 小时前
嵌入式开发学习日志37——stm32之USART
stm32·嵌入式硬件·学习
国科安芯5 小时前
ASP4644芯片低功耗设计思路解析
网络·单片机·嵌入式硬件·安全
充哥单片机设计5 小时前
【STM32项目开源】基于STM32的智能厨房火灾燃气监控
stm32·单片机·嵌入式硬件
CiLerLinux12 小时前
第四十九章 ESP32S3 WiFi 路由实验
网络·人工智能·单片机·嵌入式硬件
时光の尘12 小时前
【PCB电路设计】常见元器件简介(电阻、电容、电感、二极管、三极管以及场效应管)
单片机·嵌入式硬件·pcb·二极管·电感·三极管·场效应管
Lu Zelin12 小时前
单片机为什么不能跑Linux
linux·单片机·嵌入式硬件
宁静致远202113 小时前
stm32 freertos下基于hal库的模拟I2C驱动实现
stm32·嵌入式硬件·freertos