Драйвер для tm1638 на STM32

Пока делал систему управления для своей печки, понадобилось выводить инфу и работать с кнопками. Решил использовать давно купленный дисплейчик типа такого:
Панелька на tm1638
Возникла необходимость рулить им. Свои изыски прилагаю, может кому пригодится.

Введение:
Было изучено несколько примеров библиотек. Не утраивало, что все они синхронные. Т.е. хотим узнать состояние клавишь: немедленно читаем их с устройства. Пока не прочитали, все остальное стоит. При том, что читать их чаще 25 раз в секунду нет никакого смысла. Потому сделал свой вилосипед.
1) Я люблю решение, которое можно использовать где угодно (не только с STM32, а вообще везде, где МК потянет по скорости), с минимум доработок.
2) Решение должно быть максимально изолировано от остального кода. Вход, выход. Все.
3) Система должна быть полностью асинхронной с минимальными задержками.
Кроме того, хотелось попробовать работу SPI «голыми руками»bitbang-ом, чтобы лучше понимать его принципы.

Описание протокола tm1638:
Контроллер tm1638 (дальше просто контроллер или МК) имеет внутренню память на 16 байт (далее видео-память). Каждый байт отвечает за отображение одного символа или светика (зависит от разводки платы). В моем случае, 8 не четных байт отвечали за цифры на LED-дисплеях. А младший бит каждого четного байта отвечал за 8 светодиодов. + есть доспутный для чтения 32-битный регистр, в котором храниться состояние кнопок. + есть байт настроек.

Запись в видео-память возможна в режиме:
1) фиксированного адреса (команда 0x44): каждый раз сначала передается адрес ячейки памяти, потом байт данных
2) автоувеличения адреса (команда 0x40): каждый раз сначала передается адрес ячейки памяти, потом несколько байт данных (до 16-ти). Каждый следующий байт пишется в следующую ячейку.

Управление включением-выключеним и яркостью задается командой: 0x80 с параметрами — битами:
1) Влючение выключание задается установкой/сбросом 3-го бита.
2) Яркость задается от 0 до 7 — 3-мя младшими битами.

Запись в видео-память состоит из минимум 2-х байт:
1) Задает адрес записи (или начала записи, если задан режим автоувеличения адреса): 0xC0 + адрес 0-15.
2) Собственно байт данных.

Чтение клавиатуры состоит из 2-х этапов:
1) Отправки команды чтения клавиатуры: 0x42.
2) Чтения 32 бит данных.

Интерфейс:
Что в итоге получилось на входе (tm1638.h):
#ifndef _TM1638_
#define _TM1638_

#include "commontypes.h"

namespace tm1638
{
    void init(bool activateDisplay = true, uchar intensity = 7);
	
    void setupDisplay(bool active, char intensity);

    void LEDOn(uint pos);
    void LEDInverse(uint pos);
    void LEDOff(uint pos);
    void allLEDOff();
	
    void setChar(int pos, char sybmol);
	
    void pointOn(int pos);
    void pointInverse(int pos);
    void pointOff(int pos);

    uint getKeys();
    
    void onTick();
};

#endif // #ifndef _TM1638_


Модуль обернут в namespace, чтобы четко отделить работу с устройством в коде. Причем, я сознательно нигде не пишу using, т.к. запись tm1638::XXX четко документирует что это работа с конкретным устройством и в купе с говорящими названиями методов в дополнительном документировании не нуждается.

По интерфейсу, думаю особых вопросов нет, все понятно из названий методов и параметров. Все методы (кроме init в самом начале работы) можно вызывать асинхронно. Данные сохраняются в структуры (несколько тактов) и обновляются на экране по мере возможности (максимальная задежка 30мс). Последнее считанное состояние кнопок сохраняется во внутреннем буфере из него же и читается (несколько тактов).

Отдельно скажу по методу onTick. Т.к. у нас bitbang, причем асинхронный, то практически вся активность проходит в этом методе, его необходимо вызывать регулярно, например из таймера (как сделано у меня).

По реализации.
Система работает на конечном автомате.
Т.к. управление идет не по 4-м проводам (обычный SPI), а по трем (DIN-DOUT работает по очереди то в оду то в другую сторону), то клавиатуру приходиться читать в перемешку с обновлением состояния дисплея и светиков. Т.к. задержка в обновлении дисплея на 100мс никто не заметит, клавиатура имеет приоритет. Сделано так: есть таймер обратного отсчета: tickFromLastKeyScan, который уменьшается каждый тик, как только система ничем не занята, она решает что делать дальше, если к этому моменту таймер истек (или просрочен), то след. действие — чтение состояния клавиатуры, иначе обновление экрана (если он изменился).
Т.к. экран меняется довольно редко, то для оптимизации, обновляются только те данные, которые изменились. Если ничего не менялось, то метод завершается практически сразу.

Код не очень плохо откоментирован, да и ИМХО он не сложный, но если возникнут вопросы/поджелания, не стесняемся. Отвечу по мере поступления.

