Подключаем модуль TM1638 к Arduino без библиотеки SPI

Изначально поставка задачи была такой. У меня была в наличии плата TM1638. Нужно было научиться с ней работать (ну и проверить работоспособность самой платы) для того, чтобы использовать её в одном интересном проекте (о нём в другой раз). Под рукой оказалась платка Arduino Nano. Хотелось быстро отделаться проверить работу самой платы при помощи ардуиновской библиотеки SPI.h — не получилось. В результате проделанный объём работы вылился в эту заметку.

1fa79cde2fe6aa5c2c32ba62482c379b.jpg

ля начала скажу, что покупала я эту плату для выполнения заданий Школы Синтеза. И когда заказывала, я не смотрела на то, что она из себя представляет (у Школы есть готовые скрипты для подключения железа, что очень удобно при старте обучения с нуля). Когда она пришла, я поняла, что без скриптов я её подключить не могу, потому что у неё интерфейс SPI, а я с ним прежде никогда не работала даже в микроконтроллерах (вот уж не знаю как так вышло, что он обошёл меня стороной).

Поэтому я решила описать весь процесс работы, вдруг кому-то это будет полезно. 

В классическом варианте SPI использует для передачи четыре провода: SCLK для передачи тактового сигнала, MOSI и MISO — для передачи данных от мастера (ведущего) к ведомому и обратно и SS для выбора ведомого. Подробнее о самом SPI и о подключении устройств к шине можно почитать в интернете (например, тут описано, на мой взгляд, хорошо и с картинками).

Модуль же TM1638 имеет пять пинов, два из них это земля и питание, а вот остальные три для обмена данными. Один для передачи ему тактового сигнала от мастера (CLK), один для управления процессом передачи (STB) и один для передачи самих данных (DIO). Т.е. данная реализация SPI является полудуплексной — для передачи и приёма данных используется один и тот же провод.

Для управления модулем есть несколько команд: управление яркостью (и включение/выключение) (Display Control), задание адресации (Address Command) и выбор чтения/записи (Data Command). 

Для настройки яркости используются следующие команды (переведённая и сокращённая мной таблица из документации):

22bb97edd6066fd5f62711eca7d43848.png

Для выбора направления передачи и способа адресации есть следующие команды:

5cc9200fa4125a2a54821b4f8632abcd.png

Для передачи адреса используются команды, начинающиеся с 0xC:

dd21deaa7f6bfd75484bceb2eff9e633.png

Для тех, кто не в курсе на всякий случай (ну вдруг) поясню, что если число, начинается с 0x или заканчивается на h, то это значит, что оно записано в шестнадцатеричной системе счисления.

С командами вроде бы всё относительно понятно. Первая сложность, с которой мне пришлось столкнуться, это странная адресация памяти в микросхеме. Немного погуглив и почитав найденные статьи стало понятно, что адреса индикаторов чередуются с адресами кнопок и светодиодов.

Немного модифицированная мной табличка из документации:

75755521f1abd3b1cd46b46cc7dd9965.png

Из неё понятно (ну или на самом деле не очень), что чётные адреса с 00h по 0Eh отвечают за подключение семисегментных индикаторов. Нечётные адреса же — за подключение кнопок и светодиодов. Причём светодиоды подключены к SEG10, а кнопки к SEG9.

Т.е. чтобы вывести цифру на первый слева индикатор, нужно записать данные по адресу 00h. Для того, чтобы зажечь первый справа светодиод, нужно записать число 2 по адресу 0fh (чтобы погасить, нужно записать ноль).

Собственно про цифры. В документации имеется распиновка индикатора и схема его подключения к самому чипу. 

3bf9a6156b525bd0fabb51051ef2fedd.png

Т.е. сегмент a — это младший бит, а dp — соответственно, старший. Для того, чтобы вывести цифру ноль нужно зажечь все сегменты, кроме g и dp — это 0×3f. Чтобы вывести единицу, нужно зажечь сегменты b и c — это 0×06 и т.д. В таблице приведены двоичные и шестнадцатеричные коды для всех цифр.

3c9ec9732ba3c6a538102c892d3d9e44.png

Биты у меня в таблице с цифрами располагаются младшим справа, в таблице адресов из документации младший бит стоит слева. Мне привычней располагать младшие биты справа, поэтому я себе таблицу  с цифрами рисовала именно так, если вам так сложно воспринимать, то можно её отзеркалить. Хотя в программе, скорей всего, вы всё равно будете писать младший бит слева, по крайней мере, у меня именно так.

