一 设备
软件:Code Composer Studio (IDE from Texas Instruments)
硬件:MSP-EXP432P401R (Cortex M4F)、Educational BoosterPack MKII
二 储备知识
2.1 嵌入式系统的主要架构:
- 事件驱动
- 时间触发
2.2 嵌入式系统内核的主要组成部分:
- 微控制器
中央处理单元(CPU),主存储器和片上外设需要控制系统。市面上存在非常多的微控制器。
- 微处理器
- 数字信号处理器DSP
2.3 数据类型
不同编译器和不同处理器数据类型不同。<stdint.h>文件显式声明了类型大小。
2.4 运算符优先级
多使用圆括号
2.5 关键字
typedef
创建新类型typedef char str40[41]; str40 message; typedef uint16_t t_ticks; t_ticks time; typedef struct { int x, y, z;} t_coordinates; t_coordinates position; position.x = 100; position.y = 54;
extern
变量在其他文件中声明,允许在多个文件之间全局共享变量。
在编译时不会进行内存分配,只检查类型正确性。static
用于函数时可以减少函数的作用域;用于变量时除可以减少函数作用域外,还可以保持变量。 static 声明的变量不会在每次函数调用中创建,只初始化一次,并在其他调用时维持变量的值。而变量的作用域仅限于声明变量的文件或函数(静态局部变量)。使用局部变量允许重用源代码。volatile
用于防止编译器优化。
2.6 竞态
-
来源:两个能以不可预测的顺序(并发性)运行的独立代码段修改共享变量。如多线程任务间、普通代码和中断处理之间。
-
避免:
- 中断——不使用全局变量,创建函数来访问变量。访问变量时禁止中断(临界区与原子访问)。
- 并发系统——使用信号量,互斥,自旋锁。不要使用信号量或类似的中断。永远不要锁定中断服务程序。
2.7 超时
-
软件超时
unsigned int Timeout = some_value; while(!(data_ready) && (++Timeout));
可以调整初值,但是很难设置一个特定的时间值
-
硬件超时
Set_timer_and_run (Timeout_value); while(!(data_ready) && (!TimerFlag));
时间可以非常精确地调整,从毫秒到秒
-
最好的方法是对串行输入和定时器都使用中断。系统将受益于睡眠模式,甚至可以多任务处理。
2.8 MISRA-C
MISRA-C是C语言的一个子集,旨在改进与软件相关的安全系统
C语言的高效性也导致其实时运行错误检查工具较弱,没有try-catch。程序员需要自行进行错误检查。
偏差:一个正式的程序,涉及到整个团队甚至部门必须授权的偏差。必须有文件记录并证明是合理的。项目偏差涉及所有项目代码;特定偏差只涉及一个文件中的一个实例。
2.9 C语言扩展
- SFR(Special function register) access
//Cortex style: #define P5 ((DIO_PORT_Odd_Interruptable_Type*)(DIO_BASE+0x0040))
- 标准位定义,每个BIT均只有一位为1
#define BIT0 (0x0001u) #define BIT1 (0x0002u) #define BIT2 (0x0004u) #define BIT3 (0x0008u) …… #define BITF (0x8000u)
//为某些位清零 Var &= ~(BIT3 | BIT8 | BITD | BITF); //为某些位置一 Var |= (BIT0 | BITA | BITE); //取某些位的值 Check1 = Var & BIT0; Check2 = Var & BIT1; Check3 = Var & BIT6; //切换位/取反位:按位异或(不同为1,相同为0) aux_c ^= BIT2;
- 中断服务例程(Interrupt Service Routine, ISR)。编译器提供预定义的函数名。
- CCS编译器提供了访问底层处理器指令的内在函数。内在函数编译内联的机器代码。头文件"intrinsic.h"。
三 硬件资料
3.0 简介
- Arm®32位Cortex®-M4F CPU最高可达48MHz。
- 具备浮点单元和内存保护单元;有256KB闪存和64KB的SRAM。具备超低功耗模式,片上调试器,包括数字I/O、定时器、倍增器、ADC、通信、DMA等几种片上外设。
- 其低供电电压范围从1.62V至3.7V。
3.1 SFR
通过大量的SFR(Special Function Register)管理外围设备。SFR不是一个存储数据的寄存器,是CPU与外设通信的寄存器。
- CPU写入SFR来配置和控制外设。如在模拟输出中设置电压。
- CPU读取SFR从外设获取数据或状态。如知道数字引脚是高电压还是低电压
访问方式:
-
MSP430 style (宏指令)
P5SEL0 |= 0x2A; TA0CCTL3 &= 0x27;
-
CMSIS style (指针) 【更优选择】
P5->SEL0 |= 0x2A ; TIMER_A0->CCTL[3] &= 0x27: ADC14->MCTL[9] = 0;
3.2 振荡器和系统时钟
- 时钟来源(硬件):
- LFXT:用于 32 kHz 或以下的晶体、谐振器或外部信号的低频振荡器。
- HFXT:用于晶体,谐振器或外部信号在 1 MHz至 48 MHz 的高频振荡器。
- DCO:内部数字控制振荡器(DCO)。
- VLO:内部极低功率,9.4 KHz 频率。
- REFO:内部低功率修剪 32768 KHz 或 128 KHz。
- MODOSC:内部低功率振荡器 25 MHz。
- SYSOSC:内部 5MHz。
另:可以同时使用一个、两个或三个源。
- 时钟CPU和外设的系统时钟信号:
- MCLK:主时钟。是CPU时钟和外围设备。
- ACLK:辅助时钟。外围设备。
- HSMCLK:子系统主时钟。外围设备。
- SMCLK:低速子系统主时钟。外围设备。
- BCLK:低速备份域。
- 也可以使用原有的硬件信号。
所有这些都可以从任何硬件时钟源生成,并可以被1、2、4、8、16、32、64或128划分。
时钟源数量多的原因:低功率需要低频率;现代应用需要高频率。如果有电力限制,必须仔细设计和编程时钟系统。
3.3 操作模式
低功率并不总是意味着低功耗,低功率是指什么都不做的时候省电。现代微控制器提供多种不同性能和消耗的操作模式。应用程序决定何时进入低功耗模式,物理事件“唤醒”微控制器。
MSP432家族提供了几种低功耗操作模式(LPM)。
- 写入SFR状态寄存器(SR)进入微选择的模式(内在功能)。
- 中断从任何模式唤醒µC(需要启用)
系统总是由I/O中断和RST信号唤醒。如果时钟启用,由RTC和WDT中断唤醒。
- 如果在进入LPM前忘记使能中断:
- LPM4.5和关机的区别:
当微控制器被中断唤醒时:
- 中断服务程序(ISR)必须处理操作模式。
- 它是可能离开状态,寄存器没有改变,所以微控制器返回到相同的低功耗模式。
- 可以更改状态寄存器,并选择另一种操作模式。例如,编程定时器,睡眠微控制器,唤醒因为定时器中断,改变到活动模式,并正常工作,直到有事件改到睡眠模式。
3.4 中断
中断是表示需要注意的异步信号,使处理器保存其执行状态并开始执行中断处理程序(ISR)。ISR执行后,程序正常恢复。中断可以避免在轮询循环中浪费CPU周期。
- 中断向量是ISR的内存地址。
- 在MSP432中断是CPU的一部分(Cortex M),NVIC可以为特定的微控制器量身定制。
- 来自外围设备(而非SysTick)的中断需要三个级别的启用:全局,NVIC,外围
NVIC的主要特点:
- 64个中断源,3个优先级位。值越低,优先级越高
- 嵌套。ISR可以被一个更高优先级的中断抢占。
- 可屏蔽。三个层次:全局、NVIC、外围。
- 电平和脉冲中断。中断可以有四种状态:inactive、pending、active、active and pending。这允许ISR在状态处于挂起和活动状态时重新启动。
1) 全局中断使能
- 启用所有单独启用的中断
- CPU核心寄存器中的
PRIMASK位
允许/阻止中断- PRIMASK=0 启用中断
- PRIMASK=1 禁止中断
- 访问方式:
- CMSIS库函数(ARM内联函数)
void __set_PRIMASK (uint32_t priMask) // Set PRIMASK to value priMask uint32_t __get_PRIMASK (void) // Returns the value of PRIMASK void __enable_irq (void) //Clears PRIMASK to 0 void __disable_irq (void) //Sets PRIMASK to 1
- 内联函数(Texas Instrument)
uint32_t _enable_interrupts (void) // Returns previous value of PRIMASK and set it to 0 uint32_t _disable_interrupts (void) // Returns previous value of PRIMASK and set it to 1 void _restore_interrupts (uint32_t priMask) // Set PRIMASK to value priMask
2) NVIC中断使能
- 只启用单独启用的中断。
- 访问方式:
- CMSIS库函数(ARM内联函数)
NVIC_EnableIRQ(IRQn_Type IRQn) // IRQn is the IRQ number.
- 访问NVIC寄存器
NVIC->ISER[x] |= 0x01 << (IRQn & 31); // x = 0 for IRQs 0 to 31, and x = 1 for IRQs 32 to 63. // 保留IRQn的低5位(因为超过31重新计数),将1左移对应的位数 /* 举例 */ NVIC_EnableIRQ (37); NVIC_EnableIRQ (PORT3_IRQn); NVIC->ISER[1] |= 0x01 << (PORT3_IRQn & 31);
- 宏定义
3) 外围中断使能
每个外设有一组寄存器(SFR)。这些寄存器中的一些位允许中断。当外设引发(触发)中断时,一个或多个位被设置为1以指示该事件。这些位称为FLAGS,存储在SFR中。
- 如果只有一个FLAG可以引发中断,清除FLAG然后反应
- 如果有多个FLAG可以引发中断,检查是哪个FLAG导致的中断,清除它然后反应
- 不要声明非静态变量。
- 不要运行花费长时间的代码。
4) 中断与竞态
在ISR和其他函数之间共享变量可能会产生严重的问题。程序将以不同的方式运行,这取决于事件的顺序或时间。
解决方案是使用**原子函数(临界区)**来检查和修改共享变量,在操作修改变量前禁用中断,在完成操作之后启用中断。
static uint8_t FLAG_X=1; //module scope
uint8_t Check_FLAG_X (){
uint8_t copy;
uint32_t int_state;
int_state = disable_interrupts(); // 禁用中断
copy = FLAG_X; // 读变量值
FLAG_X = 0; // 清除变量值
_restore_interrupts (int_state); // 恢复中断
return (copy);
}
//主函数
... ...
while (1){
... ...
if (Check_FLAG_X()){
DO_TASK();
}
... ...
}
3.5 数字IO
MSP432P401R有多达84个通用输入/输出引脚(GPIO端口或简单的I/O端口)。大多数端口为8位长度。一些端口引脚与其他外设功能是多路复用的。
PxSEL0和PxSEL1用于选择引脚功能
PxDIR(8位大小)允许选择引脚方向
PxIN(8位大小)用于读取GPIO引脚的状态
—输入为低时,则该位值为0。
—输入为高时,则该位值为1。
PxOUT(8位大小)用于写入GPIO引脚的状态
—置为0,输出为低压。
—置为1,输出为高压。
PxREN(8位大小)允许在DIR设置为INPUT时启用上拉或下拉电阻
—0:禁用上拉/下拉电阻
—1:启用上拉/下拉电阻
此时PxOUT(8位大小)用于选择上下拉电阻
—置为0,下拉。
—置为1,上拉。
PxDS(8位大小)设置驱动器强度(电流)
—设置为0位,降低驱动器强度(默认)。
—设置为1位,全驱动强度
从端口P1到端口P10的每个引脚都能产生中断(引脚状态改变时产生中断)
PxIES(8位大小)控制产生中断的变化
—设置为0位,选择低到高。
—设置为1位,选择高到低。
PxIE(8位大小)启用/禁用中断
—设置为0位,表示关闭中断。
—设置为1位,表示使能中断。
PxIFG(8位大小)每个引脚一个位标志
—当选中的边出现时置位为1。
—当该标志被设置时,引发中断(启用中断时)。
必须在中断处理程序中检查PxIFG以确认中断的来源,随后立即清除标志位。
void PORT5_IRQHandler(){
if (P5->IFG & BIT0){
P5->IFG &= ~BIT0; //CLEAR THE FLAG!!!!
//write your code
}
if (P5->IFG & BIT4){
P5->IFG &=~BIT4; //CLEAR THE FLAG!!!!
//write your code
}
}
另一种方法是使用中断向量。【更优选择】
PxIV(16位大小)表示引发中断的最高优先级引脚
每次读取PxIV时,它的值都会更新到最高优先级的暂挂中断之后。读取PxIV时自动清除相应的PxIFG标记。
访问PxIV的方式有两种。第一种使用switch case语句,如果设置了两个标志,它将运行两次的ISR。第二种采用复制PxIV寄存器的值来确保正确地多次运行。
// 方法一
void PORT5_IRQHandler(){
switch (P5->IV){
case DIO_PORT_IV__IFG0:
//write your code
break;
case DIO_PORT_IV__IFG4:
//write your code
break;
default:{
}
break;
}
}
// 方法二
void PORT5_IRQHandler(){
static uint8_t copy;
copy = P5->IV;
if (copy & BIT0) {
...}
if (copy & BIT4) {
...}
}
注意
- 写入PxOUT, PxDIR, PxIES或PxREN可以导致设置相应的PxIFG,也就是说,会得到一个错误的中断。解决办法是在配置端口后清除标志。
- 任何外部中断事件都应至少为20ns / 1µs。(非毛刺滤波器/毛刺滤波器)。
- 如果你不使用PxIV,必须检查PxIFG的位,并在检查之后通过软件清除它们。
- 如果你使用PxIV,你不需要检查PxIFG或清除标志,在ISR内部只读取一次PxIV。
- 机械开关在输出时需要除颤,可通过硬件或软件实现。对触发器,RC电路,除颤IC,可以使用中断;延迟,计数器,时间触发轮询则不需要中断。
3.6 高精度ADC (ADC14)
1)特点
- 多达32个模拟输入(32个单独,16个差分)。
- 内部和外部电压参考。
- 两个采样和保持模式,包含它们独自的时钟。
- 启动转换的几个来源。
- 14,12,10,8位分辨率。
- 32个存储寄存器。
- 单采集、重复采集、序列采集(4种模式)。
- 窗口比较器。
- 中断能力准备,捕获和溢出。含38个中断源。
2)参考电压与输出
数字输出:N_ADC = 16384 × [V_(in+) — V_(r-)] / [V_(r+) — V_(r-)]
-
输入模拟电压 V_in 范围:0V~AVcc
- AVcc 范围从1.62V到3.7V。
- 若 V_in 超出操作范围:
V_in ≥ V_(r+),N_ADC = 0x3FFF (Full scale)
V_in ≤ V_(r-),N_ADC = 0x0000
V_in超过[0, Vcc],会损坏ADC
-
参考正电压 Vr+ 从1.45V到AVcc。可能的来源包括:
- 从外部引脚VeRef+。
- 从内部参考电压(1.2V, 1.45或2.5V);来自AVcc(微控制器电源)
-
参考负电压 Vr- 总是0V。可能的来源包括:
- 外部引脚VeRef-(建议为0 V)。
- 来自Avss(地面)
由此可知,ADC不会输出负信号,且分辨率可调节。
3)输入源
多达32个外部输入,6个内部输入(软件可选):内部温度传感器,AVcc/2(电池监控器)和4个输入(取决于设备)。
输入是多路复用的,因此不可能同时对两个或多个输入进行采样。外部输入与几个DIO端口共享,因此必须使用PxSEL0和PxSEL1配置每个引脚。
4)时钟
采用逐次逼近转换方式,需要时钟ADC14CLK
。
ADC14CLK有6个时钟源。
- MODCLK, SYSCLK, ACLK, MCLK, SMCLK, HSMCLK
- 时钟源可以被1、4、32或64分割
- 在上述基础上还可以再除以1、2、4、6、8
- 低频:高转换时间,高频:低转换时间。最大转化率为1 Msps。(不推荐)
5)采样保持及其模式
开始转换并不意味着“开始将模拟转换为数字”,而是意味着:
- 首先,输入信号必须被“采样”。
- 第二,保持它(就像“拍照”一样)。
- 第三,转换(16 ADC14CLK时钟)。分辨率越低,循环次数越少。
上述步骤由SHI
的上升边启动。生成SHI的几个源包括:
- AD14SC 位 (software start)。
- 计时器的几个输出。不需要CPU干预,定期捕获输入信号;检查具体设备的数据表
外部采样模式SHP=0
SHI
行为控制采样时间
脉冲采样模式SHP=1
SHI
触发采样,采样时间由ADC14 CTL0寄存器的ADCSHT0x
和ADCSHT1x
位决定。其中ADC14SHT0x适用于ADC14MCTL8到ADC14MCTL23,ADC14SHT1x用于ADC14MCTL0到ADC14MCTL7和ADC14MCTL24到ADC14MCTL31。
采样时间很关键,需要满足以下关系:
在很难确定外部源的电阻时,一个设置时间转换的实用方法如下:
- 设置你想要的采样频率:Fs
- 设置ADC (Fc)源时钟。使用低频(SAR内的DAC)更佳。
- 计算转换时间:(1/Fc) × 16 = Tc
- 计算Tc与1/ Fs之间的时间差
- 给你尽可能多的时间取样和保存。
6)转换及其模式
单通道单转换
- 外部采样模式:SHI上升边和下降边必须由ADC14ADCSC设置/复位或定时器设置/复位产生,以完成采样并开始转换
- 脉冲采样模式:只需要SHI的上升边缘
- 使用定时器触发转换:ENC位需要在新的转换前触发
- 如果使用软件触发,ENC和ADCSC必须设置在同一条指令中
多通道单转换
- 第一个通道在ADC14CTL1中设置;最后一个要转换的通道在对应的ADC14MCTLx中设置EOS位
- 使用定时器触发转换:ENC位应在开始下一个序列之前被触发
- 要允许转换下一个通道,需要设置位MSC
ADC14CTL0,或触发新的转换(ADCSC或
TimerA / B) - 每次转换写入ADC14MEMx时,都会设置相关标志(只检查最后一次)
单通道多转换
- 在新捕获前从ADCMEMx读取数据。
- 设置MSC,或触发新的转换(ADCSC或
TimerA / B)。
多通道多转换
- 在重新启动之前从内存寄存器读取数据。
- 设置MSC,或触发新的转换(ADCSC或
TimerA / B)
开始转换步骤:
- 设置程序参数,如工作模式、时钟、触发源、MSC位、DC14CTL0和ADC14CTL1等
- 设置
ADCON
位以打开转换 - 设置
ENC位
以激活转换 - 如果触发源是定时器,转换将在定时器输出边自动开始。
- 如果触发源是软件,设置位
ADCSC
。此位将自动清除。注意:ENC和ADC14SC必须用一条指令!如果在扩展采样模式下,在SAMPCON
中生成高低边
获取转换的结果:
- 在中断标志寄存器中等待ADC14IFGR0x位。
- 从ADC14MEMx读取数据。
- 读取时ADCI14FGx标志自动清除ADC14MEMx
停止转换:
- 等待忙位重置(在重复和顺序模式中不是强制性的)。
- 复位ENC位。
- 中止:将模式设置为00并重置ENC,数据不可靠。
7)寄存器
32个32位寄存器ADC14 MEMx用于保存转换结果。任何输入通道都可以存储在任何寄存器中,这简化了不同输入信号的采集。
32个32位寄存器ADC14 MCTLx来控制上述内存寄存器。注意:只在ADC14 ENC=0
时可以修改
ADC14 CTL0
PDIV(31-30):时钟分割器(一级)
DIVx(24-22):时钟分割器(二级)
SSELx(21-19):时钟选择
SHS(29-27):选择采样与保持的数据源
SHP(26):选择采样保持模式,1为脉冲采样模式
SHT1x(用于MEM8~23)和SHT0x(用于其余)(15-8):在SHP=1时选择采样和保持周期。
ON(4):ADC14开关
ENC(1):ADC14使能
SC(0):为1时开始采样-保持-转换
BUSY(16):标志转换正在进行
MSC(7):多次取样或重复
CONSEQx(18-17):选择转换模式(单次、重复、序列)
ADC14 CTL1
CHxMAP(27-24):控制内部输入通道
TCMAP(23):控制内部温度传感器输入通道
CSTARTADDx(20-16):选择用于转换的第一个内存寄存器
RES(5-4):分辨率(8、10、12、14)
DF(3):数据格式(无符号二进制或2s补码)
REFBURST(2): 参考缓冲区打开/关闭
PWRMD(1-0):正常/低功率
ADC14 MCTLx
WINCT和WINC(15)(14):控制windows比较器
DIF(13):配置输入为单端或差分
VRSEL(11-8):选择参考电压
EOS(7):表示序列结束
INCHx(4-0):选择物理输入通道
ADC14 MEMx
8)中断
首先需要全局中断和NVIC中断
NVIC_EnableIRQ (ADC14_IRQHandler);
ADC14 IER0
当转换结果存储在寄存器中时,x位对应使能ADC14MEMx的中断
ADC14 IER1
使能特殊事件的中断,如引用准备完成,溢出,窗口比较器限制等。
ADC14 IFGR0
当转换结果存储在ADC14MEMx寄存器中时会设置对应第x位的中断标志;同时,如果ADC14IER0x位被设置,对应中断会被请求。
使用ADC14CLRIFGR0寄存器来清除中断
ADC14 IFGR1
几个特殊事件发生的标志位,如引用准备完成,溢出,窗口比较器限制等。
使用ADC14CLRIFGR1寄存器来清除中断
ADC14 IV
这个寄存器值是ADC14中最高的暂挂中断的编码值
中断处理程序
ADC14_IRQHandler
判断中断来源:
- 读取ADC14IFGR0和ADC14IFGR1
读取完成后清除标志位的方法:- 读ADC14MEMx;
- 或向ADC14CLRIFGR0与ADC14CLRIFGR1中写1
- 读取ADC14IV【更优选择】
查表获得ADC14IV的可能取值,可判断对应中断发生位置
3.7 内部温度传感器
- 连接到通道22并与外部输入多路复用
- 需要打开参考电压来为传感器供电
- 采样和保持必须大于5µS
- 大偏移量(±35ºC)和器件的可变性
3.8 模拟信号与误差
- 测量值永远不等于实际值
绝对误差=|测量值-真实值|
相对误差=|测量值-真实值|/真实值 - 错误种类
- Gross,必须使用统计工具(平均值、中位数、梯度)来丢弃严重误差;
- 随机误差,不可避免;
- 伪随机的、系统的误差,可以过滤或补偿。
- 真实性:大样本集的实际值与平均值之间的距离,通常表示为漂移或偏差
- 精确性:大样本中的可变性,用可重复性和再现性来表示
- 准确率:既真实又精确,通常用跨度或满量程的%表示
- 错误是由于操作员,传感器,信号调理,干扰,温度造成的…
3.9 TimerA与PWM
MSP432P401r有以下定时器:
- Systick
- Watchdog定时器,也可以作为间隔定时器(WDT_A)
- 实时时钟(RTC_C)。
- Timer32。
- Timer_A(最多4个计时器)
脉宽调制(PWM)
- 通过增加或减少周期信号的高电平周期(调节占空比)来控制输出功率。
- PWM周期取决于被控制的设备
有多达7种捕获/比较块,它们分别是一对寄存器。每个块都关联一个引脚TAx.n。
Up模式下计时器从0计数到TAxCCR0
的值,并重复。因此,TAxCCR0的值设置PWM信号的周期。该时间段的具体大小设置则取决于所控制的设备,如Led,直流电机,伺服等,通常是直流供电设备。
当计时器匹配TAxCCRn
的值时,输出可能会改变。可以使用几种TAxCCRn同时创造不同的PWM信号和控制不同的器件。
采用TimerA做PWM生成器的方法如下:
- 选择Up模式
- TAxCCR0保存PWM周期时间
- TAxCCR1保存高电平时间(占空比)
注意:
- 不需要有中断,不需要配置PxSEL0和PxSEL1
- 无法达到0%和100%的占空比
- 定时器可以管理的占空比范围为[5%,95%]
设置极端值的方法:
- 修改输出模式为0
- 手工设置输出值(TAxCCTLn的OUT位)
- 停止计时器,清除TAxR
当改变占空比时,如果的新值TAxCCRn低于当前TAR和旧的TAxCCRn,会得到一个全功率的峰值,使用电机、放疗机或其他类型的设备时,这些电源冲程可能会产生故障,损坏设备甚至伤害到人。
解决方案:使用溢出中断以同步方式更新TAxCCRn的值。
每个TA有两个中断向量表
- TAx_0_IRQHandler():用于TAxCCR0的CCIFG,不需要清除flag。
- TAx_N_IRQHandler():用于所有其他中断来源。必须访问TAxIV寄存器,清除相关SFR中的关联标志。如果设置了其他标志中断,则改变TAxIV的值。记住只读取一次TAxIV的值。
TAxR TimerA的计数器
永远不要访问这个寄存器
TAxCTL TimerA的PWM控制器
TASSEL(9-8):选择时钟源
ID(7-6):输入时钟分频
MC(5-4):模式选择(Up mode为01)
TACLR(2):清除Timer_A,重置TAR、时钟划分和计数方向。该位一般自动重置因此读数结果常为0。
TAIE(1):Timer_A中断使能
TAIFG(0):Timer_A中断标志
TAxCCRn 存储与计时器的当前计数进行比较的值
在比较输出模式下,当计时器值与TAxCCRx中的值匹配时,数字输出触发。
比较模式:TAxCCRn保存与计时器值TAR做比较的数据
捕获模式:在执行捕获时,Timer_A寄存器TAR被复制到TAxCCRn寄存器
TAxCCTLn 配置块n的行为
OUTMODx(7-5):输出模式选择(111为Reset/set模式)
OUT(2):在输出模式为000时,1输出high,0输出low
TAxIV
四 文件结构
main.c — 主函数所在文件
startup_msp432p401r_css.c — 中断处理程序的声明
system_msp432p401r.c — 系统初始化,主要是时钟系统
game.h — 游戏库函数
buttons.c — 按钮函数定义
buttons.h — 按钮函数声明
led_control.c — LED函数定义
led_control.h — LED函数声明
analog.c — 模拟输入函数定义
analog.h — 模拟输入函数声明
timer.c — PWM函数定义
timer.h — PWM函数声明
五 代码说明
- DCOCLK设置为24MHz
#define __SYSTEM_CLOCK 24000000 //DCO的频率(Hz),用于MCLK、HSMCLS和SMCLK。
- 菜单中可用选项的数目有3个
#define NUM_OPTIONS 3
- game.h库函数
/* SysTick定时器被配置为每40ms引发一次中断。在游戏循环中调用这个函数允许每40毫秒执行一些任务。 * SysTick的ISR将值1设置为一个变量,该变量是库的私有变量,称为SysTick_flag。 * 这个函数返回值Systick_flag,随后将其清除为0。 */ uint8_t check_SysTick_flag(void); /* 初始化TFT屏幕和软件结构来创建游戏。 * 此函数启用了全局中断。 */ void Init_Game(void); /* 在屏幕上绘制游戏菜单。 * 在进入菜单管理循环之前只调用一次。 */ void Create_Menu(void); /* 高亮显示由参数选项指示的所选颜色。 * 如果选项不在 [0, NUM_OPTIONS -1] 的范围内,则不执行任何操作。 */ uint8_t Show_Menu(uint8_t option); /* 存储用户选择的汽车颜色,以便在游戏开始时使用。 * 它是一个与菜单中选项的数字相对应的数字,如果参数不在 [0, NUM_OPTIONS -1] 的范围内,则行为是未定义的 */ void Set_Car_Color (uint8_t color); /* 在屏幕上绘制汽车。它必须每40毫秒调用一次 */ void Draw_Car(void); /* 在屏幕上绘制道路。它必须每40毫秒调用一次 */ void Draw_Road(void); /* 在参数偏移量中指定的单位中更改汽车的存储位置。 * 如果offset值为负,则位置向左移动。如果offset值为正,则位置向右移动。 * 这个函数不会更新汽车在屏幕上的位置。 */ void Steering_Wheel (int8_t offset); /* 在参数单位中指定的单位中更改汽车的存储速度。 * 如果单位值为负,则速度降低。单位值为正,速度提高。 * 该函数注意保持速度在允许的最小和最大速度范围内。这个函数不更新屏幕。 */ uint8_t change_speed(uint8_t units); /* 在屏幕上显示字符串l1和l2,用于显示报错信息。 */ void show_error(int8_t *l1, int8_t *l2);
- 游戏结构分为两个循环:
- 菜单循环:允许用户选择汽车的颜色并开始游戏。
- 游戏循环:运行游戏(绘制道路并向前移动汽车)。它是一个时间触发的结构。
void main(void){ uint8_t menu, menu_option, game, error; WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD; // Stop WDT // Enable global interrupt and initialize _enable_interrupts(); Init_Game(); while (1){ Create_Menu(); menu = 1; //The menu is running while (menu){ error = Show_Menu(menu_option); if (error){ show_error("PARAM", "ERROR"); } // Go down the menu if bottom button is pressed // Choose the color if the upper buttons is pressed // If joystick button end menu loop and go to next loop } } Init_Game (); game = 1; while (game){ if (check_SysTick_flag()){ // SysTick event every 40ms Draw_Road(); Draw_Car(); // If upper button is pressed, update speed // If joystick button is pressed, end game loop // Using the analog joystick, move car left and rig