OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2551|回复: 2

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

[复制链接]

1

主题

6

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2017-8-4
在线时间
3 小时
发表于 2017-8-7 21:32:12 | 显示全部楼层 |阅读模式
本人初学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滴答定时器进行精确延迟的原理。





正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

6

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2017-8-4
在线时间
3 小时
 楼主| 发表于 2017-8-7 21:32:23 | 显示全部楼层
本帖最后由 youlikesoilove 于 2017-8-7 21:34 编辑



下一个实验室TIMx与led灯,也是一个精确延迟的程序;



[mw_shl_code=c,true]#include "time.h"

void TIM2_NVIC_Configuration(void)
{
        NVIC_InitTypeDef NVIC_InitStructure;

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先组为2,即4抢占优先级,4个响应优先级
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//配置中断向量
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能TIM2通道
NVIC_Init(&NVIC_InitStructure);//根据配置初始化NVIC
}

void TIM2_Configuration(void)
{
        TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
        TIM_DeInit(TIM2);//将外设的TIM2的寄存器设为初始值、防止上次数据的干扰
TIM_TimeBaseStructure.TIM_Prescaler=7199;
        //时钟预分频数,例如不分频为72MHz 7200 0000,分7200频之后为 10000,即每秒工作10000次;
TIM_TimeBaseStructure.TIM_ClockDivision=0x00;
        TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
        //TIM_TimeBaseStructure.TIM_RepetitionCounter=0x00;
        TIM_TimeBaseStructure.TIM_Period=10000;
        /*Specifies the period value to be loaded into the active Auto-Reload Register at the next update event. This parameter must be a number between 0x0000 and 0xFFFF.
指定要加载到活动的周期值,自动重新加载注册在下一个更新事件。数必须是0x0000和0xFFFF之间的数字。*/
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
        TIM_ClearFlag(TIM2,TIM_FLAG_Update);//清除溢出中断标志
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能TIM2中断
TIM_Cmd(TIM2,ENABLE);//使能外设TIM2
}[/mw_shl_code]

稍微总结一下,TIMx定时器与tick的巨大区别是可以对定时器再分频,比方说,不分频的话,TIMx=72MHz ,让其工作10000个时钟周期,还不到一秒

此时我们有再分频的功能之后,只给TIMx分7200,即此时TIMx的时钟为10KHz,即10000个时钟周期/1s,我们设定其计时周期为10000个系统周期,则。10000个此时的TIMx周期之后,进入中断。







Tick(滴答定时器)和TIMx定时器都能自动重载。这是两个的相同点。Tick定时器触发中断的最长时间是10ms(其工作原理确定的)

而TIMx定时器能够对定时器的频率进行设置。触发时间基本能够达到我们的要求。



接下来是一个思考。使用Tick滴答定时器能否实现与TIMx一样的效果?

我想如果执行的操作和步骤不是很长时间的话,是可以的。(10ms)内可以完成的。(大多数程序都可以的)否则的话,会出现紊乱,因为超过10ms,又会触发中断。

而TIMx自身能够设置很长时间的中断具体有多长:TIM_Period的最大值是0xffff(65535),TIM_Prescaler的最大值也是0xffff(65535),则65535/(7200,0000/65535)=59s,也就是说最多可以59s发生一次中断。还有,TIMx可以输出方波,而TIck不可以。等等。。。。所以还是TIMx好用。功能强大









回复 支持 反对

使用道具 举报

1

主题

6

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2017-8-4
在线时间
3 小时
 楼主| 发表于 2017-8-7 21:34:11 | 显示全部楼层
本帖最后由 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的跳变值。。。。。。等我学会,我再上传吧
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-17 18:18

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表