1,定时器/计数器工作原理
许多初学者弄不明白,为什么叫定时器/计数器呢?原因是这个设备既能用于定时也可以用于计数。这是什么原理呢?单片机怎么知道什么时候该定时,什么时候该计数呢?
解:
首先说一下既可以定时又可以计数的原理,实际上定时和计数的原理是一样的,都是靠数数来实现。比方说我家的水龙头坏了,关不紧,总是一滴一滴漏水,而且水滴滴的特别均匀,每两滴之间的时间间隔都是 1 秒,现在我们就可以利用数水滴滴数的方法来计算出时间,这就是定时的原理。
再举个例子,阿范有一天很无聊,站在窗前向外看着公路上跑的小汽车,不自觉的就开始数汽车的数量,1、2、3…,由于每两辆汽车之间的时间间隔不同,所以这回我数出来的数就不能算出来我那天究竟向窗外看了多长时间,而只能算作数出来的车的数量,即当成计82 数器,这就是计数器,大家会发现两次实际上都是在数数,只是数的内容不同而已,一个是匀速的水滴,另一个是非匀速通过的车辆。
2,定时器/计数器的定时和计数是怎么实现的
现在我们来说说单片机是怎么定时和计数的。其实都是数数,只不过单片机本身就是数字电路,如果让它去数数,也只能数脉冲。而脉冲来源于两个地方:一个是由单片机的 18和 19 引脚接的晶振产生的稳定而均匀的脉冲信号,这个信号到单片机内部经过 12 分频(即脉冲变宽,频率为晶振频率的十二分之一)后提供给定时器;定时器每接收一个脉冲就会自动把计的数加 1,当把装这个数的容器加满了,会自动有一个标志位从 0 变成 1,这时我们可以算出计了多少时间;另一个脉冲来源是单片机的外部引脚 P3.4 和 P3.5,当用定时器 T0时数的是 P3.4 引脚输入的脉冲,当用定时器 T1 时数的是 P3.5 引脚输入的脉冲。至于怎么才能命令单片机工作在定时状态还是工作在计数状态,这个需要设置特殊功能寄存器 TMOD中的内容,我们将在下面和大家分享。
定时器分类
定时器的基本结构是通用的,很多模块电路都能用到,所以STM32定时上扩展了非常多的功能,根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型。高级定时器通常提供更多的功能和扩展性,例如可以支持编码器模式、PWM输入输出等。
定时器组成
1.计数器
向上计数模式:计数器从0开始计数,当达到自动装载寄存器(TIMx_ARR)里的值时,自动清零且产生一个溢出事件,然后再从0开始向上计数。
向下计数模式:计数器从自动装载寄存器(TIMx_ARR)里的值开始递减计数,当计数值达到0时产生一个定时器溢出事件,并重装初值,继续向下计数。
中央对齐模式:又称为向上/向下计数,计数器从0开始递增达到ARR的值,产生一个定时器溢出事件,再从ARR的值递减到0,产生一个定时器溢出事件。
2.自动重装寄存器
3.预分频器
定时器计数原理
假设当前时钟为64Mhz,如何让定时器TIMn产生一个1s中断?
预分频器 写64-1相当于64分频 ,得到分频后的频率为1Mhz
64Mhz / 64 = 1Mhz
在1Mhz的频率下计1个数需要 1/1000000s 即 1us
向自动重装计数器写入1000000-1,并开始计数,
即计1000000个数产生一个溢出中断
可以实现每隔1s产生一个中断信号。
程序编写
//中断相关函数
//中断标志获取
boolean_t Bt_GetIntFlag(en_bt_unit_t enUnit);
//中断标志清除
en_result_t Bt_ClearIntFlag(en_bt_unit_t enUnit);
//中断使能/禁止
en_result_t Bt_EnableIrq (en_bt_unit_t enUnit);
en_result_t Bt_DisableIrq(en_bt_unit_t enUnit);
//初始化及相关功能操作
//timer配置及初始化
en_result_t Bt_Init(en_bt_unit_t enUnit, stc_bt_config_t* pstcConfig);
//Lptimer 启动/停止
en_result_t Bt_Run(en_bt_unit_t enUnit);
en_result_t Bt_Stop(en_bt_unit_t enUnit);
//重载值设置
en_result_t Bt_ARRSet(en_bt_unit_t enUnit, uint16_t u16Data);
//16位计数值设置/获取
en_result_t Bt_Cnt16Set(en_bt_unit_t enUnit, uint16_t u16Data);
uint16_t Bt_Cnt16Get(en_bt_unit_t enUnit);
//32位计数值设置/获取
en_result_t Bt_Cnt32Set(en_bt_unit_t enUnit, uint32_t u32Data);
uint32_t Bt_Cnt32Get(en_bt_unit_t enUnit);
实例:
/*******************************************************************************
* BT定时功能测试 (重载模式)
******************************************************************************/
en_result_t BtTimer2Init(void)
{
stc_bt_config_t stcConfig;
en_result_t enResult = Error;
uint16_t u16ArrData = 0xFF05;//计数到0xffff中断一次 1ms中断一次
uint16_t u16InitCntData = 0xFF05;//0xC000;
stcConfig.pfnTim2Cb = Bt2Int;
Clk_SetPeripheralGate(ClkPeripheralBt, TRUE);
stcConfig.enGateP = BtPositive;
stcConfig.enGate = BtGateDisable;
stcConfig.enPRS = BtPCLKDiv16;//4M/16= 250KHZ 滴答一次0.004ms 计数0xFA次中断一次
stcConfig.enTog = BtTogDisable;
stcConfig.enCT = BtTimer;
stcConfig.enMD = BtMode2;
//Bt初始化
if (Ok != Bt_Init(TIM2, &stcConfig))
{
enResult = Error;
}
//TIM2中断使能
Bt_ClearIntFlag(TIM2);
Bt_EnableIrq(TIM2);
EnableNvic(TIM2_IRQn, 3, TRUE);
//设置重载值和计数值,启动计数
Bt_ARRSet(TIM2, u16ArrData);
Bt_Cnt16Set(TIM2, u16InitCntData);
// Bt_Run(TIM2);
M0P_BT2->CR_f.TR = TRUE;
return Ok;
}
BT中断函数
/*******************************************************************************
* BT1中断服务函数
******************************************************************************/
void Bt2Int(void)
{
if (TRUE == Bt_GetIntFlag(TIM2))
{
Bt_ClearIntFlag(TIM2);
//UpdateStpTimer();
//秒表
msTimer++;
halmstime++;
if (msTimer >= 1000)//一秒
{
msTimer = 0;
HeartTimer++;
HeartL2Timer++;
}
}
}
————————————————
版权声明:本文为CSDN博主「烤辣大师」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44110278/article/details/134005035
|