Четырехразрядный семисегментный индикатор на 74HC595
- Войдите на сайт для отправки комментариев
Сделал четырехразрядный семисегментный индикатор на двух сдвиговых регистрах. В железе опробовал.
Схема:
Не очень "красивое" соединение между HC595 и индикатором обусловлено наличием микросхем в DIP корпусе желанием сделать одностороннюю плату с минимумом перемычек.
Плата получилась такая:
Код оформил в виде библиотеки:
Файл SevenSegmentDigitsDisplay.h
/* Описание библиотеки для работы с индикатором с общим катодом и двумя сдвиговыми регистрами 74НС595 */ #ifndef SevenSegmentDigitsDisplay_H #define SevenSegmentDigitsDisplay_H #include <Arduino.h> #define INDICATOR_DIGITS 4 //Количество разрядов индикатора #define INDICATOR_SYMBOLS 16 //Количество описанных символов, включая минус, точку и тп class SevenSegmentDigitsDisplay { public: // Инициализация дисплея. Задаются пины для clock, latch, data SevenSegmentDigitsDisplay(int clock_pin, int latch_pin, int data_pin); // Функция для перерисовки дисплея // Должна вызываться регулярно в loop void indicateValue(); // Функция для расчета значениий по разрядам. // Вызывается только при изменении отображаемого значения. Точность по умолчанию 0. void calculateValue(float val); void calculateValue(float val, byte prec); // Функция для для установки конкретного символа в разряд. Разряд от 0 до INDICATOR_DIGITS. Символ от 0 до INDICATOR_SYMBOLS из symbols[INDICATOR_SYMBOLS]. // Установленное значение будет затерто при вызове calculateValue void setDiditValue(byte digit, byte symbol); private: int _clock; // SHCP отвечает за ШИМ. Когда 1 из данных забирается бит, 0 пропускается. Синхронизация потока. На 74HC595 пин 11 int _latch; // STCP отвечает за защелкивание. Когда биты переданы, посылаем сигнал, что окончили передачу. На 74HC595 пин 12 int _data; // DS отвечает за поток битов(данные). На 74HC595 пин 14 byte _currentDigit; //Текущий отображаемый разряд индикатора byte _countDigitInication; //Количество показов подряд одного разряда. Нужно, чтобы частота выключения была много меньше частоты свечения. byte values[INDICATOR_DIGITS+1]; //Массив для значений индикатора по разрядам + 1 значение, соотв. разряду с точкой }; #endif /* SevenSegmentDigitsDisplay_H */
Файл SevenSegmentDigitsDisplay.cpp
/* Описание библиотеки для работы с индикатором с общим катодом и двумя сдвиговыми регистрами 74НС595 */ #include "SevenSegmentDigitsDisplay.h" #if defined(SevenSegmentDigitsDisplay_H) const byte digits[INDICATOR_DIGITS] = { //Массив значений для разрядов. Подключение q3->1, q2->2, q1->3, q4->4. Разряд зажигается низким уровнем. //3214 // Выводы 3,2,1,4 74HC595 на разряды 1,2,3,4 индикатора. Если будет 8-разрядный индикатор, то нужно добатить еще 4 значения B11110111, B10111111, B11011111, B11101111 }; //Пины для управления разрядами индикатора. 0 - низкий уровень подать на катод. const byte symbols[INDICATOR_SYMBOLS] = { //Массив значений для различных символов, подключение q0->G, q1->C, q2->DP,..,q7->B, индикатор с общим катодом //GC.DEAFB //Подключение выводов 74HC595 к индикатору B01011111, //0 B01000001, //1 B10011101, //2 B11010101, //3 B11000011, //4 B11010110, //5 B11011110, //6 B01000101, //7 B11011111, //8 B11010111, //9 B00000000, // (пусто) B00100000, //. (точка) B10000000, //- (минус) B10011110, //E B10001000, //r B11001011 //H }; SevenSegmentDigitsDisplay::SevenSegmentDigitsDisplay(int clock_pin, int latch_pin, int data_pin) { _clock = clock_pin; _latch = latch_pin; _data = data_pin; pinMode(_clock, OUTPUT); pinMode(_latch, OUTPUT); pinMode(_data, OUTPUT); _currentDigit = 0; _countDigitInication = 0; } // Функция для расчета значениий по разрядам. // Вызывается только при изменении отображаемого значения. Точность по умолчанию 0. void SevenSegmentDigitsDisplay::calculateValue(float val){ //Точность по умолчанию равна 0 calculateValue(val, 0); } void SevenSegmentDigitsDisplay::calculateValue(float val, byte prec){ byte i = INDICATOR_DIGITS; //Зачистить все значения while (i--) { values[i] = 10; } long intval = (long)(val * pow(10, prec)); if (intval >= 0) { i = INDICATOR_DIGITS; } else { i = INDICATOR_DIGITS - 1; //Если число отрицательное, то 1 разряд занимает минус } //Проверим влезает точность и число в разряды индикатора if (prec >= i || intval > (long)pow(10, i)) { values[2] = 13; values[1] = 14; values[0] = 14; return; } values[INDICATOR_DIGITS] = prec; //Поставить точку в нужном месте //Если все влезло, распределим число по разрядам byte prevval = 10; //Значение предыдущего старшего разряда, чтобы убрать ведущие нули i = INDICATOR_DIGITS; while (i--) { values[i] = intval / pow(10, i); intval = intval - values[i] * pow(10, i); //Уберем ведущие нули if (prevval == 10 && values[i] == 0) { values[i] = 10; } prevval = values[i]; } } // Функция для перерисовки дисплея // Должна вызываться регулярно в loop void SevenSegmentDigitsDisplay::indicateValue(){ if (!_countDigitInication) { // _countDigitInication++; return; } byte symbol = symbols[values[_currentDigit]]; //Выбрать символ для отображения из таблицы символов if (values[INDICATOR_DIGITS] && values[INDICATOR_DIGITS] == _currentDigit) { //Нужно поставить точку в текущем разряде symbol |= symbols[11]; } //Погасить все разряды digitalWrite(_latch, LOW); shiftOut(_data, _clock, LSBFIRST, B00000000); // Символы shiftOut(_data, _clock, LSBFIRST, B11111111); //Разряды digitalWrite(_latch, HIGH); //Для начала записи в 74HC595 нужно подать 0 на STCP (открыть) digitalWrite(_latch, LOW); // С данными выпускаем поток битов на DS синхроннированные с SHCP // Показать нужный символ shiftOut(_data, _clock, LSBFIRST, symbol); // Зажечь нужный разряд shiftOut(_data, _clock, LSBFIRST, digits[_currentDigit]); // Когда последний бит передали, закрываем digitalWrite(_latch, HIGH); // Перейти к следующему разряду _currentDigit++; _currentDigit = _currentDigit % INDICATOR_DIGITS; } // Функция для для установки конкретного символа в разряд. Разряд от 0 до INDICATOR_DIGITS. Символ от 0 до INDICATOR_SYMBOLS из symbols[INDICATOR_SYMBOLS]. // Это значение будет затерто при вызове calculateValue void SevenSegmentDigitsDisplay::setDiditValue(byte digit, byte symbol){ if (digit < INDICATOR_DIGITS) { values[digit] = symbol; } } #endif
Файл для теста DigitsIndicator.ino
#include "SevenSegmentDigitsDisplay.h" volatile float dfreq; unsigned long tm; SevenSegmentDigitsDisplay disp(2, 3, 4); void setup() { // put your setup code here, to run once: Serial.begin(115200); // connect to the serial port Serial.println("Test begin"); dfreq = 123.0; disp.calculateValue(dfreq, 1); //Заполнить значение для индикатора tm = millis(); } void loop() { // put your main code here, to run repeatedly: disp.indicateValue(); if (millis()-tm > 200) { tm = millis(); dfreq = dfreq + 0.1; Serial.println(dfreq); disp.calculateValue(dfreq, 1); //disp.setDiditValue(3,15); } }
Библиотека работает с положительными и отрицательными числами. Так же позволяет устанавливать спец. символы, типа H, E и т.п.
Если кому-то нужно, могу выложить проект в KiCad для модификации под другой индикатор или под два четырехразрядных индикатора.
Принимаю здоровую критику по улучшению платы и кода, т.к. планирую сделать несколько индикаторов для разных проектов(есть некоторый запас комплектующих).
7 резисторов (или 8 с точкой) на сегментах были бы лучше чем 4 общих. Хотя, дело вкуса, пожалуй. С моей стороны - сие значимо т. к. я даже th резисторы ставлю с той же стороны платы, как smd.
7 резисторов (или 8 с точкой) на сегментах были бы лучше чем 4 общих. Хотя, дело вкуса, пожалуй. С моей стороны - сие значимо т. к. я даже th резисторы ставлю с той же стороны платы, как smd.
Согласен, зря экономил :) . Уже заметил, что 1 получается ярче, чем 8. Наверно переделаю.
Еще по программе обнаружил, что зря сначала гашу все разряды. Это лишнее, только занимает время.
7 резисторов (или 8 с точкой) на сегментах были бы лучше чем 4 общих. Хотя, дело вкуса, пожалуй.
Да, нет, не дело вкуса - это грубая схемотехническая ошибка.
Да, нет, не дело вкуса - это грубая схемотехническая ошибка.
Грубая - это когда все сгорело, а я сознательно шел на это, понимая, что яркость будет немного отличаться. Увидев вживую, решил переделать.
Грубая - это когда все сгорело,
Это лишь вопрос времени. Диоды разные, один какой-то будет тянуть одеяло на себя, когда он сгорит, ток на другие увеличится и т.п.
По крайней мере в классических учебниках это называют грубой ошибкой, а уж сознательно Вы её совершали и бессознательно - это Ваше личное дело.
Это лишь вопрос времени. Диоды разные, один какой-то будет тянуть одеяло на себя, когда он сгорит, ток на другие увеличится и т.п.
В даташите максимальный прямой ток через 1 сегмент = 25мА. 5В/220Ом = 23мА, поэтому ничего не сгорит.
Вы просили замечания? Вам написали, что в классических учебниках это называется грубой ошибкой. Вы в ответ спорите и что-то доказываете. Впредь, пожалуйста, честно пишите - не "Принимаю здоровую критику", а "Похвала приветствуется". Народ и не будет с критикой напрягаться.
Теперь по поводу Вашего расчёта
5В/220Ом = 23мА, поэтому ничего не сгорит.
Вы не учли падение напряжения на светодиоде. Допустим - 2В. Значит ток у Вас не 23мА, а всего лишь 14мА. Причём не через сегмнент, а через общий вывод. Т.е. это на 8 светодиодов (если будет цифра 8 с точкой). Не маловато? По 1,75 на брата?
Кстати, если будете увеличивать ток, то не забывайте, что у 595 ограничение 70мА на корпус, т.е. если Вы захотите сделать, скажем хоть по 10мА на светодиод, то Ваш верхний 595-ый будет работать с перегрузкой (при "8 с точкой" - все 80мА). Так что там тоже считайте внимательно.
Впредь, пожалуйста, честно пишите - не "Принимаю здоровую критику", а "Похвала приветствуется". Народ и не будет с критикой напрягаться.
Я вроде же написал, что "Согласен, зря экономил".
Спасибо за комментарий.
Переделал на 8 резисторов. Пришлость изменить пару соединений между индикатором и 74HC595.
Соответственно немного изменилась библиотека.
Схема:
Плата:
Код SevenSegmentDigitsDisplay.cpp
Переделал на 8 резисторов.
Ну, я же Вам писал
Давайте посчитаем, что Вы сделали:
Резисторы у Вас по 510 ом. Хорошо. Падение напряжения на светодиоде считаем 2В (хотя, если он красный, то там даже меньше - 1,8В). Таким образом ток через светодиод (5-2) / 510 = 5,88мА. Если мы включаем все 8 сегментов (цифра "8" с точкой), то ток через общий провод разряда получается 5,88 * 8 = 47мА. Значит, через пины 1-4 нижнего по схеме сдвигового регистра может идти до 47 мА. Пока правильно?
Теперь открываем даташит на сдвиговый регистр и читаем "absolute maximum ratings" ... "Continuous output current, IO (VO = 0 to VCC) - ±35 mA".
Т.е. Вы на треть превышаете ток, заявленный как абсолютный максимум. Конечно, сразу не сгорит, тем более, что при динамической индикации, Вы включаете его ненадолго, но ... вопрос времени.
Т.е. Вы на треть превышаете ток, заявленный как абсолютный максимум
Согласен. В военное время необходимо использовать резисторы не менее 1K, но для гражданского применения сойдет и 510, т.к. светятся они не долго и по очереди.
Схема для военного применения:
Мне почему-то кажется, что вы не очень-то рады замечаниям, хотя сами о них просили. Если чё, так Вы скажите, у меня есть чем заняться, кроме как помогать Вам.
Для военного времени Ваша схема не очень подходит. 3 мА на светодиод - яркость-то слабенькая, при ярком свете (от ядерной вспышки, например) ни хрена не видно будет.
У Вас же у нижнего регистра четыре выхода пропадают, кто Вам мешает запараллелить по два вместе? ну, если боитесь, то через маленький резистор типа 1 Ом? Если Вы так сделаете, то можно ещё и увеличить яркость, используя резисторы не 510 Ом, а 390 Ом - вполне достаточно. Ток восьми светодиодов будет 8*3/390 - 62мА - всё укладывается.
Это как раз то, о чём я Вам говорил - "считайте внимательнее". Удачи!
Мне почему-то кажется, что вы не очень-то рады замечаниям, хотя сами о них просили.
Почему не рад? Очень даже рад. Очень здравые замечания.
Про "запараллелить" выводы - хорошая мысль, попробую без доп. резисторов, ибо ставить их некуда. Вроде с выводами ничего не должно случиться.
Будет вариант "для долговременного постоянного использования в светлое время суток в странах с тропическим климатом".
Если автор ещё не спит.
В библиотеку можно было бы и обработку переменной вставить.
Индикатор четырёхразрядный показывает первые четыре числа с плавающей запятой и гашением незначащих нолей допустим в течение 1,5 сек. А следующие 1,5 сек показывает знак числа и показатель степени.
Алгоритм примерно такой:
Если Х>999 999 999 пишем Err
Если Х < -999 999 999 пишем -Err
Если Х < 0.000 000 001 пишем 0,0,0,0,
Если Х > 9999 то Х/1000 и показатель степени d = d+3
Повторяем, если надо.
Если Х < 0.0001 то Х= Х*1000 и показатель степени d = d -3
Если Х<999 то Х =Х*10 и число разряда десяток ставим с запятой.
С сотнями также.
Всё.
Получаем обработку любой переменной на все случаи жизни.
Я такой алгоритм сотворил. Учебным быдлокодом. Вот библиотеку сотворить нет опыта.
Если автор ещё не спит.
Гляньте на дату последнего сообщения в теме :)
Если автор ещё не спит.
....
Получаем обработку любой переменной на все случаи жизни.
Я такой алгоритм сотворил. Учебным быдлокодом. Вот библиотеку сотворить нет опыта.
Ну почему же спит... Уже проснулся :)
Схема, плата, код библиотеки в этом сообщении: