Карма: 14
Рейтинг сообщений: 121
Зарегистрирован: Сб май 21, 2016 11:04:52 Сообщений: 2977 Откуда: Беларусь
Рейтинг сообщения:0
Коллега Martian, с нашего форуму прислал мне платы на hc595 и индикаторы к ним. Ну вот не прошло и пару лет как мне захотелось собрать на них что-то полезное. К сожалению, разводка индикаторов оказалась нескольько другой чем известные на алике аналоги, например, Tm74hc595. Но Martian так же прислал схему с примером для АВР где были указаны шестнадцатиричные коды цифр.
Поэтому решил гугллением , интернет -примерами, и чатом GPT написать код , который заряжает эти дисплейи. Тот же чатГПТ, конечно, обладается огромной эрудицией, но в плане интеллекта, не сильно убежал вперед. Целый вечер я уговарил его написать функцию для вывода на этот модуль чисел с плаввающей точкой, но так и не добился от него толку. В итого написал сам условно рабочий достаточно костыльный код, в надежде что он поправит его и оптимизирует, но Чат наоборот изувечил его логику и сделал непригодным. Это достаточно странно, я то думал, что неверно ставлю задачу, но всего лишь повторить логику имеющегося кода, оптимизировав где надо - та что может быть проще.
Короче, получился вот такой код. Поэтому приглашаю энтизуастов обратить внимание на функцию displayFloatOnIndicators(float number) и может быть что-то подсказать. Допустим 25.10 выводится как 25.10, ахотелось бы как _25.1
Код:
const int DATA_PIN = 5; // Пин для данных (подключен к PADDR0) const int CLK_PIN = 7; // Пин для сигнала тактового импульса (подключен к PADDR1) const int LATCH_PIN = 6; // Пин для сигнала защелкивания (подключен к PADDR3)
void loop() { static float currentNumber = 1.0; // Текущее значение для отображения static unsigned long lastDisplayTime = 0; // Переменная для отслеживания времени
if (millis() - lastDisplayTime > 100) { // Обновляем значение каждые 500 мс currentNumber+= 0.01; // Увеличиваем текущее значение if (currentNumber > 9999) currentNumber = 0; // Сбрасываем, если достигнуто максимальное значение lastDisplayTime = millis(); // Обновляем время последнего обновления }
//displayFourDigitNumber(currentNumber); // Отображаем текущее число displayFloatOnIndicators(currentNumber); //displayFloatOnIndicatorsII(25.3); }
void displayFourDigitNumber(int number) { // Функция вывода целочисленных чисел // Переводим число в строку char buffer[5]; sprintf(buffer, "%04d", number); // Форматируем число
int startIndex = 0; // Сдвигаем в право, не выводим незначащие нули слева, например 25 выведется как __25, а не 25__ while (buffer[startIndex] == '0') { startIndex++;}
// Отображаем каждую цифру на соответствующем индикаторе for (int i = startIndex; i < 4; ++i) { char digit = buffer[i];
void displayFloatOnIndicators(float number) { // Суть алгоритма. Вывести цифры, но без точки (берем из массива цифр) led_table[] // Определить позицию точки // Из массива цифр с точками led_table_dot[] вывести ПОВТОРНО цифру, но уже с точкой char bufferNoDot[10]; // Сюда положу цифры без точки char buffer[10]; // Здесь будут цифры с точкой dtostrf(number, 4, 3, buffer); // Преобразуем число типа float в строку с 3 знаками после запятой //dtostrf(number*1000, 4, 0, bufferNoDot); // Число без точки
// Определяем позицию точки в строке int dotPosition = strchr(buffer, '.') - buffer; // dtostrf(number*pow(10,(4-dotPosition)), 4, 0, bufferNoDot); // тут я умножаю на степень 10 найденну по позиции точки что бы получить целое число (МФТИ, ФПМИ- простите ребята) int dotDigit=buffer[dotPosition-1]; // значение цифра за которой идет точка (в конце будет ее выводить)
int startIndex = 0; // Здесь искал незначащие нули впереди( для вывода чисел int) while (bufferNoDot[startIndex] == '0') { startIndex++;}
// Отображаем каждую цифру на соответствующем индикаторе из массива без точек bufferNoDot for (int i = startIndex; i < 4; ++i) { char digit = bufferNoDot[i];
Ну, я не ориентировался на какие-то другие библиотеки, вообще, преследовалось две цели - как можно проще сделать трассировку платы (этим объясняется некий хаос в сигналах на схеме) и получить максимум универсальности - то есть, вывести ещё дискретные светодиоды, в том числе по центру платы для двоеточия часов. К тому же, это было выдрано из другого моего проекта, где являлось частью панели управления. Такой вот история...
Но проблем быть не должно. Существует всего два массива:
Код:
цифры {0x06,0x9f,0xa2,0x92,0x1b,0x52,0x42,0x9e,0x02,0x12} и разряды {0xf7,0xfd,0xfb,0xfe}
, притом старшая тетрада разрядов - дополнительные выходы и правило: сначала посылаем выбор разряда, затем цифру. Чтобы отобразить точку, достаточно в цифре сбросить бит ( & 0xFD):
Карма: 14
Рейтинг сообщений: 121
Зарегистрирован: Сб май 21, 2016 11:04:52 Сообщений: 2977 Откуда: Беларусь
Рейтинг сообщения:0
да , я не знал про сбросить бит ( & 0xFD): елси бы был более смышленым мог бы его вычислить. Но попался исходник с двумя массивами и я решил использовать этот вариант Конечно, нужно переделать , и память освободится а тка наворотил тут на 5кб. А хочется в Атмегу 8 запихнуть на ардуиновском коде.
Добавлено after 1 minute 56 seconds: идеально было бы конечно изменить библиотеку Гайвер, по моему, там можно даже бегущую строк замутить.
эта функция постоянно выводит изображение на индикатор. Но помимо этого, онав ещё и форматирует currentNumber, переданный ей, для вывода. А если он не изменился? Тогда она постоянно делает лишнюю работу. Я бы сделал следующее: 1) избавится от типа float, вряд ли он тут нужен, но ввёл бы буфер индикатора (он и так уже есть, но бесполезный); 2) форматировать только когда необходимо; 3) вывод на индикатор сделать в прерывании таймера (это необязательно);
То есть, функция вывода на индикатор упростилась бы до:
Код:
unsigned char buffer_indicator[4]; // "память дисплея" ... void displayFloatOnIndicators() { for (int i = 0; i < 4; i++) { dig(dig_n[i], buffer_indicator[i]); // Отображаем цифру } }
А вот заполнение "памяти дисплея" только когда currentNumber изменился, и там же и точку формировать.
Добавлено after 42 seconds: Бегущую строку и тут можно замутить, нет никаких ограничений.... просто надо написать
Карма: 14
Рейтинг сообщений: 121
Зарегистрирован: Сб май 21, 2016 11:04:52 Сообщений: 2977 Откуда: Беларусь
Рейтинг сообщения:0
ок, пока только добавил точку и избавился от второго массив, который с точками:
Код:
void displayFloatOnIndicators(float number) { // Суть алгоритма. Вывести цифры, но без точки (берем из массива цифр) led_table[] // Определить позицию точки из массива с точкой
char bufferNoDot[10]; // Сюда положу цифры без точки char buffer[10]; // Здесь будут цифры с точкой dtostrf(number, 4, 3, buffer); // Преобразуем число типа float в строку с 3 знаками после запятой //dtostrf(number*1000, 4, 0, bufferNoDot); // Число без точки
// Определяем позицию точки в строке int dotPosition = strchr(buffer, '.') - buffer; // dtostrf(number*pow(10,(4-dotPosition)), 4, 0, bufferNoDot); // получить целое число и положить в массив int dotDigit=buffer[dotPosition-1]; // значение цифра за которой идет точка (в конце будет ее выводить)
int startIndex = 0; // Здесь искал незначащие нули впереди( для вывода чисел int) while (bufferNoDot[startIndex] == '0') { startIndex++;}
// Отображаем каждую цифру на соответствующем индикаторе из массива без точек bufferNoDot for (int i = startIndex; i < 4; ++i) { char digit = bufferNoDot[i]; if (i==dotPosition-1) {dig(dig_n[i], led_table[digit - '0'] & 0xFD);}// Отображаем цифру c точкой else { dig(dig_n[i], led_table[digit - '0']);} // Отображаем цифру
} // Когда все цифры выведены Отображаем цифру с точкой ПОВТОРНО // dig(dig_n[dotPosition-1], led_table_dot[dotDigit - '0']);
Обычно есть два варианта: 1. сделать удобную для конкретных деталюшек схему, но усложнить создание программы к ней 2. сделать извратную схему, но предельно упростить задачу для программиста... У нас, к сожалению, первый вариант... Динамическая индикация предусматривает гашение отображаемой информации на время смены данных (по сегментным или по позиционным ключам). Это нужно для подавления паразитной подсветки дисплея. Тут же или "холостой цикл" загрузки или игра с управляющими сигналами - оба варианта по самодельному протоколу загрузки данных в регистры делать придется. Насчет массивов... вполне достаточно одного массива "видеопамяти" в ОЗУ да знакогенератора в ПЗУ с фрагментом программной перекодировки данных. Другое дело, как быть с преобразованием кодов для второго регистра (ключи знакомест + служебные индикаторы) - это уже еще одна дополнительная функция предподготовки данных (опять же "нестандартная"). Ну и сам сканер дисплея на отдельном тайм - интервале (у 328й меги просто, а вот для 8й ... повозиться надо). "Запятая" это уже наиболее простое из выше перечисленного (особо, ежли имелся ввиду DS18x20). Посмотреть как то для 328й выглядит (с моего представления)... как учебный вариант...
Последний раз редактировалось BOB51 Вс фев 11, 2024 23:52:21, всего редактировалось 1 раз.
BOB51, не к сожалению Извратиться с программой для меня гораздо проще (тем более, что надо-то один раз), чем сверлить лишнее переходное отверстие, поэтому у меня приоритет очень часто отдаётся железяке. Что же касается всего остального, то тут обычные сдвиговые регистры, и работа с ними ничем не отличается от каких-то иных проектов, хоть к SPI подключить, хоть так, к портам как выше... Про паразитную подсветку верно. Я не помню, насколько она у меня сильно проявлялась, ток светодиодов достаточно небольшой, вроде, у себя я гашение не делал.
Последний раз редактировалось Martian Пн фев 12, 2024 09:43:40, всего редактировалось 1 раз.
Вот в той "паразитке" и основное отличие от аппаратного SPI. А поскольку управляющие линии регистров общие для обеих корпусов (и Z-состояние мы в схеме не используем) придется пойти на дополнительный изврат. "паразитка" может не всегда слишком явно проявляться - но на нерву через время таки действует. В принципе... Для начала скотчик обработчика с транспортным протоколом сделать надо. А уж затем его или в отдельный файл или библиотекой оформить. Ежли не особо спешно - вполне можно поучаствовать...
Динамическая индикация предусматривает гашение отображаемой информации на время смены данных (по сегментным или по позиционным ключам). Это нужно для подавления паразитной подсветки дисплея.
зачем гасить? сначала загнали всю новую информацию в сдвиговые регистры, а потом одним импульсом переписали всё в выходные регистры. и никакой паразитки не будет
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Карма: 14
Рейтинг сообщений: 121
Зарегистрирован: Сб май 21, 2016 11:04:52 Сообщений: 2977 Откуда: Беларусь
Рейтинг сообщения:0
вот попытался поправить код Гайвера, заменил на свои цифры, кстати здесь 2 массива, с точкой и без. А вот где поправить переключение анодов пока не понятно
Спойлер
Код:
/* TM74HC595Display.cpp - Library Updated by AlexGyver 06.02.2017 (added float_dot and int_dot functions) */
будет Причина проста - операция смены сегментов (знак) производится вовсе не одновременно со сменой активного разряда. Мы можем одномоментно сменить знак, но на крошечную долю времени (до смены разряда, для которой этот новый знак предназначен), на табло будет светиться неверный для разряда знак. Для очень ярких индикаторов и не очень высокой частоты динамики очень даже заметно. Кстати, протеус у меня вообще какую-то дичь показывал, пока я перед сменой сегментов не выключал все разряды.
я с этими регистрами не работал, поэтому могу и ошибаться. но по логике запись в сдвиговые регистры не влияет на выходные регистры. и у меня протеус валял дурака даже при прямом управлении от МК 4-разрядным 7-сегментником без промежуточного гашения при переключении на другой разряд. ну, это похоже на глюк протеуса.
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Эта крошечная доля времени очень крошечная (разница между переключением двух транзисторов при одновременном управлении), а возможности человеческого глаза не беспредельны. Я гашение не делал (выше опечатался).
Starichok51 писал(а):
запись в сдвиговые регистры не влияет на выходные регистры.
BOB51, я что-то не улавливаю, чем поможет Z... и только сейчас увидел, что сброс выведен зря - вероятность его использования здесь стремится к нулю
Если бы активным уровнем что у сегментов, что у позиционных ключей был 0 (при принудительной блокировке их единицей), то перевод в Z-состояние однозначно блокировал бы индикацию. Собственно так дело с позиционными ключами на схеме. Да и сегменты от утечки по Z вряд-ли засветятся. Относительно функционала перезаписи - он то почти одновременный, но время задержки включения и отключения как у транзисторных ключей, так и у светиков имеется. Возможно особо заметно подсвечивать не будет - но то на конкретной железяке проверять надо. В том числе и на конечном варианте (фон, светофильтр).
Да, но всё равно ведь потребуется действие - перевод в Z, и на это сожрётся нога мк, что не всегда хорошо... Фактически, мы потратим вывод ради очень небольшого выигрыша в программе, если вообще будет выигрыш.
Тут уже что удобнее - или лишний цикл загрузки (расход времени) или лишняя лапка - расход выводов. Да и с Z-состоянием можно без линии сброса обойтись (и случайного "все нули" при подаче питания избегаем)... Пока морочу с тем, что уже нарисовано набросок прожки. Для начала примитив - там надо с видеопамятью погдядеть...
Я сталкивался с "паразиткой" с ключами на ULN и на полевых транзисторах. Решил так: на сегменты и на общие подавал неактивный уровень. Время подбирал, пока засветка не исчезнет. Алгоритм: заходим, ставим неактивный уровень, задержка, вывод нужных значений.
динамическая индикация... фу... фу.. фу... в глазах рябит)) лучше добавить к LED индикатору буфер... типа D-триггер... и писать в буфер... и добавить отдельный вывод "С" (запись в буфер)... будет статическая индикация. для глаз приятно))
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 28
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения