Уроки MSP430 LaunchPad. Урок 07: Жмем кнопки

Мы дошли до точки, когда уже можем запрограммировать MSP430, подавать сигналы, зажигая светодиод или два. В то время как способность изменять логические уровни на выводах микроконтроллера просто полезна, комбинация способности менять сигналы на выходе и возможности считывать сигналы на входе делает микроконтроллер могущественным. Кнопки, один из простейших способов ввода, и их полезно изучить, дабы больше узнать о работе периферии.

Много разных кнопок и переключателей можно найти в продаже, и много способов как их присоединять к MSP430. Здесь рассмотрим те, что обычно называют «микрики». На плате LaunchPad есть две таких кнопки. Одна присоединена к выводу P1.3 (в левом нижнем углу платы) и другая к выводу RST/SBWTDIO (вывод 16 на корпусе DIP).

Подключение кнопок




Если посмотреть схему LaunchPad, то можно увидеть, что кнопки включены между соответствующим выводом микроконтроллера и землей. Заметьте, что одновременно эти выводы подключены к питанию (Vcc), через резисторы 47кОм. Для использования кнопок в цифровой логике, они должны обеспечивать два состояния: логической единицы и логического ноля. Когда кнопка поднята, резистор притягивает вывод к питанию, поднимая на нем напряжение, до того уровня, которым запитана плата (в случае LaunchPad это 3.6В). Когда вы нажимаете кнопку, вывод напрямую замыкается на землю. Таким образом, не нажатая кнопка, это логическая единица, нажатая, это логический ноль.

(В LaunchPad версии 1.5, не впаяны R34 и С24, есть только места для их самостоятельной распайки. – Прим. пер.).

Вы можете использовать и обратный способ подключения, поменяв резистор и кнопку местами. По очевидным причинам, первый способ конфигурации вывода микроконтроллера зовется «подтяжка вверх», а второй «подтяжка вниз». Без этих резисторов, вывод останется на весу, и будет вести себя непредсказуемо, принимая любое значение между землей и напряжением питания. Какой бы проект платы вы самостоятельно не разрабатывали, не забывайте про резисторы подтяжки. Высокое сопротивление резисторов подтяжки (такое как 47 кОм на LaunchPad) необходимо для того, что бы избежать утечки тока при нажатии кнопок. В идеале, нам необходимо просто изменить потенциал вывода, не тратя на это никакой энергии.

Вы должны знать, что у MSP430 существуют внутренние резисторы на каждом выводе портов GPIO, и любой из них, можно сконфигурировать для подтяжки вверх или вниз. Их сопротивление, как написано в документации, достаточно большое, что бы минимизировать утечку тока. Но, как замечено теми, кто разрабатывает реальные устройства, это плохая практика, использовать эти внутренние резисторы, для подключения кнопок.

Пример


С установленными резисторами, использовать кнопки на микроконтроллере чрезвычайно просто. Давайте продемонстрируем это, написав программу, которая несколько раз мигает красным светодиодом на выводе P1.0, а после нажатия кнопки, несколько раз мигает зеленым светодиодом на выводе P1.6 Нажатие кнопки еще раз возвращает мигание красного и так далее. Для этой программы нам нужно настроить выводы светодиодов P1.0 и P1.6 на выход, а вывод кнопки P1.3, на вход. Используем код конфигурирования выводов из программы-мигалки. Так же нам понадобится переменная счетчика циклов, и второй счетчик для контроля количества вспышек после нажатия кнопки.

Нам необходимо заставить MSP430 периодично проверять состояние кнопки, это называется «опрос». Когда программа достигает места, где необходимо дождаться нажатия кнопки пользователем, она проверяет и проверяет кнопку раз за разом, пока не заметит, что кнопка нажата. Мы сделаем это, используя логическую операцию для проверки значения в регистре P1IN. Т.к. кнопка подключена к выводу P1.3, третий бит регистра P1IN сообщит нам, что кнопка нажата (логический ноль) или не нажата (логическая единица). Остальные биты в P1IN могут иметь любое значение. Так как же нам извлечь только значение бита 3? Простейший способ, это использование оператор & (логическое И) который будет работать так: (x & 0 == 0), (x & 1 == x), т.е. P1IN & BIT3 (0b00001000) вернет 0b0000x000, где x, это значение бита 3 в регистре P1IN. Важно понимать, что результат не 0 или 1, а 0 или 0b00001000, что является значением BIT3. Т.к. 0 сигнализирует о нажатии кнопки, мы можем опрашивать регистр P1IN в цикле, который продолжается пока (P1IN & BIT3) == BIT3, или, что равнозначно, пока (P1IN & BIT3) != 0.