З.Ы. Картиники работы покажу уже на готовой печке (надеюсь на этой неделе закончу).
Файлы в топике: tm1638.zip

Комментарии (28)

RSS свернуть / развернуть
А можно ссылку на платку? Где брали и описание к ней.
0
Я брал такую. Вообще на ebay много разных по разным ценам. Ключевое слово tm1638.
Описание это сложно. По сути я его нашел через поиск картинки в гугле. Одна из картинов была куском даташита. Переход по ней привел к статье. В ней есть ссылка на даташит.
Собственно, это и причина написания статьи. Информации по tm1638 очень мало.
0
А светодиоды когда загораются? Ими можно управлять программно?
0
Да. Все управляется. Единственное но: якрость задается для всех сетиков и индикаторов в целом. Т.е. помигать одной цифрой не получится.
0
Вот методы управления диодами:
void LEDOn(uint pos);
void LEDInverse(uint pos);
void LEDOff(uint pos);
void allLEDOff();
0
Получается этот контроллер аппаратный типа ПЛИСС? Он только слушает команды извне и выполняет их. На этом его функция заканчивается?
0
Нет. Это команды из моего драйвера. А так он сам обновляет дисплеи и светики. Опрашивает кнопки. Тебе только надо отдать что нарисовать и спросить какие кнопки нажаты.
0
Есть вопрос по чтению кнопок. Посылаю 0x42, делаю паузу 2 мксек, и по фронту клока считываю данные. После 4 принятых байт поднимаю строб, и нигде нет нажатой кнопки(( Что я делаю не так?
0
по какому фронту? Попробу взять мою библиотеку и тупо запуситьь таймер и USART и посмотри что там. Т.е. только бмблиотека + таймер + USART.
Еще вариант: уменьши скорость работы.
0
По возрастающему (как по даташтиту). По коду не стОит приводить, я пишу на асме под 8051, и там простыня, в который Вы не факт что что-то поймете)) Я из Вашей статьи лишь алгоритм взял, и индикация с полпинка запустилась, а вот с чтением кнопок затык получился, вот и хотелось бы понять, где я затупил…
0

Вот как идет вывод у меня (это отладочная симуляция). Между байтами 3-й промежуток (синяя линия дает точку отсчета). Помоему достаточно 2-го, но мне по логике было проще так.
0
Красный — DIO, зеленый — CLK, синий — STB?
0
Да. Все верно.
0
А можешь показать аналогичные осциллограммы, снятые с реального чипа? Согласно даташиту, там установка по rising edge и чтение по falling edge, а у тебя на картинке с точностью до наоборот.
0
Ты уверен? зеленый — CLK. Красные — данные. устанавливаем перед поднятием CLK.
Если читать, то читать перед опускаем CLK. Что не так?
З.Ы. Я аж пошел проверять свое знание английского…
0
Традиционно в SPI уровень выставляется по одному фронту, а читается по второму, так сделано для того, чтобы выдать гарантированное время и на установку уровня, и на его чтение, по полтакта. Твой код, возможно, ставит уровень и сразу же выдает клок, но TM1638 по идее (и по картинкам в даташите) должна себя вести по стандарту. Или на картинке только передача в TM1638, без ответа с нее?
0
Только передача.
0
Тогда понятно. Но я бы все же посоветовал переделать по стандарту. Линия может иметь приличную емкость (даташит рекомендует 100пФ на линии интерфейса для подавления помех), что в сочетании с резисторами (которые, правда, любители обычно не ставят) может заметно поднять время установки уровня на ней.
0
И я не уверен, что возрастающий — это не ошибка в DS.
0
Я читаю по ниспадающему.
0
Обана, интересно… Попробую вечером и так, и эдак.
0
Разобрался я. Значит дело было в том, что я после отправки байта установки режима чтения клавиатуры (01000010b) не устанавливал линию принудительно на вход. А ведь в этом байте последний битик 0, вот у меня DIO и оставалось в низком уровне, и не давал модулю протолкнуть в МК свои данные. Добавил после отправки этого байта «setb DIO» и все заработало.
0
«Семен-Семеныч» :)
Самое главное — нашел и поправил… А то у нас на проекте баг нашли 3-х летней давности — 2 строчки кода — ускорение программы в 2 раза.
0
Расскажи подробнее про эти чудодейственные строчки.
0
Если кратко: скриптовый язык. В массив клали объект по его ID. Язык такой, что он автоматом ресайзит массив до ID. Если у тебя ID == 17824987 то массив становиться большой. Но это не главное. Это просто память. Главное, что потом по этому массиву шел проход для поиска всех не пустых элементов, которых там всегда не больше 12. Заменили на словарь (аналог map из С++). Нет памяти. скорость.
0
А, забавно. Напомнило как я боролся с тормозами в lua при попытке использовать string как байтовый буфер. Не скажу, чтоб получившееся решение мне нравилось (лучше всего иметь возможность выделить строку нужной длины и писать в нее по индексу — но именно это луа и не позволяет), но все же удалось получить выигрыш в несколько порядков.
Какой язык?
0
ActionScripts
0
А так, без кода сложно сказать…
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.