«Программируем семисегментный индикатор» средствами платы Arduino.

Тема статьи: 
Электронные поделки и механизмы

https://youtu.be/6Z7bPjLF2YI

Добрый день! Мы продолжаем знакомиться с возможностями Arduino. Сегодня мы приготовили для детей нашего клуба, а, заодно, и для любезных наших читателей интересную задачу — программирование семисегментных индикаторов. Это приборы, состоящие из семи элементов индикации, включающихся и выключающихся по-отдельности. С помощью разных комбинаций такого включения-выключения можно составить изображения цифр.

Arduino7SegmIndicator01.jpg

Из старого устройства нами была извлечена цифровая панель, содержащая 8 подобных индикаторов.
От этой панели отходит 13 выводов, каждый из которых пронумерован цифрами.

Arduino7SegmIndicator02.jpg

Первое, что нужно было — это разобраться, какой из них за что отвечает.
Выяснив что к чему, мы нарисовали такую схему.

Arduino7SegmIndicator03.jpg

За включение каждого из сегментов индикатора отвечают следующие провода: 1, 2, 3, 4, 10, 11, 12, 13, так, как на рисунке.
5й провод — заземление, 7й провод — питание (+5V).
И у нас остались три провода 6, 8 и 9, которые кодируют номер одного из 8ми индикаторов в двоичной системе.

Arduino7SegmIndicator04.jpg

Подключив прибор к Arduino и поэкспериментировав, мы выяснили, что последовательность должна быть такой: 8, 6, 9. То есть провод 9 кодирует 0й бит двоичного номера, 6й провод - 1й бит, а 8й провод — 2й бит.
Далее, для наглядности и простоты мы припаяли к панели с индикаторами разноцветные провода, отдельно вывели провод питания, а остальные припаяли к блокам с металлическими штырьками.

Arduino7SegmIndicator05.jpg

Два блока по 6 штырьков. Чтобы подключать их в разъёмы Arduino можно было разом, а не каждый провод по-отдельности.

Arduino7SegmIndicator06.jpg

Arduino7SegmIndicator07.jpg

Еще в схеме понадобится кнопка. Мы припаяли к кнопке два провода — минус и плюс.\

Arduino7SegmIndicator08.jpg

Осталось только сделать каркас, на который все элементы схемы будут крепиться. Его мы разработали и напечатали на 3D принтере.

Arduino7SegmIndicator09.jpg

Это пластина из пластика ABS с заготовленными пазиками, чтобы прикрутить на свои места плату Arduino UNO, панель с 8ю семисегментными индикаторами и кнопку.
Прикручиваем все вместе и собираем схему.

Arduino7SegmIndicator11.jpg

Arduino7SegmIndicator10.jpg

Дальше подключаем Arduino к компьютеру, запускаем среду разработки программ под Arduino и пишем программу.

Arduino7SegmIndicator12.jpg

Наша программа будет зажигать по-очереди 8 индикаторов слева-направо.
Что именно загорится на каждом из них можно будет задать в специальном массиве - prog.

int prog[3][8] = {
  {0, 1, 2, 3, 4, 5, 6, 7},
  {8, 9, 10, 11, 8, 9, 10, 11},
  {7, 6, 5, 4, 3, 2, 1, 0}
};

Подаём 0 - на очередном загоревшемся индикаторе высветится 0, так до 9, а индексы 10 и 11 отвечают за запятую и тире.
Каждый ряд цифр в массиве — это отдельная зацикленная программа. Когда загорается последняя в ряду цифра мы перейдём к первой. Переключение между рядами, или между программами поведения индикаторов происходят при нажатии на кнопку.
Настала пора поработать детям. Каждый из детей, которые участвовали в наших экспериментах задал свою последовательность цифр.
Осталось загрузить программу в схему и радоваться результату.
Код программы с подробными пояснениями мы приводим ниже.
До новых встреч!

