【嵌入式项目】MSP-EXP432实践项目 小车游戏

一 设备

软件:Code Composer Studio (IDE from Texas Instruments)
硬件:MSP-EXP432P401R (Cortex M4F)、Educational BoosterPack MKII

二 储备知识

2.1 嵌入式系统的主要架构:

  • 事件驱动
  • 时间触发

2.2 嵌入式系统内核的主要组成部分:

  1. 微控制器
    中央处理单元(CPU),主存储器和片上外设需要控制系统。市面上存在非常多的微控制器。
    微控制器种类
  2. 微处理器
  3. 数字信号处理器DSP

2.3 数据类型

        不同编译器和不同处理器数据类型不同。<stdint.h>文件显式声明了类型大小。
数据类型

2.4 运算符优先级

        多使用圆括号
优先级

2.5 关键字

  1. 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;
    
  2. extern 变量在其他文件中声明,允许在多个文件之间全局共享变量
    在编译时不会进行内存分配,只检查类型正确性。
  3. static 用于函数时可以减少函数的作用域;用于变量时除可以减少函数作用域外,还可以保持变量。 static 声明的变量不会在每次函数调用中创建,只初始化一次,并在其他调用时维持变量的值。而变量的作用域仅限于声明变量的文件或函数(静态局部变量)。使用局部变量允许重用源代码。
  4. volatile 用于防止编译器优化

2.6 竞态

  1. 来源:两个能以不可预测的顺序(并发性)运行的独立代码段修改共享变量。如多线程任务间、普通代码和中断处理之间。

  2. 避免
    - 中断——不使用全局变量,创建函数来访问变量。访问变量时禁止中断(临界区与原子访问)。
    - 并发系统——使用信号量,互斥,自旋锁。不要使用信号量或类似的中断。永远不要锁定中断服务程序。

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语言扩展

  1. SFR(Special function register) access
    //Cortex style:
    #define P5 ((DIO_PORT_Odd_Interruptable_Type*)(DIO_BASE+0x0040))
    
  2. 标准位定义,每个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;
    
  3. 中断服务例程(Interrupt Service Routine, ISR)。编译器提供预定义的函数名。
  4. 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 振荡器和系统时钟

  • 时钟来源(硬件):
    1. LFXT:用于 32 kHz 或以下的晶体、谐振器或外部信号的低频振荡器。
    2. HFXT:用于晶体,谐振器或外部信号在 1 MHz至 48 MHz 的高频振荡器。
    3. DCO:内部数字控制振荡器(DCO)。
    4. VLO:内部极低功率,9.4 KHz 频率。
    5. REFO:内部低功率修剪 32768 KHz 或 128 KHz。
    6. MODOSC:内部低功率振荡器 25 MHz。
    7. SYSOSC:内部 5MHz。

        另:可以同时使用一个、两个或三个源。

  • 时钟CPU和外设的系统时钟信号:
    1. MCLK:主时钟。是CPU时钟和外围设备。
    2. ACLK:辅助时钟。外围设备。
    3. HSMCLK:子系统主时钟。外围设备。
    4. SMCLK:低速子系统主时钟。外围设备。
    5. BCLK:低速备份域。
    6. 也可以使用原有的硬件信号。

        所有这些都可以从任何硬件时钟源生成,并可以被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) 全局中断使能

  1. 启用所有单独启用的中断
  2. CPU核心寄存器中的PRIMASK位允许/阻止中断
    • PRIMASK=0 启用中断
    • PRIMASK=1 禁止中断
  3. 访问方式:
    • 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中断使能

  1. 只启用单独启用的中断。
  2. 访问方式:
    • 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. 宏定义
    msP432P401r.h

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)

高精度ADC框图

1)特点
  • 多达32个模拟输入(32个单独,16个差分)。
  • 内部和外部电压参考。
  • 两个采样和保持模式,包含它们独自的时钟。
  • 启动转换的几个来源。
  • 14,12,10,8位分辨率。
  • 32个存储寄存器。
  • 单采集、重复采集、序列采集(4种模式)。
  • 窗口比较器。
  • 中断能力准备,捕获和溢出。含38个中断源。
2)参考电压与输出

数字输出:N_ADC = 16384 × [V_(in+) — V_(r-)] / [V_(r+) — V_(r-)]

  1. 输入模拟电压 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
  2. 参考正电压 Vr+ 从1.45V到AVcc。可能的来源包括:

    • 从外部引脚VeRef+。
    • 从内部参考电压(1.2V, 1.45或2.5V);来自AVcc(微控制器电源)
  3. 参考负电压 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寄存器的ADCSHT0xADCSHT1x位决定。其中ADC14SHT0x适用于ADC14MCTL8到ADC14MCTL23,ADC14SHT1x用于ADC14MCTL0到ADC14MCTL7和ADC14MCTL24到ADC14MCTL31。

采样时间很关键,需要满足以下关系:
采样时间
在很难确定外部源的电阻时,一个设置时间转换的实用方法如下:

  1. 设置你想要的采样频率:Fs
  2. 设置ADC (Fc)源时钟。使用低频(SAR内的DAC)更佳。
  3. 计算转换时间:(1/Fc) × 16 = Tc
  4. 计算Tc与1/ Fs之间的时间差
  5. 给你尽可能多的时间取样和保存。
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)

