打印
[MM32软件]

MM32F5270 TIM 精准脉冲数量输出

[复制链接]
917|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
灵动微课堂 (第239讲)|MM32F5270 TIM 精准脉冲数量输出
脉冲信号用于设备控制是比较常见的,但在一些情况下,我们希望精准的控制脉冲的数量以实现对运动的精确控制,实现的方式也有多种多样。定时器是单片机内部最基础且常用的外设,有着非常丰富的功能,如输入功能(测量输入信号的脉冲宽度、频率,PWM 输入等),输出功能(PWM 输出、死区时间可编程的互补输出、 单脉冲模式输出等) ,容易想到使用定时器输出PWM来实现此类操作。
MM32F5270系列集成有丰富的外设模块,其中定时器部分包括 2 个 16 位高级定时器, 2 个 16 位通用定时器、 2 个 32 位通用定时器, 2 个 16 位基础定时器和1 个低功耗定时器。以TIM1为例,该模块主要由输入单元、输出单元、时基单元、捕获/比较模块、刹车单元等结构组成,功能框图如下:

这里以MM32F5270定时器应用为例,介绍几种常用的精准输出脉冲数量的方法:
1
中断计数方式
定时器配置为PWM输出模式,在PWM中断程序中计数,判断PWM输出次数达到设定值时,停止PWM输出。
中断计数的方式实现起来简单,但也存在明显的缺点。当PWM频率较高时,频繁的中断将影响程序运行的效率,占用大量的MCU资源,这在大多数情况下是不可接受的。以下几种方式较为优化。
2
定时器单脉冲重复计数
定时器单脉冲输出是定时器比较输出中的一种模式,在定时器比较输出模式的基础上进行配置。单脉冲模式(OPM)下,计数器响应一个激励,产生一个脉宽可调的脉冲。配置 TIMx_CR1 寄存器的OPM=1,选择单脉冲模式。

单脉冲模式可以使定时器输出1个脉冲,而重复计数器可以用来调整更新事件产生的频率。
边沿对齐模式下,向上计数时,重复计数器在计数器每次上溢时递减;向下计数时,重复计数器在计数器每次下溢时递减。中央对齐模式下,重复计数器在计数器上溢和下溢时皆递减。通过配置 TIMx_RCR 寄存器的 REP 来调整更新事件产生的频率,重复计数器在 REP+1 个计数周期后产生更新事件。

配置TIM1输出PWM,使能单脉冲模式,配置REP(重复计数器的值)为9,即TIM1在输出10个脉冲后发生更新事件,相关代码如下:
void TIM1_Monopulse_Init(u16 arr, u16 psc)  
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    TIM_OCInitTypeDef  TIM_OCInitStruct;
    TIM_ICInitTypeDef  TIM_ICInitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);
    TIM_DeInit(TIM1);
    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Period = arr;
    TIM_TimeBaseStruct.TIM_Prescaler = psc;  
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;  
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 9;
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct);

    TIM_OCStructInit(&TIM_OCInitStruct);  
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;  
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;  
    TIM_OCInitStruct.TIM_Pulse = arr / 2;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;  
    TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Reset;
    TIM_OC1Init(TIM1, &TIM_OCInitStruct);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM1, ENABLE);   
    TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single);  
    TIM_SetCounter(TIM1, 0);
    TIM_CtrlPWMOutputs(TIM1, ENABLE);  

    TIM_Cmd(TIM1, ENABLE);
}
逻辑分析仪接PA8(程序中配置PA8作为TIM1_CH1),观测输出波形如下:

由于REP只有8位,所以它最大是255,当然也可以进行一些判断后再次赋值,目前只有高级定时器具有重复计数功能。
3
DMA方式
使用DMA功能更新PWM的输出,DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。它允许不同速度的硬件装置来沟通,而不需要依赖于MPU的大量中断负载。该方式占用很少的MCU资源,实现脉冲发送的精确控制。
通过设置DMA传输数据的数量,可以控制发送的脉冲数。通过设置不同的装载值和顺序,可以使用不同频率和脉宽。
TIMx_DCR 和 TIMx_DMAR 寄存器跟 DMA 模式相关。DMA 控制器的目标是唯一的,必须指向TIMx_DMAR 寄存器。开启 DMA 使能后,在给定的 TIMx 事件发生时, TIMx 会给 DMA 发送请求。对TIMx_DMAR 寄存器的每次写操作都被重定向到一个 TIMx 寄存器。
TIMx_DMAR  连续模式 DMA 地址寄存器:
TIMx_DCR  DMA 控制寄存器:
程序中配置TIM1的更新周期为10ms。
TIM1_PWM_Init(10000 - 1, SystemCoreClock / 1000000 - 1);

定义一个数组,元素的数量表示可以控制发送的脉冲数,元素的值表示脉宽。
static u16 data[10] = {1000,2000,3000,4000,5000,6000,7000,8000,9000,0};

