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

Hosted by uCoz