Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить

Re: TI MSP430 Launch Pad

Чт июл 25, 2013 19:54:11

С кодом "while ((P1IN & BIT3) == 0) ;" в обработке прерывания нажатия кнопки, работает прекрасно.

Но дальше я захотел пойти дальше и проверить свою теорию, а именно организовать временную задержку, используя второй таймер (это чисто из интереса). Проблема - при отладке работает, а в реальности зависает при первом же нажатии на кнопке. :?

Сделал простейший проект, где по второму таймеру меняется состояние светодиодов - работает прекрасно.
Прочитал мануал про таймеры А и В - не нашёл никакого предложения, говорящего о нюансах их совместной работе. Яндекс помучал - не нашёл. Плохо ищу.

В чём проблема, не подскажете?

Re: TI MSP430 Launch Pad

Чт июл 25, 2013 22:20:28

Код:
while ((P1IN & BIT3) == 0);


Более классически можно записать так:

Код:
while (!(P1IN & BIT3));


"!" - логическое отрицание (его следует отличать от побитового - "~"). Если его аргумент не ноль, он превратит его в ноль. Если ноль - превратит в не ноль. :) А все условия в С проверяются на не_ноль/ноль.

В чём проблема, не подскажете?


Видимо, в архитектуре взаимодействия двух прерываний.

Re: TI MSP430 Launch Pad

Чт июл 25, 2013 22:34:51

YS писал(а):Видимо, в архитектуре взаимодействия двух прерываний.

вот полный код, проверено многократно - при отладке работает, при реальном полёте зависает при первом же нажатии на кнопку. Может, подскажете на какую-то некорректность. :?
Спойлер#include "io430g2553.h"

unsigned int maxPause=0x1F;//FFF;
unsigned int maxDelitel = 16;
unsigned int i_time = 0, i_delitel = 1;

void timer_run();

int main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;

//готовим порт с диодам
P1DIR = (BIT0 + BIT6); //инициализируем выходы 0 и 6
P1OUT = BIT6; //ставим выход 6 в 1

//готовим таймер
CCTL0 = CCIE; //разрешаем прерывание по переполнению счётчика-таймера A /* Timer0_A3 Capture/Compare Control 0 */
//используются три системных тактовых сигнала: ACLK, MCLK и SMCLK и INCLK (INCLK is device-specific)
TACTL = (TASSEL_2 + MC_2 + ID_0); //ставим источник тактов на SMCLK, и режим таймера - continuous, и делитль =1 (1 2 4 8)
__enable_interrupt(); //глобально разрешаем прерывания в status register

//готовим кнопку
P1REN |= BIT3; //разрешаем подтяжку
P1OUT |= BIT3; //подтяжка вывода P1.3 вверх, иначе будет всегда срабатывать if ((P1IN & BIT3) == 0)

P1IFG &= ~BIT3; // Очистка флага прерываний для P1.3
// P1IES |= BIT3; // Прерывание происходит по 1/0 (отпусканию/нажатию)
P1IE |= BIT3; //разрешаем прерывание для P1.3

while(1);
}

//прерывание по таймеру используем для вкл/выкл с/диодов
#pragma vector = TIMER0_A0_VECTOR //приоритет 0xFFF0
__interrupt void TIMER0_A0_PRE (void)
{
i_time += i_delitel;
if (i_time >= maxPause)
{
P1OUT ^= (BIT0 + BIT6);
i_time = 0;
}
}

//прерывание по кнопке используем для игр с паузой
#pragma vector = PORT1_VECTOR //приоритет 0xFFE4
__interrupt void PORT1_VECTOR_PRE (void)
{
//это вариант от YS
//while (!(P1IN & BIT3));

P1IE &= ~BIT3; // Запрет прерываний на P1.3
if (i_delitel<=maxDelitel) i_delitel *= 2;
else i_delitel = 1;
P1IFG &= ~BIT3; // Очистка флага прерываний для P1.3, т.к. это прерывание имеет несколько флагов

//испоьзуем таймер B для борьбы с дребезгом
TA1CCTL0 = CCIE; //разрешаем прерывание по переполнению счётчика-таймера B/* Timer1_A3 Capture/Compare Control 0 */
TA1CTL = (TASSEL_2 + MC_2 + ID_2); //ставим источник тактов на SMCLK, и режим таймера - continuous, и делитль =1 (1 2 4 8)

//комментируем для работы с таймером В
//P1IE |= BIT3; // Разрешение прерываний на P1.3
}

#pragma vector = TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_PRE (void)

{
P1IE |= BIT3; // Разрешение прерываний на P1.3
TA1CTL = 0; //останов таймера B
}

Re: TI MSP430 Launch Pad

Чт июл 25, 2013 22:52:08

TA1CCTL0 = CCIE; //разрешаем прерывание по переполнению счётчика-таймера B


Неверно. CCIE разрешает прерывание от блока сравнения. За прерывание от переполнения отвечает бит TAIE в регистре TACTL. Стр. 368 - 370 User's Guide.

