MSP430F5529小记(01): MSP430Ware使用及时钟配置
作者 | 日期 | 硬件工具 | 软件工具 |
---|---|---|---|
CNPP | 2019-08-14 | MSP-EXP430F5529LP | CCS V9, VS Code |
前言
相比于意法半导体公司(ST)的STM32系列单片机,德州仪器公司(TI)的MSP430系列同样在单片机领域占有重要的地位,再加上笔者手头有挺多TI家的LanunchPad,感觉还是有必要适应一下TI的MCU产品生态链及开发方式的,那么,就从这块性能适中且比较常见的MSP-EXP430F5529LP LaunchPad 开始吧。
MSP430Ware 安装及CCS配置
对于习惯了STM32库函数开发的笔者来说,硬着头皮一个个配MSP430寄存器实在是难以接受,于是,我还是决定使用TI为MSP430系列准备的MSP430Ware库函数开发包,其中包含了我们接下来要用到的外设驱动库DriverLib,以及USB、DSP、GUI等丰富的中间层库。当然,寄存器开发也是一项值得掌握的重要的技能。
MSP430Ware可以直接在CCS的Resource Explorer中下载并安装,也可以在TI官网下载。由于Resource Explorer时常打不开,还是推荐后者。笔者建议将Ware等相关Packages都安装在CCS的同一目录下以便管理,另外,Resource Explorer中改变安装路径的选项位于页面右上角的菜单图标中。
安装完成后,CCS在启动时理论上能自动检测到安装包,不过笔者并没有遇到过,所以还是需要我们手动安装。此前笔者在如何安装MSP430Ware并与CCS联动上花费了大量的时间,由于网上的资料大多针对的是较老版本的CCS,在笔者使用的CCS V9.0.1上根本行不通,不过好在总算摸索出了方法。浏览MSP430Ware的安装目录,可以在msp430ware_3_80_07_00\driverlib下找到说明文件release_notes,其中详细描述了手动安装的方法:
完成后,重启CCS,就能快乐地建立MSP430 DriverLib 工程了:
参考例程及库函数讲解
对于库函数开发笔者参考的资料主要是以下四个手册:
分别为库函数用户手册、单片机数据手册、LaunchPad参考手册以及单片机用户手册。另外,位于msp430ware_3_80_07_00\driverlib\examples\MSP430F5xx_6xx下的参考例程是官方库函数的最好教材。
MSP430的时钟系统被称为“统一时钟系统”,即UCS,手册中的相关介绍及库函数均以其打头(DriverLib User’s Guide中的函数介绍是以首字母顺序排列的,时钟配置反而在后面)。关于UCS的具体介绍,笔者才学疏浅,就不多说了,我们就直奔主题,想办法将LaunchPad板上连接在XT1的32.768KHz时钟晶振和连接在XT2上的4MHz高速晶振利用上,并将辅助时钟ACLK设置为32.768KHz,将主时钟MCLK及子系统主时钟SMCLK设置为XT2的六倍频,即24MHz。查阅MSP430x5xx and MSP430x6xx Family User’s Guide中UCS部分的时钟树,我们即可确定编程思路:
查阅参考例程,其中ucs_ex1_DCO12MHz.c和ucs_ex4_XTSourcesDCOInternal.c分别为使用内部基准振荡器REFO通过FLL倍频至12MHz提供给MCLK和使用外部晶振提供给ACLK与MCLK,结合这两个例程,我们就能实现我们需要的功能。
首先,MSP430F5529额定最高主频25MHz,我们选定为24MHz,根据数据手册,需要提升核心供电电压,于是,查询参考手册和库函数手册电源管理模块PMM部分的内容:
MSP430F5529片上有三个引脚可分别复用为ACLK、MCLK、SMCLK输出,另外需要将连接两个晶振的四个引脚设置为复用输入模式,分别调用GPIO_setAsPeripheralModuleFunctionOutputPin、GPIO_setAsPeripheralModuleFunctionInputPin函数。
接着,开启外部晶振,参数为两晶振的频率,单位为Hz:
开启XT1,由于使用的是32.768KHz晶振,使用低速模式,在此处可选择驱动强度和内置辅助电容容量,不同驱动强调下的功耗及辅助电容容量可在数据手册中查询:
开启XT2,根据外部晶振的频率选择参数:
接下来,我们需要进行时钟分配,使用的函数为UCS_initClockSignal,其作用即为控制时钟树中的复用器及分频器,例如,我们想让XT1作为ACLK的时钟源,不分频,则三个参数分别为UCS_ACLK,UCS_XT1CLK_SELECT,UCS_CLOCK_DIVIDER_1;同理,再次使用此函数将XT2分配给FLL的REFCLK。需要注意的是启用倍频器FLL后,其输出将自动分配给MCKLK和SMCLK,不需要手动分配。
我们希望将XT2的4MHz 6倍频为24MHz作为主时钟,调用函数UCS_initFLLSettle,第一个参数为目标频率,此处为24000,单位KHZ,第二个参数为倍频系数,此处为6:
最后,仿照例程,开启振荡器错误中断,编写中断服务函数即可。
函数编写
msp430f5529_clock.h
/*
* msp430f5529_clock.h
*
* Created on: 2019年8月13日
* Author: callo
*/
//---------------------------------------------------------------------------
//! MSP430F5529
//! -----------------
//! /|\ | P5.4/XIN|-
//! | | | 32kHz
//! ---|RST P5.5/XOUT|-
//! | |
//! | |
//! | P5.2/XT2IN|-
//! | | 4MHz
//! | P5.3/XT2OUT|-
//! | |
//! | P1.0|-->ACLK = 32kHz Crystal Out
//! | |
//! | P7.7|-->MCLK
//! | |
//! | P2.2|-->SMCLK = High Freq Xtal or Resonator Out
//---------------------------------------------------------------------------
#ifndef CUSTOM_INC_MSP430F5529_CLOCK_H_
#define CUSTOM_INC_MSP430F5529_CLOCK_H_
#include "driverlib.h"
#define UCS_XT1_TIMEOUT 50000
#define UCS_XT2_TIMEOUT 50000
#define UCS_XT1_CRYSTAL_FREQUENCY 32768
#define UCS_XT2_CRYSTAL_FREQUENCY 4000000
#define UCS_MCLK_DESIRED_FREQUENCY_IN_KHZ 24000
#define UCS_MCLK_FLLREF_RATIO 6
void MSP430F5529_Clock_Init(void);
#endif /* CUSTOM_INC_MSP430F5529_CLOCK_H_ */
msp430f5529_clock.c
/*
* msp430f5529_clock.c
*
* Created on: 2019年8月13日
* Author: callo
*/
#include "msp430f5529_clock.h"
//Variable to store status of Oscillator fault flags
uint16_t msp430f5529_clock_status;
void MSP430F5529_Clock_Init(void)
{
//Set VCore = 3 for 24MHz clock
PMM_setVCore(PMM_CORE_LEVEL_3);
//ACLK,MCLK,SMCLK set out to pins
//Optional
GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P1,
GPIO_PIN0);
GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P7,
GPIO_PIN7);
GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P2,
GPIO_PIN2);
//Port select XT1
GPIO_setAsPeripheralModuleFunctionInputPin(
GPIO_PORT_P5,
GPIO_PIN4 + GPIO_PIN5);
//Initializes the XT1 and XT2 crystal frequencies being used
UCS_setExternalClockSource(
UCS_XT1_CRYSTAL_FREQUENCY,
UCS_XT2_CRYSTAL_FREQUENCY
);
//Initialize XT1.
UCS_turnOnLFXT1WithTimeout(
UCS_XT1_DRIVE_0,
UCS_XCAP_3,
UCS_XT1_TIMEOUT
);
//Startup HF XT2 crystal Port select XT2
GPIO_setAsPeripheralModuleFunctionInputPin(
GPIO_PORT_P5,
GPIO_PIN2 + GPIO_PIN3
);
//Initialize XT2.
UCS_turnOnXT2WithTimeout(
UCS_XT2_DRIVE_4MHZ_8MHZ,
UCS_XT2_TIMEOUT
);
//Select XT1 as ACLK source
UCS_initClockSignal(
UCS_ACLK,
UCS_XT1CLK_SELECT,
UCS_CLOCK_DIVIDER_1
);
//Set DCO FLL reference = XT2
UCS_initClockSignal(
UCS_FLLREF,
UCS_XT2CLK_SELECT,
UCS_CLOCK_DIVIDER_1
);
//Set Ratio and Desired MCLK Frequency and initialize DCO
UCS_initFLLSettle(
UCS_MCLK_DESIRED_FREQUENCY_IN_KHZ,
UCS_MCLK_FLLREF_RATIO
);
// Enable global oscillator fault flag
SFR_clearInterrupt(SFR_OSCILLATOR_FAULT_INTERRUPT);
SFR_enableInterrupt(SFR_OSCILLATOR_FAULT_INTERRUPT);
}
void NMI_ISR(void)
{
do {
// If it still can't clear the oscillator fault flags after the timeout,
// trap and wait here.
msp430f5529_clock_status = UCS_clearAllOscFlagsWithTimeout(1000);
} while(msp430f5529_clock_status != 0);
}
在主函数中编写以下代码,使P4.7的LED闪烁(P1.0已作为ACLK输出):
main.c
#include "driverlib.h"
#include "msp430f5529_clock.h"
#include "msp430f5529_delay.h"
void main(void)
{
WDT_A_hold(WDT_A_BASE);
//Set P1.0 to output direction
GPIO_setAsOutputPin(
GPIO_PORT_P4,
GPIO_PIN7
);
//Initialize MCLK to 24MHz
MSP430F5529_Clock_Init();
//Enable global interrupt
__bis_SR_register(GIE);
while(1)
{
GPIO_toggleOutputOnPin(GPIO_PORT_P4, GPIO_PIN7);
// __delay_cycles(24000000);
SYS_Delay_500ms();
}
}
其中,__delay_cycles(n)是编译器自带的内联函数,作用是延时n个CPU周期,例如此时CPU周期为24MHz,那么__delay_cycles(24000000)即为延时1s,为了方便使用,我进行了一 些宏定义,大家可以自行发挥:
/*
* msp430f5529_delay.h
*
* Created on: 2019年8月13日
* Author: callo
*/
#ifndef CUSTOM_INC_MSP430F5529_DELAY_H_
#define CUSTOM_INC_MSP430F5529_DELAY_H_
#include
#define SYS_MCLK_IN_HZ 24000000
#define SYS_MCLK_IN_KHZ 24000
#define SYS_MCLK_IN_MHZ 24
#if SYS_MCLK_IN_HZ >= 1000000
#define SYS_Delay_1us() __delay_cycles(SYS_MCLK_IN_MHZ)
#define SYS_Delay_1ms() __delay_cycles(SYS_MCLK_IN_KHZ )
#define SYS_Delay_500ms() __delay_cycles(SYS_MCLK_IN_HZ / 2)
#define SYS_Delay_1s() __delay_cycles(SYS_MCLK_IN_HZ)
#else
#error SYS_MCLK_IN_KHZ is defined impoliticly!
#endif
#endif /* CUSTOM_INC_MSP430F5529_DELAY_H_ */
需要注意的是,若CCS对自行定义的delay函数给出了以下警告,则表明有数据溢出,该延时是不能正常使用的。
运行效果
将示波器探头调节至x10衰减/补偿,连接到LaunchPad的P2.2,可观察到24MHz的SMCLK输出(P7.7/MCLK在LaunchPad上并没有引出):
后记 + 吐槽
我自认为,学习单片机,入门是最难受的一步,因为有太多劝退的理由,在这些理由之中,最为劝退的又是开发环境的配置和时钟树(这就从一方面解释了Arduino成功的原因)。记得刚开始学习STM32时就因为建立标准库工程太复杂(相比于C51)而放弃挣扎了好几个礼拜(挺虚度光阴的2333)。后来,跟着教程走到时钟树,乖乖,看不下去了,又放弃挣扎了相当一段时间。后来,想要学习MSP430,离开MDK舒适的开发环境,上手基于Eclipse的CCS,仅是因为代码补全麻烦,我又放弃了(现在更加偷懒,VS Code搞定一切,真香)。同样,因为过惯了安逸的生活,拒绝寄存器开发(逃),前两天我一直在想办法将MSP430Ware装进CCS里,四处查资料无果,自己在CCS上一顿乱捣也不行,还得一遍遍忍受巨慢无比的Resource Explorer,真的是又要劝退了,好在最后发现,人家官方的解决方法就乖乖躺在DriverLib文件夹里呢。至此,MSP430我算是入门了。我是个菜鸟,这才是我第一次在完全不使用第三方教程的情况下,根据官方手册和例程来学习一款单片机,感觉,收获还是巨大的。玩单片机总归应该是一个快乐的过程,在这里吐槽完我的痛苦(算不了什么)经历,希望接下来能痛快开玩。
太感谢了,msp430资料太少找了好久
那几个PDF手册在哪里找啊