初学stm32,分享TIMx定时器与TICK(滴答)定时器,与TIMx输出pwm波的一点心得。

2019-08-14 17:46发布

本人初学stm32,今天,昨天,前天分别做了三个实验。
分别是滴答定时器精确延迟,TIM定时器与pwm波,TIM定时器+串口通讯+led灯实现精确时间跳变;

先一个一个讲我的实验、
先贴出滴答定时器精确延迟的程序
这个是SysTick.c源程序
[mw_shl_code=cpp,true]#include "SysTick.h"
#include "main.h"

int TimingDelay;

void SysTick_Init(void)
{
        /* SystemFrequency / 1000    1ms中断一次
         * SystemFrequency / 100000         10us中断一次
         * SystemFrequency / 1000000 1us中断一次
         */
       
//对以下函数的解释
//SysTick_Config();是一个配置滴答计时器的函数
//我们打开其函数原型
//static __INLINE uint32_t SysTick_Config(uint32_t ticks)
//{
//  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
//                                                               
//  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
//  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
//  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
//  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
//                   SysTick_CTRL_TICKINT_Msk   |
//                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
//  return (0);                                                  /* Function successful */
//}
//if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);  而其中这一句是判断传来的数据是否小于2的24次方(如果超过,则会处理不正常,因为STK_LOAD寄存器是24位的)。
//则可知,如果你的配置是正确的话,SysTick_Config(uint32_t ticks)的返回值是零,程序会跳过空循环,直接执行关闭滴答定时器。
//如果你的配置错误(即传来的数据是大于2的24次方),SysTick_Config(uint32_t ticks)的返回值是一,则程序if语句成立,程序会进入空循环,防止进一步出错。
//再解释SysTick_Config()的主要作用(自己理解,不标准请海涵):
//它使用芯片自带的滴答定时器记录时钟周期进行操作:
//将获取的参数传递给LOAD寄存器,LOAD寄存器检查后传递给STK_VAL寄存器
//然后每一个时钟周期就使STK_VAL寄存器的值减一
//当减到0的时候,就会触发中断。进入中断函数。

                //if(SysTick_Config(SystemFrequency / 1000))//ST3.0.0库版本
                if(SysTick_Config(SystemCoreClock / 1000))//ST3.5.0库版本
                {
                        while(1);
                }
               
                SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;//关闭滴答定时器
}
//该函数的作用是有多少个ms
void Delay_ms(int nTime)
{
                TimingDelay=nTime;
                //使能滴答计时器
                SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
                while(TimingDelay!=0);
}

void         TimingDelay_Decrement(void)
{
                if(TimingDelay !=0)
                {
                                TimingDelay--;
                }
}
[/mw_shl_code]
这个是位于stm32f10x_it.c内的源程序
[mw_shl_code=applescript,true]extern void TimingDelay_Decrement(void);//采用的外部函数此处一定要写
void SysTick_Handler(void)
{
                if(TimingDelay!=1)
                TimingDelay_Decrement();
                if(TimingDelay==1)
                        Led_Usart(num++);
                        Delay_ms(1000);
       
}[/mw_shl_code]

请允许我大概总结一下,这个TICK的原理就是,时钟不分频,然后
通过SysTick_Config(SystemCoreClock / 1000))这句话分将用户需要的中断配置给STK_LOAD寄存器,
SystemCoreCLock:
uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz;        /*!< System Clock Frequency (Core Clock) */
即时钟的频率,也就是说滴答定时器只能配置STK_LOAD寄存器,无法配置定时器分频和重载周期值,
即:TIM_Prescaler和TIM_Period,(属于TIM定时器的)
而且由于STK_LOAD寄存器是24位的,也就是说STK_LOAD的最大值1677,7216
也就是说滴答定时器最多能撑到1677,7216个以72MHz为频率的时钟周期。72MHz=7200,0000;即不到一秒;
而滴答定时器采用的是向下减的方式,就是当STK_LOAD寄存器传给STK_VAL寄存器后,STK_VAL每经一个时钟周期进行一次自减运算。
直到STK_VAL减到零,引发中断。
综上可述:如果只凭滴答定时器的中断,是无法进行1秒的延迟的,所以,我的程序里有一个外部空循环函数,而且选择每1ms进入一次滴答定时器中断的方式进行1000向下的自减运算。当1000减到零时,跳出空循环,回到主函数。
这就是整个tick滴答定时器进行精确延迟的原理。





友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
2条回答
youlikesoilove
1楼-- · 2019-08-14 21:05
 精彩回答 2  元偷偷看……
youlikesoilove
2楼-- · 2019-08-15 00:25
本帖最后由 youlikesoilove 于 2017-8-7 21:36 编辑