Re: TI MSP430 Launch Pad

Пн июл 29, 2013 14:30:19

YS [добрался до MSP430] да, я неправильно вставил комментарии.

Теперь работают два таймера, только одно НО: до одной точки прерывания [часто] выполнение программы доходит 2 раза, скриншот прилагаю. У меня только одно соображение - таймер останавливается не сразу? Если я неправ, то каким образом выполнение программы доходит до точки прерывания (на скриншоте отмечен) 2 раза?
Изображение

Re: TI MSP430 Launch Pad

Пн июл 29, 2013 18:51:35

Э-э-э, это надо смотреть в пошаговом режиме. Так я, честно говоря, не совсем понял, в чем дело.

Re: TI MSP430 Launch Pad

Пн июл 29, 2013 20:54:04

Возня вознёй с отладкой проблемы (суть: 2 раза запускается процедура прерывания таймера В и 2 раза - [почти полностью :) ] процедура прерывания нажатия кнопки), привела к:
- источник проблемы исходит из процедуры обработки прерывания нажатия кнопки;
- в этой процедуре прерывания нажатия кнопки интересно: точка остановки срабатывает 2 раза, только если поставил эту самую точку останова на:
Код:
TA1CCTL0 = CCIE;
или следующих строчках.
Спойлер//используем таймер B для борьбы с дребезгом
TA1CCTL0 = CCIE; //Разрешение прерывания захвата/сравнения счётчика-таймера B/* Timer1_A3 Capture/Compare Control 0 */
TA1CTL = (TASSEL_2 + MC_2 + ID_2); //ставим источник тактов на SMCLK, и режим таймера - continuous, и делитель =4 (1 2 4 8)

Отсюда напрашивается вывод: прерывание таймера В вызывается, когда разрешаешь прерывание этого самого таймера. Я не прав?

Re: TI MSP430 Launch Pad

Пн июл 29, 2013 21:39:53

Какой отладчик используете?

Re: TI MSP430 Launch Pad

Вт июл 30, 2013 08:55:43

YS писал(а):Отсюда напрашивается вывод: прерывание таймера В вызывается, когда разрешаешь прерывание этого самого таймера. Я не прав?

Не должно.
У вас нет таймера В...у вас два таймера А.
В эррате на ваш кристалл ТА16 говорится, что после TACLR вызывается дополнительное прерывание. Попробуйте остановить таймер MC=0, не трогая TACLR.

PS Че то глюканула цитата с автором :dont_know:

Re: TI MSP430 Launch Pad

Сб авг 03, 2013 13:42:17

Всю тему не читал, наверняка уже был такой вопрос:
Можно ли этот лаунчпад использовать тупо как переходник USB - UART?
И если да то куда подключаться для приема данных с него?

Re: TI MSP430 Launch Pad

Сб авг 03, 2013 21:55:39

Можно ли этот лаунчпад использовать тупо как переходник USB - UART?


Да. Подключаться к пинам, помеченным как RXD и TXD.

Изображение

Пинхедер J3. Перемычки, естесственно, надо снять.

Re: TI MSP430 Launch Pad

Сб авг 03, 2013 21:57:03

Да, можно. Нужно только правильно выставить 2 джампера на плате. Именно, этими джамперами можно подключть преобразователь к МК нз плате, или наоборот отключить от него. В последнем случае преобразователь можно будет использовать и с внешними устройствами вне платы. Какие именно
джамперы написано в инструкции пользователя для платы.

Re: TI MSP430 Launch Pad

Вт авг 13, 2013 20:44:42

Добрался до MSP430 (прошу прощенья за паузу).

Дело я 29го июля закончил тем, что заместо кода:
Код:
  //используем таймер B для борьбы с дребезгом
  TA1CCTL0 = CCIE; //Разрешение прерывания захвата/сравнения счётчика-таймера B/* Timer1_A3 Capture/Compare Control 0 */
  TA1CTL = (TASSEL_2 + MC_2 + ID_2); //ставим источник тактов на SMCLK, и режим таймера - continuous,  и делитль =4 (1 2 4 8)

я записал нехитрый код (с объявлением временной "экспериментальной" глобальной bool переменной b):
Код:
  //используем таймер B для борьбы с дребезгом
  b = true;
  TA1CCTL0 = CCIE; //Разрешение прерывания захвата/сравнения счётчика-таймера B/* Timer1_A3 Capture/Compare Control 0 */
  TA1CTL = (TASSEL_2 + MC_2 + ID_2); //ставим источник тактов на SMCLK, и режим таймера - continuous,  и делитль =4 (1 2 4 8)
  b = false;

, обработчик таймера B (или "второй таймер А", как подсказывает Psych) выглядит таким образом:
Код:
#pragma vector = TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_PRE (void)

  if (!(P1IN & BIT3)) return;
  if (b==true) return; // вот с этим стало нормально работать :)
 
  P1IE |= BIT3; // Разрешение прерываний на P1.3
  TA1CTL = 0; //останов таймера B
}