Я написал свою версию программы pushy_G2211.c (листинг приведен в конце статьи – Прим. пер.). А вам рекомендую, попробовать написать программу самостоятельно, а потом сравнить с моей. Пара нововведений, относительно мигалки, это использование команды #define в заголовке, и заключение повторяющегося участка кода в функцию. Если вы не понимаете, что это такое, то изучите основы языка Си, по любой хорошей методичке или книге. Например, этой. По сути, #define позволяет мне заменить имя BIT0, на что-то более конкретное, в данном случае на red_LED. Это не меняет значение BIT0, его все-еще можно использовать в коде. Вы даже можете присвоить BIT0 два разных имени, например если у вас есть по светодиоду на P1.0 и на P2.0, нулевых выводах двух разных шин одного микроконтроллера. Функция delay(), так же делает код более читабельным, и демонстрирует мощь инкапсуляции. Т.к. функция delay(), чрезвычайно распространенная, я могу поместить ее в библиотеку, и вызывать во всех своих программах, избегая необходимости набирать один и тот же код много раз. Я рекомендую вам использовать эти и другие методы программирования как можно больше, что бы писать легкие для понимания и легко модифицируемые программы.

Данная программа, не идеальна, и ее логика не подойдет для любых ситуаций. Что произойдет, если вы нажмете кнопку в момент, когда светодиод горит? Т.к. ввод проверяется только в цикле while(), ничего не произойдет. Если у вас рефлексы ниндзя, и вы будете жать кнопку точно в моменты до, и после ее опроса, программа никогда не узнает, что кнопка была нажата. А что случится, если нажать и не отпускать кнопку? Решением этих проблем, будут — прерывания. Это тема следующего урока. Пока вы изучили самый примитивный способ использования кнопок.

Упражнение 1: Скопируйте программу в новый проект CCS и попробуйте загрузить в G2211. Пройдитесь по коду отладчиком. (Не забудьте уменьшить задержки в циклах опроса кнопки и мигания). Учтите, что бы отладчик обработал нажатие кнопки, она должна быть нажата в момент перехода на соответствующую строчку кода. Понаблюдайте за значением регистра P1IN, посмотрите, как изменяется его значение.

Упражнение 2: Мы не обсудили работу второй кнопки (RST) на LaunchPad. Попробуйте нажать ее, пока мигает красный светодиод. Что она делает? Пока что, LaunchPad сконфигурирован на использование этой кнопки единственным способом, это полезный инструмент, особенно если вам необходимо перезапустить вашу программу.


/* pushy_G2211: простое демо работы с кнопкой на
 * выводе микроконтроллера.
 */

#include <msp430g2211.h>

#define red_LED   BIT0
#define grn_LED   BIT6
#define BTN       BIT3

void delay(void);

void main(void) {
    unsigned int flash;
    WDTCTL = WDTPW + WDTHOLD;

    P1OUT = 0;
    P1DIR |= red_LED + grn_LED;  // Выводы светодиодов на выход

    // Если у вас LaunchPad версии 1.5, используйте эти две строчки
//  P1REN |= BTN; //разрешаем подтяжку
//  P1OUT |= BTN; //подтяжка вывода P1.3 вверх

    for (;;) {

        for (flash=0; flash<7; flash++) {
            P1OUT |= red_LED;    // включаем красный светодиод 
            delay();             // вызов функции задержки
            P1OUT &= ~red_LED;   // выключаем красный светодиод 
            delay();             // опять задержка
        }

        while ((P1IN & BTN) == BTN);  // ждем нажатия кнопки

        for (flash=0; flash<7; flash++) {
            P1OUT |= grn_LED;    // включаем зеленый светодиод
            delay();
            P1OUT &= ~grn_LED;   // выключаем зеленый светодиод
            delay();
        }

        while ((P1IN & BTN) == BTN);  // ждем нажатия кнопки

    }
} // main

void delay(void) {
    unsigned int count;
    for (count=0; count<60000; count++);
} // задержка

Архив с файлом программы.

