Симуляция светофора, или первые шаги в использовании GPIO

  • Автор
Собираем на макетной плате и реализуем на "Си" симулятор светофора с кнопкой - простой пример для начала изучения работы с GPIO.


Вместо вступления

 В этой записи я хочу рассказать об одном из первых моих проектов, который объясняет основы работы с GPIO выводами Raspberry Pi. Этот проект не является простейшей мигалкой, которую писал, наверное, каждый обладатель Малины, и также не является супер-сложным проектом, который новичку просто не потянуть.Сразу оговорюсь, что идея проекта принадлежит не мне, а одному английскому разработчику - Гордону Хендерсону, который, кстати, написал и библиотеку для Си, которая была использована в проекте. Запись, в принципе, является описанием проекта с моей стороны. Писалась она для новичков, с целью показать основы программирования для работы с GPIO. Теперь, когда все дисклеймеры написаны, приступим к делу.

Идея

 Идея заключается в сборке макета (симуляции) светофора с кнопкой, а точнее даже двух светофоров - автомобильного и пешеходного, которые связаны той самой кнопкой. Суть таких светофоров в том, что зеленый свет для пешеходов включается по требованию (читай - по нажатию кнопки). Такой тип светофоров очень распространен в США, где пешеходов в небольших городах довольно мало. Принцип работы таков: Во время простоя автомобилям все время горит зеленый, а пешеходам - красный. По нажатию на кнопку автомобильный светофор останавливает движение, после чего пешеходный светофор зажигает зеленый свет, тем самым разрешая пешеходам перейти улицу. Через некоторое время (не будем его уточнять, это не столь важно) светофоры снова возвращаются в исходное положение: зеленый для машин и красный для пешеходов.

Реализация

 Реализацию этого проекта, как и любого другого с использованием GPIO, можно разделить на два этапа: сборка электрической схемы и написание программы, которая будет управлять элементами этой схемы. 
1. Сборка схемы
Для сборки схемы данного проекта нам понадобится:1) Пластиковая макетная плата. Подойдет любая, я использовал похожую на эту  .1377973666522235a2dff8d.jpg 2) Светодиоды. Нужно пять штук, два зеленых, два красных и один желтый. Подойдут и любые другие, но для наглядности проекта лучше подобрать именно такие цвета. .13779737925222362046fac.jpg 3) Резисторы. Я использовал резисторы в 330 Ом, но можно использовать и большего номинала. Однако не советую использовать номинал выше 1,5 кОм. Резисторов нужно шесть - пять для светодиодов, и один для кнопки. 13779739185222369e6e0a5.jpg 4) Кнопка. Тут уточнять нечего, кроме того, что она должна быть замыкающей, а не размыкающей.  13779741735222379d4ee9a.jpg  Также вам понадобится что-то, чтобы соединять нашу макетную плату и выводы GPIO на Малине. Тут уже выбор за вами, скажу лишь что я для этих целей использовал обрезанный IDE шлейф, второй конец которого распаял на 26 DLS-штырьков, который вставляются прямиком в макетку. Как это примерно будет выглядеть в собранном виде и визуальная схема сборки приведена ниже. Советую пользоваться именно графической схемой для сборки.13780316325223181043aea.jpgОбратите внимание на полярность светодиодов. На схеме оранжевым цветом обозначен "+" светодиода (длинная ножка). Многие, должно быть, заметили, что нумерация пинов GPIO выглядит не совсем стандартной. Об этом позднее, а пока, если со сборкой закончили, приступим к следующему шагу. 
2. Пишем программу.
Писать будем на "Си", с использованием библиотеки wiringPi, автором которой является все тот же Гордон Хендерсон. Эта библиотека имеет упрощенный синтаксис и очень схожа с библиотекой для написание скетчей для Arduino. Мне, как новичку, было намного приятнее и удобнее работать с ней, чем со стандартной библиотекой, синтаксис которой читается и воспринимается сложнее. Для начала установим библиотеку. Это сделаем в два шага, выполнять которые нужно в консоли RaspberryPi. Если у Вас уже установлен пакет GIT-core, то первый шаг можно смело пропускать. Шаг 1: Устанавливаем GIT:
sudo apt-get install git-core
sudo apt-get update
sudo apt-get upgrade
Шаг 2: Скачиваем и компилируем wiringPi:
git clone git://git.drogon.net/wiringPi
cd wiringPi
git pull origin
./build
После выполнения этих действий библиотека будет установлена и готова к использованию.Для удобства пользования автор изменил нумерацию пинов GPIO. Распиновку для этой библиотеки вы уже видели выше, на всякий случай ознакомьтесь с "чистой" картинкой распиновки:137803253352231b953c3ed.jpg Теперь создадим в удобном для вас месте файл walk.c:
touch walk.c
Откроем его:
nano walk.c
И заполним следующим кодом. Этот код и есть кодом для нашей программы, то как он работает я попытался максимально объяснить в комментариях:
//**********Подключаем необходимые библиотеки************
#include "wiringPi.h" //Библиотека для работы с GPIO
#include "stdio.h"
#include "stdlib.h"
//**********Назначаем буквенные метки номерам пинов******
#define RED 0 //Красный автомобильный
#define YEL 1 //Желтый автомобильный
#define GRE 2 //Зеленый автомобильный
#define P_RED 3 //Красный пешеходный
#define P_GRE 4 //Зеленый пешеходный
#define BUT 8 //Кнопка
//-------------------------------------------------------
void init() {
if (wiringPiSetup () == -1) //Инициализация wiringPi
exit (1);
//**********Устанавливаем пины светодиодов в режим вывода
pinMode (RED, OUTPUT);
pinMode (YEL, OUTPUT);
pinMode (GRE, OUTPUT);
pinMode (P_RED, OUTPUT);
pinMode (P_GRE, OUTPUT);
//-------------------------------------------------------
pinMode (BUT, INPUT); // Пин кнопки ставим в режим ВВОДА

//**********Устанавливаем изначальную комбинацию ********
// Функция записи значения имеет следующий синтаксис:
// digitalWrite(номер_пина, состояние)
//Состояния: LOW - светодиод не горит, HIGH - светодиод горит
digitalWrite(RED, LOW);
digitalWrite(YEL, LOW);
digitalWrite(GRE, HIGH);
digitalWrite(P_RED, HIGH);
digitalWrite(P_GRE, LOW);
//-------------------------------------------------------
printf("Okay..\n"); //Выводим в консоль как знак завершения инициализации
}