Это всё работает нормально, что как-бэ :))) подтвержало тогда мою догадку. 8) И я тогда, с мыслью о сделанной работе, успокоился :).

Теперь вот, [вернувшись в эту ветку,] попробую предворить в жизнь слова пользователя Psych: "В эррате на ваш кристалл ТА16 говорится, что после TACLR вызывается дополнительное прерывание. Попробуйте остановить таймер MC=0, не трогая TACLR".

P.S. Отладчик использую IAR EW for MSP430 IDE 5.51.6, скачанный с официального сайта. Тип лицензии - бесплатный, с ограничением ИК 4кб.

Re: TI MSP430 Launch Pad

Пт авг 16, 2013 14:14:44

Вот рабочий пример обработки кнопок и борьбы с дребезгом. Различаются короткое и длинное нажатие кнопок. В качестве таймера задержки на нажатие/отпускание используется сторожевой таймер WDT. Легко делается на любое количество кнопок. Идея взята отсюда: http://bennthomsen.wordpress.com/ti-msp430-launchpad/using-the-switches/
Спойлер
Код:
//******************************************************************************
// Обработка кнопок с определением короткого и длинного нажатия.
// Антидребезг реализован с помощью задержки на нажатие/отпускание кнопки.
// Определение состояния кнопок происходит по прерываниям на портах i/o.
// Длительность нажатия определяется по счетчику в интервальном таймере
// (в данном примере каждые 4ms). Задержка выполнена на строжевом таймере (WDT)
// переводимом на это время в режим интервального таймера.
// Выполнение действий назначенных на кнопки осущесвляется в основном цикле
// программы.
//******************************************************************************

#include "msp430g2553.h"
#include <stdint.h>         // Standard integer types

#define     LED_1   BIT6                // Зеленый светодиод
#define     LED_2   BIT0                // Красный светодиод
#define     S1      BIT3                // Кнопка 1
#define     S2      BIT4                // Кнопка 2

#define LONG_PRESS_TIME 700             // Продолжительность нажатия клавиши определяемая как "долгое нажатие"

volatile uint8_t Pressed = 0;           // Флаг состояния кнопки
volatile uint8_t ButtonPress = 0;       // Флаг нажатия кнопки (содержит установленный бит нажатой кнопки)
volatile uint8_t LongPress = 0;         // Флаг длинного нажатия кнопки
volatile uint16_t PressCount = 0;       // Счетчик времени нажатия


// Обработчик прерывания от PORT1
#pragma vector = PORT1_VECTOR
__interrupt void Port1_ISR(void)
{
    uint8_t btn;                                // Буфер для P1IFG
   
    if(P1IFG & S1 || P1IFG & S2)
    {
        btn = P1IFG;
        P1IE &= ~(S1 + S2);                     // Запрещаем прерывания от кнопки
        WDTCTL = WDT_ADLY_16;                   // Запуск сторожевого таймера (WDT) в режиме интервального  с периодом 48ms
        IFG1 &= ~WDTIFG;                        // Очистка флага прерывания WDT
        IE1 |= WDTIE;                           // Разрешаем прерывания от WDT
        if (P1IES & btn)
        { // Если спадающий фронт
            Pressed |= btn;                     // Устанавливаем флаг нажатия кнопки
            PressCount = 0;                     // Сбрасываем счетчик длинного нажатия
        }
        else
        { // Если нарастающий фронт
            if (PressCount < LONG_PRESS_TIME)   // Если короткое нажатие на кнопку
            {
                ButtonPress = btn;
            }
            Pressed = 0;                        // Сброс флага состояния кнопки
        }
        P1IES ^= btn;                           // Прерывание по нарастающему фронту
        P1IFG &= ~(S1 + S2);                    // Сброс флага прерывания кнопки
        btn = 0;                                // Обнуляем буфер
    }
}

// Обработчик прерываний от Timer A1
#pragma vector = TIMER1_A0_VECTOR
__interrupt void TimerA1_ISR(void) {

    if (Pressed)                                // Проверка, нажата ли кнопка
    {
        if (++PressCount == LONG_PRESS_TIME)    // Зафиксировано длительное нажатие 700*4ms = ~3s
        {
            ButtonPress = Pressed;              // Устанавливаем флаг нажатой кнопки
            LongPress = 1;                      // Устанавливаем флаг длительного нажатия кнопки
            Pressed = 0;                        // Сброс флага состояния кнопки
        }
    }
}

// Обработчик прерываний от таймера WDT
#pragma vector = WDT_VECTOR
__interrupt void WDT_ISR(void)
{
    IE1 &= ~WDTIE;                      // Запрет прерываний от WDT
    IFG1 &= ~WDTIFG;                    // Очистка флага прерывания WDT
    WDTCTL = WDTPW + WDTHOLD;           // Отключение таймера WDT
    P1IFG &= ~(S1 + S2);                // Сброс флага прерывания кнопки
    P1IE |= S1 + S2;                    // Разрешаем прерывания от кнопки
}

