Update on 2021.1.3
最近为了驱动一块SPI通信的4.0寸TFT带触摸彩色屏幕,配置MSP432P401R的时钟抓破了头皮。我跑遍了各大论坛,总算找到了详细且能够使用的决办法。具体代码配置链接如下,方便大家查看,我也对代码进行了粘贴和注释处理:
http://dev.ti.com/tirex/explore/node?node=AIt11.GE8nvJkpoVjCiItQz-lQYNjLATEST 官方配置例程
//******************************************************************************
// MSP432P401 Demo - Device configuration for operation @ MCLK = DCO = 48MHz
//
// Description: Proper device configuration to enable operation at MCLK=48MHz
// including:
// 1. Configure VCORE level to 1
// 2. Configure flash wait-state to 1
// 3. Configure DCO frequency to 48MHz
// 4. Ensure MCLK is sourced by DCO
//
// After configuration is complete, MCLK is output to port pin P4.3.
//
// MSP432P401x
// -----------------
// /|\| |
// | | |
// --|RST |
// | P4.3|----> MCLK
// | |
//
// William Goh
// Texas Instruments Inc.
// June 2016 (updated) | November 2013 (created)
// Built with CCSv6.1, IAR, Keil, GCC
//******************************************************************************
#include "ti/devices/msp432p4xx/inc/msp.h"
#include "stdint.h"
void error(void);
int main(void)
{
volatile uint32_t i;
uint32_t currentPowerState;
WDT_A->CTL = WDT_A_CTL_PW |
WDT_A_CTL_HOLD; // Stop WDT
P1->DIR |= BIT0; // P1.0 set as output
/* NOTE: This example assumes the default power state is AM0_LDO.
* Refer to msp432p401x_pcm_0x code examples for more complete PCM
* operations to exercise various power state transitions between active
* modes.
*/
/* Step 1: Transition to VCORE Level 1: AM0_LDO --> AM1_LDO */
/* Get current power state, if it's not AM0_LDO, error out */
currentPowerState = PCM->CTL0 & PCM_CTL0_CPM_MASK;
if (currentPowerState != PCM_CTL0_CPM_0)
error();
while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));
PCM->CTL0 = PCM_CTL0_KEY_VAL | PCM_CTL0_AMR_1;
while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));
if (PCM->IFG & PCM_IFG_AM_INVALID_TR_IFG)
error(); // Error if transition was not successful
if ((PCM->CTL0 & PCM_CTL0_CPM_MASK) != PCM_CTL0_CPM_1)
error(); // Error if device is not in AM1_LDO mode
/* Step 2: Configure Flash wait-state to 1 for both banks 0 & 1 */
FLCTL->BANK0_RDCTL = (FLCTL->BANK0_RDCTL & ~(FLCTL_BANK0_RDCTL_WAIT_MASK)) |
FLCTL_BANK0_RDCTL_WAIT_1;
FLCTL->BANK1_RDCTL = (FLCTL->BANK0_RDCTL & ~(FLCTL_BANK1_RDCTL_WAIT_MASK)) |
FLCTL_BANK1_RDCTL_WAIT_1;
/* Step 3: Configure DCO to 48MHz, ensure MCLK uses DCO as source*/
CS->KEY = CS_KEY_VAL ; // Unlock CS module for register access
CS->CTL0 = 0; // Reset tuning parameters
CS->CTL0 = CS_CTL0_DCORSEL_5; // Set DCO to 48MHz
/* Select MCLK = DCO, no divider */
CS->CTL1 = (CS->CTL1 & ~(CS_CTL1_SELM_MASK | CS_CTL1_DIVM_MASK)) |
CS_CTL1_SELM_3;
CS->KEY = 0; // Lock CS module from unintended accesses
/* Step 4: Output MCLK to port pin to demonstrate 48MHz operation */
P4->DIR |= BIT3;
P4->SEL0 |=BIT3; // Output MCLK
P4->SEL1 &= ~(BIT3);
while (1) // continuous loop
{
P1->OUT ^= BIT0; // Blink P1.0 LED
for (i = 200000; i > 0; i--); // Delay
}
}
void error(void)
{
volatile uint32_t i;
while (1)
{
P1->OUT ^= BIT0;
for(i = 20000; i > 0; i--); // Blink LED forever
}
}
以上是官方的历程,英文有点晦涩,配合具体配置步骤,我加入了自己的翻译:
// Description: Proper device configuration to enable operation at MCLK=48MHz
// including:
// 1. Configure VCORE level to 1
// 2. Configure flash wait-state to 1
// 3. Configure DCO frequency to 48MHz
// 4. Ensure MCLK is sourced by DCO
官方原文提到,如果想要将MSP432P401R主频飙至48MHz,必须包括以下4个步骤,先不管它具体内部的原理是怎样的,翻译就完了=_=:
- 配置电压核心水平等级到1
- 配置Flash等待状态到1
- 配置数控时钟振荡频率到48MHz
- 确保主时钟MCLK的时钟源来自DCO
步骤一:配置电压核心水平等级到1
官方的历程中引入了P1.0作为电压等级是否配置完成的提示标志,如果配置不成功,将会执行错误自定义函数,其中变量currentPowerState用来获取当前电压等级,如果此时没有处在AM0_LDO, 将会执行error函数,error函数中主要执行的内容是进行LED闪烁报警,提示用户电压等级目前配置异常。仔细想了一下其中LED的报警对我的项目没有实质性的作用,不出意外肯定能够配置成功,所以此处不严谨地把LED地报警代码删除了。currentPowerState = PCM->CTL0 & PCM_CTL0_CPM_MASK;//获取电压状态语句
if (currentPowerState != PCM_CTL0_CPM_0) error();//判断MSP432P401R的电压等级现在是否是AM0_LDO,如果此时没有处在AM0_LDO, 将会执行error函数,提示用户电压等级目前配置异常。
while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));//判断此时电源模式转换是否忙,忙的话执行空循环,直到电源模式转换完成
PCM->CTL0 = PCM_CTL0_KEY_VAL | PCM_CTL0_AMR_1;
while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));//判断此时电源模式转换是否忙,忙的话执行空循环,直到电源模式转换完成
if (PCM->IFG & PCM_IFG_AM_INVALID_TR_IFG)
error(); // Error if transition was not successful
if ((PCM->CTL0 & PCM_CTL0_CPM_MASK) != PCM_CTL0_CPM_1)
error(); // Error if device is not in AM1_LDO mode
其中PCM是个什么东西??
找到官方的Datasheet【MSP432P401R, MSP432P401M SimpleLink™ Mixed-Signal Microcontrollers datasheet (Rev. H)】的第121页提到PCM(Power Control Manager)控制设备的工作模式和模式之间的切换。这是由应用程序控制的,它可以选择模式来满足其功率和性能要求。
在参数手册【MSP432P4xx SimpleLink™ Microcontrollers Technical Reference Manual (Rev. I)】中详细给出了PCM(Power Control Manager)的各个控制寄存器,具体内容如下:
CPM(Current Power Mode)电源模式。这些位反映当前电源模式,并将在电源模式请求完成后更新。
PMR_BUSY(Power mode request busy flag)。电源模式请求忙位。此标志是在处理功率更改请求时设置的。当请求完成时,标志将被清除。清楚了这些寄存器表示的功能后,再对官方的代码进行注释。
PCMKEY(Current Power Mode KEY)。参数手册的解释是必须编写适当的密钥以允许对PMR的写访问。任何不正确的密钥都不允许写入PMR,也不会生成功率更改请求。PCMCTL0和PCMCTL1都有PCMKEY位。
AMR(Active Mode Request)。用于请求活动模式。只有当PCMCTL1的PMR_BUSY = 0时,才能修改这些位。
接下来是PCMIFG(PCM Interrupt Flag Register),电源控制管理中断标志寄存器。
LPM_INVALID_TR_IFG(LPM invalid transition flag),如果请求的活动模式到LPM3的转换无效,则设置此标志。标志将保持设置,直到软件清除。简单的理解就是转换不成功将会把这个标志变量置1。
步骤二:配置Flash等待状态到1
//将存储体0和1的Flash等待状态都配置为1
FLCTL->BANK0_RDCTL = (FLCTL->BANK0_RDCTL & ~(FLCTL_BANK0_RDCTL_WAIT_MASK)) | FLCTL_BANK0_RDCTL_WAIT_1;
FLCTL->BANK1_RDCTL = (FLCTL->BANK0_RDCTL & ~(FLCTL_BANK1_RDCTL_WAIT_MASK)) | FLCTL_BANK1_RDCTL_WAIT_1;
Flash等待状态?????What are you talking about??为什么要配置??这有什么关系么??在【MSP432P4xx SimpleLink™ Microcontrollers Technical Reference Manual (Rev. I)】参数手册中,官方做出了如下解释:
FLCTL(Flash Controller)闪存是字节、字(4字节)和全字(16字节)可寻址和可编程的。flash控制器是软件(应用程序)与设备上flash存储器所支持的各种功能之间的控制和访问接口,一堆废话是不是。。。。。
接下来我们看看代码中提到的具体寄存器地详细信息:
在该页的底部,有不起眼的小字,但是它非常重要,具体翻译如下:
- 读取所需的等待状态的数量取决于总线时钟频率和读取模式。
- 如果总线时钟频率被更改为更高的值,则由应用程序负责确保在频率更改之前更改等待状态值。如果没有执行这个命令,设备的行为就不是确定的。
- 只有FLCTL_RDBRST_CTLSTAT的burst status(17:16)显示为Idle状态时,该字段才可写。在所有其他情况下,位保持锁定状态,以不中断正在进行的操作。
简单地来说就是时钟频率很高时,这个Flash的状态必须要提高,因为不更改Flash的状态的话,设备的运行状态将会不确定。
步骤三:配置数控时钟振荡频率到48MHz
//步骤3:将DCO配置为48MHz,确保MCLK使用DCO作为源
CS->KEY = CS_KEY_VAL ; // 解锁CS模块以进行寄存器访问
CS->CTL0 = 0; // 重置调整参数
CS->CTL0 = CS_CTL0_DCORSEL_5; // Set DCO to 48MHz
/* Select MCLK =SMCLK= DCO=48MHz, no divider */
CS->CTL1 = CS_CTL1_SELS_3 | CS_CTL1_SELM_3;
CS->KEY = 0; // 锁定CS模块以防止意外访问
CSKEY(Clock Status Key)时钟状态密匙:Write CSKEY = xxxx_695Ah解锁CS寄存器。所有16个lsb需要一起编写。使用任何其他值写入CSKEY会导致CS寄存器被锁定,对这些寄存器的任何写操作都会被忽略,而读操作仍然被执行。总是回读A596h。
简单地来说,只有输入密匙正确才能够对CS寄存器进行配置。
解锁CS寄存器后,接下来将DCO的时钟配置到48MHz的区间段。
CS->CTL0 = CS_CTL0_DCORSEL_5;
接下来将MCLK,SMCLK的时钟源配置为DCO=48MHz的时钟运行。
CS->CTL1 = CS_CTL1_SELS_3 | CS_CTL1_SELM_3;
CS->KEY = 0; // 锁定CS模块以防止意外访问
步骤四:确保主时钟MCLK的时钟源来自DCO
确保MCLK时钟是否来自DCO=48MHz最快速的方法是运用MSP432P401R的引脚复用功能,将IO引脚复用作为MCLK的时钟输出。查阅官方参数手册【MSP432P401R, MSP432P401M SimpleLink™ Mixed-Signal Microcontrollers datasheet (Rev. H)】的第150页的引脚配置表格如下:
P4DIR |= BIT3; //将P4.3作为MCLK的输出引脚
P4SEL0 |= BIT3;
P4SEL1 &= ~BIT3;
当然Px口的寄存器不止这些,只是本次项目只用到了这些寄存器而已。根据程序,首先P4.3设置为输出,将P4SEL0的第3位置1,将P4SEL1的第3位置0,这样一来,上电运行后P4.3就会输出48MHz时钟,不信??见下图:
示波器采用正点原子DS100,50M带宽,250M采样率,可以观察到手持示波器显示的是48MHz,时钟配置正确,MCLK确实是运行在48MHz上的。
以下给出我消化官方的例子后精简的代码,已经去除LED的时钟配置异常报警,因为这个几乎不会出现,干脆就删了,嘿嘿。经过烧录,完全有效:
#include "ti/devices/msp432p4xx/inc/msp.h"
void system_clk_init() //将运行频率锁定在48MHz,开启步骤原因详见:http://dev.ti.com/tirex/explore/node?node=AIt11.GE8nvJkpoVjCiItQ__z-lQYNj__LATEST
{
P4DIR |= BIT3; //将P4.3作为MCLK的输出引脚
P4SEL0 |= BIT3;
P4SEL1 &= ~BIT3;
while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));//判断此时电源模式转换是否忙,忙的话执行空循环,直到电源模式转换完成
PCM->CTL0 = PCM_CTL0_KEY_VAL | PCM_CTL0_AMR_1;
while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));//判断此时电源模式转换是否忙,忙的话执行空循环,直到电源模式转换完成
//将存储体0和1的Flash等待状态都配置为1
FLCTL->BANK0_RDCTL = (FLCTL->BANK0_RDCTL & ~(FLCTL_BANK0_RDCTL_WAIT_MASK)) | FLCTL_BANK0_RDCTL_WAIT_1;
FLCTL->BANK1_RDCTL = (FLCTL->BANK0_RDCTL & ~(FLCTL_BANK1_RDCTL_WAIT_MASK)) | FLCTL_BANK1_RDCTL_WAIT_1;
//步骤3:将DCO配置为48MHz,确保MCLK使用DCO作为源
CS->KEY = CS_KEY_VAL ; // 解锁CS模块以进行寄存器访问
CS->CTL0 = 0; // 重置调整参数
CS->CTL0 = CS_CTL0_DCORSEL_5; // Set DCO to 48MHz
/* Select MCLK =SMCLK= DCO=48MHz, no divider */
CS->CTL1 = CS_CTL1_SELS_3 | CS_CTL1_SELM_3;
CS->KEY = 0; // 锁定CS模块以防止意外访问
}