gpio

GPIO для чайников (часть 5)

Снова возвращаемся к кнопкам и создаём первое полезное приложение.

Во второй части статей «GPIO для чайников» мы подключали кнопку между двумя портами. Один порт устанавливали на «вывод» и в состояние «1», а другим портом читали эту «1» через кнопку. При отпущенной кнопке на втором порту читался «0», а при нажатой- «1».

Всё вроде бы работало. Но недавно у такого способа подключения обнаружился один неприятный недостаток. Такое подключение кнопки оказалось неустойчивым к помехам. Когда я свою RPI подключил к сети при помощи Wi-Fi USB донгла, то все программы, в которых использовалось такое включение кнопки, просто сошли с ума. RPI детектировал нажатие кнопки даже тогда, когда к ней никто не притрагивался. Виной всему оказался длинный провод, которым была подключена кнопка к RPI. Работающий Wi-Fi адаптер наводил на нём потенциал, с уровнем достаточным для детектирования «1» на порте GPIO. Такое поведение кнопки никуда не годится.

Нужно принимать меры по помехозащищённости. В данном случае поможет использование экранированного провода до кнопки, либо даже простое уменьшение длины проводов, соединяющих кнопку с RPI. Но можно поступить проще. Раз наш RPI реагирует на помехи при отсутствии сигнала на входе, то разумным решением будет перевести порт, которым мы читаем состояние кнопки в состояние логической «1». Раз на нём всегда будет «1», то уже никакая помеха не сможет этот порт перевести в «0». А значит, наш порт станет невосприимчивым к помехам.

Давайте подключим нашу кнопку по такой схеме:

Если вы использовали кнопку от системного блока с коннектором BLS, то достаточно его просто развернуть на 90 градусов. Теперь наша кнопка подключена к порту Р1-05 и к пину «GND» (земля, или 0 по русски).

Ну и напишем такую простенькую программу button1.c

// Проверка работоспособности кнопки, подключённой
// к порту Р1_05 и GND. 
// Компиляция: gcc -o button1 button1.c -lbcm2835 -lrt
// Исполнение: sudo ./button1

#include 'stdio.h'
#include 'bcm2835.h'
#define PIN_IN RPI_GPIO_P1_05  
//#define PIN_IN RPI_V2_GPIO_P1_05  // Для RPI ревизии v2 раскомментировать эту строку и
удалить предыдущую

int main()
{

        if (!bcm2835_init())   // Инициализация GPIO
  return 1;              //Завершение программы, если инициализация не удалась
   
bcm2835_gpio_fsel(PIN_IN, BCM2835_GPIO_FSEL_INPT); // Порт на ввод
printf("Ждём нажатия на кнопку.\n");
while(bcm2835_gpio_lev(PIN_IN))   // Повторяем все действия, заключённые в скобки {} пока не будет нажата кнопка
        {}
        printf("Кнопочка нажата!\n");

        return (bcm2835_close ());     // Выход из программы
}

Компилируем и запускаем. Убеждаемся, что наша кнопка, включённая таким образом, действительно работает. Можно после запуска программы повертеть провод перед Wi-Fi донглом и убедиться, что RPI никак на него не реагирует, а реагирует только на нажатую кнопку.

А теперь разберёмся, как это работает.

Если мы зайдём на страницу http://elinux.org/RPi_Low-level_peripherals То в таблице

«HeaderPinout,bottomrow:»

мы увидим такое описание портов:

Pin Number     Pin

Name Rev1           Pin Name Rev2           Hardware Notes         

P1-01              3.3

V                           3.3 V                       50 mA max (01 &

17)

 P1-03             GPIO 0                        GPIO

2                    1K8

pull up resistor

 P1-05             GPIO 1                        GPIO

3                    1K8

pull up resistor

 P1-07             GPIO 4                        GPIO

4

Мы видим, что в описании порта Р1-05 написано «1K8 pull up resistor». Это значит, что в RPI вывод этого порта подключён к шине +3,3в через резистор сопротивлением 1,8кОм. Это называется подтяжкой. А раз этот порт подключён к +3,3в, то это значит, что в любом состоянии на входе этого порта будет присутствовать логическая «1». Если мы теперь с учётом этой информации перерисуем схему подключения нашей кнопки, то она у нас будет выглядеть вот так:

Теперь мы видим, что если кнопка не нажата, то с порта мы будем считывать «1». А когда мы нажмём на кнопку, то мы замкнём вход порта с «землёй», а значит, получим на входе в порт логический «0». Т.е. нам теперь, чтобы отследить нажатие на кнопочку, нужно просто ждать, когда на порте появится «0». Собственно чем наша выше написанная программа и занимается.