void main(void) {

    WDTCTL = WDTPW + WDTHOLD;           // WatchDog off
    BCSCTL1 = CALBC1_16MHZ;             // 16MHz clock
    DCOCTL = CALDCO_16MHZ;
    BCSCTL2 |= DIVS_3;                  // SMCLK = DCO / 8 = 2MHz
    BCSCTL3 |= LFXT1S_2;                // Set ACLK -> VLOCLK = 12kHz

    // *** led ***
    P1DIR |= LED_1 + LED_2;
    P1OUT &= ~(LED_1 + LED_2);

    // *** button ***
    P1DIR &= ~(S1 + S2);                // Установка вывода кнопки как входного
    P1OUT |= S1 + S2;                   // Подтяжка к питанию
    P1REN |= S1 + S2;                   // Включение подтягивающего резистора
    P1IES |= S1 + S2;                   // Прерывание по спадающему фронту
    P1IE |= S1 + S2;                    // Разрешаем прерывание
    P1IFG = 0x00;                       // Очистка регистра прерываний

        // *** Timer1 ***
    TA1CTL = TASSEL_2 + ID_3 + MC_1 + TACLR;    // SMCLK/8, upmode
    TA1CCR0 = 1000;                             // sample every 4ms
    TA1CCTL0 = CCIE;                            // CCR0 interrupt enabled

    __bis_SR_register(GIE);                     // Разрешаем прерывания

    while(1)
    {
        if (ButtonPress) {                      // Если была нажата кнопка
            switch (ButtonPress & (S1 + S2))
            {
                case S1:                        // Нажата S1
                    if (LongPress) {            // Если длинное нажатие
                        LongPress = 0;          // Сбрасываем флаг длительного нажатия
                        P1OUT ^= LED_2;         // Переключаем состояние выхода на противоположное
                    } else {                    // Если короткое нажатие
                        P1OUT ^= LED_1;         // Переключаем состояние выхода на противоположное
                    }
                    break;
                case S2:                        // Нажата S2
                    if (LongPress) {
                        LongPress = 0;          // Сбрасываем флаг длительного нажатия
                        // тут что-то делаем если длинное нажатие
                    } else {
                        // тут что-то делаем если короткое нажатие
                    }
                    break;
            }
            ButtonPress = 0;                    // Сбрасываем флаг нажатия кнопки
        }
    }
}

То же, но без использования дополнительного таймера задержки:
Спойлер
Код:
//******************************************************************************
// Обработка кнопок с определением короткого и длинного нажатия.
// Антидребезг реализован с помощью задержки на нажатие/отпускание кнопки.
// Определение состояния кнопок происходит по прерываниям на портах i/o.
// Длительность времени задержки и нажатия определяются по счетчикам
// в интервальном таймере (в данном примере каждые 4ms).
// Выполнение действий назначенных на кнопки осущесвляется в основном цикле
// программы.
//******************************************************************************

#include "msp430g2553.h"
#include <stdint.h>         // Standard integer types

#define     LED_1   BIT6                // Зеленый светодиод
#define     LED_2   BIT0                // Красный светодиод
#define     S1      BIT3                // Кнопка 1
#define     S2      BIT4                // Кнопка 2

#define LONG_PRESS_TIME 700             // Продолжительность нажатия клавиши определяемая как "долгое нажатие"
#define LOCK_PRESS_TIME 10              // Продолжительность блокироки нажатия кнопки

volatile uint8_t Pressed = 0;           // Флаг состояния кнопки
volatile uint8_t ButtonPress = 0;       // Флаг нажатия кнопки (содержит установленный бит нажатой кнопки)
volatile uint8_t LongPress = 0;         // Флаг длинного нажатия кнопки
volatile uint8_t LockPress = 0;         // Флаг блокировки нажатия кнопки
volatile uint8_t LockPressCount = 0;    // Счетчик времени блокировки нажатия
volatile uint16_t PressCount = 0;       // Счетчик времени нажатия


// Обработчик прерывания от PORT1
#pragma vector = PORT1_VECTOR
__interrupt void Port1_ISR(void)
{
    uint8_t btn;                                // Буфер для P1IFG
   
    if(P1IFG & S1 || P1IFG & S2)
    {
        btn = P1IFG;                            // Состояние регистра P1IFG в буфер
        P1IE &= ~(S1 + S2);                     // Запрещаем прерывания от кнопки
        LockPressCount = 0;                     // Обнуляем счетчик времени блокировки кнопки
        LockPress = 1;                          // Устанавливаем флаг блокировки нажатия клавиши
        if (P1IES & btn)
        { // Если спадающий фронт
            Pressed |= btn;                     // Устанавливаем флаг нажатия кнопки
            PressCount = 0;                     // Сбрасываем счетчик длинного нажатия
        }
        else
        { // Если нарастающий фронт
            if (PressCount < LONG_PRESS_TIME)   // Если короткое нажатие на кнопку
            {
                ButtonPress = btn;
            }
            Pressed = 0;                        // Сброс флага состояния кнопки
        }
        P1IES ^= btn;                           // Прерывание по нарастающему фронту
        P1IFG &= ~(S1 + S2);                    // Сброс флага прерывания кнопки
        btn = 0;                                // Обнуляем буфер
    }
}

