工具与软件:
我有一个在启动时默认进入 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 } }