TI MSP430 Launch Pad

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт июл 04, 2013 15:15:22

Сообщение VLeshka »

Psych писал(а):Введите хотя бы паузу в PORT1_VECTOR
спасибо, проблема решена. :)

И ещё вопрос. Как я понял, самый цивилизованный метод решения - это делать паузу используя второй таймер (предварительно запретив прерывания от кнопки), в срабатывании которого разрешается прерывания от кнопки. Так?
Реклама
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

делать паузу используя второй таймер
Это обычно избыточно. Я чаще всего делаю так (код - в псевдо-именах):

Код: Выделить всё

#define MY_PORT_IN   ...
//битовая маска кнопки
#define MY_BUTTON    ...


...


while (MY_PORT_IN & MY_BUTTON) //если кнопка подключена к питанию
{
  delay_ms(10);
}

Т.е., просто ждем, пока кнопку не отпустят. Ну и хорошо бы еще и аппаратный debounce добавить. Тот же конденсатор параллельно кнопке.

Код: Выделить всё

#pragma vector = TIMER0_A0_VECTOR //приоритет 0xFFF0
__interrupt void TIMER0_A0_PRE (void)
{ 
 //
 __disable_interrupt(); //глобально запрещаем прерывания в status register

...
Этого не нужно. При входе в прерывание остальные запрещаются автоматически.

User's Guide, стр. 35:
2.2.3.1 Interrupt Acceptance
The interrupt latency is 5 cycles (CPUx) or 6 cycles (CPU), starting with the acceptance of an interrupt
request and lasting until the start of execution of the first instruction of the interrupt-service routine, as
shown in Figure 2-6. The interrupt logic executes the following:
1. Any currently executing instruction is completed.
2. The PC, which points to the next instruction, is pushed onto the stack.
3. The SR is pushed onto the stack.
4. The interrupt with the highest priority is selected if multiple interrupts occurred during the last
instruction and are pending for service.
5. The interrupt request flag resets automatically on single-source flags. Multiple source flags remain set
for servicing by software.
6. The SR is cleared. This terminates any low-power mode. Because the GIE bit is cleared, further
interrupts are disabled.

7. The content of the interrupt vector is loaded into the PC: the program continues with the interrupt
service routine at that address.
Последний раз редактировалось YS Чт июл 25, 2013 14:10:19, всего редактировалось 1 раз.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Реклама
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт июл 04, 2013 15:15:22

Сообщение VLeshka »

Спасибо!

А ещё, в IAR переменной типа string нельзя присваивать значение, кроме как при создании переменной. Простейший выход - использовать массив символов?
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

переменной типа string
Забудьте про string. В стандарте С такого нет.

Строка в С - массив ASCII-кодов, заканчивающийся нулем (именно нулем, не ASCII-кодом нуля). Все функции из string.h работают именно с такими строками.

Все "присвоения" строк (кроме как при инициализации) - это уже ООП с неявными вызовами методов и сопутствующим выжиранием ресурсов. Для ПК это удобно и гламурно, для МК - ненужная красотулька.

Вот такая запись

Код: Выделить всё

uint8_t str[] = {"Hello"};
Определяет строку в стандарте С. Компилятор здесь сам положит завершающий ноль. А можно сделать так:

Код: Выделить всё

uint8_t str[6];

...

str[0]='H';
str[1]='e';
str[2]='l';
str[3]='l';
str[4]='o';
str[5]=0;

...
Это одно и то же.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт июл 04, 2013 15:15:22

Сообщение VLeshka »

YS, спасибо!
Реклама
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Не за что. :)
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Реклама
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт июл 04, 2013 15:15:22

Сообщение VLeshka »

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

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

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

В чём проблема, не подскажете?
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Код: Выделить всё

while ((P1IN & BIT3) == 0);
Более классически можно записать так:

Код: Выделить всё

while (!(P1IN & BIT3));
"!" - логическое отрицание (его следует отличать от побитового - "~"). Если его аргумент не ноль, он превратит его в ноль. Если ноль - превратит в не ноль. :) А все условия в С проверяются на не_ноль/ноль.
В чём проблема, не подскажете?
Видимо, в архитектуре взаимодействия двух прерываний.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт июл 04, 2013 15:15:22

Сообщение VLeshka »

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
}
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

TA1CCTL0 = CCIE; //разрешаем прерывание по переполнению счётчика-таймера B
Неверно. CCIE разрешает прерывание от блока сравнения. За прерывание от переполнения отвечает бит TAIE в регистре TACTL. Стр. 368 - 370 User's Guide.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт июл 04, 2013 15:15:22

Сообщение VLeshka »

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

Теперь работают два таймера, только одно НО: до одной точки прерывания [часто] выполнение программы доходит 2 раза, скриншот прилагаю. У меня только одно соображение - таймер останавливается не сразу? Если я неправ, то каким образом выполнение программы доходит до точки прерывания (на скриншоте отмечен) 2 раза?
Изображение
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Э-э-э, это надо смотреть в пошаговом режиме. Так я, честно говоря, не совсем понял, в чем дело.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт июл 04, 2013 15:15:22

Сообщение VLeshka »

Возня вознёй с отладкой проблемы (суть: 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)
Отсюда напрашивается вывод: прерывание таймера В вызывается, когда разрешаешь прерывание этого самого таймера. Я не прав?
Встал на лапы
Аватара пользователя
Сообщения: 131
Зарегистрирован: Пт апр 01, 2011 20:59:47
Откуда: Саратов

Сообщение Кефир »

Какой отладчик используете?
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 848
Зарегистрирован: Ср мар 02, 2011 07:47:39
Откуда: Уфа

Сообщение Psych »

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

PS Че то глюканула цитата с автором :dont_know:
Мучитель микросхем
Аватара пользователя
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

Сообщение ANALOG »

Всю тему не читал, наверняка уже был такой вопрос:
Можно ли этот лаунчпад использовать тупо как переходник USB - UART?
И если да то куда подключаться для приема данных с него?
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Можно ли этот лаунчпад использовать тупо как переходник USB - UART?
Да. Подключаться к пинам, помеченным как RXD и TXD.

Изображение

Пинхедер J3. Перемычки, естесственно, надо снять.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 3795
Зарегистрирован: Ср дек 24, 2008 09:58:58

Сообщение Ser60 »

Да, можно. Нужно только правильно выставить 2 джампера на плате. Именно, этими джамперами можно подключть преобразователь к МК нз плате, или наоборот отключить от него. В последнем случае преобразователь можно будет использовать и с внешними устройствами вне платы. Какие именно
джамперы написано в инструкции пользователя для платы.
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт июл 04, 2013 15:15:22

Сообщение VLeshka »

Добрался до 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кб.
Первый раз сказал Мяу!
Аватара пользователя
Сообщения: 23
Зарегистрирован: Пт июн 05, 2009 12:06:56

Сообщение Bujhm666 »

Вот рабочий пример обработки кнопок и борьбы с дребезгом. Различаются короткое и длинное нажатие кнопок. В качестве таймера задержки на нажатие/отпускание используется сторожевой таймер WDT. Легко делается на любое количество кнопок. Идея взята отсюда: http://bennthomsen.wordpress.com/ti-msp ... -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;                    // Сбрасываем флаг нажатия кнопки
        }
    }
}
You cannot arrest an idea.
Ответить

Вернуться в «Разные вопросы по МК»