本篇文章包含的内容
笔者学习采用单片机型号为MSP430F5529,使用MSP-EXP430F5529LP开发板。
一、MSP430定时器简介
1.1 定时器简介
MSP430含有三个定时器模块,分别为Timer_A、Timer_B和Timer_D。对于每个模块,都有对应的实例化子模块,例如对于Timer_A而言,它拥有三个子模块,即Timer_A0、Timer_A1、Timer_A2、Timer_A
3。每个子模块都拥有7个捕获/比较通道,一般可以用来测量脉冲频率,输出PWM波形,定时产生中断等。定时器的中断可以由计数溢出产生,也可以由捕获/比较寄存器产生。
Timer_A相关的寄存器主要有TAxCTL、TAxR、TAxCCTLn、TAxCCRn、TAxIV、TAxEX0这几个寄存器。
1.2 时基单元的四种工作模式
定时器有四种工作模式:停止模式、向上模式、连续模式和向上/向下模式。使用 MC 位(两位)选择工作模式。
MC | 模式 | 描述 |
---|---|---|
00 | 停止 | 定时器停止计数。 |
01 | 向上 | 定时器从零重复计数到 TAxCCR0 的值。 |
10 | 连续 | 定时器从零不断计数到 0FFFFh。 |
11 | 向上/向下 | 定时器从零重复计数到 TAxCCR0 的值,然后回到零计数。 |
1.3 定时器中断优先级
以上图的Timer_A的中断向量寄存器TAxIV为例,可以看到与定时器相关的中断一共有7个,分别是通道1到通道6的输入捕获/输出比较中断和时基单元的溢出中断,且这些中断的优先级是从高到低排列的。MSP430在这里并不支持中断优先级的自由配置。
1.4 输出比较模块
Timer_A 中有最多七个相同的捕获/比较模块,TAxCCRn(其中n = 0 到 7)。任何一个模块都可以用来捕获定时器数据或生成时间间隔。
二、定时器相关库函数
2.1 Timer_A库函数
// 启动计数
extern void Timer_A_startCounter(uint16_t baseAddress, uint16_t timerMode);
// 初始化循环计数模式
extern void Timer_A_initContinuousMode(uint16_t baseAddress, Timer_A_initContinuousModeParam *param);
// 初始化向上计数模式
extern void Timer_A_initUpMode(uint16_t baseAddress, Timer_A_initUpModeParam *param);
// 初始化上下计数模式
extern void Timer_A_initUpDownMode(uint16_t baseAddress, Timer_A_initUpDownModeParam *param);
// 初始化捕获模式
extern void Timer_A_initCaptureMode(uint16_t baseAddress, Timer_A_initCaptureModeParam *param);
// 初始化比较模式
extern void Timer_A_initCompareMode(uint16_t baseAddress, Timer_A_initCompareModeParam *param);
// 使能中断
extern void Timer_A_enableInterrupt(uint16_t baseAddress);
// 禁止中断
extern void Timer_A_disableInterrupt(uint16_t baseAddress);
// 获取中断标志位状态
extern uint32_t Timer_A_getInterruptStatus(uint16_t baseAddress);
// 使能捕获/比较中断
extern void Timer_A_enableCaptureCompareInterrupt(uint16_t baseAddress, uint16_t captureCompareRegister);
// 禁止捕获/比较中断
extern void Timer_A_disableCaptureCompareInterrupt(uint16_t baseAddress, uint16_t captureCompareRegister);
// 获取捕获/比较中断标志位
extern uint32_t Timer_A_getCaptureCompareInterruptStatus(uint16_t baseAddress, uint16_t captureCompareRegister, uint16_t mask);
// 重置/清除定时器相关设置
extern void Timer_A_clear(uint16_t baseAddress);
// 获取同步的CCR输入
extern uint8_t Timer_A_getSynchronizedCaptureCompareInput(uint16_t baseAddress, uint16_t captureCompareRegister, uint16_t synchronized);
// 获取输出模式的输出位
extern uint8_t Timer_A_getOutputForOutputModeOutBitValue(uint16_t baseAddress, uint16_t captureCompareRegister);
// 获取CCR的值
extern uint16_t Timer_A_getCaptureCompareCount(uint16_t baseAddress, uint16_t captureCompareRegister);
// 设置输出模式的输出位
extern void Timer_A_setOutputForOutputModeOutBitValue(uint16_t baseAddress, uint16_t captureCompareRegister, uint8_t outputModeOutBitValue);
// 输出PWM
extern void Timer_A_outputPWM(uint16_t baseAddress, Timer_A_outputPWMParam *param);
// 停止定时器运行
extern void Timer_A_stop(uint16_t baseAddress);
// 设置CCR的值
extern void Timer_A_setCompareValue(uint16_t baseAddress, uint16_t compareRegister, uint16_t compareValue);
// 设置输出模式
extern void Timer_A_setOutputMode(uint16_t baseAddress, uint16_t compareRegister, uint16_t compareOutputMode);
// 清除中断标志
extern void Timer_A_clearTimerInterrupt(uint16_t baseAddress);
// 清除捕获/比较中断标志
extern void Timer_A_clearCaptureCompareInterrupt(uint16_t baseAddress, uint16_t captureCompareRegister);
// 获取定时器的计数值
extern uint16_t Timer_A_getCounterValue(uint16_t baseAddress);
三、代码实现
3.1 定时器定时产生中断
main.c
#include <driverlib.h>
void Delay_ms(int x_ms)
{
while (x_ms--)
{
__delay_cycles(1000);
}
}
void Timer_A_Init(void)
{
Timer_A_initUpModeParam Timer_A_InitStructure;
Timer_A_InitStructure.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE;
Timer_A_InitStructure.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
Timer_A_InitStructure.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_10;
Timer_A_InitStructure.startTimer = true;
Timer_A_InitStructure.timerClear = TIMER_A_SKIP_CLEAR;
Timer_A_InitStructure.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE;
Timer_A_InitStructure.timerPeriod = 32767; // 计数的目标值,即将要被写入CCR0的值
Timer_A_initUpMode(TIMER_A1_BASE, &Timer_A_InitStructure);
}
/**
* main.c
*/
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
Timer_A_Init();
Timer_A_enableInterrupt(TIMER_A1_BASE);
GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN0);
_EINT(); // 开启全局中断使能
while (1)
{
}
}
#pragma vector = TIMER1_A1_VECTOR;
__interrupt void Port1_interrupt_process(void)
{
_DINT(); // 关闭全局中断
GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN0);
Timer_A_clearTimerInterrupt(TIMER_A1_BASE);
_EINT(); // 开启全局中断
}
3.2 PWM波形驱动位置伺服舵机
PWM.h
/*
* PWM.h
*
* Created on: 2023年7月18日
* Author: Include everything
*/
#ifndef HARDWARE_PWM_H_
#define HARDWARE_PWM_H_
void PWM_Init(void);
void PWM_SetPeriod(uint16_t periodValue);
void PWM_SetCompare1(uint16_t value);
void PWM_SetCompare2(uint16_t value);
#endif /* HARDWARE_PWM_H_ */
PWM.c
/*
* PWM.c
*
* Created on: 2023年7月18日
* Author: Include everything
*/
#include <driverlib.h>
void PWM_Init(void)
{
GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P1, GPIO_PIN2); // TA0.1
GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P1, GPIO_PIN3); // TA0.2
Timer_A_initUpModeParam Timer_A_UpModeInitStructure = {0};
Timer_A_UpModeInitStructure.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE;
Timer_A_UpModeInitStructure.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; // 32MHz
Timer_A_UpModeInitStructure.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_32; // 计时频率为1MHz
Timer_A_UpModeInitStructure.startTimer = true;
Timer_A_UpModeInitStructure.timerClear = TIMER_A_SKIP_CLEAR;
Timer_A_UpModeInitStructure.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
Timer_A_UpModeInitStructure.timerPeriod = 20000 - 1; // 周期为20ms
Timer_A_initUpMode(TIMER_A0_BASE, &Timer_A_UpModeInitStructure);
Timer_A_outputPWMParam Timer_A_outputPWMInitStructure;
Timer_A_outputPWMInitStructure.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; // 32MHz
Timer_A_outputPWMInitStructure.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_32; // 计时频率为1MHz
Timer_A_outputPWMInitStructure.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
Timer_A_outputPWMInitStructure.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
Timer_A_outputPWMInitStructure.timerPeriod = 20000 - 1; // 写入CCR0的值
Timer_A_outputPWMInitStructure.dutyCycle = 500; // 写入CCR1的值
Timer_A_outputPWM(TIMER_A0_BASE, &Timer_A_outputPWMInitStructure); // 在TA0.1引脚输出PWM波形
Timer_A_outputPWMInitStructure.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_2;
Timer_A_outputPWMInitStructure.compareOutputMode = TIMER_A_OUTPUTMODE_SET_RESET;
Timer_A_outputPWMInitStructure.dutyCycle = 1000; // 写入CCR2的值
Timer_A_outputPWM(TIMER_A0_BASE, &Timer_A_outputPWMInitStructure); // 在TA0.2引脚输出PWM波形
}
// 设置周期
void PWM_SetPeriod(uint16_t periodValue)
{
Timer_A_setCompareValue(TIMER_A0_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_0, periodValue);
}
void PWM_SetCompare1(uint16_t value)
{
Timer_A_setCompareValue(TIMER_A0_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_1, value);
}
void PWM_SetCompare2(uint16_t value)
{
Timer_A_setCompareValue(TIMER_A0_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_2, value);
}
Servo.h
/*
* Servo.h
*
* Created on: 2023年7月26日
* Author: Include everything
*/
#ifndef HARDWARE_SERVO_H_
#define HARDWARE_SERVO_H_
void Servo_Init(void);
void Servo_SetAngle_270(float angle);
void Servo_SetAngle_180(float angle);
#endif /* HARDWARE_SERVO_H_ */
Servo.c
/*
* Servo.c
*
* Created on: 2023年7月26日
* Author: Include everything
*/
#include <driverlib.h>
#include "PWM.h"
void Servo_Init(void)
{
PWM_Init();
}
// 驱动270°舵机
void Servo_SetAngle_270(float angle)
{
PWM_SetCompare1((angle / 270) * 2000 + 500);
}
// 驱动180°舵机
void Servo_SetAngle_180(float angle)
{
PWM_SetCompare2((angle / 180) * 2000 + 500);
}
main.c
#include <driverlib.h>
#include "OLED.h"
#include "System_CLK.h"
#include "Servo.h"
#include "Key.h"
#include "Delay.h"
#include "PWM.h"
uint8_t KeyNum;
float servoAngle_1, servoAngle_2;
/**
* main.c
*/
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
SystemClock_Init();
Key_Init();
OLED_Init();
Servo_Init();
OLED_ShowString(1, 1, "Servo Control");
OLED_ShowString(2, 1, "Angle:");
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
servoAngle_1 = 45.0;
}
if (KeyNum == 2)
{
servoAngle_1 = 225.0;
}
OLED_ShowNum(2, 7, servoAngle_1, 3);
Servo_SetAngle_270(servoAngle_1);
}
}
持续更新完善中……
原创笔记,码字不易,欢迎点赞,收藏~ 如有谬误敬请在评论区不吝告知,感激不尽!博主将持续更新有关嵌入式开发、机器学习方面的学习笔记~