Тройной 7-сегментный дисплей
Если бы тройной 7-сегментник имел по 9 ног для каждой цифры, как одинарный и двойной, у него бы было 27 ног. Это слишком много, поэтому разработчики решили оставить только 11, соединив катоды одинаковых сегменов трех цифр вместе и оставив болтаться три анода отдельно.
Так выглядит этот дисплей:
и его схема:
Пристальный взгляд на его внутреннее устройство сразу заставляет заметить, что халява кончилась - одновременно дисплей может показывать или только одну цифру, или три одинаковые цифры. Но нам-то надо одновременно три разные цифры!
Ладно, для начала попробуем просто зажечь все цифры, пусть даже одинаковые.
Собираем схему
Мы будем передавать коды с процессора на ноги светящихся сегментов по SPI, и поэтому центральной частью схемы снова становятся сдвиговые регистры 74HC595, которые мы уже хорошо обскакали.
Помним, что нам нужно подключить 11 ног, из них 3 анодные и 8 - катодные. Катодные ноги отвечают за то, какая цифра будет показываться, а анодные - где эта цифра будет показываться. Логично использовать один сдвиговый регистр целиком для управления катодными ногами, а три вывода другой микросхемы - для управления анодными.
Принципиальная схема нашего изобразительного устройства будет такова:
Если вы внимательно разбирались с предыдущими схемами подключения регистров 74HC595, то у вас не должно возникнуть проблем. В кратце:
- красный провод - +5В;
- черный провод - земля;
- желтый провод - клок;
- оранжевые провода - последовательные данные (проходят через правый регистр "насквозь");
- коричневый провод - защелкивание регистров;
- зеленые провода - параллельные данные для анодов 7-сегментника;
- синие и серые (рисовал - не подумал) - параллельные данные для катодов 7-сегментника.
Вообще с лицевой стороны получилось довольно симпатично:
Пустой левый сокет является "system reserved" - я впаял его для дальнейшего расширения функционала, а сейчас он останется пустым.
Первый тест
Схемотехника собранного устройства определяет следующий порядок передачи информации по SPI: первым передается байт, отвечающий за то, какая цифра будет показываться (назову его катодным байтом), а вторым передается байт, отвечающий за позицию цифры (назову его анодным байтом).
Катодный байт во время сеанса SPI-передачи проползает сквозь правый (см. схему) регистр и заползает в левый. Анодный байт заползает в правый регистр, да там и остается. В результате показываются циферки, и все радуются.
Табличку для катодного байта мы уже рассчитывали, можем без изменения взять нашу наработку - найдем строку в программке, где мы определяли массив готовых к отправке свеженьких байтов.
static uint8_t digit[16] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
Тут у нас лежат коды цифр от 0 до F. Выкинем ABCDF (они нафиг не нужны) и добавим в конце под десятым номером пустышку (она понадобится нам, когда мы будем отображать двузначные и однозначные числа). Не забываем сменить объявляемый размер массива:
static uint8_t digit[11] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};
В качестве анодного байта просто зафигачим 0xFF - чтобы на всех анодах были единицы, и зажигались одинаковые циферки.Стряпаем код:
// подключаем библиотеку SPI
#include <SPI.h>
// провод CS подсоединяем к 8-му пину Arduino
enum { reg = 8 };
void setup()
{
// инициализируем SPI
SPI.begin();
// настраиваем 8-й пин как выход
pinMode(reg, OUTPUT);
}
// храним в массиве все цифры
static uint8_t digit[11] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};
void loop()
{
for (int i=0;i<11;i++){
// притягиваем CS к земле - начало передачи
digitalWrite(reg, LOW);
// передаем катодный байт
SPI.transfer(digit[i]);
// передаем анодный байт
SPI.transfer(0xFF);
// отпускаем CS - конец передачи
digitalWrite(reg, HIGH);
// ждем секунду
delay(1000);
}
}
#include <SPI.h>
// провод CS подсоединяем к 8-му пину Arduino
enum { reg = 8 };
void setup()
{
// инициализируем SPI
SPI.begin();
// настраиваем 8-й пин как выход
pinMode(reg, OUTPUT);
}
// храним в массиве все цифры
static uint8_t digit[11] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};
void loop()
{
for (int i=0;i<11;i++){
// притягиваем CS к земле - начало передачи
digitalWrite(reg, LOW);
// передаем катодный байт
SPI.transfer(digit[i]);
// передаем анодный байт
SPI.transfer(0xFF);
// отпускаем CS - конец передачи
digitalWrite(reg, HIGH);
// ждем секунду
delay(1000);
}
}
Все работает:
Отображение трехзначного числа
Что же мы будем делать, чтобы одновременно показывать разные цифры? Ясно что - обманывать свои глаза, пользуясь принципом мультипликации - из статических изображений получать динамическое путем быстрой их смены. Мы будем включать каждую цифру на короткое время, потом выключать ее и включать следующую.
Практика показала, что если при переключении цифры каждые 8 миллисекунд еще можно различить мерцание, то при значении в 7 миллисекунд глаз уже не видит дискретизацию.
Однако сначала нам нужно разобрать число на косточки. Пусть есть число int f, которое в десятичной системе имеет число разрядов не более трех, то есть число меньше 1000. Действуем так:
- Делим f на 100, оставаясь в рамках int - мы аккуратно получаем количество сотен в нашем числе.
- Отнимаем от f число сотен, помноженное на 100, делим остаток на 10 -получаем число десятков.
- Отнимаем от f число сотен, помноженное на 100 и число десятков, помноженное на 10 -получаем количество единиц.
- Если число сотен равно нулю, то ничего не отображаем в левом разряде.
- Если число сотен и десятков равно нулю, то ничего не отображаем в левом и среднем разрядах.
Код для Arduino получился такой:
// подключаем библиотеку SPI
#include <SPI.h>
// провод CS подсоединяем к 8-му пину Arduino
enum { reg = 8 };
void setup()
{
// инициализируем SPI
SPI.begin();
// определяем 8-й пин Arduino как выход
pinMode(reg, OUTPUT);
}
//мы разобъем число на сотни, десятки и единицы, объявляем их здесь
int hundreds=0, tens=0, ones=0;
//время отображения каждой цифры
int delayTime=7;
//коды цифр на семисегментнике (0-9 и пустота)
static uint8_t digit[11] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};
// коды позиций зажигаемого семисегментника (левый, центральный, правый)
static uint8_t pos[3]= {0xE7,0xEB,0xF3};
void loop()
{
//число, которое будем выводить
int f=707;
//выделяем сотни
hundreds=f/100;
//выделяем десятки
tens=(f-hundreds*100)/10;
//выделяем единицы
ones=f-hundreds*100-tens*10;
//если сотен нет, не отображаем ничего в 3м разряде
if (hundreds==0) hundreds=10;
//если сотен и десятков нет, не отображаем ничего во 2м разряде
if (hundreds==10 && tens==0)tens=10;
//начинаем передачу по SPI
digitalWrite(reg, LOW);
//передаем код цифры, соответствующей разряду сотен
SPI.transfer(digit[hundreds]);
//выбираем левый 7-сегментник
SPI.transfer(pos[0]);
//заканчиваем передачу
digitalWrite(reg, HIGH);
//пауза, равная delayTime
delay(delayTime);
//передаем код цифры, соответствующей разряду десятков
digitalWrite(reg, LOW);
SPI.transfer(digit[tens]);
//выбираем центральный 7-сегментник
SPI.transfer(pos[1]);
digitalWrite(reg, HIGH);
delay(delayTime);
//передаем код цифры, соответствующей разряду единиц
digitalWrite(reg, LOW);
SPI.transfer(digit[ones]);
//выбираем правый 7-сегментник
SPI.transfer(pos[2]);
digitalWrite(reg, HIGH);
delay(delayTime);
}
#include <SPI.h>
// провод CS подсоединяем к 8-му пину Arduino
enum { reg = 8 };
void setup()
{
// инициализируем SPI
SPI.begin();
// определяем 8-й пин Arduino как выход
pinMode(reg, OUTPUT);
}
//мы разобъем число на сотни, десятки и единицы, объявляем их здесь
int hundreds=0, tens=0, ones=0;
//время отображения каждой цифры
int delayTime=7;
//коды цифр на семисегментнике (0-9 и пустота)
static uint8_t digit[11] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};
// коды позиций зажигаемого семисегментника (левый, центральный, правый)
static uint8_t pos[3]= {0xE7,0xEB,0xF3};
void loop()
{
//число, которое будем выводить
int f=707;
//выделяем сотни
hundreds=f/100;
//выделяем десятки
tens=(f-hundreds*100)/10;
//выделяем единицы
ones=f-hundreds*100-tens*10;
//если сотен нет, не отображаем ничего в 3м разряде
if (hundreds==0) hundreds=10;
//если сотен и десятков нет, не отображаем ничего во 2м разряде
if (hundreds==10 && tens==0)tens=10;
//начинаем передачу по SPI
digitalWrite(reg, LOW);
//передаем код цифры, соответствующей разряду сотен
SPI.transfer(digit[hundreds]);
//выбираем левый 7-сегментник
SPI.transfer(pos[0]);
//заканчиваем передачу
digitalWrite(reg, HIGH);
//пауза, равная delayTime
delay(delayTime);
//передаем код цифры, соответствующей разряду десятков
digitalWrite(reg, LOW);
SPI.transfer(digit[tens]);
//выбираем центральный 7-сегментник
SPI.transfer(pos[1]);
digitalWrite(reg, HIGH);
delay(delayTime);
//передаем код цифры, соответствующей разряду единиц
digitalWrite(reg, LOW);
SPI.transfer(digit[ones]);
//выбираем правый 7-сегментник
SPI.transfer(pos[2]);
digitalWrite(reg, HIGH);
delay(delayTime);
}
Наслаждаемся вот таким зрелищем:
Если у вас что-то работает неправильно, для дебага измените delayTime на, например, 500 - тогда вам станет видно, что именно в устройстве происходит не так.
Динамическая индикация
В предыдущем примере мы научились отображать любое число до 999 на 7-сегментном дисплее. Смена чисел - задача творческая, однако мы ее уже, считай, решили, когда разбирали одновременное отображение змейки на двойном дисплее и циферок на одинарном дисплее.
Мы заводим переменные timer и timerPrev:
- timer хранит время с начала работы программы в миллисекундах,
- timerPrev хранит время после совершения последнего переключения.
Как только разница между ними достигнет периода переключения (обзовем его thinkingTime), производим новое переключение и запоминаем значение timer в timerPrev.
Чтобы нам не было скучно мы будем выводить на дисплей последовательность Фибоначчи. Время отображения каждого элемента последовательности тоже будет расти, как будто процессор все дольше и дольше складывает.
Запихнем разбиение числа на разряды и их вывод в функцию void output (int f), чтобы это нам не мешалось в void loop(). А в главном цикле мы будем ждать, пока не пройдет thinkingTime после последнего переключения и тогда вычислять очередной элемент последовательности Фибоначчи и отображать его. Код получился таким:
#include <SPI.h>
enum { reg = 8 };
void setup()
{
SPI.begin();
pinMode(reg, OUTPUT);
}
int hundreds=0, tens=0, ones=0, fnn=0, fn=1, f=0;
int delayTime=7;
static uint8_t digit[11] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};
static uint8_t pos[3]= {0xE7,0xEB,0xF3};
unsigned long timer=0, timerPrev=0, thinkingTime=1000;
void output(int f)
{
hundreds=f/100;
tens=(f-hundreds*100)/10;
ones=f-hundreds*100-tens*10;
if (hundreds==0) hundreds=10;
if (hundreds==10 && tens==0)tens=10;
digitalWrite(reg, LOW);
SPI.transfer(digit[hundreds]);
SPI.transfer(pos[0]);
digitalWrite(reg, HIGH);
delay(delayTime);
digitalWrite(reg, LOW);
SPI.transfer(digit[tens]);
SPI.transfer(pos[1]);
digitalWrite(reg, HIGH);
delay(delayTime);
digitalWrite(reg, LOW);
SPI.transfer(digit[ones]);
SPI.transfer(pos[2]);
digitalWrite(reg, HIGH);
delay(delayTime);
}
void loop()
{
timer=millis();
if(timer-timerPrev>thinkingTime){
//вычисляем элемент последовательности Фибоначчи номер n
f=fnn+fn;
//элементу номер n-2 присваиваем значение элемента n-1
fnn=fn;
//элементу номер n-1 присваиваем значение элемента n
fn=f;
//запоминаем время
timerPrev=timer;
//увеличиваем время "раздумий"
thinkingTime+=f;
//если f дорос до 1000, возвращаем все как было
if (f>1000){f=0;fn=1;fnn=0;thinkingTime=1000;}
}
//выводим число
output(f);
}
Далее по теме Arduino и SPI
А где ва нашли 3x7-сегметный элемент для Fritzing? Не поделитесь?:)
ОтветитьУдалитьСам рисовал. Могу потом прислать
УдалитьВышлите пожалуйста.... А если не трудно, то всю схему Fritzing. Dr_Semyon@mail.ru
УдалитьНе могли бы Вы выслать, весь инет перелазил, не могу найти. Хочу сделать шилд...
УдалитьСкажите размеры 7-сегментника и расстояние между рядами ног.
Удалить