//********Функция ожидания нажатия кнопки****************
void button_wait() {
printf("Ожидание нажатия...\n");
//Пока состояние кнопки = HIGH (логическая единица) - ждем одну милисекунду
while(digitalRead(BUT)==HIGH) {
delay(1);
}
//Как только состояние изменится цикл прервется и в консоль будет выведена надпись
printf("Нажато!\n");
}
//-------------------------------------------------------

//********Функция остановки трафика**********************
void stop_traff() {
int i;
printf("Останавливаем движение...\n");
// Трижды мигаем зеленым светом для автомобилей
for(i=0; i<3; i++){
digitalWrite(GRE, LOW);
delay(500);
digitalWrite(GRE, HIGH);
delay(500);
}
// Выключаем зеленый для автомобилей, включаем желтый
digitalWrite(GRE, LOW);
digitalWrite(YEL, HIGH);
//Ожидаем три секунды, выключаем желтый и включаем красный для автомобилей
delay(3000);
digitalWrite(YEL, LOW);
digitalWrite(RED, HIGH);
}
//-------------------------------------------------------

//********Функция запуска пешеходов**********************
void p_start() {
//Выключаем пешеходный красный, затем включаем зеленый. Выводим в консоль надпись.
digitalWrite(P_RED, LOW);
digitalWrite(P_GRE, HIGH);
printf("Можно идти!\n");
}
//-------------------------------------------------------

//********Функция восстановления режимов*****************
// Запускается по истечению времени движения пешеходов.
void change_time() {
printf("Окончание перехода..!\n");
int i;
// Выключаем красный для автомобилей и зеленый для пешеходов
digitalWrite(RED, LOW);
digitalWrite(P_GRE, LOW);
//Трижды мигаем одновременно зеленым для пешеходов и желтым
for(i=0; i<3; i++){
digitalWrite(P_GRE, HIGH);
digitalWrite(YEL, HIGH);
delay(500);
digitalWrite(P_GRE, LOW);
digitalWrite(YEL, LOW);
delay(500);
}
}
//-------------------------------------------------------

//********Функция запуска автомобилей********************
void start_traff() {
printf("Движение автомобилей восстановлено.\n\n");
//Включаем зеленый для автомобилей и красный для пешеходов
digitalWrite(GRE, HIGH);
digitalWrite(P_RED, HIGH);
}
//-------------------------------------------------------

