This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSP430G2553:当启用 LPM4时、启用按钮中断中的计时器

Guru**** 2380860 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1389943/msp430g2553-enabling-timers-in-button-interrupt-when-lpm4-enabled

器件型号:MSP430G2553

工具与软件:

大家好!

在中断中激活定时器时出现问题、此问题在微控制器处于 LPM4时发生。

在我的主函数中、我启用低功耗模式以节省能源、直到用户按下按钮。 之后、我进入了按钮的 ISR、想激活一个计时器以提供待机功能。 遗憾的是、我无法激活计时器、因为微控制器仍处于低功耗模式。

我在 ISR 中尝试了函数_BIC_SR_register 来立即停用低功耗模式、但这样无效、因为在完成 ISR 后、程序会在 LPM4中再次跳转。

我还尝试了_BIC_SR_register_on_exit 函数。 这样可以正确地停用 LPM、但只是当 ISR 完成并且我想激活 ISR 中的计时器时、所以计时器不会以这种方式激活。

我已将代码放在下方、且要激活和停用 LPM4的部分中注释为"DEEPSLEEP"

你们中有人能帮我解决问题吗?

非常感谢。

此致、

Timo Kurz

 

#include <msp430.h>
#include <stdint.h>
#include "asciizeichen.h"
#include <stdio.h>
#include <string.h>

// Definitions for Nokia 5110 display
#define LCD_RST BIT2
#define LCD_DC BIT4
#define LCD_DIN BIT5
#define LCD_CLK BIT6
#define LCD_CE BIT1
#define LCD_LED BIT7

#define SW BIT3                             // ToDo: change to correct activation bit - VORSICHT!!!! AKTUELL NOCH PORT1 STATT PORT2
#define OUT BIT0                            // ToDo: change to correct output bit
#define SENSOR_PIN (BIT1 | BIT2)

#define STANDBY_TIMEOUT 32768                   // Timer for standby modus
#define DEBOUNCE_DELAY 5000                 // debounce Timer

#define CHARGING 1000000                    // Value for recognizing if device is plugged in
#define MAX_TEMP 120                         // Value for safety overheat shutdown
#define MIN_VOLTAGE 0                        // Value for safety shutdown if battery is empty


volatile uint16_t adc_result[3] = {0};      // Variable to store ADC results
volatile uint16_t current = 0;              // Variable for actual charging current
volatile uint16_t voltage = 0;              // Variable for actual battery voltage
volatile uint16_t temperature = 0;          // Variable for actual temperature
static int started = 0;


void delay(unsigned int t);
void LCD_write_byte(unsigned char data, unsigned char mode);
void LCD_init();
void LCD_clear();
void LCD_string(char *characters);
void LCD_char(char character);
void ausgabe_string(char *str1, double kommazahl);


void main(void) {

    double zahl = 0.0;
    int zaehler = 0, i = 0;

    WDTCTL = WDTPW | WDTHOLD;               // Stop watchdog timer

    P1DIR |= OUT;                           // Set Output pin -> Output
    P1DIR &= ~SW;                           // Set SW pin -> Input
    P1REN |= SW;                            // Enable Resistor for SW pin
    P1OUT |= SW;                            // Select Pull Up for SW pin
    P1OUT &= ~OUT;                          // Disable Output at start

    P1IES |= SW;                            // Select Interrupt on Falling Edge
    P1IE |= SW;                             // Enable Interrupt on SW pin

    // configure ADC
    ADC10CTL1 = INCH_2 + CONSEQ_3;          // Channel select A2 + sequence of channels
    ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON + ADC10IE;    // REF = VCC & VSS + ADC SampleAndHoldTime 16 x ADC10CLKs  + + //
    ADC10AE0 |= SENSOR_PIN;                 // These bits enable the PIN 1.1 & P1.2 for analog input
    P1SEL |=  SENSOR_PIN;                   // ADC input select mode P1.1 & P1.2

    // Timer0 to interrupt after 10 seconds
    TA0CCR0 = STANDBY_TIMEOUT / 8;              // 32.768 kHz crystal oscillator, divided by 8
    TA0CTL = TASSEL_1 + MC_0 + ID_3;        // ACLK (32.768 kHz), Stop mode, Divider 8
    TA0CCTL0 |= CCIE;

    // Timer1 for debouncing
    TA1CCTL0 |= CCIE;                       // Enable Timer1 interrupt
    TA1CCR0 = DEBOUNCE_DELAY;               // Set debounce delay
    TA1CTL = TASSEL_2 + MC_0 + TACLR;       // SMCLK, Stop mode, clear timer

    __bis_SR_register(GIE);                 // Enable global interrupts

    while(1)
    {
        if(started)
        {
            LCD_init();
            P1OUT |= OUT;                   // Toggle Output Pin 1.0
            P1OUT |= LCD_LED;               // Backlight on
            LCD_clear();

            while (started) {
                char str1[20] = "Zahl: ";
                ausgabe_string(str1, zahl);
                LCD_string("     ");
                LCD_string("Funktioniert  ");

                for (i = 0; i <= zaehler; i++) {
                    LCD_char(128);
                }

                zaehler += 1;
                if (zaehler >= 5) {
                    zaehler = 0;
                }

                zahl += 0.1;
                if (zahl >= 10.0) {
                    zahl = 0.0;
                }

                start_ADC_conversion();     // Start the ADC conversion
                __delay_cycles(1000000);    // one sec delay
            }
        }
        // ToDo: value if device is currently charging
        else if(current > CHARGING)         // State if device Timeout and device is currently charging
        {
            P1OUT &= ~LCD_LED;              // Backlight off
            LCD_clear();                    
        }
        else
        {
            P1OUT &= ~LCD_LED;              // Backlight off
            LCD_clear();
            P1OUT &= ~OUT;                  // Turn off Output
            // __bis_SR_register(LPM4_bits + GIE); // ToDo: deepsleep
        }
    }
}