Прочитав документацию по диагонали, и ещё пару статей в интернете, я стала подключать модуль TM1638 к Arduino Nano. Для этого мне понадобилось заглянуть в распиновку адруины и выбрать там пины аппаратного SPI. Получились такие: пин 13 — SCK, пин 11 — MOSI, пин 10 — SS (ножек три, так как у нас полудуплекс). 

Подключив библиотеку, я написала код передачи данных на плату и он не заработал — завис на первой же библиотечной функции, функции инициализации. Почесав затылок, я решила, что не хочу копаться в непонятной мне библиотеке и решила делать без неё (тем более, что приём через неё всё равно не реализовать из-за полудуплексного подключения). Правда для облегчения своей работы я использовала встроенную функцию shiftOut, но она, слава богу, заработала, хотя я морально уже была готова писать и её.

Ножки при этом я выбрала другие, но на самом деле это абсолютно неважно. Передача команды у меня выглядела так:

void transfer (uint8_t data){
 digitalWrite (SS,LOW);
 shiftOut (DATA_PIN, CLK, LSBFIRST, data);
 digitalWrite (SS,HIGH);
 _delay_ms(100);
}

Где SS — это пин, к которому подключен вход STB модуля TM1638, CLK — вход SCLK модуля, DATA_PIN — вход DIO. У меня это были пины 10, 12 и 11 (PB2, PB4 и PB3). Писать функцию digitalWrite через регистры я тоже не стала, ибо в данной ситуации мне это ни к чему. Функция shiftOut осуществляет сдвиг данных, т.е. выдаёт биты по одному на ножку DATA_PIN, сопровождая при этом передачу тактовыми импульсами на ножке CLK. Параметр LSBFIRST указывает, что передача будет осуществляться старшим битом вперёд (да, в документации указано, что передача должна осуществляться именно так).

Выдав с помощью этой функции команду включения и настройки яркости, команду настройки инкремента адреса (в данном случае подходит только запись с фиксированным адресом, т.к. адреса расположены не по порядку), сам адрес и какие-то данные, я ожидала, что всё заработает. Экран зажегся (уже прогресс) и вывел всякий мусор. 

e9ed33ff98a0514a894b1b6e9510ee05.jpeg

Потратив уйму времени на перечитывание документации и статей в интернете, я эту проблему решить не смогла. Хотя делала всё по примеру, но он оказался неполный. Спасибо коллеге, он смог нагуглить, что я делаю не так. Оказалось, что после передачи адреса ячейки не нужно поднимать провод выбора микросхемы (SS) и делать паузу, а нужно сразу следом за адресом передавать данные. 

Вообще, это всё описано в документации и даже с картинками, но я до туда не дочитала (как всегда).

Вот, собственно, временные диаграммы передачи данных из документации:

8f9bc67df40c563341d638ad2709ea76.png

Где:

  • Command1:   установить режим записи с фиксированным адресом

  • Command2: установить адрес

  • Data1: Отправка данных по адресу, указанному в Command2

  • Command3: установить адрес

  • Data2: отправка данных по адресу, указанному в Command3 и т.д.

Т.е. для отправки команд настройки, нужно сначала опустить в ноль STB, отправить восемь бит команды (не забывая передавать импульсы по CLK), поднять STB (установить логическую единицу) и сделать паузу. Для того, чтобы передать данные, нужно опустить STB, отправить восемь бит адреса, затем восемь бит данных и только потом поднять STB. 

Исходя из этой информации, я дописала вторую функцию для передачи данных.

 void transferData(uint8_t addr, uint8_t data)
{
	digitalWrite(SS,LOW);
	shiftOut(DATA_PIN, CLK, LSBFIRST, addr);
  shiftOut(DATA_PIN, CLK, LSBFIRST, data);
  digitalWrite(SS,HIGH);
  _delay_ms(100);
}

В общем, после всех мытарств, оно заработало и вывело красивые циферки.

07494764fa66526456600939bb50bad0.jpeg

Для осуществления приёма с кнопок я попробовала использовать функцию shiftIn (). И оно не заработало. Если верить документации, то при отправке запроса на чтение, плата должна вернуть четыре байта друг за другом, в которых биты, соответствующие нажатым кнопкам установлены в единицы. У меня же принимается 32 нуля. Продолжу свои попытки и опишу результат в следующий раз.

Полный код из двадцати строк здесь.

Буду рада, если кому-то это оказалось полезным.

© Habrahabr.ru