// Обработчик прерываний от Timer A1
#pragma vector = TIMER1_A0_VECTOR
__interrupt void TimerA1_ISR(void) {

    if (LockPress)                          // Если установлен флаг блокировки кнопки
    {
        if (++LockPressCount == LOCK_PRESS_TIME) // Если время блокировки истекло (10*4ms = 40ms)
        {
            P1IFG &= ~(S1 + S2);            // Сброс флага прерывания кнопки
            P1IE |= S1 + S2;                // Разрешаем прерывания от кнопки
            LockPress = 0;                  // Сбрасываем флаг блокировки нажатия кнопки
        }
    }

    if (Pressed)                            // Проверка, нажата ли кнопка
    {
        if (++PressCount == LONG_PRESS_TIME)    // Зафиксировано длительное нажатие 700*4ms = ~3s
        {
            ButtonPress = Pressed;          // Устанавливаем флаг нажатой кнопки
            LongPress = 1;                  // Устанавливаем флаг длительного нажатия кнопки
            Pressed = 0;                    // Сброс флага состояния кнопки
        }
    }
}

void main(void) {

    WDTCTL = WDTPW + WDTHOLD;           // WatchDog off
    BCSCTL1 = CALBC1_16MHZ;             // 16MHz clock
    DCOCTL = CALDCO_16MHZ;
    BCSCTL2 |= DIVS_3;                  // SMCLK = DCO / 8 = 2MHz

    // *** led ***
    P1DIR |= LED_1 + LED_2;
    P1OUT &= ~(LED_1 + LED_2);

    // *** button ***
    P1DIR &= ~(S1 + S2);                // Установка вывода кнопки как входного
    P1OUT |= S1 + S2;                   // Подтяжка к питанию
    P1REN |= S1 + S2;                   // Включение подтягивающего резистора
    P1IES |= S1 + S2;                   // Прерывание по спадающему фронту
    P1IE |= S1 + S2;                    // Разрешаем прерывание
    P1IFG = 0x00;                       // Очистка регистра прерываний

        // *** Timer1 ***
    TA1CTL = TASSEL_2 + ID_3 + MC_1 + TACLR;    // SMCLK/8, upmode
    TA1CCR0 = 1000;                             // sample every 4ms
    TA1CCTL0 = CCIE;                            // CCR0 interrupt enabled

    __bis_SR_register(GIE);                     // Разрешаем прерывания

    while(1)
    {
        if (ButtonPress) {                      // Если была нажата кнопка
            switch (ButtonPress & (S1 + S2))
            {
                case S1:                        // Нажата S1
                    if (LongPress) {            // Если длинное нажатие
                        LongPress = 0;          // Сбрасываем флаг длительного нажатия
                        P1OUT ^= LED_2;         // Переключаем состояние выхода на противоположное
                    } else {                    // Если короткое нажатие
                        P1OUT ^= LED_1;         // Переключаем состояние выхода на противоположное
                    }
                    break;
                case S2:                        // Нажата S2
                    if (LongPress) {
                        LongPress = 0;          // Сбрасываем флаг длительного нажатия
                        // тут что-то делаем если длинное нажатие
                    } else {
                        // тут что-то делаем если короткое нажатие
                    }
                    break;
            }
            ButtonPress = 0;                    // Сбрасываем флаг нажатия кнопки
        }
    }
}

Без испльзования прерываний от кнопок, дополнительного таймера задержки и антидребезгом на сдвиговом регистре:
Спойлер
Код:
//******************************************************************************
// Обработка кнопок с определением короткого и длинного нажатия.
// Антидребезг реализован с помощью сдвигового регистра путем сравнения
// нескольких последовательных состояний. Определение состояния кнопок
// происходит по прерыванию интервального таймера (в данном примере каждые 4ms).
// Выполнение действий назначенных на кнопки осущесвляется в основном цикле
// программы.
//******************************************************************************

#include "msp430g2553.h"
#include <stdint.h>         // Standard integer types

#define     LED_1   BIT6                // Зеленый светодиод
#define     LED_2   BIT0                // Красный светодиод
#define     S1      BIT3                // Кнопка 1
#define     S2      BIT4                // Кнопка 2

// Константы для сравнения состояния сдвигового регистра
#define PRESS_THRESHOLD     0x3F        // Пороговое значение регистра при нажатии кнопки
#define RELEASE_THRESHOLD   0xFC        // Пороговое значение регистра при отпускании кнопки

#define LONG_PRESS_TIME     700         // Время определяемое как длительное нажатие кнопки (700*4ms = ~ 3s)

uint8_t LongPress = 0;                  // флаг длительного нажатия кнопки
volatile uint8_t ButtonPress = 0;       // Бит нажатой кнопки


