工具与软件:
我有一个在启动时默认进入 LPM3的主循环。 我还配置了一个计时器(TB2)、以每2秒唤醒 CPU 大约10ms。 一个相应的 UART (eUSCI_A1)中断会将设置 enter_LPM3标志、该标志要么将 CPU 从 LPM3唤醒、要么将其返回到 LPM3。
由于 LPM3中禁用了 SMCLK 和 UART、因此我预计 CPU 能够捕获'w'(唤醒)命令的唯一方法是命令在10ms 窗口期间到达。 这只是每2秒10毫秒–大约是0.5%的时间。 逻辑上,这应该失败的大部分时间(99.5%),但在实践中,我的代码神奇地工作100%的时间。 我可以将 CPU 置入 LPM3或随时将其唤醒、而且我绝不会错过该'w'命令。
除非我错译了自己的代码、否则我猜是 CPU 在 LPM3中、而 SMCLK/UART 据称是关闭的 接收移位寄存器 仍然可能会移入 UART 数据。 然后、只要 CPU 在该10ms 窗口内唤醒并启用 SMCLK、数据就会显示在中 UCAxRXBUF并触发中断。 否则、我无法解释为什么我的代码能够如此可靠地工作。 希望有人能告诉我这一点。
我不确定是否假设代码是必要的,但这里是它只是在情况下:
我的主循环:
// Beginning of my main loop
// 'enter_LPM3' is initialized to be true
// Main loop
while(1)
{
// 'enter_LPM3' is initialized to be true, meaning we enter LPM3 after power up
if (enter_LPM3)
{
// (LPM3) Small delay to increase the time-window in catching 'w' UART command (~10ms @ 1MHz MCLK)
// before entering LPM3.
__delay_cycles(10000);
P1OUT &= ~BIT0; // LED Off
// Check enter_LPM3 again before entering LPM3 because the UART ISR may set 'enter_LPM3' to false
// with a 'w' wake-up command
if (enter_LPM3)
{
__bis_SR_register(LPM3_bits | GIE); // Enter LPM3 (Wake up by Timer_B2)
}
}
// Other work
}
我的计时器和计时器 ISR:
// (LPM3) Configure Timer2_B3 (TB2)
void configure_tb2_wakeup(void)
{
TB2CTL |= TBCLR; // (TB2) Clear Timer_B2
TB2CTL |= TBSSEL__ACLK; // (TB2) Source Timer_B2 from ACLK (32.768 kHz)
//TB2CTL |= ID__1; // (TB2) No clock division
//TB2EX0 |= TBIDEX__1; // (TB2) No extra division
//TB2CCR0 = 327; // (TB2) Set wake-up interval (~10ms: 327 / 32.768kHz ≈ 10ms)
//TB2CCR0 = 32768; // (TB2) Set wake-up interval (~1s: 32768 / 32.768kHz ≈ 1s)
//TB2CCR0 = 65535; // (CANNOT USE - OVERFLOW!)(TB2) Set wake-up interval (~2s: 32.768kHz / 65536 ≈ 2s)
// Because TB2CCR0 is a 16-bit counter that has a maximum value of 65535 and overflows at 65536,
// we can use the clock divider to slow down ACLK in order to achieve a more accurate 2s interrupt
TB2CTL |= ID__4; // (TB2) Divide ACLK by 4 (32.768 kHz / 4 = 8.192 kHz)
TB2EX0 |= TBIDEX__8; // (TB2) Further divide by 8 (8.192 kHz / 8 = 1.024 kHz)
TB2CCR0 = 2048; // (TB2) Set wake-up interval (~2s: 2048 / 1.024 kHz = ~2s interval
TB2CCTL0 |= CCIE; // (TB2) Enable Timer_B2 interrupt
TB2CTL |= MC__UP; // (TB2) Set Timer_B2 to UP mode
}
#pragma vector = TIMER2_B0_VECTOR
__interrupt void ISR_TB2_Wakeup(void)
{
// Wake up the CPU if it's in LPM3. Otherwise, do nothing (CPU is already awake).
if (enter_LPM3)
{
P1OUT |= BIT0; // LED On
__bic_SR_register_on_exit(LPM3_bits); // Wake up from LPM3 (not immediately but after we exit this ISR)
}
}
UART 和 UART ISR:
void configure_uart(void)
{
UCA1CTLW0 |= UCSWRST; // (eUSCI_A1)(UART) hold eUSCI_A1 in SW reset
UCA1CTLW0 |= UCSSEL__SMCLK; // (eUSCI_A1)(UART) clock source: use SMCLK 1MHz
UCA1BRW = 8; // (eUSCI_A1)(UART) 115200 baud (UG-p.589) <-- neg error
UCA1MCTLW |= 0xD600; // (eUSCI_A1)(UART) 115200 baud (UG-p.589) <-- neg error
P4SEL1 &= ~BIT3; // (eUSCI_A1)(UART) P4.3/TXD (P4SEL1.3=0)
P4SEL0 |= BIT3; // (eUSCI_A1)(UART) P4.3/TXD (P4SEL0.3=1)
P4SEL1 &= ~BIT2; // (eUSCI_A1)(UART) P4.2/RXD (P4SEL1.2=0)
P4SEL0 |= BIT2; // (eUSCI_A1)(UART) P4.2/RXD (P4SEL0.2=1)
UCA1CTLW0 &= ~UCSWRST; // Take UART out of reset
}
#pragma vector = EUSCI_A1_VECTOR
__interrupt void ISR_EUSCI_A1(void)
{
if((UCA1IFG & UCRXIFG) != 0)
{
char rx_char = UCA1RXBUF; // Read UART input
if(rx_char == 'w') // UCRXIFG is cleared automatically when UCA1RXBUF is read
{
P1OUT |= BIT0; // LED On
enter_LPM3 = false; // (LPM3) Stays awake
__bic_SR_register_on_exit(LPM3_bits); // (LPM3) * Forces wake-up immediately *
}
else if(rx_char == 's')
{
P1OUT &= ~BIT0; // LED Off
enter_LPM3 = true; // (LPM3) Let main.c enters LPM3
}
}
// UART transmit the byte read from the SPI
if((UCA1IFG & UCTXCPTIFG) != 0)
{
// See INFO.txt
if(position_UART == ((sizeof(read_result)/2) - 1)) // if all characters in a string has completed its transmission
{
UCA1IE &= ~UCTXCPTIE; // (eUSCI_A1)(UART) disable UCTXCPTIE IRQ
}
else // more characters to be sent
{
position_UART++;
UCA1TXBUF = read_result[position_UART];
}
UCA1IFG &= ~UCTXCPTIFG; // (eUSCI_A1)(UART) clear UCTXCPTIFG IFG
}
}