Примечание переводчика: Я добавил пару закомментированных строк в пример, для тех, у кого LaunchPad версии 1.5. Дело в том, что в новом LaunchPad не впаян подтягивающий резистор к выводу P1.3, так что без этих строк, кнопка просто не заработает. В этих строках мы разрешаем использование подтяжки в регистре порта REN, а затем указываем в OUT какой тип подтяжки, вверх или вниз, мы используем.

Если у вас IAR, просто создайте новый проект на Си, настройте в нем тип микроконтроллера и отладчика, замените содержимое main.c на пример из урока. Не забудьте заменить #include <msp430g2211.h> на ваш микроконтроллер, например на #include <msp430g2553.h>. Всё должно заработать.


Оригинал урока на английском: Tutorial 07: Pushing Buttons

Предыдущий урок этого цикла: Урок 06: Избавляемся от ошибок
Следующий урок этого цикла: Урок 08: Бьющееся Сердце BCS+
  • +12
  • 17 ноября 2012, 04:49
  • Tabke
  • 1
Файлы в топике: pushy_G2211.zip

Комментарии (14)

RSS свернуть / развернуть
У меня в CCS, приведённый в примере код, нормально заработал только после перемещения объявления переменной count из подпрограммы delay в начало кода.
Может кто знает, почему?
Работающий у меня код лежит здесь: pushy_G2553_CCS.c
0
Вообще, с точки зрения Си, должны оба варианта работать. А исходный вариант как не работал? Не компилировался, или не работал после компиляции?
0
Скомпилировался, и в общем-то работал, но не так, как надо.
Посмотрел в отладке — программа шаг delay(); просто проскакивала. Т.е. в подпрограмму delay просто не входила.
Как результат — при нажатии на кнопку оба светодиода светились «вполнакала», задержки-же небыло.
п.с. Плата версии 1.5, контроллер 2553, CCS 5.1.1.00031
0
Видимо delay (); необходимо объявить как volatile:
volatile void delay (void);
Оптимизатор её сокращает. Хотя и не ясно, по какой такой логике. Ведь она используется в main ().
Попробуйте, интересно просто, заработает или нет.
0
По очень простой причине. Нет никакого смысла 100500 раз ничего не делать.
Можно сделать это один раз и вообще не выполнять эту глупую функцию.
Если уж хочется сделать именно так (хотя довольно неумно использовать «задержки на нопах» порядка миллисекунд, для этого есть таймеры), то гарантированный способ — это инлайн ассемблер.
Вот пример из доки к mspgcc:

void Pause(register unsigned int n)
{
	__asm__ __volatile__
	(
			"1: \n"
			" dec %[n] \n"
			"nop \n"
			"nop \n"
			" jne 1b \n"
			: [n] "+r"(n)
	);
}

Один круг ровно за 5 тактов, емнип.
-1
На худой конец, внутрь своего цикла вставьте __NOP() (или как она там в ЦЦС)
0
Ну это-же пример для совсем новорожденных, а вы предлагаете такие сложности ) да ещё и с ассемблером.
+1
Поделом, оптимайзер функцию delay расстрелял за тунеядство. Заработает если убавить оптимизацию, сделать count volatile или еще что-нить в этом духе (методов не так уж мало).
0
Сделал volatile count в подпрограмме delay
всё заработало.
А вот на volatile void delay (); компилятор ругается:
#1234 void return type cannot be qualified
0
Получается что на void нельзя ничего навешивать. Ок. Не знал.
0
аналогично компилятор ругался. долго мучался (новичок в этом деле)
в итоге подпрограмма стала выглядеть так
void delay(void) {
unsigned int volatile count;
for (count=0; count<6000; count++);
}

все заработало
0
У меня такая вот проблема: заливаю Ваш код на свой 2553, а там горят два светодиода одновременно и ничего не происходит (подключил <msp430g2553.h>, пробовал <msp430.h>, ничего не меняет, коментарии к 1.5 убрал), что может быть?
0
Тупанул, ламер01 уже писал решение: void delay(void) {
unsigned int volatile count;
for (count=0; count<6000; count++);
}
0
«Для этой программы нам нужно настроить выводы светодиодов P1.0 и P1.6 на выход, а вывод кнопки P1.3, на вход. „
Выводы светодиодов на выход вижу. А где вывод кнопки на вход?
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.