器件型号:MSP430f6779
主题中讨论的其他器件:MSPWARE、MSP-EXP430F5529、MSP430F6779A、MSP-FET
问:我想检测电源电压下降并将数据写入内部闪存。
实际遇到的问题是,我的代码会在上电时而不是断电时进入 ISR。
以下是我的代码
#include <msp430.h>
/*
* main.c
*/
void write_SegC(void);
void copy_from_FlashC(void);
void PMM_config(void);
void UCS_config(void);
char Flash_Data[20] = {0,0,3,4,5,6,7,8,0,0,0,5,5,5,0,8,8,9,2,3};
char Flash_Data1[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // 停止看门狗计时器
P1DIR |= BIT0;
P1REN |= BIT6;
UCS_config();
PMM_config();
UCS_config();
while(1)
{
copy_from_FlashC();
}
return 0;
}
void copy_from_FlashC(void)
{
unsigned int i;
char *Flash_ptrC;
// char *Flash_ptrD;
Flash_ptrC = (char *) 0x1880; // 初始化闪存段 C ptr
// Flash_ptrD = (char *) 0x1800; // 初始化闪存段 D ptr
// Flash_ptrD = (char*) Flash_Data[0];
FCTL3 = FWKEY; // 清除锁定位
// FCTL1 = FWKEY | ERASE; // 设置擦除位
// *Flash_ptrD = 0; // 虚拟写入以擦除闪存段 D
// FCTL1 = FWKEY | WRT; // 为写入操作设置 WRT 位
FCTL1 = FWKEY;
FCTL4 = FWKEY | MGR0 ;
for (i = 0; i < 20; i++)
{
// *Flash_ptrD++ = *Flash_ptrC++; // 将值段 C 复制到段 D
Flash_Data1[i] = *Flash_ptrC++;
}
FCTL1 = FWKEY; // 清除 WRT 位
FCTL3 = FWKEY | LOCK; // 设置锁定位
}
void write_SegC(void)
{
P1OUT |= BIT0;
unsigned int i;
char *Flash_ptr; // 初始化闪存指针
Flash_ptr = (char *)0x1880;
FCTL3 = FWKEY; // 清除锁定位
FCTL1 = FWKEY | ERASE; // 设置擦除位
*Flash_ptr = 0; // 虚拟写入以擦除闪存段
FCTL1 = FWKEY | WRT; // 为写入操作设置 WRT 位
for (i = 0; i < 20; i++)
{
*Flash_ptr++ = Flash_Data[i]; // 将值写入闪存
}
FCTL1 = FWKEY; // 清除 WRT 位
FCTL3 = FWKEY | LOCK; // 设置锁定位
P1OUT &= ~BIT0;
}
void PMM_config(void)
{
//解锁 PMM
PMMCTL0_H=PMMPW_H;
PMMCTL0_L|=PMMCOREV_3;
//检查电压电平
switch(PMMCTL0&PMMCOREV_3)
{
//最高内核电压设置
case PMMCOREV_3:
//设置高侧监控器和监测器
SVSMHCTL=SVMHE|SVSHE|SVSHRVL_3|SVSMHRRL_7;
break;
}
//清除中断标志
PMMIFG&=~(SVMLIFG|SVMHIFG|SVMHVLRIFG|SVMLVLRIFG);
//设置中断
PMMRIE|=SVMLIE|SVMHIE|SVMHVLRIE|SVMLVLRIE;
//锁定 PMM
PMMCTL0_H=0;
}
#pragma vector = SYSNMI_VECTOR
__interrupt void SYS_NMI(void)
{
switch(SYSSNIV)
{
//内核电源电压监测器中断
case SYSSNIV_SVMLIFG:
//报告错误的事件
//设置标志
break;
//输入电源电压监测器中断
case SYSSNIV_SVMHIFG:
write_SegC();
// Clear_SegC(value); // 仅用于测试
//报告错误的事件
//设置标志
break;
//内核电源电压监测器延迟中断
case SYSSNIV_DLYLIFG:
break;
//中断电源电压监测器延迟中断
case SYSSNIV_DLYHIFG:
break;
//空内存访问中断
case SYSSNIV_VMAIFG:
break;
//JTAG 邮箱输入中断
case SYSSNIV_JMBINIFG:
break;
//JTAG 邮箱输出中断
case SYSSNIV_JMBOUTIFG:
break;
//SVMLVLRIFGSVMHVLRIFG
case SYSSNIV_VLRLIFG:
//清除中断标志位
//解锁 PMM
PMMCTL0_H=PMMPW_H;
//清除中断标志
PMMIFG&=~(SVMLIFG|SVMLVLRIFG);
//锁定 PMM
PMMCTL0_H=0;
break;
//SVMHVLRIFGSVMHVLRIFG
case SYSSNIV_VLRHIFG:
//清除中断标志位
//解锁 PMM
PMMCTL0_H=PMMPW_H;
//清除中断标志
PMMIFG&=~(SVMHIFG|SVMHVLRIFG);
//锁定 PMM
PMMCTL0_H=0;
break;
}
}
void UCS_config(void)
{
PMMCTL0_H = 0xA5;
PMMCTL0_L = 0x03;
UCSCTL0 = UCSCTL0 | 0x1F00;
UCSCTL1 = UCSCTL1 & 0x0000;
UCSCTL1 = UCSCTL1 | 0x0040;
UCSCTL2 = UCSCTL2 & 0x0000 ; // FLL 分频器
UCSCTL2 = UCSCTL2 | 0x304F ; // 对于 20MHz MCLK 和 2.6MHz SMCLK
UCSCTL3 = 0x0000;
UCSCTL4 = UCSCTL4 & 0X0000;
UCSCTL4 = UCSCTL4 | 0X0043;
}
答:在我的代码中(见下文),当 DVCC 达到或低于 2.7V 时,LED 会闪烁;超过 2.7V 时,LED 将一直亮着。接下来,我将介绍代码的工作方式。
当电路板初次上电时,DVCC 设置为 3.0V。然后,使用外部工作台电源将 DVCC 降低至 2.7V(或稍微更低,但高于 2.4V 限值)。当 SVMHIFG 发生高电平(SVSMHRRL 指定的电平)触发时,将进入 ISR 并手动设置一个标志。此标志会指示 main() 开始使 LED 闪烁。在您的设计中,您可以使用此标志指示是否出现电源故障。请注意,在 ISR 中读取中断矢量 SYSSNIV 时,SVMHIE 被禁用(如用户指南中的表 2-22 所述)。在我意识到这一点之前,我的代码只会检测电源电压的第一次下降。如图 2-5 所示,禁用的 SVMHIE 会阻止 SVMH 中断发生,从而阻止进入 ISR!SVMHIE 稍后将重新启用。
接下来,我将 DVCC 从 2.7V 重新增大到 3.0V。这将触发 SVMHVLRIFG,并会重新进入 ISR。此时将会打开 PMM 寄存器以进行写入访问,清除 SVMHIFG 和 SVMHVLRIFG,重新启用 SVMHIE 和 SVMHVLRIE,关闭 PMM 寄存器,并手动设置一个标志以显示 SVMH 已复位。现在,LED 停止闪烁并一直亮着。该操作可不断重复。
此外,无论是否连接了 MSP-FET,此代码都能正常工作。如有任何问题,请告诉我,我希望这有助于您快速入门。
#include "driverlib.h"
uint16_t status = STATUS_SUCCESS;
static volatile int isr_was_entered = 0;
void UCS_config(void);
void PMM_config(void);
void main(void) {
//停止 WDT
WDT_A_hold(WDT_A_BASE);
//将 VCore 更改为 3 级,此函数负责处理增量步骤
status = PMM_setVCore(PMMCOREV_3);
//设置 UCS
UCS_config();
//配置 PMM SVMH 以检测 DVCC 压降,SVMH 在 2.7V 时触发
PMM_config();
//将 P1.0 (LED) 设置为输出方向
P1DIR |= 0x01;
while(1) {
//如果在 2.7V 时触发 SVMH,则开始使 LED 闪烁
if(isr_was_entered == 4) {
//切换 LED
P1OUT ^= 0x01;
__delay_cycles(800000);
}
//如果不是,仅使 LED 常亮
else {
//当 DVCC 高于 SVMH 时打开 LED
P1OUT |= 0x01;
}
}
}
void UCS_config(void) {
UCSCTL0 = UCSCTL0 | 0x1F00;
UCSCTL1 = UCSCTL1 & 0x0000;
UCSCTL1 = UCSCTL1 | 0x0040;
UCSCTL2 = UCSCTL2 & 0x0000 ; //FLL 分频器
UCSCTL2 = UCSCTL2 | 0x304F ; //对于 20MHz MCLK 和 2.6MHz SMCLK
UCSCTL3 = 0x0000;
UCSCTL4 = UCSCTL4 & 0X0000;
UCSCTL4 = UCSCTL4 | 0X0043;
}
void PMM_config(void) {
uint16_t PMMRIE_backup, SVSMHCTL_backup, SVSMLCTL_backup;
//检查 VCore 电平
switch(PMMCTL0&PMMCOREV_3) {
//最高 VCore 电压的设置
case PMMCOREV_3:
//用于增加 Vcore 的代码流已被更改以便解决
//错误 FLASH37。
//请参阅勘误表以了解是否特定器件受到影响
//请勿更改此函数
//打开 PMM 寄存器以进行写入访问
HWREG8(PMM_BASE + OFS_PMMCTL0_H) = 0xA5;
//备份原始寄存器设置并禁用中断
PMMRIE_backup = HWREG16(PMM_BASE + OFS_PMMRIE);
HWREG16(PMM_BASE + OFS_PMMRIE) &= ~(SVMHVLRPE | SVSHPE | SVMLVLRPE |
SVSLPE | SVMHVLRIE | SVMHIE |
SVSMHDLYIE | SVMLVLRIE | SVMLIE |
SVSMLDLYIE
);
SVSMHCTL_backup = HWREG16(PMM_BASE + OFS_SVSMHCTL);
SVSMLCTL_backup = HWREG16(PMM_BASE + OFS_SVSMLCTL);
//清除中断标志
HWREG16(PMM_BASE + OFS_PMMIFG) = 0;
//根据 VCore 将 SVM 高侧设置为新电平,SVSMRRL_5 = 2.7V(典型值)
//请参阅 MSP430F6779 数据表中的第 71 页以了解更多信息
//HWREG16(PMM_BASE + OFS_SVSMHCTL) = SVMHE | SVSHE | SVSMHRRL_5;
HWREG16(PMM_BASE + OFS_SVSMHCTL) = SVMHE | SVSMHRRL_5;
//等到 SVM 高侧稳定
while((HWREG16(PMM_BASE + OFS_PMMIFG) & SVSMHDLYIFG) == 0) {
;
}
//清除所有标志
HWREG16(PMM_BASE + OFS_PMMIFG) &= ~(SVMHVLRIFG | SVMHIFG | SVSMHDLYIFG |
SVMLVLRIFG | SVMLIFG | SVSMLDLYIFG
);
//恢复 PMM 中断使能寄存器并启用 SVM 高侧中断
HWREG16(PMM_BASE + OFS_PMMRIE) = PMMRIE_backup | SVMHVLRIE | SVMHIE;
//锁定 PMM 寄存器以进行写入访问
HWREG8(PMM_BASE + OFS_PMMCTL0_H) = 0x00;
break;
}
}
#pragma vector = SYSNMI_VECTOR
__interrupt void SYSNMI_ISR(void) {
//根据中断执行某项操作
switch(SYSSNIV) {
//内核电源电压监测器中断
case SYSSNIV_SVMLIFG:
//报告错误的事件
//设置标志
break;
//输入电源电压监测器中断
case SYSSNIV_SVMHIFG:
//write_SegC();
//Clear_SegC(value); // 仅用于测试
//设置标志以显示 SVMH 已被触发
isr_was_entered = 4;
//报告错误的事件
//设置标志
break;
//内核电源电压监测器延迟中断
case SYSSNIV_DLYLIFG:
break;
//中断电源电压监测器延迟中断
case SYSSNIV_DLYHIFG:
break;
//空内存访问中断
case SYSSNIV_VMAIFG:
break;
//JTAG 邮箱输入中断
case SYSSNIV_JMBINIFG:
break;
//JTAG 邮箱输出中断
case SYSSNIV_JMBOUTIFG:
break;
//SVMLVLRIFG
case SYSSNIV_VLRLIFG:
break;
//SVMHVLRIFG
case SYSSNIV_VLRHIFG:
//打开 PMM 寄存器以进行写入访问
HWREG8(PMM_BASE + OFS_PMMCTL0_H) = 0xA5;
//清除 SVMH 中断标志
HWREG16(PMM_BASE + OFS_PMMIFG) &= ~(SVMHIFG | SVMHVLRIFG);
//重新启用 SVMH 中断标志(必须在清除中断后完成)
HWREG16(PMM_BASE + OFS_PMMRIE) |= SVMHIE | SVMHVLRIE;
//锁定 PMM 寄存器以进行写入访问
HWREG8(PMM_BASE + OFS_PMMCTL0_H) = 0x00;
//设置标志以显示 SVMH 已复位
isr_was_entered = 3;
break;
}
}