定时器区别
一开始我以为三个不同的定时器模块区别很大,后来粗略对比了一下发现,其实大部分内容是一样的。
从这几个图来看的话,主要能看到区别是CCRA和CCRP的位数。我对比了一下不同计时器的工作模式,发现居然没有那个输入捕获功能,我记得这个功能虽然用的少,但是应该都有这个功能才对?可能真的不常用所以就砍掉了。
我个人对那个比较匹配输出的模式没有什么好感🤬比较匹配输出可能是跟定时计数模式时一样的,不过前者带一个硬件输出接口,还有其他不同的吗?
另外一个比较大的区别就是,STM是有单独一个中断向量的,算是吧。但是PTM和CTM要公用一个中断向量。
而且PTM没有那个选择CCRA和CCRP哪个作为占空比和哪个作为周期的寄存器,数据手册显示的是未定义。
注意事项
具体也没有看太明白,主要了解一个点就是,写CCRA和CCRP的值先写低位再写高位。
读的话,先读高位再读低位,但是这个值个人觉得读取意义并不大。
意思应该是比较器P是不能控制到定时器的输出口,而且CCRA的溢出周期也不能设置成0。
定时器属性
HT66F3195
的定时器模块并不算太复杂,如果只是简单用定时功能带中断的话,需要配置的内容直接少了一半。因为很多配置都是针对那些输出控制的。
大概汇总一下需要配置的一些寄存器:
- 模块使能
- 模块中断使能
- 多功能中断使能
- 定时器工作模式
- 定时器时钟源
- 定时器触发引脚功能
- 定时器初始输出控制
- 定时器输出极性
- PWM模式下占空比周期选择
- 比较器选择
- 比较器溢出周期
也不是很多吧,具体就不介绍了,看看后续有没有特殊应用案例可以仔细讲讲,个人觉得定时器模块预留了太多的功能,以至于有些寄存器配置的说明我都没有想到是哪些时候能用得上的。
#ifndef _TM_H_
#define _TM_H_
/*-------------------函数声明------------------- */
void CTM_Init(void);
/*-------------------宏定义封装------------------- */
#define CTM_Cmd(x) _cton = x
#define STM_Cmd(x) _ston = x
#define PTM_Cmd(x) _pton = x
#define CTM_A_FlagReset() _ctmaf = 0
#define CTM_P_FlagReset() _ctmpf = 0
#define STM_A_FlagReset() _stmaf = 0
#define STM_P_FlagReset() _stmpf = 0
#define PTM_A_FlagReset() _ptmaf = 0
#define PTM_P_FlagReset() _ptmpf = 0
#define CTM_A_InterruptCmd(x) _ctmae = x
#define CTM_P_InterruptCmd(x) _ctmpe = x
#define STM_A_InterruptCmd(x) _stmae = x
#define STM_P_InterruptCmd(x) _stmpe = x
#define PTM_A_InterruptCmd(x) _ptmae = x
#define PTM_P_InterruptCmd(x) _ptmpe = x
#define CTM_Clock(x) _ctmc0 = (_ctmc0&(~(REG_3_BIT<<4)))|(x<<4)
#define STM_Clock(x) _stmc0 = (_stmc0&(~(REG_3_BIT<<4)))|(x<<4)
#define PTM_Clock(x) _ptmc0 = (_ptmc0&(~(REG_3_BIT<<4)))|(x<<4)
/* 时钟选择 */
typedef enum
{
TM_Clock_Fsys_Div4 = 0,
TM_Clock_Fsys,
TM_Clock_Fh_Div16,
TM_Clock_Fh_Div64,
TM_Clock_Fsub,
TM_Clock_xTCK_Rise = 6,
TM_Clock_xTCK_Fall,
}TM_Clock_T;
#define CTM_Mode(x) _ctmc1 = (_ctmc1&(~(REG_2_BIT<<6)))|(x<<6)
#define STM_Mode(x) _stmc1 = (_stmc1&(~(REG_2_BIT<<6)))|(x<<6)
#define PTM_Mode(x) _ptmc1 = (_ptmc1&(~(REG_2_BIT<<6)))|(x<<6)
/* 工作模式 */
typedef enum
{
TM_Mode_CompareOutput = 0,
TM_Mode_Null,
TM_Mode_PWM,
TM_Mode_Counter,
}TM_Mode_T;
#define CTM_Output_Mode(x) _ctmc1 = (_ctmc1&(~(REG_2_BIT<<4)))|(x<<4)
#define STM_Output_Mode(x) _stmc1 = (_stmc1&(~(REG_2_BIT<<4)))|(x<<4)
#define PTM_Output_Mode(x) _ptmc1 = (_ptmc1&(~(REG_2_BIT<<4)))|(x<<4)
/* 输出功能 */
typedef enum
{
TM_Output_Mode1 = 0,
TM_Output_Mode2,
TM_Output_Mode3,
TM_Output_Mode4,
}TM_Output_Mode_T;
/*
比较匹配输出模式
00:无变化
01:输出低
10:输出高
11:输出翻转
PWM 输出模式
00:强制无效状态
01:强制有效状态
10:PWM 输出
11:未定义
*/
#define CTM_Output_Control(x) _ctoc = x
#define STM_Output_Control(x) _stoc = x
#define PTM_Output_Control(x) _ptoc = x
/* 输出控制 */
typedef enum
{
TM_Output_Low = 0,
TM_Output_High,
}TM_Output_Control_T;
/*
比较匹配输出模式
0: 初始低
1: 初始高
PWM 输出模式
0: 低有效
1: 高有效
*/
#define CTM_Output_Polarity(x) _ctpol = x
#define STM_Output_Polarity(x) _stpol = x
#define PTM_Output_Polarity(x) _ptpol = x
/* 输出同相反相 */
typedef enum
{
TM_Polarity_Inphase = 0,
TM_Polarity_Outphase,
}TM_Output_Polarity_T;
#define CTM_Duty_Period(x) _ctdpx = x
#define STM_Duty_Period(x) _stdpx = x
/* PTM 貌似没有预留这个位 */
/* 占空比周期选择 */
typedef enum
{
TM_A_Duty_P_Period = 0,
TM_P_Duty_A_Period,
}TM_Duty_Period_T;
#define CTM_Match(x) _ctcclr = x
#define STM_Match(x) _stcclr = x
#define PTM_Match(x) _ptcclr = x
/* 比较器选择 */
typedef enum
{
TM_Match_P = 0,
TM_Match_A,
}TM_Match_T;
#define CTM_CCRA_Period(x) _ctmal = (x&0xFF); _ctmah = (x>>8)
#define STM_CCRA_Period(x) _stmal = (x&0xFF); _stmah = (x>>8)
#define PTM_CCRA_Period(x) _ptmal = (x&0xFF); _ptmah = ((x>>8)&0x03)
/* 比较器A溢出周期 */
#define CTM_CCRP_Period(x) _ctmrp = x
#define STM_CCRP_Period(x) _stmrp = x
#define PTM_CCRP_Period(x) _ptmrpl = (x&0xFF); _ptmrph = ((x>>8)&0x03)
/* 比较器P溢出周期 */
#endif
定时器PWM呼吸灯
这个实现起来比之前用时基中断模拟的PWM就简单太多了,只需要完成一件事情:每隔一段时间去改变那个PWM值。
定时器本身的配置我已经封装成库了,按照顺序一路配置下来就基本不会有问题的。
但是需要注意的是,如果配置的占空比大于溢出周期的话,会一直都处于有效状态,即亮度不变。
这里用到的CTM输出引脚CTP,可以从引脚分布看到CTP分布在PB3和PD3上面。
如果要使用这些IO的定时器引脚输出功能,要切换它们的复用功能。
void CTM_Init()
{
GPIOB_3_Mode(IO_Mode2);
CTM_A_InterruptCmd(Enable); /* PWM这两个应该都可以不用打开吧 */
CTM_P_InterruptCmd(Enable);
CTM_Clock(TM_Clock_Fsys);
CTM_Mode(TM_Mode_PWM);
CTM_Output_Mode(TM_Output_Mode3);
CTM_Output_Control(TM_Output_High);
CTM_Output_Polarity(TM_Polarity_Inphase);
CTM_Duty_Period(TM_A_Duty_P_Period);
CTM_CCRA_Period(1); /* 手动计算PWM值,可以封装一下自动计算的函数 */
CTM_CCRP_Period(1);
MUL_FUNC_1_InterruptCmd(Enable);
CTM_Cmd(Enable);
}
中断函数这边暂时没有用上。
DEFINE_ISR(mul_1_int, MUL_FUNC_1_IntAddress)
{
if(CTM_A_IntFlag)
{
CTM_A_FlagReset();
}
MUL_FUNC_1_FlagReset();
}
这个LED_PWM_Dir
变量定义成bit类型即可,是控制PWM亮度边界然后转向用的。
void LED_PWM_UpdateHandler()
{
if(LED_PWM_Dir)
{
LED_PWM_Duty += 1;
if(LED_PWM_Duty == 255)
{
LED_PWM_Dir = 0;
}
}
else
{
LED_PWM_Duty -= 1;
if(LED_PWM_Duty == 0)
{
LED_PWM_Dir = 1;
}
}
CTM_CCRA_Period(LED_PWM_Duty);
}
更新PWM占空比的话,频率可以自己控制,10ms更新一次的话现象会比较明显一些。
if(TB0.FragmentFlag & Fragment_10ms_Mask)
{
TB0.FragmentFlag ^= Fragment_10ms_Mask;
LED_PWM_UpdateHandler();
}