Цикл while(bcm2835_gpio_lev(PIN_IN)){} повторяется до тех пор, пока функция bcm2835_gpio_lev(PIN_IN) не вернёт в программу «0». Мы эту функцию уже использовали раньше в наших программах, только там был ещё оператор инвертирования результата, в виде восклицательного знака перед функцией, что означало, что мы ждём возвращения «1». Тут же мы ждём возвращения «0», по этому от инверсии мы избавились.

При замкнутой кнопке у нас через резистор течёт небольшой ток I=U/R=3,3/1800=1,8мА. Ток этот очень маленький, что практически никак не скажется на потребляемой RPI мощности. Как видим, это очень удачный способ подключения кнопки к RPI. Мало того, что он очень помехоустойчивый (ведь помеха может навести в проводе ток, но вот избавить провод от тока она не в состоянии), так ещё мы освободили целый порт для любых других нужд. Одна кнопка- один порт.

Но, разумеется, это совершенно не значит, что наша первая схема подключения кнопки оказалась никуда негодной. Например, подключить матричную клавиатуру можно лишь первым способом. Тогда например, для подключения клавиатуры 4х4 кнопки нам понадобится всего 8 портов. Если же мы попытаемся подключить такую клавиатуру по нашему сегодняшнему способу, то мы займём 16 портов GPIO. И тем не менее, для всех остальных случаев, нашу сегодняшнюю схему подключения можно считать оптимальной.

Теперь мы знаем, что благодаря наличию подтяжки на порту Р1-05, мы можем легко использовать этот порт для подключения кнопки. Так же из таблицы следует, что подтяжкой обладает ещё и порт Р1-03. И мы можем точно так же подключить к нему ещё одну кнопочку. Возникает резонный вопрос: «А если я хочу подключить, скажем, 3 кнопочки, а портов с подтяжкой всего 2, как быть?». Ответ прост- можно выполнить подтяжку любого порта самостоятельно, впаяв дополнительный резистор на своей плате и соединив им любой другой порт GPIO с +3,3в. К стати, сопротивление этого резистора не обязано быть именно 1,8кОм. Схема будет прекрасно работать и с резистором в 10кОм. Значит нам можно поставить в цепь подтяжки порта любой резистор сопротивлением от 1,8 кОм и до 10-12кОм. Но не торопитесь бежать за паяльником. У RPI маленькие секреты ещё не закончились. Попробуем обойтись без самодельной подтяжки.

Для начала предлагаю провести маленький эксперимент. Давайте подключим нашу кнопку к портам Р1-13 и Р1-14. Р1-13, это обычный порт GPIO, а Р1-14, это GND. Собственно у нас ничего не изменилось. Всё так же наша кнопка подключена между портом GPIO и GND, за исключением того, что этот порт не имеет резистора подтяжки. Изменим нашу программу button1.c заменив определение порта Р1-05 на Р1-13. Откомпилируем программу и запустим её. Мы видим, что она не работает должным образом. Программа считает, что кнопка нажата, хотя мы к ней и не притрагивались. А всё потому, что отсутствует подтяжка порта и на нём всё время находится «0». А по логике работы нашей программы, наличие «0» на порту говорит о нажатии кнопки.

Пришло время вспомнить о том, что я говорил в первой части статей «GPIO для чайников». А говорил я о том, что внутри процессора BCM2835

порты GPIO имеют возможность подключения внутренними резисторами подтяжки как к +3,3в, так и к 0 (или GND). Так почему бы нам не воспользоваться этим чудесным свойством в своих корыстных целях? Для воплощения нашей задумки нам достаточно лишь включить подтяжку порта Р1-13 к +3,3в. Тогда наша схема подключения кнопки автоматически превратится в такую:

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

Для включения подтягивающего резистора существует функция

void bcm2835_gpio_set_pud (uint8_tpin, uint8_t pud)

где:

pin- номер порта, к которому мы применяем данную функцию;

pud- управляющая команда, включающая нужный нам режим подтяжки (0- подтяжка отключена, 1- подтяжка к GND, 2- подтяжка к +3,3в).

Ну что ж, добавим эту функцию в нашу программу. Теперь она должна выглядеть так:

// Проверка работоспособности кнопки, подключённой
// к порту Р1_13 и GND. 
// Компиляция: gcc -o button2 button2.c -lbcm2835 -lrt
// Исполнение: sudo ./button2

#include 'stdio.h'
#include 'bcm2835.h'

#define PIN_IN RPI_GPIO_P1_13  
//#define PIN_IN RPI_V2_GPIO_P1_13  // Для RPI ревизии v2 раскомментировать эту строку и удалить предыдущую

