MSP430F5529的定时器有定时器A与定时器B,定时器B比定时器A强大一点。这里先只简单介绍定时器A,主要是教你使用库函数,详细资料请看用户手册,其他博客,视频教程。
定时器A可以硬件输出PWM,脉冲捕获,定时中断。本文只讲定时中断部分
定时器A介绍
(1)资源简介
定时器 A 是一个十六位的定时/计数器,MSP430F5529 中包含有 3 个定时器 A 的子模块 和 7 个捕捉/比较模块。定时器 A 支持多重捕获/比较,PWM 输出和内部定时。定时器还有 扩展中断功能,中断可以由定时器溢出产生或由捕获/比较寄存器产生。定时器 A 的特性包括:
四种运行模式的异步 16 位定时/计数器
可选择配置的时钟源
可配置的 PWM 输出
异步输入和输出锁存
对所有 TA 中断快速响应的中断向量寄存器
(2)定时器A时钟选择
MSP430F5529定时器时钟 TACLK 可以选择 ACLK,SMCLK 或者来自外部的 TAxCLK。SMCLK系统默认 1048576Hz,ACLK系统默认为32768Hz。
(3)Timer_A工作模式
看手册,我们知道有四中工作模式,第一个是停止状态,不计数,所以不讲。
【1】增计数模式(01)
此模式下,从0开始计数,可通过TAxCCR0的数值定义定时的周期。TAxCCR0数值小于0FFFFh。
一般用于软件PWM,定时中断。这个用到应该是最多的。
【2】连续计数模式(10)
从0开始计数,直至计数到0FFFFh之后从0开始重新计数。与增计数模式不同的是,不能被提前结束,必须是从0计数到0FFFFh。定时周期只能由时钟源频率决定。
一般用于捕获脉冲。
【3】增减计数模式(11)
从0开始计数,增加到TAxCCR0,再从TAxCCR0减少到0。
(4)Timer_A中断
Timer_A有两个中断向量,CCIFG0中断和TAIV中断。后面会有介绍
TIMERx_A0_VECTOR // CCR0 的中断向量 TIMERx_A1_VECTOR // TAIV 的中断向量
函数介绍
Timer_A_startCounter()
函数声明
void Timer_A_startCounter (uint16_t baseAddress, uint16_t timerMode);
作用
指定定时器A中的3个子定时器,并且以指定方式开始计数。
参数
baseAddress
TIMER_A0_BASE TIMER_A1_BASE TIMER_A2_BASE
timerMode
TIMER_A_STOP_MODE TIMER_A_UP_MODE //增计数模式 TIMER_A_CONTINUOUS_MODE //连续计数模式 TIMER_A_UPDOWN_MODE //增减计数模式
Timer_A_initUpMode()
函数声明
void Timer_A_initContinuousMode (uint16_t baseAddress,Timer_A_initContinuousModeParam ∗param)
作用
配置指定的3个子定时器为增计数模式
参数
baseAddress与上面一样
Param的值为如下
(1)clockSource:选择时钟源 TIMER_A_CLOCKSOURCE_EXTERNAL_TXCLK [Default] TIMER_A_CLOCKSOURCE_ACLK TIMER_A_CLOCKSOURCE_SMCLK TIMER_A_CLOCKSOURCE_INVERTED_EXTERNAL_TXCLK (2)clockSourceDivider:选择时钟分频次数 TIMER_A_CLOCKSOURCE_DIVIDER_1 [Default] TIMER_A_CLOCKSOURCE_DIVIDER_2 TIMER_A_CLOCKSOURCE_DIVIDER_3 TIMER_A_CLOCKSOURCE_DIVIDER_4 TIMER_A_CLOCKSOURCE_DIVIDER_5 TIMER_A_CLOCKSOURCE_DIVIDER_6 TIMER_A_CLOCKSOURCE_DIVIDER_7 TIMER_A_CLOCKSOURCE_DIVIDER_8 TIMER_A_CLOCKSOURCE_DIVIDER_10 TIMER_A_CLOCKSOURCE_DIVIDER_12 TIMER_A_CLOCKSOURCE_DIVIDER_14 TIMER_A_CLOCKSOURCE_DIVIDER_16 TIMER_A_CLOCKSOURCE_DIVIDER_20 TIMER_A_CLOCKSOURCE_DIVIDER_24 TIMER_A_CLOCKSOURCE_DIVIDER_28 TIMER_A_CLOCKSOURCE_DIVIDER_32 TIMER_A_CLOCKSOURCE_DIVIDER_40 TIMER_A_CLOCKSOURCE_DIVIDER_64 TIMER_A_CLOCKSOURCE_DIVIDER_48 TIMER_A_CLOCKSOURCE_DIVIDER_56 (3)timerPeriod:指定的Timer_A时间段。这是写入的值进入CCR0。限制为16位[uint16_t] (4)timerInterruptEnable_TAIE:使能还是失能定时器中断 TIMER_A_TAIE_INTERRUPT_ENABLE //使能定时器中断 TIMER_A_TAIE_INTERRUPT_DISABLE //失能定时器中断 (5)captureCompareInterruptEnable_CCR0_CCIE:选择中断响应 TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE //CCR0中断 TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE //TAIE中断 (6)timerClear:选择是否把定时器的定时计数器,分频计数器的计数值清零 TIMER_A_DO_CLEAR //清除 TIMER_A_SKIP_CLEAR //不清除 (7)startTimer:选择初始化之后是否立即启动定时器 true //初始化后立即启动定时器 false //初始化后不启动定时器
中断
当定时器计数到CCR0的值的时候,置为标志位CCIFG。当定时器的值从CCR0计数到0的瞬间,置为TAIFG。
使用
我们定时0.5s
首先我们选择SMCLK作为主时钟,32分频之后就是32750HZ,计数16375次数(写入值要-1)。
然后打开中断,最后两个设置与我一致即可,不用变化。
void Timer_A_Init(void) { Timer_A_initUpModeParam htim = {0}; htim.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; //时钟源选为SMCLK = 1048576 HZ htim.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_32; //32分频 32768 htim.timerPeriod = 16384 - 1; //计数值设为16375(32768/2=16375),定时0.5s htim.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE; //使能TALE中断 htim.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE; htim.timerClear = TIMER_A_DO_CLEAR; //把定时器的定时计数器,分频计数器的计数值清零 htim.startTimer = true; //初始化后立即启动定时器 //配置定时器A为增计数模式 Timer_A_initUpMode(TIMER_A0_BASE, &htim); }
Timer_A_initContinuousMode()
函数声明
void Timer_A_initContinuousMode (uint16_t baseAddress,Timer_A_initContinuousModeParam ∗ param )
作用
配置定时器A为连续计数模式
参数
baseAddress与上面一样
与Timer_A_initUpModeParam相比,Timer_A_initContinuousModeParam 少了(3)timerPeriod,因为连续计数模式是从0到0FFFFh的,所以不需要配置周期。
也少了(5)captureCompareInterruptEnable_CCR0_CCIE,因为当定时值从FFFFh到0的瞬间,回设置TAIFG的标志位。所以他没有CCIFG0中断。
使用
Timer_A_initContinuousModeParam htim = {0}; htim.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; htim.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1; htim.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE; htim.timerClear = TIMER_A_DO_CLEAR; htim.startTimer = true; Timer_A_initContinuousMode(TIMER_A2_BASE, &htim);
Timer_A_initUpDownMode()
函数声明
void Timer_A_initUpDownMode (uint16_t baseAddress,Timer_A_initUpDownModeParam ∗ param )
作用
配置定时器A为增/减计数模式
参数
baseAddress与上面一样
与Timer_A_initUpModeParam相比,Timer_A_initUpDownModeParam 一摸一样。
使用
Timer_A_initUpDownModeParam htim = {0}; htim.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; //时钟源选为SMCLK = 1.048MHz htim.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_32; //64分频 32750 htim.timerPeriod = 16375 - 1; //计数值设为16375 - 1 htim.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE; //使能TALE中断 htim.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE; htim.timerClear = TIMER_A_DO_CLEAR; //把定时器的定时计数器,分频计数器的计数值清零 htim.startTimer = true; //初始化后立即启动定时器 //配置定时器A为增计数模式 Timer_A_initUpDownMode(TIMER_A0_BASE, &htim);
实验
因为增计数模式最简单,且使用方便,所以我以下都使用增计数模式。连续计数模式是在后续捕获实验中讲解。增减模式这个我也想不到有啥应用场景,知道的可以在评论区评论。
实验为软件模拟实现PWM。周期为1S,占空比为50%
TALE中断
因为TALE的中断向量被公用,所以需要使用Switch语句,
#include "driverlib.h" #define MCLK_IN_HZ 25000000 #define delay_us(x) __delay_cycles((MCLK_IN_HZ/1000000*(x))) #define delay_ms(x) __delay_cycles((MCLK_IN_HZ/1000*(x))) void Timer_A_Init(void) { Timer_A_initUpModeParam htim = {0}; htim.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; //时钟源选为SMCLK = 1048576 HZ htim.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_32; //32分频 32768 htim.timerPeriod = 16384 - 1; //计数值设为16375(32768/2=16375),定时0.5s htim.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE; //使能TALE中断 htim.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE; htim.timerClear = TIMER_A_DO_CLEAR; //把定时器的定时计数器,分频计数器的计数值清零 htim.startTimer = true; //初始化后立即启动定时器 //配置定时器A为增计数模式 Timer_A_initUpMode(TIMER_A0_BASE, &htim); } void main (void) { //Stop WDT WDT_A_hold(WDT_A_BASE); //P4.1为输出 GPIO_setAsOutputPin(GPIO_PORT_P4, GPIO_PIN1); Timer_A_Init(); //interrupts enabled __bis_SR_register(GIE); while(1) { } } #pragma vector=TIMER0_A1_VECTOR __interrupt void TIMER0_A1_ISR (void) { switch(TA0IV) { case TA0IV_NONE: break; case TA0IV_TACCR1: break; case TA0IV_TACCR2: break; case TA0IV_TACCR3: break; case TA0IV_TACCR4: break; case TA0IV_5: break; case TA0IV_6: break; case TA0IV_TAIFG: GPIO_toggleOutputOnPin(GPIO_PORT_P4, GPIO_PIN1); break; default: break; } }
CCIFG0中断
看注释掉的部分,就是要改的地方。
#include "driverlib.h" #define MCLK_IN_HZ 25000000 #define delay_us(x) __delay_cycles((MCLK_IN_HZ/1000000*(x))) #define delay_ms(x) __delay_cycles((MCLK_IN_HZ/1000*(x))) void Timer_A_Init(void) { Timer_A_initUpModeParam htim = {0}; htim.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; //时钟源选为SMCLK = 1048576 HZ htim.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_32; //32分频 32768 htim.timerPeriod = 16384 - 1; //计数值设为16375(32768/2=16375),定时0.5s // htim.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE; //使能TALE中断 // htim.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE; htim.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE; //失能TALE中断 htim.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE; htim.timerClear = TIMER_A_DO_CLEAR; //把定时器的定时计数器,分频计数器的计数值清零 htim.startTimer = true; //初始化后立即启动定时器 //配置定时器A为增计数模式 Timer_A_initUpMode(TIMER_A0_BASE, &htim); } void main (void) { //Stop WDT WDT_A_hold(WDT_A_BASE); //P4.1为输出 GPIO_setAsOutputPin(GPIO_PORT_P4, GPIO_PIN1); Timer_A_Init(); //interrupts enabled __bis_SR_register(GIE); while(1) { } } //#pragma vector=TIMER0_A1_VECTOR //__interrupt //void TIMER0_A1_ISR (void) //{ // switch(TA0IV) // { // case TA0IV_NONE: // break; // case TA0IV_TACCR1: // break; // case TA0IV_TACCR2: // break; // case TA0IV_TACCR3: // break; // case TA0IV_TACCR4: // break; // case TA0IV_5: // break; // case TA0IV_6: // break; // case TA0IV_TAIFG: // GPIO_toggleOutputOnPin(GPIO_PORT_P4, GPIO_PIN1); // break; // default: // break; // } //} #pragma vector=TIMER0_A0_VECTOR __interrupt void TIMER0_A0_ISR (void) { GPIO_toggleOutputOnPin(GPIO_PORT_P4, GPIO_PIN1); }
实验现象