Начнем с модулей:
Для своих проектов я искал семисегментные индикаторы, яркие и компактные. Сделал ряд проектов на индикаторах с драйверами MAX7219
Недавно покупая на Алиэкспрессе всякую мелочевку наткнулся на эти индикаторы, на микросхеме TM1637. Мне понравилось компактное исполнение, невысокая цена и управление всего по двум проводам. Заказал парочку вместе с кучей других деталек.
Модули приехали запаянные в пакетики
Характеристики:
Вместе в посылке пришли и ESP8266 ES07, о которых упоминал в начале статьи. Подробно написал про них в своем блоге.
Последнее время много ковырялся с ESP-шками и решил опробовать индикаторы именно с ними. Особенностью данной реализации стало программирование из среды Arduino IDE.
Настройка Arduino IDE для работы с ESP8266 хорошо описана здесь
Хочу немного добавить, что купленные мною модули ESP-07 распаянные на платы-адаптеры уже практически готовы к использованию. Подключаем TX/RX и GND к TTL-USB конвертеру, например, такому. Подключаем питание 3.3В к VCC. Для загрузки прошивки GPIO0 присоединяем к земле. Собственно и все. Подключить GPIO0 и RESET к данному конвертеру у меня не получилось, но это оказалось не сильно нужно. При подсоединенном GPIO0 к земле ESP находится в режиме загрузки прошивки во флэш-память, но после загрузки происходит запуск программы без отсоединения данного GPIO. Это позволяет вполне комфортно отлаживать свою программу. Нужно перед загрузкой только перезагрузить модуль передергиванием питания или кратковременно подав на вывод RESET на землю. Для этого можно припаять специальную кнопку.
На фотке немного другой модуль ES12 из предыдущей партии. Конденсатор установлен для повышения стабильности работы. Последние модули использую без них.
Итак модуль подключен, среда настроена. Для ES07 все настройки ESP8266 ставятся «по умолчанию»:
Для работы с дисплеем на TM1637 я взял готовую библиотеку для Ардуино DigitalTube. «По умолчанию», дополнительные библиотеки устанавливаются в каталог: «Мои документы\Arduino\libraries\». Чтобы оправдать использование WiFi-модуля, эти часы буду синхронизировать время с NTP сервером в интернете.
Подключаем ESP, дисплей к CLK -> GPIO4, DIO ->GPIO5, стабилизатор 3.3В и USB-TTL конвертер макетными проводами
Грузим скетч, который можно взять здесь.
Настраиваем параметры своей точки доступа: AP_SSID и AP_PASS, свою таймзону TIMEZONE. У меня установлена для Перми/Екатеринбурга. Для Москвы будет 3.
Функцией tm1637.set(2) устанавливаем необходимую яркость индикатора от 0 до 7.
Компилируем:
Готово. Часы показывают часы и минуты, мигая двоеточием. Время точное из интернета, синхронизируется с NTP сервером. При длительном отсутствии соединения происходит перезагрузка ESP-шки.
В корпус все это делать не стал, так как это только прототип для будущего проекта.
Прочитать об этом можно будет в моем блоге
Полезные ссылки
mysku.ru
Намедни приблудился китайский дисплейный модуль на контроллере TM1637 (далее в тексте — контроллер, устройство) .
Вещь конечно, приятная во всех отношениях. По цене, сопоставим с ценой «голого» четырёхразрядного индикатора. Управляется всего по двум проводам, позволяя применять в качестве «ядра» дешевые маловыводные микроконтроллеры (далее в тексте — МК) вплоть до шестиногих букашек вроде Tiny10. Причём, от процессора не потребуется отвлекаться на динамическую индикацию,.достаточно отправить контроллеру посылку из нескольких байтов, и заниматься своими делами. «Выстрелил и забыл».
Но и это ещё не всё. К устройству можно подключить шестнадцатикнопочную клавиатуру, получив почти полноценный терминал (кстати, борьбу с дребезгом контроллер тоже берёт на себя. )) )
И все эти радости — по двум проводочкам… Вкусно.
Но не без ложки китайского дёгтя , ессно.
Ибо даташит, переведенный с китайского на английский, это… что-то. (Здесь должен быть абзац, составленный исключительно из матерных слов).
Говоря коротко, не пытайтесь в нём что-то понять, не изучив предварительно этот контроллер по другим источникам.
И вот с этим-то — самая засада. Поскольку посторонние источники крайне скудны. Существуют библиотеки под некоторые среды, в том числе — Ардуино (не к ночи будь помянута), но понимания работы ТМ1637 они не добавляют.
Примеров ассемблерного кода для AVR найти и вовсе не удалось, что сподвигло меня добавить в конце сего материала демонстрационную программульку, чтобы по мере способностей, восполнить этот пробел. На исчерпывающую полноту, абсолютную истину, и красоту кода не претендую. ))
Итак — «О контроллере ТМ1637 человеческим языком».
Начнём с «физики».
Последовательный интерфейс, разработанный для этого контроллера, является творческой переработкой широко известного «квадратного» I2C. Отличия состоят в других названиях линий, обратном порядке следования битов, и в отсутствии адреса устройства.
С чем связаны эти странности, с китайской ли самобытностью, или с патентными ограничениями, неведомо. (Памятуя о смешной цене устройства, логичнее предполагать второе))).
Конечно есть решения, позволяющие применять для обмена с устройством стандартные аппаратные интерфейсы USI, встроенные во многие современные МК. Но здесь мы их рассматривать не будем. Отчасти по той причине, что не у всех «букашек» есть встроенный USI, поэтому строить интерфейс программным путём всё равно когда-то, да придётся.
Обмен данными происходит по двум шинам — тактовой CLK, и данных DIO. При отсутствии передачи, на обеих шинах сохраняется высокий уровень.
Для передачи одного байта требуется девять импульсов синхронизации CLK. Биты передаются начиная с младшего.
Активной стороной всегда является МК. Контроллер TM1637 только принимает данные и отвечает на запросы.
Важно 1 — изменения состояния шины DIO должны происходить только при низком уровне на шине CLK.
Перед восходящим (передним) фронтом на CLK, на шине DIO уже должен быть установлен актуальный потенциал, соответствующий состоянию передаваемого бита.
Важно 2 — это правило нарушается лишь в начале и в конце посылки, (в пределах одной посылки может быть передано от одного до нескольких байтов). Маркером начала посылки является переход от высокого уровня к низкому на DIO при высоком CLK. Маркер окончания посылки — переход от низкого уровня к высокому на DIO при высоком CLK.
Эти два условия соответствуют стандартному протоколу I2C.
Сигнал подтверждения ACK тоже почти похож на аналогичный от I2C, и представляет собой низкий уровень на шине DIO, выставляемый устройством по заднему (спадающему) фронту восьмого импульса CLK (напомню, что по переднему фронту этого же импульса происходит запись последнего, восьмого бита передаваемого байта). Этот низкий уровень удерживается в течение одного такта синхронизации — до заднего фронта девятого импульса CLK. Если после этого не будет сформирован маркер окончания передачи, то следующий импульс CLK будет считаться первым синхроимпульсом следующего передаваемого байта.
По «фэншую», во избежание коллизий, на время действия сигнала ACK нужно или переводить шину DIO в высокоимпедансное состояние, или переконфигурировать соответствующий вывод МК как вход. Но есть мнение, что можно в это время тупо держать на линии ноль — «два нуля не подерутся».
Полагаю (могу и ошибаться), что при наличии на линиях только одного передающего и одного приёмного устройства, низкой скорости передачи, и отсутствии сильных помех, нет необходимости в анализе сигнала подтверждения приёма (ACK). Поэтому в моём примере кода (ниже) этого нет.
С физикой вроде разобрались, теперь — СОФТ.
Что нужно сделать, чтобы на табло появились заветные цифры или буквы?
1. Даташит требует перво-наперво провести «инициализацию». Причём нигде в даташите не объяснено, что сие значит. В голову сразу лезут страшные воспоминания о громоздких процедурах для дисплеев на контроллере HD44780 и ему подобных…
Но здесь инициализация представляет собой всего лишь одну однобайтную команду, позволяющую включить дисплей и установить нужную пользователю яркость (да-да, и яркостью мы тоже можем управлять))). С помощью этой же команды можно и выключать дисплей. Например с целью энергосбережения.
Структура этого байта такова:
Bit 7 _ Всегда 1.
Bit 6 _ Всегда 0
Bit 5 _ 0 или безразлично
Bit 4 _ 0 или безразлично
Bit 3 _ 1 — включить дисплей, 0 — выключить дисплей
Bit 2, Bit 1,Bit 0 _ Три бита 0…2 задают 8 градаций яркости дисплея.
NB: Касаемо содержимого битов 4 и 5, даташит гласит: «Zero should be inserted for irrelevant items». Что по-видимому следует понимать как «Неиспользуемый бит. Рекомендуется записывать в него ноль». Однако при записи туда единиц, ровным счётом ничего не меняется.
z.B: Команда 0b10001000 (0х88) — включить дисплей на минимальной яркости. (см.Фиг.2)
2.Затем нужно передать ещё одну однобайтную команду конфигурации, определяющую последующие действия:
а. Включим ли мы дисплей в нормальный режим, или в тестовый. Что такое тестовый режим, даташит умалчивает.
б. Будем ли мы передавать данные пакетом в несколько знаков (с автоинкрементом адреса), или изменять данные в каждом знакоместе по отдельности (по фиксированному адресу).
в. Будем ли мы вообще передавать в устройство данные для отображения, или будем читать состояние подключенной клавиатуры.
Структура команды конфигурации
Bit 7 _ Всегда 0
Bit 6 _ Всегда 1.
Bit 5 _ 0 или безразлично
Bit 4 _ 0 или безразлично
Bit 3 _ 1 — тестовый режим, 0 — нормальный режим
Bit 2 _ 1 — с фиксированным адресом знакоместа, 0 — с автоинкрементом адреса
Bit 1 _ 1 — чтение клавиатуры, 0 — запись отображаемых данных
Bit 0 _ Всегда 0
z.B: Команда 0b01000000 — (0х40) будем записывать данные пакетом, в нормальном режиме.
3. И наконец, нужно передать в устройство адрес знакоместа, и данные которые мы хотим в нём увидеть.
Здесь вспоминаем пункт «2.б». Если выбран режим фиксированного адреса, то посылки будут двухбайтовыми: [адрес]+[передаваемые_данные] для каждого знакоместа.
Данные представляют собой сумму «весовых коэффициентов» зажигаемых сегментов, что проиллюстрировано на фиг.6.
Адреса знакомест начинаются с 0xC0 (крайне левое, оно же — первое), и заканчиваются на 0xC3 для четырёхзнакового индикатора, или на 0xC5 для шестизнакового.
z.B: Посылка (0хC2,0x73) — отобразит в третьем разряде букву «Р». (см.Фиг.3)
Если выбрана пакетная передача данных, то достаточно передать начальный адрес (например для первого знакоместа — 0xC0 ) а затем передать группу байтов данных. При этом адрес для каждого из байтов будет добавлен автоматически, и все они последовательно попадут на свои места. Всё это, и начальный адрес, и несколько байтов данных передаются за одну посылку.
z.B: Посылка контроллеру подряд пяти байтов (0xC0,0x06,0x5B,0x4F,0x66) — отобразит, начиная с первого знакоместа, число «1234».
Работа с клавиатурой
Её чтение происходит так:
1. Если инициализация не проведена ранее, проводим её.
2. Подаём команду конфигурации, которая в данном случае выглядит примерно так: 0b01000010 (0х42)
Это запрос текущего состояния клавиатуры.
3 Получив запрос, устройство переключается в режим передачи, и в ответ на каждый следующий передний фронт получаемых от МК импульсов CLK, выставляет на шину DIO уровень, соответствующий значению передаваемого бита.
Поэтому нам потребуется перевести шину DIO в режим чтения (переконфигурируя соответствующий порт МК как вход), чтобы снимать с неё данные.
Данные передаются в течение первых восьми тактов CLK. Девятый такт образует стоп-бит.
Всю процедуру чтения нужно производить одной двухбайтной посылкой [команда]+[чтение_данных].
Запрос клавиатуры при ненажатых кнопках, возвращает значение 0xFF.
В полученном байте состояния клавиатуры, интерес представляют первые (младшие) пять битов.
Биты 0…2 дают код нажатой кнопки в пределах одной группы (клавиатура организована как две группы по восемь кнопок. см.фиг.4)
Низкий уровень бита 3 указывает на активность в первой группе (К1).
Низкий уровень бита 4 указывает на активность во второй группе (К2).
Высокий уровень в обеих этих битах (Bit3,Bit4) указывает на отсутствие нажатых кнопок.
Чтение клавиатуры проиллюстрировано в документации картинкой (см.Фиг.5). Картинки — то немногое что ещё можно понять в даташите. )))
При работе с клавиатурой следует учитывать три особенности.
1. Клавиатура «не понимает» нажатия двух и более кнопок одновременно.
2. При изменении состояния клавиатуры, контроллер не проявляет никакой активности на интерфейсных шинах. Поэтому, вызывать от неё прерывания МК не получится. Клавиатуру придётся регулярно опрашивать с достаточной периодичностью.
3. Судя по осциллограммам, выход устройства в режиме передачи данных в МК представляет собой «открытый исток», хотя в даташите это нигде не упомянуто.
Поэтому скорость нарастания напряжения на шине DIO ограничена параметрами RC цепи, состоящей из резистора подтяжки (10 КОм) и конденсатора неведомой ёмкости, установленного параллельно шине. Это обстоятельство ограничивает скорость пересылки данных в режиме чтения клавиатуры. При попытках уменьшения периода импульсов CLK до 100…150 мкс и менее, моё устройство отказывалось читать клавиатуру правильно.
В примере кода, этот период составляет около 300 мкс (его можно изменять, модифицируя подпрограмму «pause:»).
Быстродействие же устройства «на индикацию» может быть достаточно высоким. У меня оно бодро выводило символы на табло при периоде синхроимпульсов на шине CLK менее четырёх микросекунд. Работу при более высокой частоте, я не проверял.
И ещё.
Если планируется прошивка МК прямо в устройстве, старайтесь не использовать ISP выводы МК для линий DIO и SCK. Контроллер может принять активность на этих линиях за обмен с ним, и в неподходящий момент выставить на DIO свой ACK.
Впрочем, разъём между МК и устройством гарантированно позволит избежать коллизий. ))
Внизу выложен файл «ТМ1637.asm» — листинг демонстрационной программки на Ассемблере для ATtiny24 с тактовой частотой 8 МГц (писано в AVRStudio_4.14).
В ней присутствуют все четыре основных действия для работы с TM1637 — инициализация, передача данных с автоинкрементом адреса, передача данных по фиксированному адресу, и чтение клавиатуры.
Программка выводит на табло цифры «123» в старших разрядах, и «кракозябры» соответствующие необработанному коду чтения клавиатуры, в младшем разряде. Расшифровать код клавиатуры можно с помощью «весовых коэффициентов», показанных на фиг.6.
Приложение: TM1637.asm
(В более удобоваримом виде — тут: https://www.chipmake…les/file/16019/ )
;*****************************************
;
; TM1637 + Tiny24 8MHz
; CLK = PA3
; DIO = PA1
; Отображение в старших разрядах надписи «123»
; и необработанного байта состояния клавиатуры в 4-м разряде
;
;*******************************************
.include «tn24def.inc»
;****** РЕГИСТРЫ
.def tmp0 =r16
.def tmp1 =r17
.def tmp2 =r18
.def tmp3 =r19
;****** БИТЫ
.equ CLK =0x03 ;
.equ DIO =0x01 ;
rjmp RESET ; Reset Handler
rjmp EXINT ; IRQ0 Handler
rjmp PCIN0 ; PCINT0 Handler
rjmp PCIN1 ; PCINT1 Handler
rjmp WDT ; Watchdog Interrupt Handler
rjmp TIM1_CAPT ; Timer1 Capture Handler
rjmp TIM1_COMPA ; Timer1 Compare A Handler
rjmp TIM1_COMPB ; Timer1 Compare B Handler
rjmp TIM1_OVF ; Timer1 Overflow Handler
rjmp TIM0_COMPA ; Timer0 Compare A Handler
rjmp TIM0_COMPB ; Timer0 Compare B Handler
rjmp TIM0_OVF ; Timer0 Overflow Handler
rjmp ANA_COMP ; Analog Comparator Handler
rjmp ADCHN ; ADC Conversion Handler
rjmp EE_RDY ; EEPROM Ready Handler
rjmp USI_STR ; USI STart Handler
rjmp USI_OVF ; USI Overflow Handler
RESET: ldi tmp1, low(RAMEND)
out SPL,tmp1
ldi tmp0,0xFF ;
out ddra,tmp1 ;весь porta как выход
out porta,tmp1 ;всем 1
;***************************************************
;***************************************************
LOOP:
;инициализация
rcall start ;маркер начала посылки
ldi tmp0,0x88 ;включение дисплея. Яркость минимальная.
rcall outcom ;вывожу команду
rcall end ;маркер конца посылки
rcall pause ;пауза 100 мкс
;включение режима передачи данных с автоинкрементом адреса
rcall start ;маркер начала посылки
ldi tmp0,0x40 ;режим передачи данных с автоинкрементом адреса
rcall outcom ;вывожу команду
rcall end ;маркер конца посылки
rcall pause ;пауза 100 мкс
;вывод сообщения «123» на табло
rcall start ;маркер начала посылки
ldi tmp0,0xC0 ;УСТАНОВКА НАЧАЛЬНОГО АДРЕСА (0хC0 — крайнее левое знакоместо)
rcall outcom ;вывожу адрес
ldi tmp0,0x06 ;1 в первый разряд
rcall outcom ;вывожу данные
ldi tmp0,0x5b ;2 во второй разряд
rcall outcom ;вывожу данные
ldi tmp0,0x4f ;3 в третий разряд
rcall outcom ;вывожу данные
rcall end ;маркер конца посылки
rcall pause ;пауза 100 мкс
;чтение клавиатуры
rcall start ;маркер начала посылки
ldi tmp0,0x42 ;режим чтения клавиатуры
rcall outcom ;вывожу команду
rcall incom ;получаю данные
rcall end ;маркер конца посылки
mov tmp2,tmp0 ;сохраняю данные клавиатуры в tmp4
;включение режима передачи данных с фиксированным адресом
rcall start ;маркер начала посылки
ldi tmp0,0x44 ;режим передачи данных с фиксированным адресом
rcall outcom ;вывожу команду
rcall end ;маркер конца посылки
;вывод необработанного байта состояния клавиатуры в четвертый разряд табло
rcall pause ;пауза 100 мкс
rcall start ;маркер начала посылки
ldi tmp0,0xC3 ;УСТАНОВКА АДРЕСА (0хC3 — четвертое знакоместо)
rcall outcom ;вывожу адрес
mov tmp0,tmp2 ;восстанавливаю в tmp0 сохранённые данные клавиатуры
rcall outcom ;вывожу данные
rcall end ;маркер конца посылки
rjmp LOOP
;***********************************
start: ;маркер начала посылки
rcall pause ;пауза 100 мкс
sbi porta,clk ;
sbi porta,dio ;
rcall pause ;пауза 100 мкс
cbi porta,dio ;
rcall pause ;пауза 100 мкс
ret
;***********************************
end: ;маркер конца посылки
sbi porta,clk ;
rcall pause ;пауза 100 мкс
sbi porta,dio ;
rcall pause ;пауза 100 мкс
ret
;***********************************
outcom: ;последовательный вывод. выводимый байт должен находиться в tmp0
push tmp1
; вывод восьми битов регистра tmp0
ldi tmp1,0x08 ;
outc10: cbi porta,clk ;опускаю CLK
rcall pause ;пауза 100 мкс
lsr tmp0 ; \
brcc outc20 ; |
sbi porta,dio ; > младший бит tmp0 выставляю на DIO
rjmp outc30 ; |
outc20: cbi porta,dio ; /
outc30: rcall pause ;пауза 100 мкс
sbi porta,clk ;поднимаю CLK
rcall pause ;пауза 100 мкс
dec tmp1 ;
brne outc10 ;последовательно вывожу весь байт
; стоп-бит
cbi ddra,dio ;переключаю DIO как вход, чтобы исключить коллизию с ACK
cbi porta,clk ;опускаю CLK
rcall pause ;пауза 100 мкс
sbi porta,clk ;поднимаю clk
rcall pause ;пауза 100 мкс
cbi porta,clk ;опускаю CLK
cbi porta,dio ;
sbi ddra,dio ;окончание стоп-бита, переключаю DIO как выход
; (выхожу с нулями на обеих шинах)
rcall pause ;пауза 100 мкс
pop tmp1 ;
ret ;
;********************************
incom: ;Чтение кнопок. выходные данные будут в tmp0
push tmp1
clr tmp0 ;
;последовательный ввод восьми битов в tmp0
ldi tmp1,0x08 ;
inc10: cbi porta,clk ;опускаю CLK
cbi ddra,dio ;переключение линии DIO как входа
sbi porta,dio ;включаю подтяжку DIO
rcall pause ;пауза 100 мкс
sbi porta,clk ;поднимаю CLK
rcall pause ;пауза 100 мкс
lsr tmp0 ;
sbr tmp0,0x80 ;\
sbis pina,dio ; >перенос пина DIO в tmp0
cbr tmp0,0x80 ;/
dec tmp1 ;
brne inc10 ;последовательно ввожу весь байт
rcall pause ;пауза 100 мкс
cbi porta,clk ;
;стоп-бит
rcall pause ;пауза 100 мкс
sbi porta,clk ;девятый импульс CLK
rcall pause ;пауза 100 мкс
cbi porta,clk ;опускаю CLK
cbi porta,dio ;выключаю подтяжку DIO
sbi ddra,dio ;переключение линии DIO как выхода
; выход с нулями на обеих шинах
rcall pause ;пауза 100 мкс
pop tmp1
ret
;********************************
pause: ;пауза 100 мкс
push tmp3 ;
ldi tmp3,0xff
de10: dec tmp3
brne de10
pop tmp3
ret
;*****************************************************************
;*****************************************************************
EXINT: ; IRQ0 Handler
PCIN0: ; PCINT0 Handler
PCIN1: ; PCINT1 Handler
WDT: ; Watchdog Interrupt Handler
TIM1_CAPT: ; Timer1 Capture Handler
TIM1_COMPA: ; Timer1 Compare A Handler
TIM1_COMPB: ; Timer1 Compare B Handler
TIM1_OVF: ; Timer1 Overflow Handler
TIM0_COMPA: ; Timer0 Compare A Handler
TIM0_COMPB: ; Timer0 Compare B Handler
TIM0_OVF: ; Timer0 Overflow Handler
ANA_COMP: ; Analog Comparator Handler
ADCHN: ; ADC Conversion Handler
EE_RDY: ; EEPROM Ready Handler
USI_STR: ; USI STart Handler
USI_OVF: ; USI Overflow Handler
rjmp reset ;
www.chipmaker.ru
Всем привет.
Сегодня для тестирования, подключаем к Arduino Nano, четырех значный, 7 х 4 сегментный, I2C дисплей TM1637.
Еще несколько лет назад подключить такой дисплей было не так просто и приходилось использовать все выводы платы Ардуино. Но сейчас благодаря микросхеме TM1637, преобразующей данные с шины I2C, в параллельные сигналы, эти дисплеи стало очень просто подключать к контроллерам. Так как для подключения этого дисплея нужно всего 4 провода, мама-мама или если использовать макетную плату, то 4 провода папа-папа. Из них 2 повода это питание +5 В, GND и оставшиеся 2 провода это шина I2C.
Вкратце расскажу про характеристики дисплея:
Микросхема драйвера дисплея TM1637
Логические уровни толерантные как к уровням напряжения 5 В и 3.3 В.
Четыре семи сегментные цифры плюс разделительные точки.
Сам дисплей с общим анодом.
Яркость дисплея можно регулировать и имеет 3 уровня.
Цвета свечения сегментов бывают разные красный, синий, желтый, белый и зеленый. Но самый популярный цвет красный. Я бы для себя предпочел бы белый, но как мне кажется белый при ярком дневном освещении будет плохо читаем. Наверно с этим и связана популярность красного.
И так, перейдем к подключению сегментного экрана к Arduino nano. Подключение занимает всего несколько секунд.
Схема подключения:
Принципиальная схема:
Для того что бы на экране индикатора появилась какая либо информация, нужно скачать и установить библиотеку TM1637, потом запустить Arduino IDE и выбрать «Файлы — примеры — TM1637» и после чего нажать кнопочку «Загрузка в контроллер», кнопку обозначил на изображении
Если все прошло успешно , то Ваш дисплей начнет демонстрировать свои возможности.
В выше приведенной библиотеке есть много разных эффектов, такие как бегущая строка, скроллинг, вращение, управление яркостью и пр.
Что бы создать и загрузить в дисплей свой символ, для его расчета нужно воспользоваться инструкций на изображении ниже. Если в двух словах, то значение каждого включенного сегмента нужно суммировать
Например нам нужно на экране получить цифру 4. Для этого нужно суммировать следующие значения 2 + 4 + 32 + 64 = 102. Переводим в шестнадцатеричную систему исчисления и получаем 0x66.
Ну вот как бы и все, что я хотел Вам рассказать про этот замечательный I2C экранчик. Надеюсь ничего не упустил. Если будут вопросы. Не стесняйтесь задавайте. Я отвечаю всегда, но может не всегда быстро как многим хотелось бы.
И на последок видео демонстрация возможностей сегментного индикатора TM1637
duino.ru
В данной статье рассматривается пример создания часов рального времени. На индикатор будет выводиться точное время, а двоеточие на нем будет моргать раз в секунду. Точное время будет автоматически устанавливаться во вермя компиляции прошивки.
В нашем проекте мы используем:
Мы используем модуль с часами реального времени от Seeed Studio. Они построены на базе микросхемы DS1307 от Maxim Integrated. Из элементов обвязки она требует три резистора, часовой кварц и батарейку, которые уже имеются на данном модуле. Модуль обладает следующими свойствами:
Подсчет времени (секунды, минуты, часы), даты (год, месяц, число), дня недели
Двухпроводной интерфейс I²C
Суть часов реального времени в том, что при наличии батарейки, они могут идти даже если основное устройство обесточено. Мы с такими часами сталкиваемся постоянно в ноутбуках или цифровых фотоаппаратах. Если достать из этих устройств аккумулятор, а через некоторое время вернуть их обратно, то время не сбросится. В этом заслуга часов реального времени, Real Time Clock (RTC).
Все необходимые библиотеки можно скачать с официального сайта.
Мы используем четырёхразрядный индикатор от Seeed Studio. Основное в индикаторе — микросхема TM1637, представляющая собой драйвер для отдельных 7-сегментных разрядов. В данном модуле используется 4 разряда. Модуль обладает следующими свойствами:
8 градаций яркости
Двухпроводной интерфейс работы (CLK, DIO)
Данный модуль мы используем для показа времени: часов и минут. Удобство модуля в том, что подключается он всего по двум проводам и не требует программной реализации динамической индикации, поскольку все уже реализовано внутри модуля.
Динамическая индикация — это процесс, при котором индикаторы в нашем модуле загораются последовательно. Но мерцания мы не видим, поскольку человеческой глаз обладает большой инертностью. Данный метод позволяет очень хорошо экономить количество соединений между индикаторами и контроллером:
Статическая индикация: 4 цифры × 7 сегментов = 28 соединений.
Динамическая индикация: 7 сегментов + 4 общих анода или катода = 11 соединений.
Микросхема TM1637: 2 соединения.
Выгода очевидна.
Библиотека для данного модуля также может быть скачана с сайта производителя.
Модуль часов реального времени необходимо подключить к выводам SCL/SDA, относящимся к шине I²C. Также необходимо подключить линии питания (Vcc) и земли (GND).
Линии SDA/SCL имеют собственные отдельные пины на Arduino, однако внутри они так или иначе подключены к пинам общего назначения. Если рассмотреть Arduino Uno, линии SDA соответствует пин A4, а SCL — A5.
В комплекте с модулем поставляется шлейф с мама-контактами, которые удобнее подключать к Troyka Shield. Однако отдельные пины SDA и SCL на ней не выведены, поэтому мы осуществили подключение прямо через пины A5 и A4.
В плане подключения индикатора — все гораздо проще. Выводы CLK и DIO можно подключить к любым цифровым выводам. В данном случае используются 12-й и 11-й выводы соответственно.
Функция setup
должна инициализировать часы реального времени и индикатор, а также записывать время компиляции во внутреннюю память часов реального времени. Все действие, а точнее, чтение времени из RTC и вывод его на индикатор, будет производиться в функции loop
.
Код для этого выглядит следующим образом:
#include <Wire.h> #include <EEPROM.h> #include "TM1637.h" #include "DS1307.h" //Массив, содержащий время компиляции char compileTime[] = __TIME__; //Номера пинов Arduino, к которым подключается индикатор #define DISPLAY_CLK_PIN 12 #define DISPLAY_DIO_PIN 13 //Для работы с микросхемой часов и индикатором мы используем библиотеки //Классы TM1637 и DS1307 объявлены именно в них TM1637 display(DISPLAY_CLK_PIN, DISPLAY_DIO_PIN); DS1307 clock; void setup() { //Включаем и настраиваем индикатор display.set(); display.init(); //Запускаем часы реального времени clock.begin(); //Получаем число из строки, зная номер первого символа byte hour = getInt(compileTime, 0); byte minute = getInt(compileTime, 3); byte second = getInt(compileTime, 6); //Готовим для записи в RTC часы, минуты, секунды clock.fillByHMS(hour, minute, second); //Записываем эти данные во внутреннюю память часов. //С этого момента они начинают считать нужное для нас время clock.setTime(); } void loop() { //Значения для отображения на каждом из 4 разрядов int8_t timeDisp[4]; //Запрашиваем время с часов clock.getTime(); //Получаем десятки часов с помощью целочисленного деления timeDisp[0] = clock.hour / 10; //Получаем единицы часов с помощью остатка от деления timeDisp[1] = clock.hour % 10; //Проделываем то же самое с минутами timeDisp[2] = clock.minute / 10; timeDisp[3] = clock.minute % 10; //... а затем выводим его на экран display.display(timeDisp); //у нас нет отдельных разрядов для секунд, поэтому //будем включать и выключать двоеточие каждую секунду display.point(clock.second % 2 ? POINT_ON : POINT_OFF); } //Содержимое функции объяснено ниже char getInt(const char* string, int startIndex) { return int(string[startIndex] - '0') * 10 + int(string[startIndex+1]) - '0'; }
Теперь загружаем этот код в среду разработки, компилируем и заливаем. Смотрим на дисплей — бинго! Время на дисплее — время компиляции.
Для начала необходимо понять, откуда же в массиве compileTime
появляется время.
Оно появляется в этой строчке:
unsigned char compileTime[] = __TIME__;
Компилятор вместо __TIME__
подставляет строку, содержащую время компиляции в виде __TIME__ = "hh:mm:ss"
, где hh — часы, mm — минуты, ss — секунды.
Вернемся к коду, который необходимо объяснить:
char getInt(const char* string, int startIndex) { return int(string[startIndex] - '0') * 10 + int(string[startIndex+1]) - '0'; }
В массиве string
, передаваемом в качестве параметра в функцию getInt
, мы получаем символ с индексом startIndex
и следующий за ним, чтобы в итоге получить двухзначное целое число. Однако, изначально это не число, а пара символов. Чтобы получить число по символу, нам необходимо вычесть из этого символа символ нуля ('0
‘): ведь в таблице ASCII все символы цифр идут одна за другой, начиная с символа нуля. Поэтому код int(string[startIndex]) - '0')
, дословно, делает следующее: «Берем символ номер startIndex
, вычитаем из него символ нуля и переводим в целочисленный тип».
Да, этот код рабочий, и часы будут идти. Однако, если отключить питание, а через несколько минут включить, то после включения время время вновь станет тем, которое было при компиляции.
Это происходит потому что после включения питания, вновь исполняется код, находящийся в функции setup
. А он записывает в часы реального времени старое значение времени.
Чтобы этого избежать, нам необходимо еще чуть-чуть модифицировать код. Каждый раз в функции setup
будет происходить подсчет «хэша» времени компиляции — будет рассчитываться количество секунд, прошедшее с 00:00:00 до времени компиляции. И этот хэш будет сравниваться с хэшем в EEPROM. Напомним EEPROM — память, которая не обнуляется при отключении питания.
Если значения посчитанного и сохранённого ранее хэша совпадают, то это значит, что перезаписывать время в модуль часов нет необходимости: это уже было сделано. А вот если эта проверка не проходит, то происходит перезапись времени в RTC.
Для записи/чтения числа типа unsigned int
в/из EEPROM написаны две дополнительные функции EEPROMWriteInt
и EEPROMReadInt
. Они добавлены потому что функции EEPROM.read
и EEPROM.write
могуть читать и писать только данные типа char
.
#include <Wire.h> #include <EEPROM.h> #include "TM1637.h" #include "DS1307.h" //Массив, содержащий время компиляции char compileTime[] = __TIME__; //Номера пинов Arduino, к которым подключается индикатор #define DISPLAY_CLK_PIN 12 #define DISPLAY_DIO_PIN 13 //Для работы с микросхемой часов и индикатором мы используем библиотеки TM1637 display(DISPLAY_CLK_PIN, DISPLAY_DIO_PIN); DS1307 clock; void setup() { //Включаем и настраиваем индикатор display.set(); display.init(); //Запускаем часы реального времени clock.begin(); //Получаем число из строки, зная номер первого символа byte hour = getInt(compileTime, 0); byte minute = getInt(compileTime, 3); byte second = getInt(compileTime, 6); //Импровизированный хэш времени //Содержит в себе количество секунд с начала дня unsigned int hash = hour * 60 * 60 + minute * 60 + second; //Проверяем несовпадение нового хэша с хэшем в EEPROM if (EEPROMReadInt(0) != hash) { //Сохраняем новый хэш EEPROMWriteInt(0, hash); //Готовим для записи в RTC часы, минуты, секунды clock.fillByHMS(hour, minute, second); //Записываем эти данные во внутреннюю память часов. //С этого момента они начинают считать нужное для нас время clock.setTime(); } } void loop() { //Значения для отображения на каждом из 4 разрядов int8_t timeDisp[4]; //Запрашиваем время с часов clock.getTime(); //Получаем десятки часов с помощью целочисленного деления timeDisp[0] = clock.hour / 10; //Получаем единицы часов с помощью остатка от деления timeDisp[1] = clock.hour % 10; //Проделываем то же самое с минутами timeDisp[2] = clock.minute / 10; timeDisp[3] = clock.minute % 10; //... а затем выводим его на экран display.display(timeDisp); //у нас нет отдельных разрядов для секунд, поэтому //будем включать и выключать двоеточие каждую секунду display.point(clock.second % 2 ? POINT_ON : POINT_OFF); } char getInt(const char* string, int startIndex) { return int(string[startIndex] - '0') * 10 + int(string[startIndex+1]) - '0'; } //Запись двухбайтового числа в память void EEPROMWriteInt(int address, int value) { EEPROM.write(address, lowByte(value)); EEPROM.write(address + 1, highByte(value)); } //Чтение числа из памяти unsigned int EEPROMReadInt(int address) { byte lowByte = EEPROM.read(address); byte highByte = EEPROM.read(address + 1); return (highByte << 8) | lowByte; }
В данной статье был показан пример работы с микросхемой часов реального времени RTC DS1307 и микросхемой-драйвером индикатора TM1637, также мы научились получать дату и время на этапе компиляции. Теперь, если выставить нужное время на часах, а потом отключить питание хоть на несколько часов, то после включения время вновь будет точным. Проверено!
wiki.amperka.ru
Подключение TM1367 к PIC (первый опыт)
Подключение TM1367 к PIC (первый опыт).Недавно решил использовать в таймере четырехзначный семисегментный индикатор ТМ1637 вместо знакосинтезирующего ЖКИ на базе контроллера HD44780.
Конструкция получается дешевле, освобождаются четыре порта, меньше команд в программе .
Вспомнив, что знакомство с ЖКИ на HD44780 начиналось с просмотра в интернете простых примеров программ на ассемблере, набрал запрос в поисковике.
Но ответа на вопрос «как подключить TM1637 к PIC» не получил. Дейташит микросхемы и статьи с пояснениями ее работы многое прояснили, но следуя им, работоспособную программу написать не удалось- то путались знакоместа, отсутствовали символы или произвольно выключался индикатор. Видимо не все, кажущееся авторам элементарным, доходит до впервые обратившегося к этой микросхеме. Программы в библиотеке MPLAB я тоже не нашел (или не сумел ей воспользоваться).
Поэтому пришлось отложить изучение интерфейса ТМ1637 и выводить символы по-своему, руководствуясь принципами I2C , не пытаясь объяснять свои решения . Так написал программку на ассемблере и использовал ее в своем таймере. Может быть она кому-то пригодится для знакомства с ТМ1637 или для демонстрации физического уровня I2C. Буду краток, считая, что читающий знаком с материалами по ТМ1637 и уже пытался их использовать ))).
В программе данные на шину DIO ТМ1637 выводятся группами по 5 байт . Включается индикатор этой же процедурой- первый байт, команда включения .140, второй, конфигурация .64, третий, номер какого-либо знакоместа; два последних байта могут быть нолями (сегменты не зажигаются). Следующая группа из пяти байт содержит номер знакоместа первой цифры и коды четырех выводимых цифр. Вывод данных происходит синхронно с состоянием тактовой шины CLK- уровень на DIO переключается, когда на CLK ноль. Между информационными байтами выдерживается промежуток в один период CLK на время ответа ACK. Старт и стоп происходит при переключении DIO на высоком уровне CLK. Тактовые импульсы имеют скважинность 2 и период 300мкс. В перерывах передачи импульсы не формируются, на шинах устанавливают единицу подтягивающие резисторы по 10 Ком. Порты RA4 (CLK) и RA5 (DIO) подключены к ним через развязывающие диоды “на всякий случай”, вдруг какое-либо новое устройство по своему усмотрению выставит на шинах ноль и двухтактный выход порта сгорит. Хотя стабильность частоты CLK не требуется, предпочтительно запретить прерывания при выводе знаков. Порты контроллера назначены согласно рисунку 1.
Для демонстрации интерфейса предлагаю программу » Часы «.
Меню программы имеет три режима:
— установка часов на рисунке 2,
— установка минут на рисунке 3,
— индикация времени на рисунке 4.
Интерфейс ТМ1637 реализован в процедуре PWR. В ней создан стек из пяти байт BTR1 — BTR5, в которые предварительно записываются данные. Байты синхронно с CLK сдвигаются вправо и правый крайний бит определяет уровень на шине DIO. После восьми информационных бит устанавливается высокий уровень и не делается сдвиг во время одного такта CLK, отводимого на ACK.
В таблице ниже поясняется назначение некоторых процедур программы:
PWR | Интерфейс ТМ1637. RA4- CLK, RA5- DIO. |
CLOCK | Часы, считающие переполнения TMR1 |
JK и PI9_ | Переключение пунктов меню, увеличение или уменьшение параметра |
BDPP | Преобразование числа из двоичной формы в двоично-десятичную, подготовка к выводу на индикатор |
RDEP2C и IND2C | Преобразование полубайтов выводимого числа в коды символов, запись кодов в BTR1 – BTR5. Коды семисегментных цифр записаны в ЕЕР по адресам от 0 до 9 соответственно. |
KTRI_ и C_IND | Управление работой программы. |
Поскольку программа вырезана из таймера (сам многорежимный таймер ничем не отличается от многих других и не достоин внимания Радиокотов), в ней много ‘’некрасивых” адресов и лишних переменных. Прошу не обращать на них внимания; цель статьи- предложить действующий вариант интерфейса для ТМ1637, например, в виде незамысловатых часов.
Освоив вывод на дисплей цифр, не трудно выводить и буквы в семисегментном алфавите, как показано на рис. 5.
К сожалению, в отличие от LCD1602 , в PROTEUS нет эмулятора ТМ1637.
Debugger I2C дает непонятные комментарии о работе интерфейса. Для отладки программы подходит логический анализатор.
На рис.6 показано начало передачи,- сначала команда «Старт», потом байт 00110001.Мы помним, что биты в интерфейсе передаются начиная с младшего, поэтому исходный байт «перевернут» и имеет вид 10001100, что соответствует .140, команде включения. Потом перерыв (единица на DIO) на время ответа ТМ.Этот же байт можно увидеть с помощью реального осциллографа (рисунок 7), только труднее добиться стабильной синхронизации. Хотя, если непременно нужна осциллограмма, можно использовать свободный порт микроконтроллера для формирования синхросигнала в нужном байте последовательности DIO.
К статье приложены проекты в MPLAB и PROTEUS’е.
Вот так я открыл для себя недорогой и удобный индикатор и поупражнялся в двоичном счете.
Файлы:
Проект в Proteus
Проект в MPLAB
Все вопросы в Форум.
Как вам эта статья? | Заработало ли это устройство у вас? |
www.radiokot.ru