开始转换步骤:

  1. 设置程序参数,如工作模式、时钟、触发源、MSC位、DC14CTL0和ADC14CTL1等
  2. 设置ADCON位以打开转换
  3. 设置ENC位以激活转换
  4. 如果触发源是定时器,转换将在定时器输出边自动开始。
  5. 如果触发源是软件,设置位ADCSC。此位将自动清除。注意:ENC和ADC14SC必须用一条指令!如果在扩展采样模式下,在SAMPCON中生成高低边

获取转换的结果:

  1. 在中断标志寄存器中等待ADC14IFGR0x位。
  2. 从ADC14MEMx读取数据。
  3. 读取时ADCI14FGx标志自动清除ADC14MEMx

停止转换:

  1. 等待忙位重置(在重复和顺序模式中不是强制性的)。
  2. 复位ENC位。
  3. 中止:将模式设置为00并重置ENC,数据不可靠。
7)寄存器

寄存器

32个32位寄存器ADC14 MEMx用于保存转换结果。任何输入通道都可以存储在任何寄存器中,这简化了不同输入信号的采集。

32个32位寄存器ADC14 MCTLx来控制上述内存寄存器。注意:只在ADC14 ENC=0时可以修改

ADC14 CTL0

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

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

MCTLx
WINCT和WINC(15)(14):控制windows比较器
DIF(13):配置输入为单端或差分
VRSEL(11-8):选择参考电压
EOS(7):表示序列结束
INCHx(4-0):选择物理输入通道

ADC14 MEMx

MEMx

8)中断

首先需要全局中断和NVIC中断

NVIC_EnableIRQ (ADC14_IRQHandler);
ADC14 IER0

IER0
当转换结果存储在寄存器中时,x位对应使能ADC14MEMx的中断

ADC14 IER1

IER1
使能特殊事件的中断,如引用准备完成,溢出,窗口比较器限制等。

ADC14 IFGR0

IFGR0
当转换结果存储在ADC14MEMx寄存器中时会设置对应第x位的中断标志;同时,如果ADC14IER0x位被设置,对应中断会被请求。
使用ADC14CLRIFGR0寄存器来清除中断

ADC14 IFGR1

IFGR1
几个特殊事件发生的标志位,如引用准备完成,溢出,窗口比较器限制等。
使用ADC14CLRIFGR1寄存器来清除中断

ADC14 IV

IV
这个寄存器值是ADC14中最高的暂挂中断的编码值

中断处理程序

ADC14_IRQHandler
判断中断来源:

  1. 读取ADC14IFGR0和ADC14IFGR1
    读取完成后清除标志位的方法:
    • 读ADC14MEMx;
    • 或向ADC14CLRIFGR0与ADC14CLRIFGR1中写1
  2. 读取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周期取决于被控制的设备
    PWM

有多达7种捕获/比较块,它们分别是一对寄存器。每个块都关联一个引脚TAx.n。

Up模式下计时器从0计数到TAxCCR0的值,并重复。因此,TAxCCR0的值设置PWM信号的周期。该时间段的具体大小设置则取决于所控制的设备,如Led,直流电机,伺服等,通常是直流供电设备。

当计时器匹配TAxCCRn的值时,输出可能会改变。可以使用几种TAxCCRn同时创造不同的PWM信号和控制不同的器件。
输出模式
采用TimerA做PWM生成器的方法如下:
TimerA做PWM生成器

  1. 选择Up模式
  2. TAxCCR0保存PWM周期时间
  3. TAxCCR1保存高电平时间(占空比)

注意:

  • 不需要有中断,不需要配置PxSEL0和PxSEL1
  • 无法达到0%和100%的占空比
  • 定时器可以管理的占空比范围为[5%,95%]

设置极端值的方法:

  1. 修改输出模式为0
  2. 手工设置输出值(TAxCCTLn的OUT位)
  3. 停止计时器,清除TAxR

当改变占空比时,如果的新值TAxCCRn低于当前TAR和旧的TAxCCRn,会得到一个全功率的峰值,使用电机、放疗机或其他类型的设备时,这些电源冲程可能会产生故障,损坏设备甚至伤害到人。
解决方案:使用溢出中断以同步方式更新TAxCCRn的值。
峰值

每个TA有两个中断向量表

  • TAx_0_IRQHandler():用于TAxCCR0的CCIFG,不需要清除flag。
  • TAx_N_IRQHandler():用于所有其他中断来源。必须访问TAxIV寄存器,清除相关SFR中的关联标志。如果设置了其他标志中断,则改变TAxIV的值。记住只读取一次TAxIV的值。
TAxR TimerA的计数器

永远不要访问这个寄存器
TAxR

TAxCTL TimerA的PWM控制器

TAxCTL
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中的值匹配时,数字输出触发。
TAxCCR
比较模式:TAxCCRn保存与计时器值TAR做比较的数据
捕获模式:在执行捕获时,Timer_A寄存器TAR被复制到TAxCCRn寄存器

TAxCCTLn 配置块n的行为

TAxCCTL
OUTMODx(7-5):输出模式选择(111为Reset/set模式)
OUT(2):在输出模式为000时,1输出high,0输出low

TAxIV

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函数声明

五 代码说明

  1. DCOCLK设置为24MHz
    #define __SYSTEM_CLOCK 24000000
    //DCO的频率(Hz),用于MCLK、HSMCLS和SMCLK。
    
  2. 菜单中可用选项的数目有3个
    #define NUM_OPTIONS 3
    
  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);
    
  4. 游戏结构分为两个循环:
    • 菜单循环:允许用户选择汽车的颜色并开始游戏。
    • 游戏循环:运行游戏(绘制道路并向前移动汽车)。它是一个时间触发的结构。
    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
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值