void start_ADC_conversion() {
    ADC10CTL1 = INCH_10 + ADC10DIV_3;       // Select channel A10 for temperature, divide by 4
    ADC10CTL0 = SREF_1 + ADC10SHT_2 + ADC10ON + ADC10IE + REFON + REF2_5V; // Internal reference, 2.5V, ADC on, enable interrupt
    __delay_cycles(1000);                   // Wait for reference to settle
    ADC10CTL0 |= ADC10SC + ENC;             // Start conversion
    while (ADC10CTL1 & ADC10BUSY);          // Wait for conversion to finish
    adc_result[0] = ADC10MEM;               
    temperature = ((adc_result[0] - 673) * 423) / 1024; // Convert ADC value to temperature in Celsius
    if(temperature > MAX_TEMP)               // overheat protection
    {
        P1OUT &= ~OUT;                      // Turn off Output
        // ToDo: Response, for overheating
    }
    ADC10CTL0 &= ~ENC;                      // Disable conversion
    ADC10CTL0 &= ~REFON;                    // Turn off reference to save power

    ADC10CTL1 = INCH_2;                     // select channel A2 as input
    ADC10CTL0 |= ADC10SC + ENC;
    while (ADC10CTL1 & ADC10BUSY);
    adc_result[1] = ADC10MEM;               
    voltage = adc_result[1];                // ToDo: conversion to actual value
    // ToDo: safety shutdown if battery is empty
    // if(voltage < MIN_VOLTAGE)
    // {
    //     P1OUT &= ~OUT;                  // Turn off Output
    //     // ToDo: Response, for empty battery
    // }
    ADC10CTL0 &= ~ENC;

    ADC10CTL1 = INCH_1;                     // select channel A1 for ADC + Conversion sequence mode select ==  Sequence-of-channels    + ACLK - clock (no input div)
    ADC10CTL0 |= ADC10SC + ENC;             // ADC10SC == 0 >> no sample start conversation + ENC == EnableConversation
    while (ADC10CTL1 & ADC10BUSY);
    adc_result[2] = ADC10MEM;               
    current = adc_result[2];                // ToDo: conversion to actual current
    ADC10CTL0 &= ~ENC;                      // disable conversation
}

#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void) {
    ADC10CTL0 &= ~ADC10IFG;                 // Clear interrupt flag
}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void) {
    P1IE &= ~SW;                            // Disable button interrupt
    P1IFG &= ~SW;                           // Clear SW interrupt flag
    started = 1;
    // __bic_SR_register(LPM4_bits);        // ToDo: Wakeup from deepsleep
    TA0CTL |= MC_1;                         // Start Timer0 in up mode
    TA1CTL |= TASSEL_2 + MC_1;              // Start Timer1 for debouncing
}

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0_ISR(void) {
    TA0CTL &= ~MC_1;                        // Stop Timer0
    started = 0;
    TA0CCTL0 &= ~CCIFG;
}

#pragma vector=TIMER1_A0_VECTOR
__interrupt void Timer_A1_ISR(void) {
    TA1CTL &= ~MC_1;                        // Stop Timer1
    P1IE |= SW;                             // Re-enable button interrupt
    TA1CTL |= TACLR;                        // Clear Timer1
    TA1CCTL0 &= ~CCIFG;                     // Clear Timer1 interrupt flag
}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您似乎并不了解低功耗模式的工作原理。

    可以通过摆弄 STATUS 寄存器中的位来设置低功耗模式。 发生中断时、系统退出低功耗模式(更改 SR)、同时将当前 SR 中的值保存到堆栈中。

    执行从中断返回指令后、将从堆栈上的值恢复状态寄存器。 返回到中断之前的任何模式。 除非您已使用 ON_EXIT 宏更改了堆栈上的数据。

    然后从设置低功耗模式后中断的位置开始执行。 您可以在 ISR 中启动一个计时器、如果 ISR 的时钟正在运行、它将在 ISR 退出时继续运行。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 David:

    我认为您完全正确。 我不明白如何正确编码此函数。

    如上所述、我启动了计时器、并在完成 ISR 后使用 ON_EXIT 宏来退出 LPM。 采用该设置时、计时器不起作用、代码永远不会进入计时器中断。 在不启用 LPM 的情况下、计时器使用相同的代码工作。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    1) SMCLK (TASSEL=2)不在 LPM0以下运行。 LPM4中没有运行的定时器。

    2)如果 main ()不在 LPM 中(它不被记住),唤醒(_BIC_SR_Register_on_exit(LPM_mumble))是无效的。 可以随时按下按钮、包括当 main ()处于活动状态时。

    我认为很难解开这个问题。 我建议您在 LPM3模式下睡眠、并使用 ACLK (在 LPM3模式下运行)运行去抖计时器。

    未经请求:当开关完成反弹时、几乎可以确定 P1IFG:SW 位是否已设置。 我建议您在重新启用 P1IE:SW 之前将其清除。