技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152
这里可以看到,有4部分,1是时钟来源,2是时基电路,3左边是输入检测,右边4是输出控制.
这里咱们知道,时钟来源确定以后,这里时基电路,计数器,会开始向上计数,当计数到超过设置的初始化的值的时候
会产生中断,咱们可以在中断中让LED亮灭来实现这个实验.
首先看这个内部时钟,咱们知道这个定时器1的时钟来源是内部时钟
这个从模式控制寄存器,可以配置几种模式,可以看到,如果配置为位2到0,为000的话,那么
预分频器会由内部时钟驱动.CK_INT
CEN=1
这个内部时钟的来源是什么呢?
这里AHB预分频时钟,会作为APB1的时钟来源,然后经过
分频,如果预分频APB1的预分频系数是1的话,那么是x1输出,APB1的预分频系数是2的话
那么就属于x2输出.
那么这个x1输出,还有x2输出是什么意思呢?
可以看到这里APB1的时钟,这个时钟的来源是,AHB时钟经过一个预分频系数,得来的,然后,
因为咱们知道,AHB=72,APB1=36,那么预分频是2,所以CK_INT的时钟,会乘以2,就是
36*2=72m,也就是CK_INT=72M
然后这个时候得到CK_PSC这个时钟,然后这个时钟再去/N,会最终得到这个通用定时器的时钟
CK_CNT
这个计数器的模式,上一讲也已经说过了.
这里这个,看看向上计数器模式,这里
可以看到首先使能CNT_EN 然后,使能以后,计数器开始向上计数,当,比如初始化值是36,超过36的时候
会产生一个计数器溢出,这个时候,会产生UEV更新时间,然后,如果我们使能了中断,他就会更新这个中断标志,
并且产生中断,会执行中断函数.
首先看这个CNT他就是,计数器的当前值,其实就是框图中的
上面cnt部分.
这个预分频寄存器,实际上就是预分频器的值,,
也就是对应的最终定时器的时钟频率的值等于=FCK_PSC/(PSC[15:0]+1)
FCK_PSC其实前面有图可以看到,是AHB/2=36M APB1的值,也就是
APB1*2=72M的这个值,这个值再去/(PSC[15:0]+1)
这里要加1,前面说了
注意这里的时钟分频因子=1是加了1以后的值是1 .
这里咱们说了两个寄存器,一个是CNT寄存器存当前的值,一个是PSC寄存器,存分频系数,注意这里分频系数需要+1
这个寄存器重装载寄存器,包含了计数器需要计数的开始值.
要从这个值开始向上,或向下计数.
这个是控制寄存器,控制计数方向,还有个使能位,要用的话,需要使能.
这次咱们用到这个,UIE允许更新中断,也就是允许产生中断事件.
后面还会用到比如捕获事件等.
然后通过代码,咱们看一下如何来操作这些寄存器.
这个STM32F10X_TIM.C这个文件记录了所有的函数,所有的定时器的函数.
这个STM32F10X_TIM.H这个定义了定时器需要的所有的函数.
注意,这里的结构体,
Prescalar这个是预分频系数
CounterMode这个是向上计数,还是向下计数
然后Period这个是自动装载值
然后ClockDivision这个是在捕获比较试验中用
然后RepetitionCounter这个是在高级定时器中用的.
我们用的只有上面的三个.
基本上就是设置了图中的几个寄存器,然后
其实就是这个函数,一会咱们去写.
然后就是定时器使能函数,
这个是使能计数器的作用.操作这个寄存器.
然后
定时器中断使能函数,这个函数配置的是
是操作的这个DIER寄存器.
还有就是状态标志位的获取和清除函数
然后再看一下定时器中断实现的步骤
首先使能定时器时钟,2.然后初始化定时器,然后再去开启定时器的中断,这个时候需要配置中断优先级
然后使能定时器,然后编写中断函数.
这个溢出时间,实际上是由1,时钟频率决定的,2和装载的值决定的.
这里很好理解,首先这个TCLK是定时器的时钟频率,也就是1s钟震动的次数
那么1/TCLK就是震动一次需要多少时间,然后PSC+1这个是预分频系数,所以
PSC+1/TCLK就是,这个定时器,经过预分频后的,时钟周期,也就是震动一次,需要的时间,变多了
变成了PSC+1/TCLK,然后
震动多少次呢,这里是ARR+1次,需要的时间就是(ARR+1)*(PSC+1)/TCLK
的时间,也就是这个溢出时间.
再看一下,咱们知道,AHB的值是72mhz,然后APB1的是36MHZ,所以这里的预分频系数是2
那么,这里的TCLK定时器的时钟就是36mhz*2=72mhz
如果我们的预分频系数PSC设置为7199的话,那么通用定时器的时钟频率就会变成
72MHZ/7200=72000000 / 7200 = 10 000赫兹
也就是10Khz 也就是1s震动 10 000次
震动1次就是1/10 000 =0.0001s
0.0001 * 1000 =0.1ms
注意1mhz=1000000 1百万hz 赫兹
也就是说,震动一次是0.1ms,那么我们要每500ms震动一次,这里就把ARR
设置为4999 加1 的话也就是 5000
5000 * 0.1ms =500 ms
然后咱们来写代码:
可以用跑马灯的实验来做:
首先在HARDWARE文件夹中把原来的文件删除掉,然后把
timer.c timer.h新建出来.
然后把timer.c加进来,然后把文件夹路径,包含进来.
然后把STM32官方的库函数,stm32f10x_tim.c也添加进来.
然后再timer.h文件中定义用到的函数
因为这里可以看到初始化定时器需要配置ARR,PSC,所以这里就配置两个参数
然后再去写timer.c
可以看到首先要使能定时器时钟
然后下面的这个参数可以去定义中去找,第一个参数是哪个定时器,第二个是使能标志.
然后第二步去初始化,定时器这个函数.
可以看到,这个
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的
值,也就是从多少开始倒计数
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
//这个是在高级定时器中才用到的.
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式,设置计数方向
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
然后再去开启定时器中断
使用TIM_ITConfig这个函数,可以去定义看他的参数
这里第一个参数是哪个定时器,第二个是采用什么中断,这里咱们用更新中断,然后最后一个是使能.
然后使能中断优先级分组.
可以看到:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断 中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级设置,先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级设置,从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
然后,设置中断优先级分组,再main.c中设置
然后4,使能定时器
这里是使能定时器用到的函数.
这里第一个参数是哪个定时器,第二个参数是使能.
然后就可以写中断函数了
写之前先看一下,这里的
计数到预定的值的时候,就产生一个中断,到预定的值的时候就产生一个中断,向上计数到预定的值,传入的arr这个值
就跟下图画的一样.
看看这个中断函数:
//定时器3中断服务程序
void TIM3_IRQHandler(void) //TIM3中断
{
//一般上来都这样写,判断到底是不是定时器3,的更新中断产生的中断,
//如果是的话==SET,SET就是1,也可以写成!=RESET !=0的意思
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
//3.然后把定时器3的更新中断标志位清除掉.
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
//2.产生中断了以后,设置LED1翻转一下.
LED1=!LED1;
}
}
然后再去写main函数
之前咱们就算好了,如果这里,咱们要实现500ms发生一次中断,那么需要把
重装载值设置为4999,把预分频系数设置为7199,具体怎么计算的前面有说,当然也可以根据自己的需要,设置成
其他的值.
根据上面程序写的,当产生中断的时候,每隔500msLED1会闪烁一次,
而正常的情况每隔200ms,LED0会闪烁一次,所以LED0会闪烁的比LED1闪烁的要快,
下载程序试一下.