STM Урок 169. CMSIS. STM32F1. GPIO. Input



В уроке 165 мы настраивали ножки порта на выход. Также мы хорошо знаем, что порт — это такая шина, которая умеет работать также хорошо как на выход, так и на вход. Поэтому сегодня мы попробуем настроить ножку порта на вход. В этом случае контроллер не управляет состоянием тех или иных ножек портов, а, наоборот, следит за их состоянием, которое управляется извне. Мы также будем следить за состоянием ножки, менять которое будем с помощью кнопки, которая на нашей схеме, которая со времён последних уроков не изменилась, уже присутствует на нашей схеме и одним контактом притянута к общему проводу, а другим — к ножке порта микроконтроллера PA1

 

 

В связи с этим у нас ножка PA1 при ненажатом состоянии кнопки будет находиться в высоком состоянии и мы будем следить за тем, когда на ней установится низкий уровень.

Какие регистры и какие их биты отвечают за те или иные настройки GPIO, мы также знаем из того же урока, поэтому можно уже приступать и к проекту.

Проект был сделан из проекта прошлого урока с именем CMSIS_TIM2 и назван был CMSIS_BUTTON01.

Откроем наш проект в Keil и в файле main.c из обработчика таймера мы пока удалим вот этот код, оставив только сброс флага прерывания, так как таймер этот нам ещё пригодится в данном уроке

 

switch(tim2_count)

{

   case 0: LED10_OFF(); LED1_ON(); break;

   case 1: LED1_OFF(); LED2_ON(); break;

   case 2: LED2_OFF(); LED3_ON(); break;

   case 3: LED3_OFF(); LED4_ON(); break;

   case 4: LED4_OFF(); LED5_ON(); break;

   case 5: LED5_OFF(); LED6_ON(); break;

   case 6: LED6_OFF(); LED7_ON(); break;

   case 7: LED7_OFF(); LED8_ON(); break;

   case 8: LED8_OFF(); LED9_ON(); break;

   case 9: LED9_OFF(); LED10_ON(); break;

}

tim2_count++;

if(tim2_count>9) tim2_count=0;

 

Прежде чем следить за состоянием ножки, её нужно настроить.

Порт A у нас уже тактируется, поэтому включать тактирование нам не надо.

Давайте для начала решим какие биты и где нам надо включить для нашей ножки PA1, чтобы она работала на вход и чтобы на ней установился сразу высокий уровень.

Для этого в битовом поле CNF1[1:0], отвечающего за настройку именно ножки 1 порта A, регистра GPIOA_CRL мы установим вот такую комбинацию битов

 

 

Таким образом мы настроим подтягивание резистора. А куда именно он подтянется, — к общему проводу или к питанию, будет зависеть уже от настройки соответствующего бита регистра ODR.

А в битовом поле MODE1[1:0] мы выберем самую первую комбинацию битов

 

 

 

Проделаем всё это в нашем проекте в функции main(). Для этого нам не нужно будет писать новую команду, добавим данные настройки в уже существующую

 

MODIFY_REG(GPIOA->CRL, GPIO_CRL_CNF7 | GPIO_CRL_CNF6 | GPIO_CRL_CNF5 |\

                       GPIO_CRL_CNF4 | GPIO_CRL_CNF3 | GPIO_CRL_CNF2 | GPIO_CRL_CNF1_0 |\

                       GPIO_CRL_MODE1,\

                       GPIO_CRL_CNF1_1 | GPIO_CRL_MODE7_0 | GPIO_CRL_MODE6_0 | GPIO_CRL_MODE5_0 |\

                       GPIO_CRL_MODE4_0 | GPIO_CRL_MODE3_0 | GPIO_CRL_MODE2_0);

 

Соберём и прошьём наш код и посмотрим в отладке, что у нас всё правильно установилось

 

 

Настроим бит 1 регистра ODR, чтобы резистор подтянулся именно к питанию

 

 

Удалим старт нашего таймера

 

TIM_EnableCounter(TIM2);

 

В бесконечный цикл функции main() добавим условие, в тело которого мы будем попадать при условии, когда на ножке PA1 установится уровень 0, то есть будет нажата кнопка. В данном теле мы зажжём первый светодиод

 

 

Проверим работу нашего кода, собрав и прошив код. Пока мы не нажмём кнопку светодиод светиться не будет, а как только мы её нажмём, он засветится

 

 