TIMx与PWM波:



[mw_shl_code=c,true]]#include "pwm_output.h"
extern int Speed;
static void TIM2_GPIO_Config(void)//此处使用静态函数,不知道为啥。、、、、。。。。
{
GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//TIM2在APB1通道
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);

        GPIO_AFIODeInit();//将复用功能(重映射事件控制和 EXTI 设置)重设为缺省值,又做初始化交错功能(remap, event control和 EXTI 配置) 寄存器
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2 , ENABLE); //这个就是重映射功能函数、、改变指定管脚的映射
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//作为TIM3的通道,要求使用复用推挽输出(详见数据手册“外设的GPIO配置 表20通用定时器TIM/2/3/4/5”)
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11;//此处希望直流电机反向转,如果只使舵机朝一个方向转动的话,只需配置一个端口
//注:使用pin_10及pin_11的原因是:直流电机模块及直流电机转接板的原理图
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOB,&GPIO_InitStructure);
}

static void TIM2_Mode_Config(void)
{
        TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure ;
        TIM_OCInitTypeDef TIM_OCInitStructure;

        /*PWM信号电平跳变值*/
u16 CCR3_Val=Speed*100;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//设置时钟分频系数:不分频。
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseStructure.TIM_Period=999;//设置定时周期为1000次
TIM_TimeBaseStructure.TIM_Prescaler=0;//设置预分频,不预分频即为72MHz

        //TIM_TimeBaseStructure.TIM_RepetitionCounter=0x00;//据说这个叫做重复计数器,仅适用于TIM1和TIM8
        /*具体说明如下
Specifies the repetition counter value. Each time the RCR downcounter reaches zero, an update event is generated and counting restarts from the RCR value (N).
        This means in PWM mode that (N+1) corresponds to:
        - the number of PWM periods in edge-aligned mode
        - the number of half PWM period in center-aligned mode
        This parameter must be a number between 0x00 and 0xFF.
        @note This parameter is valid only for TIM1 and TIM8.
指定重复计数器值。 每次RCR downcounter达到零,生成更新事件并重新计数从RCR值(N)。
        这意味着在PWM模式下,(N + 1)对应于:
- 边沿对齐模式下的PWM周期数
- 中心对齐模式下的半周期PWM周期数
        此参数必须是0x00和0xFF之间的数字。
@note此参数仅对TIM1和TIM8有效。*/


TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
        TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//配置为PWM模式1


        /*
        PWM1与PWM2模式的区别:
PWM1:向上计数时,(计数寄存器)TIMx_CNT<TIMx_CCRn(比较寄存器)时输出有效电平,TIMx_CCRn<=TIMx_CNT<=TIMc_ARR(自动重载寄存器)时输出无效电平。
        有效电平和无效电平稍后定义。(有效电平是高电平还是低电平是自己定义的)
PWM2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx输出无效电平xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx输出有效电平。
*/

TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//配置输出模式的状态,使能或关闭输出,当前为使能状态
TIM_OCInitStructure.TIM_Pulse=CCR3_Val;//设置跳变值,当计数器计数到这个值时,电平发生跳变。
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//设置有效电平的极性,当前为高电平。
TIM_OC3Init(TIM2,&TIM_OCInitStructure);

        TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Enable);

        TIM_ARRPreloadConfig(TIM2, ENABLE);
        TIM_Cmd(TIM2,ENABLE);//使能定时器三

}

void TIM2_PWM_Init(void)
{
        TIM2_GPIO_Config();
TIM2_Mode_Config();
}[/mw_shl_code]




本PWM波花了我两天时间,原因并不是他难,而是,我手里面的单片机的借口是内置的,只能用固定的端口。所以刚开始一直输出不出来。后来补充知识之后,才知道需要重映射,经过查复用功能重映像,发现其端口是TIM2的重映像。至此顺风顺水。







允许我总结一下,其实TIMx输出PWM就是设置一个跳变值和周期值,系统以系统时钟周期为单位进行计数(可设置加和减),加则加到跳变值电平反转。减则减到跳变值电平反转,加的话继续计数到周期值。计数恢复初始值,减的话。减到零,计数恢复初始值。

距离;如果设定,有效电平为高电平,跳变值为500,周期值为999;则,在0-500系统时钟周期内,输出高电平。500-999周期内是低电平。500/1000是占空比。





但是有个问题,就是:我尝试使跳变值为50,周期值为99,我认为占空比和500/1000一样。但是连上电机其实实际效果不一样、现在还在困惑我。


而且目前不会改变pwm的跳变值。。。。。。等我学会,我再上传吧

一周热门 更多>