int main()
{
        if (!bcm2835_init())   // Инициализация GPIO
        return 1;              //Завершение программы, если инициализация не удалась

        bcm2835_gpio_fsel(PIN_IN, BCM2835_GPIO_FSEL_INPT); // Порт на ввод

        bcm2835_gpio_set_pud(PIN_IN, 2); // Включаем подтяжку порта к +3,3в

        printf("Ждём нажатия на кнопку.\n");

        while(bcm2835_gpio_lev(PIN_IN))   // Повторяем все действия, заключённые в скобки {} пока не будет нажата кнопка
        {}

        printf("Кнопочка нажата!\n");

        bcm2835_gpio_set_pud(PIN_IN, 0); // Отключаем подтяжку порта

        return (bcm2835_close ());     // Выход из программы

}

Сохраним нашу программу под именем button2.c , скомпилируем и запустим. Всё, теперь наша программа адекватно реагирует на нажатие кнопки.

Ну вот, теперь вы знаете ещё полтора способа подключения кнопки к RPI.

Настало время испытать наши знания в деле. На этот раз мы не будем играть с моргающими светодиодами. Наших знаний уже вполне достаточно, чтобы превратить RPI в самостоятельное, работающее, законченное устройство, которое сможет выполнять уже полезную работу. Предлагаю превратить наш RPI в интрнет-радио! Причём это «радио» должно быть автономным, т.е. обходиться без клавиатуры, мыши и монитора. А управлять мы им будем при помощи кнопочек, которые мы только что научились подключать к RPI.

Тэги:

 

Автор:

Комментариев: 14

  • Mike3
    15.01.2013 в 14:50 ответ

    Нравятся мне ваши статьи. Разжёвано так всё всегда хорошо. Вам в школы/университеты надо идти преподавать

  • bmax9
    15.01.2013 в 16:14 ответ

    Спасибо. С нетерпением ждем продолжение.

  • Vitorrio10
    29.05.2013 в 00:55 ответ

    Так живо пишете, хотелось бы про прерывания статью, видел пример с прерываниями, вот функция if (bcm2835_gpio_eds(PIN)) { // Now clear the eds flag by setting it to 1 bcm2835_gpio_set_eds(PIN); printf("low event detect for pin 15\n"); } // wait a bit delay(500); Только ну незнай, тут тоже нужно опрашивать через некоторое время bcm2835_gpio_set_eds, а хотелось бы, чтобы например в режиме ожидания while (1) { delay(500); } Инициализировалось это самое прерывание(на кнопочку например нажали) и программа заканчивает обрабатывать предыдущий delay и САМА(без опроса) запускает функцию ту которую мы обозначили.

  • Voltage8
    29.07.2013 в 22:38 ответ

    Не совсем понял почему, когда нажимаем кнопку, то с порта P1-05 мы будем считывать ноль? Ведь на него же подается напряжение +3.3 В

    • UginL77
      17.08.2013 в 09:27 ответ

      3.3В присутствует всегда, это логическая 1, а когда конпка нажимается, то на порту появляется логический 0, т.к. 3,3В ушли на землю.

  • I am mehanic1
    30.09.2013 в 23:24 ответ

    По схеме GPIO --1K8 pull up resistor-- имеют только P1-03 и P1-05... P1-13 не имеет внутренней подтяжки.. Соответственно включать нечего..

  • I am mehanic1
    17.10.2013 в 22:58 ответ

    А точно подтяжка у P1-05 через 1K8 pull up resistor на 3,3 В.. Или через 1K8 pull up на GND. тэ е. Куда именно подтяжка на землю или на питание 3.3 В?

  • I am mehanic1
    18.10.2013 в 09:28 ответ

    Вопрос закрыт. Up значит вверх...к питанию...

  • KiVor
    18.06.2015 в 22:48 ответ

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

  • Dimonira
    27.07.2015 в 01:48 ответ

    Схема подключения кнопки в самом начале — это я даже не знаю как назвать. Такое даже любителю в голову не придёт. А уж в качестве «познавательного» материала это вообще нельзя показывать, это не может быть учебным материалом. Вы бы лучше это убрали и не позорились! И где защита от дребезга контактов кнопки? В примере при нажатии кнопки просто выход из программы — прокатит, но если вы будете управлять программой, то такие фортели получите, мама не горюй! Стыдно должно быть такие «учебные» материалы выкладывать.

  • GAMER
    21.04.2016 в 17:16 ответ

    Есть ли разница как работать с пином: 3.3В + «Пин» или «Пин» + «Земля»?

    • GAMER
      21.04.2016 в 17:16 ответ

      То, что логика инвертируется — понятно.

  • arno
    06.06.2016 в 14:51 ответ

    Всем привет. есть raspberry pi3 нужно собрать монтажную плату и подключить пару кнопок. за вознаграждение. пишите если у кого есть желание.

  • Proteus
    21.01.2020 в 13:02 ответ

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

Ваш комментарий

Авторизуйтесь для отправки комментария

© Сообщество пользователей RaspberryPi 2021