/*Программа для 8ми 7мисегментных индикаторов и кнопки. По очереди слева-направо выводятся заданные числа на индикаторах.*/
/*LEDS - это массив, хранящий номера контактов ардуино. К ним присоединяются провода, зажигающие сегменты индикатора. 13й контакт ардуино - нижний сегмент, 12й - левый нижний, 11й - левый верхний, 10й - средний, 5й - верхний, 4й - правый верхний, 2й - правый нижний, 3й - запятая*/

int LEDS[8] = {13, 12, 11, 10, 5, 4, 2, 3};

/*LEDCODE - это массив, хранящий номера контактов ардуино. К ним присоединяются провода, кодирующие в двоичной системе порядковый номер индикатора, который высветит заданый номер. */
int LEDCODE[3] = {7, 9, 6};

/*ORDERS - это массив, хранящий порядковые номера всех 8ми индикаторов. Кажды из трёх битов одного индикатора соответствует элементу массива LEDCODE, то есть посылается на определённый контакт ардуино команда зажечь или погасить*/
int ORDERS[8][3] = {
  {0,0,0},
  {0,0,1},
  {0,1,0},
  {0,1,1},
  {1,0,0},
  {1,0,1},
  {1,1,0},
  {1,1,1}
};

/*NUMBERS - это массив, хранящий коды цифр от 0 до 9 (0 - сегмент не горит, 1 - сегмент горит), плюс элемент 10 - запятая, 11 - тире.
Каждый из восьми битов одной цифры соответствует элементу массива LEDS, то есть посылается
на определённый контакт ардуино команда зажечь или погасить*/

int NUMBERS[12][8] = {
  {1,1,1,0,1,1,1,0},
  {0,0,0,0,0,1,1,0},
  {1,1,0,1,1,1,0,0},
  {1,0,0,1,1,1,1,0},
  {0,0,1,1,0,1,1,0},
  {1,0,1,1,1,0,1,0},
  {1,1,1,1,1,0,1,0},
  {0,0,0,0,1,1,1,0},
  {1,1,1,1,1,1,1,0},
  {1,0,1,1,1,1,1,0},
  {0,0,0,0,0,0,0,1},
  {0,0,0,1,0,0,0,0},
};

//переменные, хранящие размеры всех заданных массивов
int sizeof_LEDS = 8;
int sizeof_LEDCODE = 3;
int sizeof_NUMBERS = 12;

/*задержка в милисекундах, через которую происходит опрашивание состояния кнопки*/
int check_but_state_delay = 5;

/*задержка в милисекундах, через которую меняется поведение схемы (переход программы на следующий шаг)*/
int change_diode_state_delay = 500;

/*контакт ардуино, к которому подключён провод, опрашивающий состояние кнопки*/
const int BUTTON = 8;

/*состояние кнопки true - нажата, false - отжата (по умолчанию выключена)*/
bool but_state = false;

// индекс текущей программы
int current_prog_step = 0;

//индекс текущего шага в текущей программе
int current_index_step = 0;

// максимальное число программ
int max_prog_steps = 4;

/*prog - это массив, хранящий программы поведения индикаторов. Цифры, стоящие в одном ряду - загораются на индикаторах по-очереди и зацикленно, то есть по достижению восьмого элемента программа переходит к 0му. Индекс шага хранит переменная current_index_step
При нажатии на кнопку меняется current_prog_step и программа переходит к воспроизведению следующего ряда. При достижении последнего ряда программа переходит к 1му. Все цифры - это индексы, адресующие к элементам массива NUMBERS*/

int prog[4][8] = {
  {9, 5, 2, 8, 9, 5, 1, 8},
  {3, 8, 11, 4, 11, 9, 11, 7},
  {5, 6, 1, 1, 5, 2, 4, 5},
  {4, 1, 11, 10, 9, 4, 4, 4}
};

/*функция, в которой происходит инициация всех переменный и задание начальных значений*/