//*****Главная функция, в которой будут вызваны остальные
int main() {
printf("__________________Симулятор светофора_________________\n");
init();
for( ; ; ){ //Повторяем бесконечное число раз
button_wait(); // Ожидаем нажатие кнопки, код снизу будет выполнен только после нажатия
stop_traff(); // Останавливаем автомобили
delay(1000); // Ждем секунду, прежде чем запустить пешеходов
p_start(); // Запускаем пешеходов
delay(5000); //Время для движения пешеходов - 5 секунд
change_time(); // Режим восстановления движения
start_traff(); // Движение восстановлено
}
// Чтобы завершить программу нужно нажать Ctrl+C
return(0);
}
Теперь сохраним наш файл комбинацией Ctrl+O и закроем его комбинацией Ctrl+X (комбинации актуальны при использовании текстового редактора nano).Для запуска нашей программы нам нужно её скомпилировать. Делается это следующей командой в консоли:
gcc -Wall -o walk walk.c -lwiringPi
Теперь запускаем:
sudo ./walk
Если вы все сделали правильно - загорится зеленый светодиод на "светофоре" для автомобилей и красный на "светофоре" для пешеходов.По нажатию на кнопку выполнится программа. То, как эта программа работает можно посмотреть на видео - http://youtu.be/R_jUUYhD0BUЕсли что-то работает неправильно - значит на каком-то из этапов вы допустили ошибку. Проверьте все очень внимательно и попробуйте еще раз. 

Заключение.

Собрав и запустив этот проект Вы получили базовые знания в работе с GPIO выводами. Если что-то из проделанного вам показалось сложным или не понятным - пишите об этом в комментарии, с радостью помогу вам разобраться. Надеюсь этот проект не будет вашим последнем, и в следующий раз вы уже соберете что-то свое. Если эта запись будет кому-то полезна, то в будущем я могу написать еще несколько инструкций для новичков. Хорошего дня и удачи в ваших начинаниях ;)

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

Sergeyiv2
Очень хорошая статья! Не могу только найти эту удобную "распаечную" панельку.
denfox12
Странно, у нас (Днепропетровск, Украина) такую можно найти практически в любом магазине радиодеталей.
kukabu1
странно конечно, но как вариант у китайцев http://dx.com/p/140716
Gasinskiy12
У меня что-то не получилось... После загрузки Малинка вырубается... Разве резисторы не к аноду (положительному контакту светодиода надо подключать), а потом все это может нужно к 3,3 В?
denfox12
Особой разницы в подключении нету. У меня все работает в таком режиме.
Перезагрузки малины после запуска скрипта - пожалуй причина для волнения. Точно нормальный блок питания используете?
Gasinskiy12
Ясно. Верю!:-) Да вроде нормальный, буду разбираться =)
Gasinskiy12
По такой схеме не работает, а вот при отключении земли и 22-й пин на минус работает красный для пешеходов, красный и зеленый для автомобилистов и кнопка.
I am mehanic1
Здесь расчет желателен. Например если диод потребляет 10мА, то резистор по закону г-на Ома R=U/I, R=5,5/0.01=550 Ом, можно с запасом до 1 кОМ, будет тусклее гореть. Выводы GPIO 5,5 В видимо с плавким предохранителем (Supply through input poly fuse), палить нежелательно, как восстановишь?
Если брать с питание с 3.3 В, то R=3,3/0.01=330 Ом, плюс запас, можно тоже до 1 кОм.... Порты 3.3 В имеют ограничение -- 50 mA max (01 & 17)... Потом видимо умирают.. Идеальный вариант переменный резистор от 500 Ом до 1,5 кОм и подстроить до нужного значения, учитывая яркость свечения...
GABBER0
Огромное спасибо за статейку, все работает.
igkl4
Лучшая статья в рунете на эту тему для начинающих!
Lord12
denfox,
если увидите мой комментарий, сообщите контакты для связи, личка на сайте не работает.
Lord12
хочу уточнить некоторые моменты
Valiuha
Привет. Хочу осуществить управление освещением в доме через интернет с помощью Малинки и модуля реле, управляемого малинкой.

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

ПС: Это будет мой первый проект с малинкой, так что руки чешутся, и не знаю как правильно и куда их засунуть)))
vladik_tip
У меня проблема: при компиляции выдаёт

pi@raspberrypi:~ $ gcc -Wall -o walk walk.c -lwiringPi

/usr/lib/gcc/arm-linux-gnueabihf/4.9/../../../arm-linux-gnueabihf/crt1.o: In function `_start':

/build/glibc-6f8a9a/glibc-2.19/csu/../ports/sysdeps/arm/start.S:119: undefined reference to `main'

collect2: error: ld returned 1 exit status

Что это значит и как от этого избавиться?

Для написания комментариев необходимо зарегистрироваться