бит ОЗУ к реальным сегментам для индикатора на основе контроллера НТ1621
с применением адуринки.
Довольно часто попадаются ЖКИ дисплейчики с различными конструктивами и разводкой сегментов индикатора относительно внутреннего содержимого ОЗУ контроллера HT1621.
Производитель может подключать сегменты в любом, выгодно-оптимальном с точки зрения производства и/или особенностей применяемого в устройстве-доноре программного обеспечения.
По факту нам уже достается какое-то готовое изделие, которое надо с максимальной пользой для себя обратить в прикладную самоделку.
Протокол и подключение внешних шин управления обычно явно известны, в вот какому сегменту какой бит ОЗУ соответствует и в какой последовательности они на реальном дисплее следуют — это и есть задача прикладного теста.
Далее на основе полученных данных можно и конкретный прикладной знакогенератор строить и соответствующие прожки обработки данных.
Для теста привязки нам понадобится ардуинка (нано или про-мини или еще какая из простейших), собственно сам дисплейчик, терминалка (встроенная в ардуино IDE или стороннего производителя) и распечатка шаблона с сегментным полем и полем адресного пространства ОЗУ HT1621.
Суть теста.
Прогоняя по ОЗУ последовательно активную единичку, сопровождаемую выводом в окно терминала записи о номере ячейки с активной единицей отмечаем карандашиком на карте сегментов и на карте адресов ОЗУ соответствие вручную.
Информация в окошке терминала обновляется через 1-2 секунды в кольцевом режиме.
Заранее стоит отметить, что не всем позициям ячеек ОЗУ будет соответствовать физический сегмент ЖКИ.
Также не следует ожидать непрерывности размещения данных — это уж как изготовитель заложил, так и будет.
В самом тесте используется программный ногодрыг.
Для работы с загрузчиком НТ1621 принимаем упрощенный протокол, каждый раз высылающем в дисплей ВЕСЬ массив ОЗУ (а не потетрадную загрузку) с всего лишь одним командным словом. Также используется однократно блок начальной инициализации дисплея и функции Serial.
ОЗУ индикатора представляем не как тетрады, а как единое пространство в 128 бит, представленное 16 байтовым буфером outbuf.
Итак...
Пустографка (в моем случае 10-позиционный 7-сегментный индикатор (файл kasdis_stest0 в splan) ).
http://img.radiokot.ru/files/20529/1kjtd4jdeq.GIF
Собственно схемка макета:
http://img.radiokot.ru/files/20529/1kjtd5hok2.GIF
она же в файле ard168_stest в формате splan в приложениях.
Теперь собственно сама программа...
Спойлер
Предполагается вывод на консоль терминала данных о положении текущего активного сегмента.С этой части программы и начнем.
Собственно нам необходимо с определенной периодичностью выводить на дисплей терминала сообщение типа « в данный момент активен сегмент соответствующий биту» и значение номера бита в формате 0хNN.
Благо предоставленный средой арсенал средств позволяет это сделать весьма просто.
Прожку для начала будем писать в самом примитивном варианте, без заголовочных и подключенных файлов.
В верхней части оставим местечко для объявления констант и самодельных функций (легче будет при «окультуривании» в дальнейшей доработке программы).
У меня там закомментированная строчка для будущей доработки.
В разделе setup активируем последовательный интерфейс терминала:
Код: Выделить всё
void setup() {
Serial.begin(9600); // запуск последовательного интерфейса
// put your setup code here, to run once:
}В разделе основной прожки создадим
Код: Выделить всё
void loop() {
// put your main code here, to run repeatedly:
for (byte a=0; a<256; a++)
{
Serial.println("в данный момент активен сегмент, соответствующий биту");
Serial.print("0x"); Serial.print(a,HEX); Serial.println("");
delay(500);
}
}Проверим, скомпилируем и зальем этот тест в ардуинку.
После запуска должна выводится строчка сообщения и текущий двоичный код байта счетчика в диапазоне 0x00 – 0xFF .
При компиляции вылезло замечание по поводу byte в качестве определения типа для счетчика.
Пришлось для “кошерности» поменять на int.
Итогом получилось
Код: Выделить всё
for (int a=0; a<256; a++)в остальном тест вывода контрольного сообщения прошел вполне успешно.
Теперь можно приступить к созданию собственно теста.
Объявления констант, переменных и определение функций public вида размещаем в верхней части исходника
ДО вызова
void setup(){ }
а реализацию функций public вида пишем ниже завершающей скобки
void loop() {
}
Тем самым наши заготовки подпрограмм будут выведены за пределы основных циклов программы и более подготовлены к выносу во внешние файлы.
Итак...
начнем со схемки.
Подключение дисплея проводится во трем линиям:
WR\ - строб записи конкретного бита, активный уровень 0, запись выполняется по заднему фронту (переход из 0 в 1). Исходное состояние =1 (линия деактивирована).
DATA — собственно линия данных. Исходное состояние =1 (линия деактивирована). Во время обмена на данной линии находится состояние текущего бита данных.
CS\ - строб выборки кристалла,активный уровень 0 дает возможность доступа к линиям WR\ и DATA со стороны управляющей системы. Исходное состояние =1 (линия деактивирована).
Сама HT1621 имеет весьма развитую систему команд.
Собственно даташит в файле HT1621.pdf приложений.
Однако для тест-контроля и простейшего применения достаточно команд начальной инициализации и блочной записи.
Исходя из вышеуказанного...
Объявляем константы (НЕ переменные!) в соответствии с именами линий управления/ввода
Код: Выделить всё
#define data 10 // соответствие номеру вывода D10
#define wr 11 // соответствие номеру вывода D11
#define cs 12 // соответствие номеру вывода D12объявляем константы в соответствии с применяемыми в тесте командными словами
Код: Выделить всё
#define biass B00101000 // LCD 1/2 bias option, 4 common option
#define clc_on 1 // пуск системного генератора
#define lcd_on 3 // turn ON LCD bias generanorобъявляем переменные общего доступа
Код: Выделить всё
byte ln_dat = data; // переменная со значением номера вывода линии данных
byte ln_wr = wr; // переменная со значением номера вывода линии строба записи
byte ln_cs = cs; // переменная со значением номера вывода линии строба выборки кристалла
byte bufout[ ] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // массив буфера данных для пакетной загрузкиобъявляем примитив-функции для операций обмена
Код: Выделить всё
// начальный статус линий интерфейса (он же и заглушка завершения пакета)
void stop_pack ();
// строб сопровождения данных
void data_clock();
// запись заголовка с составляющими командными словами для активации режима дисплея
void wr_uksda(byte a,byte b,byte c);
// запись байта команды
void trs_com(byte);
// запись пакета bufout (в комплекте с командным заголовком)
void wr_ksda();
// блок вращения бита в массиве
void rotor_16();В разделе setup добавляем (с расчетом на возможне перемещение в блок сетапа драйвера для библиотеки в будущих вариантах реализации программы):
Код: Выделить всё
pinMode(ln_dat, OUTPUT);
pinMode(ln_wr, OUTPUT);
pinMode(ln_cs , OUTPUT);Теперь переходим к самим функциям, вернее в самый них исходника и создаем РАЗДЕЛ РЕАЛИЗАЦИИ:
Код: Выделить всё
/*
здесь размещаем реализацию наших функции - подпрограммок
используемых в вышерасположенных void setup() , void loop() а также
наши самодельные функции поменьше, используемые при реализации более крупных
собственно всего того, что заявлено выше в разделе
описанием (определением) применяемых в программе самодельных функций
*/
// начальный статус линий интерфейса (он же и заглушка завершения пакета)
//подразумевается, что линия ln_wr уже установлена в 1 или при начальной
// инициализации или по завершении строб-импульса
void stop_pack ()
{
digitalWrite(ln_dat, HIGH);
delayMicroseconds(6);
digitalWrite(ln_cs, HIGH);
}
// строб сопровождения данных
void data_clock()
{
digitalWrite(ln_wr, LOW);
delayMicroseconds(3);
digitalWrite(ln_wr, HIGH);
delayMicroseconds(3);
}
Несколько о задержках — ардуинка на основе меги с кварцем в 16 МГц штука весьма быстроходная по сравнению с НТ1621. Посему добавлены маахонькие задержки в операциях дрыголапа.
Почему именно программный дрыголап, а не функция Serial или shiftOut ?
В одном случае аппаратная привязка к линиям обмена да и сигналы обмена иные, в другом не совсем байтовый протокол и возможность свободно управлять нужными в собственном определении линиями.
Вобщем некритично — при отсылке пакета можно и shiftOut добавить после командного заголовка — это на усмотрение повторяющих/модернизирующих базову прожку под свои интересы.
Код: Выделить всё
// запись байта команды (может быть выполнена как shiftOut)
void trs_com(byte com_word)
{
digitalWrite(ln_dat, bitRead(com_word,7));
data_clock();
digitalWrite(ln_dat, bitRead(com_word,6));
data_clock();
digitalWrite(ln_dat, bitRead(com_word,5));
data_clock();
digitalWrite(ln_dat, bitRead(com_word,4));
data_clock();
digitalWrite(ln_dat, bitRead(com_word,3));
data_clock();
digitalWrite(ln_dat, bitRead(com_word,2));
data_clock();
digitalWrite(ln_dat, bitRead(com_word,1));
data_clock();
digitalWrite(ln_dat, bitRead(com_word,0));
data_clock();
data_clock(); // стробирует девятый бит командного слова (его состояние безразлично)
}
Использовано свойство того, что параметром функции может быть и константа, определенная как
Код: Выделить всё
#define clc_on 1Код: Выделить всё
// запись заголовка с составляющими командными словами для активации режима дисплея
void wr_uksda()
{
digitalWrite(ln_cs, LOW);
delayMicroseconds(6);
data_clock(); // линия ln_dat предварительно была в состоянии 1 !!!
digitalWrite(ln_dat, LOW);
data_clock();
data_clock(); // преамбула "запись команд" 100
trs_com(biass); // LCD 1/2 bias option, 3 common option
trs_com(clc_on); // пуск системного генератора
trs_com(lcd_on); // включить LCD bias generator
stop_pack();
}Хотя... а почему бы те параметры не ставить при вызове самой wr_uksda?
Тогда несколько изменим и объявление и реализацию:
добавим три значения команд размером в байт
Код: Выделить всё
void wr_uksda(byte,byte,byte);и изменим реализацию – заявим три переменных byte a,byte b,byte c а затем подставим эти внутренние переменные как константы при вызове функций trs_com(...)
Код: Выделить всё
void wr_uksda(byte a,byte b,byte c)
{
digitalWrite(ln_cs, LOW);
delayMicroseconds(6);
data_clock();
digitalWrite(ln_dat, LOW);
data_clock();
data_clock(); // преамбула "запись команд" 100
trs_com(a); // LCD 1/2 bias option, 3 common option
trs_com(b); // пуск системного генератора
trs_com(c); // включить LCD bias generator
stop_pack();
}
Следующая подпрограммка пересылает ВЕСЬ массив данных из bufout в контроллер дисплея
Код: Выделить всё
/* пересылка данных в ОЗУ дисплея из ОЗУ bufout
* без разрушения содержимого bufout
*/
void wr_ksda()
{
digitalWrite(ln_cs, LOW);
delayMicroseconds(6);
data_clock();
digitalWrite(ln_dat, LOW);
data_clock();
digitalWrite(ln_dat, HIGH);
data_clock(); // преамбула "запись ОЗУ" 101
digitalWrite(ln_dat, LOW);
for (byte cnt=0; cnt<=5; cnt++)
{
data_clock(); // начальный адрес (6бит) = 0
} // отсылаем начальный адрес пакета
//заголовок-преамбула завершен, далее собственно пошли данные
for (byte cnt=0; cnt<=15; cnt++)
{
// неразрушающая обработка байта с пересылкой
digitalWrite(ln_dat, bitRead(bufout[cnt],0)); // бит 0 текущего байта
data_clock();
digitalWrite(ln_dat, bitRead(bufout[cnt],1)); // бит 1 текущего байта
data_clock();
digitalWrite(ln_dat, bitRead(bufout[cnt],2)); // бит 2 текущего байта
data_clock();
digitalWrite(ln_dat, bitRead(bufout[cnt],3)); // бит 3 текущего байта
data_clock();
digitalWrite(ln_dat, bitRead(bufout[cnt],4)); // бит 4 текущего байта
data_clock();
digitalWrite(ln_dat, bitRead(bufout[cnt],5)); // бит 5 текущего байта
data_clock();
digitalWrite(ln_dat, bitRead(bufout[cnt],6)); // бит 6 текущего байта
data_clock();
digitalWrite(ln_dat, bitRead(bufout[cnt],7)); // бит 7 текущего байта
data_clock();
}
stop_pack();
}
Замечание:
Команды и адреса пересылаются СТАРШИМИ битами вперед, а данные — МЛАДШИМИ!
Блок пересылки только для теста — в реальности из-за возможных вариаций с привязкой бит к конкретным сегментам вероятнее всего потребуется дополнительный модуль «перетасовки» и/или несколько измененный передатчик (с учетом незначащих, но требуемых к пересылек бит массива).
Следующая прожка основа «бегущей 1» - смещение бита в массиве.
Под ассемблером эта задача весьма легко решается, а вот в Си без использования библиотек и ассемблерных вставок (простоначинающему) достаточно досадки доставляет...
Код: Выделить всё
// блок вращения бита в массиве
void rotor_16()
{
byte b = 0; static byte a = 0; // описываем и проводим начальную инициализацию передаточных флагов
// как static переменной a - флаг предыдущего переноса из бита 7,
// и обычной переменной b — флаг (рвх) текущего переноса из бита 7
byte c; // буфер обработчика
for (int x=0; x<=15; x++)
{
c = bufout[x]; // читаем элемент массива в буфгр обработки
b = bitRead(c,7); // b может принять значения 0 иди 1
c = c << 1; // сдвиг данных
c = c | a; // запись содержимого а (0000000N) в бит 0 с
bufout[x] = c; // вернуть новое значение в элемент массива
a = b; // скрпируем текущий флаг переноса из b в а
}; // и повторим это для всех 16 байт массива
}
Вот пока все компоненты собраны.
Можно прожку теста продолжить. Чего выявится по ходу работ добавим.
В разделе setup
добавляем начальную инициализацию состояния выволов:
Код: Выделить всё
digitalWrite(ln_cs, HIGH); // исходное состояние = 1
digitalWrite(ln_wr, HIGH); // исходное состояние = 1
digitalWrite(ln_dat, HIGH); // исходное состояние = 1Далее нужно запустить работу индикатора и ввести туда исходное значение «все сегменты погашены»
Код: Выделить всё
wr_uksda(biass, clc_on, lcd_on);
wr_ksda(); // начальная инициализация дисплея "все погашены"
Serial.println("Test of the determination of the segment to bit OZU"); // Тест посегментной привязки бит ОЗУ
Serial.println("For indicator on base of the controller HT1621"); // Для индикатора на основе контроллера НТ1621
delay(1000);Далее пишем сам «вечно вертящийся по кругу» тест в разделе loop
Код: Выделить всё
bufout[15] = 0x80; // начальная активация для предустановки значения static byte a = 0
rotor_16(); // дубль - аналог перемещения бита из последней ячейки буферного ОЗУ
// для устранения ошибок положения бита в цикле
for (int a = 0; a <=127; a++)
{
rotor_16(); // перемещаем бит в следующую позицию
Serial.println("at present active segment, corresponding to bit");
// "в данный момент активен сегмент, соответствующий биту"
Serial.print("0x"); Serial.print(a, HEX); Serial.println("");
wr_ksda(); // проводим вывод пакета в дисплей и сообщения в окно терминала
delay(3500); // и после задержки в … секунды снова повторяем
}Нажимаем проверку м получаем заключение без всяких замечаний компилятора
3034 байт памяти и 355 байт ОЗУ...
…
ну да надо б проверить на макетке...
заливаю сей скетч в мою платку...
Сегменты забегали.
Но с такой скоростью...
Меняю задержку на 3500
Код: Выделить всё
delay(3500); // и после задержки в … секунды снова повторяеми снова перекомпилируем и перезагружаем.
Теперь можно спокойненько следить и за сообщениями терминмла и за сегментами дисплея.
Заносим данные в пустографку.
Однако такой алгоритм не есть хорошо — в цикле все время выполняется начальная перезагрузка
Код: Выделить всё
bufout[15] = 0x80; // начальная активация для предустановки значения static byte a = 0
rotor_16(); // дубль - аналог перемещения бита из последней ячейки буферного ОЗУжелательно бы от оной избавится...
Возможно на основе замены цикла на основе FOR на цикл на основе IF или
DO – While дабы получить полноценный кольцевой цикл перемещения битика.
Исходник несколько подправлен заменой текста сообщение на латиницу — дабы на любом компе и с любым терминалом читалось одинаково.
НО...ТО уже опосля первомайских застолий...
и все вышеизложенное в архивчике