// - - - - - - - - - - - - - - - - - - - - -
// PRESS_THRESHOLD = 0x3F = 0 b00111111
// RELEASE_THRESHOLD = 0xFC = 0 b11111100
// - - - - - - - - - - - - - - - - - - - - -
#pragma vector = TIMER1_A0_VECTOR
__interrupt void Timer1_A(void)
{
    uint8_t BtnMask = 0;                        // Бит нажатой кнопки
    static uint16_t PressCount = 0;             // Время нажатия кнопки
    static uint8_t BtnState = 0;                // Флаг устйчивого состояния кнопки (после дребезга)
    static uint8_t Pressed = 0;                 // Флаг - кнопка была нажата
    static uint8_t ShiftReg = 0xFF;             // Сдвиговый регистр для хранения состояния кнопки
   
    ShiftReg >>= 1;                             // Сдвигаем содержимое регистра на 1 вправо
   
    if (!(P1IN & S1)) BtnMask = S1;             // Опрос входов кнопок
    if (!(P1IN & S2)) BtnMask = S2;
   
    if (!BtnMask) {                             // Если ни одна кнопка не нажата (или пропал контакт во время дребезга)
        ShiftReg |= BIT7;                       // Устанавливаем в 1 старший разряд сдвигового регистра
    }

    if (BtnState == 1) {                            // Проверяем статус кнопки - если нажата
        if (!(PressCount >= LONG_PRESS_TIME)){      // Если счетчик уже досчитал, то ничего не деламе, иначе...
            if (++PressCount >= LONG_PRESS_TIME) {  // Увеличиваем счетчик и если досчитал
                LongPress = 1;                      // Устанавливаем флаг длинного нажатия
                ButtonPress = Pressed;              // И бит нажатой кнопки
            }
        }
        // Проверяем не отжата ли кнопка
        if (ShiftReg >= RELEASE_THRESHOLD) {    // Если кнопка отжата
            BtnState = 0;                       // Устанавливаем статус кнопки в отжата (0)
        }
    } else {                                    // Если статус кнопки - отжата
        if (Pressed) {                          // Но было зафиксировано нажатие
            if (PressCount < LONG_PRESS_TIME) { // И если время нажатия меньше чем заданное как длительное
                LongPress = 0;                  // Сбрасываем флаг длинного нажатия
                ButtonPress = Pressed;          // Сохраняем бит нажатой кнопки
            }
            PressCount = 0;                     // Обнуляем счетчик длинного нажатия
            Pressed = 0;                        // Обнуляем флаг нажатой кнопки
        }
        // Проверяем не нажата ли кнопка
        if (ShiftReg <= PRESS_THRESHOLD) {      // Если кнопка нажата
            BtnState = 1;                       // Устанавливаем статус кнопки в нажата (1)
            Pressed = BtnMask;                  // Сохраняем бит нажатой кнопки
        }
    }
}

void main (void)
{
    WDTCTL = WDTPW | WDTHOLD;           // Останавливаем сторожевой таймер
    BCSCTL1 = CALBC1_16MHZ;             // 16MHz clock
    DCOCTL = CALDCO_16MHZ;
    BCSCTL2 |= DIVS_3;                  // SMCLK = DCO / 8 = 2MHz

    // *** led ***
    P1DIR |= LED_1 + LED_2;
    P1OUT &= ~(LED_1 + LED_2);

    // *** button ***
    P1DIR &= ~(S1 + S2);                // Установка вывода кнопки как входного
    P1OUT |= S1 + S2;                   // Подтяжка к питанию
    P1REN |= S1 + S2;                   // Включение подтягивающего резистора

    // *** Timer1 ***
    TA1CTL = TASSEL_2 + ID_3 + MC_1 + TACLR;    // SMCLK/8, upmode
    TA1CCR0 = 1000;                             // sample every 4ms
    TA1CCTL0 = CCIE;                            // CCR0 interrupt enabled

    __bis_SR_register(GIE);                     // Разрешаем прерывания
   
    while(1)
    {
        if (ButtonPress) {                      // Если была нажата кнопка
            switch (ButtonPress & (S1 + S2))
            {
                case S1:                        // Нажата S1
                    if (LongPress) {            // Если длинное нажатие
                        P1OUT ^= LED_2;         // Переключаем состояние выхода на противоположное
                    } else {                    // Если короткое нажатие
                        P1OUT ^= LED_1;         // Переключаем состояние выхода на противоположное
                    }
                    break;
                case S2:                        // Нажата S2
                    if (LongPress) {

                    } else {

                    }
                    break;
            }
            ButtonPress = 0;                    // Сбрасываем флаг нажатия кнопки
        }
    }
}

Re: TI MSP430 Launch Pad

Пт авг 16, 2013 18:29:59

Хорошая подборка идей! От себя добавлю, что основной цикл в приведенных реализациях молотит все время и сводит на нет все преимущества обработки нажатий и обработки дребезга через прерывания. Лучше отправлять МК в сон. Кроме того, определенным недостатком программ является частая обработка прерываний от таймера - каждые 4 мс даже если кнопки не нажаты.

Улучшить ситуацию можно несколько модифицировав алгоритм. Именно, разрешать прерывания по совпадению в самом начале обработки дребезга (получения первого прерывания от кнопки) и запрещать их по окончании обработки (в момент когда опять разрешаются прерывания от кнопки). Моменты совпадения нужно будет каждый раз пересчитывать в прерывании по совпадению (добалять одну и ту-же константу к счетчику), что не проблема. Так сделано, например, в моей конструкции
http://radiokot.ru/circuit/digital/measure/71/
В этой реализации, правда, каждый канал совпадения таймера обслуживает только одну кнопку. Но это не сильное ограничение, поскольку в современных моделях семейства MSP430 имеется несколько таймеров и у каждого 3-7 каналов сравнения.

Re: TI MSP430 Launch Pad

Сб авг 17, 2013 07:18:51

Согласен. Если нужен экономичный режим, то вы предлагаете хороший вариант. Передо мной такая задача не стояла. Эти примеры просто заточены под мой проект лабораторного БП, поэтому и 16МГц и 4мс (там не только кнопки), это я думаю каждый настроит под себя. Перепробовав много способов борьбы с дребезгом, я остановился на самом первом варианте (с задержкой на таймере WDT), из тех примеров, что я привел на мой взгляд он менее всего загружает контроллер.
Вот еще один рабочий код обработки кнопок с защитой от дребезга (это мне на easyelectronics.ru ShadS предложил):
Спойлер
Код:
//******************************************************************************
//
// Пример работы с кнопками
//
// Определение короткого и длинного нажатия. Антидребезг реализован с помощью
// задержек на нажатие/отпускание кнопок.
// Организовать вызов из прерывания с частотой 100Гц - функцию BtnExe();
// Чтение значения состояния флагов кнопок производится с помощью
// глобальной переменной BtnFlags например так (в главном цикле):
// if (BtnFlags) {
//      if (BtnMask & BTN_SHRT_S1) {....ветка короткого нажатия кнопки S1}
//      if (BtnMask & BTN_SHRT_S2) {....ветка короткого нажатия кнопки S1}
//      if (BtnMask & BTN_LONG_S1) {....ветка длинного нажатия кнопки S1}
//      и т.д.
//      BtnFlags = 0    //Обнулить байт флагов нажатия кнопок
//  }
//
//******************************************************************************

#include "msp430g2553.h"
#include <stdint.h>         // Standard integer types

#define     LED_1   BIT6                // Зеленый светодиод
#define     LED_2   BIT0                // Красный светодиод
#define     S1      BIT3                // Кнопка 1
#define     S2      BIT4                // Кнопка 2

//настройка параметров работы функций
#define BTN_LOCK_TIME   30              //время обработки дребезга в милисекундах (10-100)
#define BTN_LONG_TIME   1000            //время фиксации длинного нажатия в милисекундах (1000 - 2500)

//глобальные переменные
volatile uint8_t BtnFlags;              //байт флагов нажатия кнопки
#define BTN_SHRT_S1         (1<<0)      //бит короткого нажатия кнопки S1
#define BTN_SHRT_S2         (1<<1)      //бит короткого нажатия кнопки S2
#define BTN_LONG_S1         (1<<2)      //бит длинного нажатия кнопки S1
#define BTN_LONG_S2         (1<<3)      //бит длинного нажатия кнопки S2

void BtnExe (void);


// Обработчик прерываний от Timer A1
#pragma vector = TIMER1_A0_VECTOR
__interrupt void TimerA1_ISR(void) {

    BtnExe();
}

void main(void) {

    WDTCTL = WDTPW + WDTHOLD;           // WatchDog off
    BCSCTL1 = CALBC1_1MHZ;              // 1MHz clock
    DCOCTL = CALDCO_1MHZ;

    // *** led ***
    P1DIR |= LED_1 + LED_2;
    P1OUT &= ~(LED_1 + LED_2);

    // *** button ***
    P1DIR &= ~(S1 + S2);                // Установка вывода кнопки как входного
    P1OUT |= S1 + S2;                   // Подтяжка к питанию
    P1REN |= S1 + S2;                   // Включение подтягивающего резистора

        // *** Timer1 ***
    TA1CTL = TASSEL_2 + ID_3 + MC_1 + TACLR;    // SMCLK/8, upmode
    TA1CCR0 = 2000;                             // sample every 10ms
    TA1CCTL0 = CCIE;                            // CCR0 interrupt enabled

    __bis_SR_register(GIE);                     // Разрешаем прерывания

    while(1)
    {
        if (BtnFlags) {                         // Если была нажата кнопка
            if (BtnFlags & BTN_SHRT_S1) {       // Ветка короткого нажатия кнопки S1
                P1OUT ^= LED_1;                 // Переключаем состояние выхода на противоположное
            }
            if (BtnFlags & BTN_SHRT_S2) {       // Ветка короткого нажатия кнопки S2

            }
            if (BtnFlags & BTN_LONG_S1) {       // Ветка длинного нажатия кнопки S1
                P1OUT ^= LED_2;                 // Переключаем состояние выхода на противоположное
            }
            if (BtnFlags & BTN_LONG_S2) {       // Ветка длинного нажатия кнопки S2

            }
            BtnFlags = 0;
        }
    }
}