void setup() {

/*задаём значение всем контактам ардуино значение выходного (то есть выдающего напряжение), кроме контакта BUTTON, который должен быть входным и регистрировать наличие или отсутствие напряжения*/
  for(int i = 0; i < sizeof_LEDS; i++)
  {
    pinMode(LEDS[i], OUTPUT);
  }

  for(int i = 0; i < sizeof_LEDCODE; i++)
  {
    pinMode(LEDCODE[i], OUTPUT);
  }

  pinMode(BUTTON, INPUT);
}

/*циклическая функция, выполняющая по-кругу заданые действия*/
void loop() {

/*вспомогательная переменная, которая будет отсчитывать время и указывать когда пора выходить из цикла*/
  int init = 0;
  do{
//вызов функции, опрашивающей состояние кнопки
    check_button_state();
//заданная временная задержка
    delay(check_but_state_delay);
//прибавляем задержку к init
    init += check_but_state_delay;
  }
/*если init достигла заданной задержки выходим из цикла, чтобы перейти к следующему шагу программы*/
  while(init < change_diode_state_delay);

/*функция, устанавливающая порядковый номер индикатора, который будет загораться*/
  setOrderNum(current_index_step);
/*функция, устанавливающая цифру, которая загорится на этом индикаторе*/
  setNumber(prog[current_prog_step][current_index_step]);

//увеличиваем шаг на единицу, проверив в функции CheckCurrentIndexStep, не вышел ли этот шаг за пределы размера массива
  current_index_step = CheckCurrentIndexStep(++current_index_step);
}

/*функция, устанавливающая порядковый номер индикатора, который будет загораться. */
void setOrderNum(int ordernum){
  if( (ordernum < 0)||(ordernum >= sizeof_LEDS ) )
    return;

  digitalWrite( LEDCODE[0], ORDERS[ordernum][0] );
  digitalWrite( LEDCODE[1], ORDERS[ordernum][1] );
  digitalWrite( LEDCODE[2], ORDERS[ordernum][2] );
}

/*функция, устанавливающая цифру, которая загорится на этом индикаторе*/
void setNumber(int number){
  digitalWrite( LEDS[0], NUMBERS[number][0] );
  digitalWrite( LEDS[1], NUMBERS[number][1] );
  digitalWrite( LEDS[2], NUMBERS[number][2] );
  digitalWrite( LEDS[3], NUMBERS[number][3] );
  digitalWrite( LEDS[4], NUMBERS[number][4] );
  digitalWrite( LEDS[5], NUMBERS[number][5] );
  digitalWrite( LEDS[6], NUMBERS[number][6] );
  digitalWrite( LEDS[7], NUMBERS[number][7] );
}

//функция, опрашивающая состояние кнопки
void check_button_state()
{
//считываем в локальную переменную текущее состояние кнопки
  bool current_but_state = digitalRead(BUTTON);

/*сравниваем его с предыдущим. Если кнопка раньше не была нажата, а теперь нажали, то переходим к следующей программе, увеличивая
current_prog_step на единицу и проверив переменную на выход за пределы массива*/

  if( (current_but_state == true) && (but_state == false) )
  {
    but_state = true;
    current_index_step = 0;
    current_prog_step = CheckCurrentProgStep(++current_prog_step);
  }
/*если кнопка была нажата, а теперь отжали - возвращаем текущее состояние кнопки к false*/
  else if( (current_but_state == false) && (but_state == true) )
  {
    but_state = false;
  }
}

/*функция, проверяющая, не вышло ли значение current_prog_step за пределы массива prog. Если вышло - возвращаем 0й элемент массива - то есть начинаем цикл сначала*/
int CheckCurrentProgStep(int currentstep){
  if(currentstep >= max_prog_steps)
    return 0;
  else
    return currentstep;
}

/*функция, проверяющая, не вышло ли значение current_index_step за пределы одного ряда в массиве prog. Если вышло - возвращаем 0й элемент массива - то есть начинаем цикл сначала*/
int CheckCurrentIndexStep(int currentstep){
  if(currentstep >= sizeof_LEDS)
    return 0;
  else
    return currentstep;
}