По идее, цель урока уже достигнута, мы научились настраивать ножку порта на вход, но так как порой имеет место ситуация с дребезгом кнопок, давайте как-то с ней поборемся, иначе урок будет как-то неполный. Также мы задействуем все светодиоды в планке, чтобы не простаивали и при каждом новом нажатии у нас будет светодиод, светящийся в данный момент, гаснуть, а зажигаться будет следующий. Думаю, так будет интересней.

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

 

 

Во-первых, мы уменьшим период срабатывания таймера, а то 100 милисекунд слишком долгий, и нам придётся долго держать кнопку нажатой, чтобы набрать наших тиков.

Для этого в функции TIM2_Init внесём изменения в коде

 

WRITE_REG(TIM2->ARR, 50);

 

Данный период срабатывания подбирается опытным путём. Теперь таймер будет у нас срабатывать каждые 2.5 милисекунды. Думаю, нормально.

Добавим ещё один глобальный счётчик. Это будет счётчик тиков таймера, но именно при нажатой кнопке

 

 

В функции обработчика прерываний TIM2_IRQHandler от таймера добавим код, который будет инкрементировать количество тиков при условии нажатой кнопки, а также в другой переменной — общее количество тиков

 

 

Добавим функцию, в которой мы будем решать, — случайное нажатие произошло у кнопки или намеренное

 

 

Если кнопка нажата, запустим наш таймер

 

 

А если кнопка не нажата, то покинем нашу функцию совсем

 

 

Подождём, пока таймер насчитает некоторое количество срабатываний (подбирается также опытным путём)

 

 

Остановим таймер

 

 

Прежде чем исследовать количество срабатываний именно при нажатой кнопке, добавим ещё одну глобальную переменную, в которой будет храниться состояние блокировки и разблокировки доступа к кнопке. Блокировка нужна, чтобы наша кнопка не порождала бесконечное количество событий нажатого состояния

 

 

Вернёмся в функцию IsBut01On и, если таймер насчитал более 10 тиков с нажатым состоянием кнопки, запишем в результат 1 и заблокируем доступ к кнопке. Мы его разблокируем потом там, где будем вызывать данную функцию

 

 

В противном случае запишем в результат 0

 

 

Обнулим наши счётчики и вернём результат

 

 

В бесконечном цикле удалим весь код и добавим новый, в котором для начала узнаем, есть ли у нас доступ к кнопке

 

 

Если кнопка не заблокирована, то узнаем, было ли реальное нажатие, с помощью нашей функции

 

 

Добавим ещё одну глобальную переменную, в котором будем хранить номер светящегося в данный момент светодиода

 

 

Вернёмся в бесконечный цикл функции main() и добавим вот такую конструкцию, подобную той, которая ранее была у нас в обработчике прерываний от таймера

 

 

Перейдём теперь в обработку противного случая блокировки кнопки (то есть когда доступа к кнопке у нас нет) и добавим там код, который в случае, если кнопка уже отжата, будет инкрементировать счётчик номера светящегося светодиода, затем в случае достижения порога будет его обнулять. Также затем мы предоставим снова доступ к нашей кнопке, сняв блокировку

 

 

Вот и весь код. Проверим его на практике

 

 

Теперь по каждому новому нажатию на кнопку у нас будет зажигаться следующий светодиод, а текущий будет погасать. Получился у нас такой вот бегущий огонь, управляемый нажатиями на кнопку (теперь реальными). Вернее, мы отследили теоретически не нажатие кнопки, а её пребывание в какое-то время в нажатом состоянии. Нажатие кнопки — это переход её состояния из отжатого в нажатое. Конечно, классический вариант должен работать по-другому. Как правило в различных устройствах отслеживается не нажатие на кнопку, а обратный процесс — процесс её отжатия, то есть такое явление в нашем случае когда уровень на ножке порта будет меняться из низкого состояния в высокое. Данный процесс мы попробуем отследить, когда будем работать с внешними прерываниями.

Итак, сегодня мы научились работать с ножками порта, настроенными на вход, также поработали с состоянием подключенной кнопки, проведя также некоторую борьбу с явлением дребезга её контактов.

Всем спасибо за внимание!

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

Исходный код

 

 

Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM CMSIS. STM32F1. GPIO. Input

2 комментария на “STM Урок 169. CMSIS. STM32F1. GPIO. Input
  1. В других стм F4 другие регистры

  2. Gennadiy:

    Отличные уроки. Большое спасибо!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*