//----------
//ФУНКЦИЯ ОБРАБОТКИ НАЖАТИЙ КЛАВИШ (вызывать в прерывании с частотой 100 Гц)
//короткое нажатие устанавливает бит BTN_SHRT_X глобальной переменной BtnFlags
//длинное нажатие устанавливает бит BTN_LONG_X глобальной переменной BtnFlags
void BtnExe (void)
{   
    static uint8_t BtnLockBit;            //защелка (защита от дребезга)
    static uint8_t BtnLockCoun;           //счетчик защелки (защита от дребезга)
    static uint8_t BtnLongCoun;           //счетчик длинного нажатия
    static uint8_t BtnLastState;          //последнее состояние кнопок перед отпусканием

    uint8_t mask = 0;
    if (!(P1IN & S1)) mask = BTN_SHRT_S1;
    if (!(P1IN & S2)) mask = BTN_SHRT_S2;
   
    if (mask){                                      //опрос состояния кнопки
        if (BtnLockCoun < (BTN_LOCK_TIME/10)){      //клавиша нажата
            BtnLockCoun++;
            return;                                 //защелка еще не дощитала - возврат
        }
        BtnLastState = mask;
        BtnLockBit =1;                              //нажатие зафиксировано               
        if (BtnLongCoun >= (BTN_LONG_TIME/10))                               
            return;                                 //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше       
        if (++BtnLongCoun >= (BTN_LONG_TIME/10))
            BtnFlags |= (BtnLastState<<2);          //счетчик досчитал до максимума - устанавливаем биты длинного нажатия
    }           
    else{                                           //клавиша отжата           
        if (BtnLockCoun){
            BtnLockCoun --;
            return;                                 //защелка еще не обнулилась - возврат
        }
        if (! BtnLockBit)                           //СТАТИЧЕСКИЙ ВОЗВРАТ
            return;                               
        BtnLockBit =0;                              //отжатие зафиксировано
        if (BtnLongCoun < (BTN_LONG_TIME/10))
            BtnFlags |= BtnLastState;               //установка бита короткого нажатия
        BtnLongCoun = 0;                            //сброс счетчика длительности нажатия
    }
}

Re: TI MSP430 Launch Pad

Сб авг 17, 2013 19:43:58

Опять-таки, прерывание происходит каждые 10 мс независимо от того нажата кнопка или нет. Если это прерывание еще для чего-то нужно (как у Вас в проекте) то сойдет. В противном случае процессор будет слишком часто и понапрасну отвлекаться на обработку прерывания.

Я всегда делаю обработку нажатий кнопок на основе прерываний порта, регистра сдвига, и отключаемых прерываний каналов сравнения таймера. Сейчас заканчиваю проект с участием кнопок, где вышеупомянутый алгоритм адаптирован с FR57xx для MSP430G2332. Выложу его сюда на форум с исходником скорее всего после конкурса. Кстати, Bujhm666, может напишите сюда статью про Ваш блок питания или дадите ссылку если она уже опубликована?

Re: TI MSP430 Launch Pad

Вс авг 18, 2013 11:34:07

Ser60 писал(а):Сейчас заканчиваю проект с участием кнопок, где вышеупомянутый алгоритм адаптирован с FR57xx для MSP430G2332

Ну можно было бы только код обработки кнопок выложить. Желательно на Си, если можно конечно.
Насчет моего блока питания, статью не напишу (не силен я в этом), а код и схему покажу, как доведу до ума (он хоть уже в корпусе и работает, но пока в стадии доводки). В схемотехнике там ничего нового, просто вместо обычных здесь пиков или меги управление от MSP430. Вот фотографии я выкладывал на схем.нет http://forum.cxem.net/index.php?showtopic=41521&st=1560#entry1622650

Re: TI MSP430 Launch Pad

Пн авг 19, 2013 08:05:26

Bujhm666 писал(а):просто вместо обычных здесь пиков или меги управление от MSP430


Вот это и похвально. Не понимаю, чего остальные ждут. Вообще, конструкция выглядит добротной. Насчет моего кода, он на АСМе. Вместо того, чтобы вырезать куски кода, вот ссылка на весь код:
http://mcs.uwsuper.edu/sb/Electronics/BigLCD/clock.s43

Re: TI MSP430 Launch Pad

Пн авг 19, 2013 10:29:55

Читаю здесь о ЦАП
Приложения, нуждающиеся в генерации периодических колебаний, могут получить преимущества от использования контроллера DMA с ЦАП12. К примеру, приложение, вырабатывающее синусоидальное колебание может сохранит значения синуса в таблице. Контроллер DMA может непрерывно автоматически переносить эти значения в ЦАП12 через заданные интервалы, создавая синусоиду без участия ЦПУ

Не подскажете, где бы поглядеть рабочий пример по работе ЦАП и DMA?
Ответить