LPCXpresso Урок 5. GPIO input. Подключаем кнопку.
Мигать светодиодом научились, теперь же попробуем реагировать на нажатие кнопки. Продолжаем осваивать LPC13xx в рамках курса для новичков.
Как это не парадоксально, но на вход у LPCXpresso нет никаких интегрированных средств. У той же Discovery от STM и то кнопка имеется. Ну, ничего, зато у нас есть макетное поле.
Собираем схему
Конечно схема это громко сказано, тут и собирать-то нечего. Нам требуется добавить одну кнопку. Можно брать и с фиксацией и обычную тактовую кнопку. Подключаем её следующим образом:
Один вывод кнопки подключаем к контакту GND (земля), коих на плате к частью хватает, второй к P2.10. Почему к нему? Да просто я подумал, что так будет удобнее.
Дорабатываем библиотеку
К сожалению, в библиотеке LPC13xx_Lib отсутствует функция получения состояния вывода порта, аналогичная GPIOSetValue. Что ж, давайте, напишем её сами. Для этого в проекте библиотеки открываем файл gpio.h и после строки с описанием GPIOIntClear добавляем следующий код:
uint32_t GPIOGetValue( uint32_t portNum, uint32_t bitPosi );
Тем самым мы «объявили» что у нас будет функция, принимающая номер порта и номер вывода в порту как значения типа uint32_t и возвращающая его состояние как значение uint32_t.
Раз объявили, надо её и написать. В файле gpio.c добавляем саму функцию
uint32_t GPIOGetValue( uint32_t portNum, uint32_t bitPosi ) {
uint32_t regVal = 0;
switch ( portNum )
{
case PORT0:
regVal = (LPC_GPIO0->DATA >> bitPosi) & 1;
break;
case PORT1:
regVal = (LPC_GPIO1->DATA >> bitPosi) & 1;
break;
case PORT2:
regVal = (LPC_GPIO2->DATA >> bitPosi) & 1;
break;
case PORT3:
regVal = (LPC_GPIO3->DATA >> bitPosi) & 1;
break;
default:
break;
}
return regVal;
}
Вообще то, все то же самое что и в функции вывода. В зависимости от значения параметра port оператор switch выберет для выполнения одну из строк case и перейдет к её выполнению. Все записи case у нас аналогичные и сводятся к чтению значений всех выводов из запрошенного порта. Далее производится сдвиг так, что бы значение интересующего нас вывода оказалось в младшем бите. После чего накладывается маска, оставляя только младший бит значения, и результат сохраняется в переменную regVal.
Если переданное значение не совпало ни с одним значением case, то выполнится строка default, для которой возвращаемое значение остается нулевым.
Таким образом, функция будет возвращать нам 1, в случае если запрошенный вывод порта имеет единичное состояние и 0 во всех остальных случаях (даже когда запрошен не существующий порт).
Всё сохраняем и компилируем библиотеку. Запускать на отладку не надо. Мы ещё не добавили код, который будет нашу функцию использовать.
Пишем программу
Задача будет простая: при нажатии кнопки мигает светодиод. Возвращаемся в проект в файле main.c добавляем после группы define’ов для светодиода, свою группу define’ов для кнопки:
#define BUTTON_PORT 2 /* Порт для подключения кнопки */
#define BUTTON_BIT 10 /* Номер бита в порту для кнопки */
#define BUTTON_UP 1 /* Кнопка отжата */
#define BUTTON_DOWN 0 /* Кнопка нажата */
Делаем мы это для удобства. Если вы подключите кнопку к другому выводу, то вам будет достаточно изменить соответствующий номер только здесь, и не придется искать по всему коду места обращения к кнопке. Это пока у нас проект небольшой и вы легко можете всё найти, а когда он вырастет, не составит труда пару мест пропустить.
Теперь о том, куда можно кнопку подключить. Да практически к любому выводу. Исключения составляют специализированные выводы, например, USB-DP и USB-DM которые работают только в качестве выводов USB шины. Так же не рекомендую выводы AD4 и P0.10 т.к. их использует отладчик и P0.7 т.к. на нем подключен светодиод. В общем, используйте любой Px.x, за исключением упомянутых.
Ну и доработаем функцию main, приведу её полностью:
int main(void) {
GPIOInit();
GPIOSetDir(LED_PORT, LED_BIT, 1);
GPIOSetDir(BUTTON_PORT, BUTTON_BIT, 0);
SysTick_Config(SystemCoreClock / 1000); // настройка таймера на период 1мс
while(1) {
if(GPIOGetValue(BUTTON_PORT, BUTTON_BIT) == BUTTON_UP)
continue;
GPIOSetValue(LED_PORT, LED_BIT, LED_ON);
delay_ms(500);
GPIOSetValue(LED_PORT, LED_BIT, LED_OFF);
delay_ms(500);
}
return 0 ;
}
Собственно и писать нечего. Проинициализировали порты, настроили светодиод на вывод, а кнопку на ввод (третий параметр 0 - по идеи тоже следовало бы через define сделать, ну да ладно). И ушли в цикл обработки.
Обработка простая. Если кнопка в состоянии "не нажата", то возвращаемся к началу цикла (continue). Как только кнопку нажмут, функция GPIOGetValue вернёт нам нуль, который соответствует состоянию BUTTON_DOWN. В результате условие if не выполнится и выполнение цикла продолжится дальше с кода соответствующего миганию. По завершении цикла мигания снова возвращаемся к проверке нажатия кнопки.
Запуск
Собственно как обычно, сохранили, откомпилировали и запустили полученные 3544 байта кода.
Вместо заключения
При добавлении нового кода для работы с периферией контроллера нам больше потребовалось знания в области программирования на C, чем в области самого контроллера. Но это простой пример. Для добавления поддержки скажем I2C, нам потребуется уже более глубокие познания контроллера. Либо же можно будет взять уже готовую библиотеку и снова ограничиться только знанием библиотеки и программирования.
В целом же с изучением Cortex, на мой взгляд, дела обстоять намного проще, чем с 8-ми битными контроллерами. Да, пока информации по ним мало, зато она проще усваивается.
Файлы: blinky_button.zip