配置TIM1输出PWM,相关代码同上,使能COM的DMA请求,配置DMA初始化,使能DMA传输完成中断,TIM1_CH1对应DMA1_Channel2。
void TIM1_DMA_Init(void)
{
    DMA_InitTypeDef  DMA_InitStruct;
    DMA_Channel_TypeDef* channel;
    channel = DMA1_Channel2;
    RCC_DMA_ClockCmd(DMA1, ENABLE);

    DMA_DeInit(channel);
    DMA_StructInit(&DMA_InitStruct);
    //Transfer register address
    DMA_InitStruct.DMA_PeripheralBaseAddr    = (u32) & (TIM1 ->CCR1);
    //Transfer memory address
    DMA_InitStruct.DMA_MemoryBaseAddr        = (u32)data;
    //Transfer direction, from memory to register
    DMA_InitStruct.DMA_DIR                   = DMA_DIR_PeripheralDST;
    DMA_InitStruct.DMA_BufferSize            = 10;
    DMA_InitStruct.DMA_PeripheralInc         = DMA_PeripheralInc_Disable;
    //Transfer completed memory address increment
    DMA_InitStruct.DMA_MemoryInc             = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize    = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStruct.DMA_MemoryDataSize        = DMA_MemoryDataSize_HalfWord;
    DMA_InitStruct.DMA_Mode                  = DMA_Mode_Normal;//DMA_Mode_Circular;
    DMA_InitStruct.DMA_Priority              = DMA_Priority_High;
    DMA_InitStruct.DMA_M2M                   = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_reload = DMA_Auto_Reload_Disable;
    DMA_Init(channel, &DMA_InitStruct);

    DMA_ITConfig(channel, DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA1_Channel2, ENABLE);
}

DMA中断服务子程序:
void DMA1_Channel2_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC2)) {
        DMA_ClearITPendingBit(DMA1_IT_TC2);
        TIM_Cmd(TIM1, DISABLE);
    }
}

逻辑分析仪接PA8(程序中配置PA8作为TIM1_CH1),观测输出波形如下:
输出9个脉冲,脉宽分别为10%、20%、30%......90%。
DMA方式算是一个很确定的方式,不会丢失脉冲。当需要发送较多数量的脉冲时,则可以使用DMA传输完成中断中切换DMA传输的数据起始地址及发送数量。
4
主从模式
定时器同步功能可以配置多个定时器在内部相连。
利用定时器的主从模式,即一个是主定时器,一个是从定时器,由主定时器输出脉冲信号,主定时器产生的更新触发传递给从定时器进行计数,溢出时触发从定时器的中断服务函数。通过主从定时器进行设定,不占用主程序时钟,且能精准控制。
主从关系要遵循参考手册中所提供的配置,TIMx之间的互联:
参考TIMx_CR2和TIMx_SMCR寄存器配置主从模式。
TIMx_CR2 控制寄存器 2:
TIMx_SMCR 从模式控制寄存器:
配置TIM1为主模式,输出PWM:
void TIM1_Master_Init(u16 arr, u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    TIM_OCInitTypeDef  TIM_OCInitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);

    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Period = arr;
    TIM_TimeBaseStruct.TIM_Prescaler = psc;
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct);

    TIM_OCStructInit(&TIM_OCInitStruct);
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse = 499;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM1, &TIM_OCInitStruct);

    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM1, ENABLE);
    TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);   
    TIM_SelectOutputTrigger(TIM1, TIM_TRIGSource_Update);

    TIM_SetCounter(TIM1, 0);
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_Cmd(TIM1, ENABLE);
}

配置TIM3为从模式,选择ITR0触发(对应内部触发源TIM1),使能更新中断:
void TIM3_Slave_Init(u16 arr, u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM3, ENABLE);

    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Period = arr;
    TIM_TimeBaseStruct.TIM_Prescaler = psc;
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);

    TIM_ARRPreloadConfig(TIM3, DISABLE);

    TIM_SelectInputTrigger(TIM3, TIM_TS_ITR0);  
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);
    TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);

    TIM_ClearFlag(TIM3, TIM_FLAG_Update);
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    TIM_SetCounter(TIM3, 0);
    TIM_Cmd(TIM3, ENABLE);
}

TIM3控制脉冲数量,此处设置为10:
TIM3_Slave_Init(10, 0);

TIM3中断服务子程序:
void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);   
        TIM_CtrlPWMOutputs(TIM1, DISABLE);
        TIM_Cmd(TIM1, DISABLE);
        TIM_Cmd(TIM3, DISABLE);
        TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE);
    }
}

逻辑分析仪接PA8(程序中配置PA8作为TIM1_CH1),观测输出波形如下:
TIM1输出10个脉冲后停止。
以上简要列举了几种控制脉冲数量输出的方式,以MM32F5270为例演示其实现的可行性。在实际应用中,几种方法各有优缺点,具体的方式还需要根据资源和需求进行综合考虑。



使用特权

评论回复
沙发
小小蚂蚁举千斤| | 2022-12-20 15:38 | 只看该作者
楼主这个配置挺好的,脉冲输出也很稳定,程序指令也很齐全

使用特权

评论回复
板凳
tpgf| | 2023-1-4 11:14 | 只看该作者
在输出脉冲的过程中进行参数的更改需要等待多长时间才可以生效呢

使用特权

评论回复
地板
磨砂| | 2023-1-4 11:26 | 只看该作者
如果多路脉冲输出 如何保证互相不产生串扰呢

使用特权

评论回复
5
晓伍| | 2023-1-4 11:47 | 只看该作者
我看楼主的配置 是把两个定时器进行了一个级联的模式是吗

使用特权

评论回复
6
八层楼| | 2023-1-4 12:01 | 只看该作者
级联模式的使用会不会放大误差呢

使用特权

评论回复
7
观海| | 2023-1-4 12:11 | 只看该作者
这几种组合方式,哪种方式的精度更高呢

使用特权

评论回复
8
guanjiaer| | 2023-1-4 12:52 | 只看该作者
如果输出的频率有所偏差的话 我们如何发现并校正呢

使用特权

评论回复
9
Jacquetry| | 2023-1-5 17:11 | 只看该作者
如何保证多路脉冲输出互相不产生串扰呢

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

355

帖子

2

粉丝