NanoTerminal. Часть 3. OLED-дисплей на чипе SSD1306. Общее описание.

1306
Как и обещал, начинаю публиковать серию статей по работе с OLED-дисплеем на базе чипа SSD1306 разрешением 128 на 64 пикселя. Хотя практически всё нижеописанное будет относиться и к дисплеям с другим разрешением. Datasheet SSD1306 позволяет управлять через параллельный интерфейс 8080, последовательные интерфейсы I2C и SPI. Самыми дешевыми, простыми в освоении, хотя и наиболее тормозными являются версия c I2C-интерфейсом, но для использования в составе терминала — то, что нужно, если конечно мультики на экране не смотреть.

Как видно из картинки (которую я украл на просторах интернета), у рассматриваемого дисплея управление происходит по двум проводам — SDA (данные) и SCL (такт). Это самый распространенный вариант дисплея и самый демократичный — его цена около 170 рублей (2.5 — 3 $). Преимущества и недостатки кратко описаны в соответствующей табличке, но самый главный недостаток — очень хрупкое исполнение. Стекло дисплея неполностью прилегает к плате из стеклотекстолита, большой участок стекла — шириной более 5 мм (на фото —  нижняя прозрачная часть) просто «висит» в воздухе, что при малейшем надавливании приводит к излому. А излом приводит к разрыву внутренних проводников, что в свою очередь выводит из строя часть сегментов экрана.

Преимущества

Недостатки

— отличная контрастность
— невысокая цена за вариант 0.96 дюймов
— простота управления
— множество способов управления
— возможность управлять через I2C, то есть всего по двум проводам
-УЖАСНО хрупкая конструкция
-небольшие размеры, хотя иногда это можно отнести к преимуществам
-высокая цена за размеры отличные от 0.96 дюймов
-относительно невысокий FPS через I2C

Существуют модификации с одноцветными OLED-пикселями, например, белыми, голубыми, желтыми или даже зелеными. Также имеются двухцветные дисплеи, самый распространенный из который синий с желтой полоской, шириной 16 пикселей сверху.


Ну вот в целом и всё, что можно сказать о конструктиве дисплея. Самое время перейти к внутреннему устройству и основам управления. Основными составляющими SSD1306 являются:
— MCU Interface — блок взаимодействия SSD1306 с микроконтроллерами. Воспринимает команды по одному из четырех интерфейсов — параллельный, последовательные 4-wire SPI, 3-wire SPI, 2-wire I2C, транслируют их в единообразном формате в блок обработки команд — Command Decoder. Через MCU Interface также поступают данные, которые транслируются в блок памяти.
— GDDRAM — блок памяти графических данных дисплея (Graphic Display Data RAM). Собственно внутренний буфер SSD1306, в котором хранятся состояния каждого из пикселей дисплея. Путём записи данных в данный блок мы управляем картинкой на экране.
— Display Controller — блок управления экраном. Читает данные из GDDRAM и отображает эту информации на экране в режиме динамической индикации.
Есть и еще несколько блоков , полная картинка есть в datasheet‘e на странице №7.

Нас интересуют в основном два блока — GDDRAM и Command Decoder. Именно с ними происходит всё общение программиста. Причем неважно каким образом Вы направите данные в эти блоки — MCU Interface всё скушает и направит по правильному адресу. Соответственно для управления SSD1306 нужно всего лишь направлять нужные команды и нужные данные. Мы будем эти данные направлять через двухпроводной интерфейс I2C и для этого нужно две базовых функции — для данных (sendData) и для команд (sendCommand). Они практически ничем не отличаются, разница лишь в том, что перед отправкой данных мы шлем в шину I2C значение 0x40, а перед отправкой команды — 0x80, а MCU Interface распознает эти значения и решает куда направить следующий байт.  Иначе говоря, управление дисплеем сводится к отправке последовательностей двухбайтовых комбинаций по адресу SSD1306 равному 0x3C.

#include "Wire.h" 
#define SSD1306_Address               0x3C
#define SSD1306_Command_Mode          0x80
#define SSD1306_Data_Mode             0x40

void sendCommand(unsigned char command)
{
  Wire.beginTransmission(SSD1306_Address);
  Wire.write(SSD1306_Command_Mode);
  Wire.write(command);
  Wire.endTransmission();
}

void sendData(unsigned char Data)
{
     Wire.beginTransmission(SSD1306_Address);
     Wire.write(SSD1306_Data_Mode);
     Wire.write(Data);
     Wire.endTransmission();
}

На этом общая информация заканчивается. Далее мы рассмотрим инициализацию дисплея.

NanoTerminal. Автономный отладчик на Atmega 328 (Arduino). Часть 2.

miniterminal1

miniterminal1Первая вводная часть здесь.
А в этой части о железном воплощении NanoTerminal’а: текущая версия выполнена на двусторонней
стеклотекстолитовой плате размером 7х5 см. Фотографии все сделаны на мобильный телефон в условиях комнатного освещения, так что особо не придирайтесь.

На верхней стороне платы расположен собственно дисплей на базе контроллера SSD1306, распространенный и дешевый. Стоил он около 170 рублей на Aliexpress. Под ним прячется микросхема EEPROM-памяти AT24C256 в SMD-корпусе SOIC8, а также часть центрального микроконтроллера ATMEGA328PU в корпусе DIP28. Также видно кварц нереальных размеров, светодиод 3мм, шесть кнопок и разъем для UART и питания. Слева две кнопки — RESET и ESC, а слева четыре — три из них для навигации по меню и пролистывания лога UARTа, а четвертая пока ни для чего кроме Тетриса не используется.

miniterminal_backПрактически весь нехитрый монтаж я выполнил на нижней стороне платы SMD — компонентами. Резисторы для подтяжки кнопок к шине GROUND и для сопряжения уровней 5 и 3.3 Вольта. К сожалению, только на таких уровнях работает микросхема оперативной памяти 23K256, очень кстати капризная. На фотке её не видно потому, что я её выпаял, остались только резисторы и пустое место. Дело в том, что я в этой версии подключил её к питанию 5В, а он чот не выдержала. Капризная, говорю же. Ошибку в схеме, я заметил уже после того спалил микросхему, но о ней позже в отдельном разделе посвященном работе с 23K256. Для программирования контроллера я подпаял провода интерфейса SPI и вывод RESET.
 
miniterminal3Вот на этой фотке главное меню:

TERMINAL — ну тут понятно, в этом режиме терминал ловит в UART всё что ему шлют и отображает на экране

SETUP — за этим пунктом меню я спрятал настройки

SERVOTESTER — тут мы сервоприводы тестируем

TETRIS -тут мы в Тетрис гоняем
 
term_sett
Вот подменю настроек в котором можно выбрать скорость порта от 300 до 57600 бод, а также задать крайние значения длительности импульсов ШИМ для сервопривода. Ну а еще можно вернуть значения по умолчанию или сохранить во внутреннем EEPROMе свои настройки. Они читаются всякий раз при загрузке терминала.
 
Поскольку программа изначально писалась в Arduino IDE и для китайского аналога Nano V3.0 (как я и говорил ранее, «Nano» стала приставкой в названии терминала), то чтобы ничего не переписывать, я залил фьюзы от Arduino Nano и шью контроллер как Arduino.
 
v01
Но обо всём попозже, а сейчас несколько фотографий первой и второй версии терминала. Первая версия была собрана на коленках. Из новых компонентов там были только кнопки и дисплей. OLED-дисплей SSD 1306 0.96 дюймов — ОООООЧЕНЬ хрупкая вещь. Его очень легко убить. Вот посмотрите на фотке обведено самое хрупкое место — ломается на раз и обрывает дорожки внутренних проводников. А этих дорожек там четыреста штук. В результате часть колонок или строк дисплея не работает, создавая полосатую картинку. Будьте очень аккуратны при работе с этим дисплеем. Я убил таких два. Один раз когда нёс в кармане куртки (ну тут понятно — раздавил чем-то), а второй раз даже не пойму как. Он просто начал показывать полосы — я пригляделся и увидел трещину в стекле.
 
v01_2
 
Это нижняя часть «платы» первой версии. И она работала, пока я не спалил что-то в Nano, подозреваю, что стабилизатор или CH340G. Короче будет время — разберусь, может восстановлю, а может и на распайку пойдет.
 
v02
 
И на последней фотографии останки второй версии, которую я выполнил на двух платах 5х5 см, соединяющихся, поначалу коннекторами, а позднее спаянных проводами. Atmega вставлялась в кроватку, рядом стояла микросхема ОЗУ. На второй плате был дисплей, кнопки и EEPROM AT24C256. Короче, очень неудачная конструкция, коннекторы отваливались, память не читалась. Было решено распаять её и собрать всё на одной плате. Собственно так и получился вариант №3, описанный в самом начале.

Замечу сразу, что это не окончательный вариант. Как минимум, потому, что нужно будет запилить схему питания для ОЗУ на простеньком стабилизаторе AMS 1117 3.3 В. Но и на этом варианте можно писать и отлаживать программу.

В следующих статьях, я расскажу как взаимодействовать дисплеем на базе SSD1306. Тема достаточно обширная, так как придется описать всё, начиная с работы с дисплеем на физическом уровне, заканчивая достаточно высокоуровневыми вещами, такими как свой знакогенератор, вывод текста, чисел и т.д.

NanoTerminal. Автономный отладчик на Atmega 328 (Arduino). Часть 1.

miniterminal1
Очень часто для отлаживания программы загруженной в микроконтроллер используется последовательный интерфейс UART подключенный к реальному или виртуальному COM-порту компьютера. В частности, в Arduino IDE имеется простенький, но вполне рабочий инструмент — Монитор последовательного порта. Очень удобно отлавливая баг или просто наблюдая значение переменной написать строчку вроде Serial.println(variable); и в Монитор порта полетят цифры, буквы и прочая дребедень.

Это всё хорошо, когда мы отлаживаем изделие, которое просто лежит на столе и подключено к компьютеру. А как быть с автономными роботами, которых мёдом не корми — дай поездить, побегать, поплавать или полетать? Провода исключаются — робот ведь автономный, а значит любит свободу. Радиоканал, вроде Wi-Fi, bluetooth или что-то вроде — хорошо, но очень недалеко, а вообще желательно в пределах помещения. А самое главное надо специально переписывать программу под средства отладки по радиоканалу.

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

Первая версия была построена буквально за вечер, что называется «на коленках» на базе китайского клона ArduinoNano (отсюда и название NanoTerminal), с OLED-дисплеем на базе контроллера SSD1306. Всё это хозяйство было смонтировано на китайской макетной плате весьма поганого качества. Но тем не менее на этой первой версии я смог написать основной код программы.

Основные характеристики терминала на текущий момент (октябрь 2016):
1) Разрешение дисплея 128х64;
2) МК — Atmega328;
3) Расширенная память — 32 кбайта статического ОЗУ и 32 кбайта EEPROM;
4) Гибкая система меню;
5) Настраиваемая скорость приема/передачи;
6) Встроенный тестер сервоприводов;
7) Игра Тетрис;

Планируется:
1) Встроенный макроязык, аналогичный AT-командам, для управления функциями минитерминала, такими как: сохранение содержимого ОЗУ в EEPROM, чтение EEPROM, очистка памяти, включение/выключение дисплея и так далее.
2) Батарейное питание, выгрузка данных через USB на компьютер.

В серии статей об этом изделии я опишу работу с контроллером дисплея SSD1306, микросхемой EEPROM AT24C256 через интерфейс I2C, а также с микросхемой статической памяти 23К256 через интерфейс SPI.

Тестер сервоприводов своими руками

Тестер сервоприводов мне понадобился, когда я разрабатывал робота, играющего в Piano Tiles 2 на планшетном компьютере. Можно здесь глянуть на его работу. Механические «пальцы» данного робота приводятся в движения четырьмя сервоприводами. Сервопривод должен работать в узком диапазоне углов вращения (примерно 20-30 градусов) для того, чтобы движения «пальцев» точно синхронизировались с движением плиток по экрану планшета. Для точного подбора крайних значений («палец нажимает на плитку» и «палец поднят вверх»), можно было бы обойтись и подбором величин прямо в программе робота, но проще, быстрее и точнее сделать это с помощью тестера.

Для наглядности, я записал короткое видео с примером работы данного тестера.

Данный тестер состоит всего из трёх основных элементов — китайский Arduino Nano V3.0, переменный резистор, включенный по схеме потенциометра и цифровой 4-х значный дисплей, управляемый с помощью драйвера TM1637 по протоколу I2C. Всё это собрано на беспаечной макетной плате с помощью типовых проводников типа «папа-папа» (male-male).

Схема_1Для начала средний вывод потенциометра соединим с любым аналоговым входом Nano (в моем случае A0), крайние выводы потенциометра к земле (GND) и к питанию 5V. Таким образом у нас получится управляемый делитель напряжения, выдающий на среднем выводе потенциометра аналоговый сигнал от 0 до 5 Вольт, в зависимости от положения движка.

Далее, подключим модуль дисплея TM1637 к питанию, для этого нужно соединить выводы питания (5V и GND) c соответствующими выводами Arduino Nano. Управляющие лапки DIO и CLK можно цеплять на любые выводы Arduino Nano, так как будет использоваться библиотека, в которой протокол I2C реализован программно. В моем случае я соединил лапку DIO с пином D3, а CLK c пином D2.

Типовой разъем сервопривода состоит из трёх контактов, коричневый — земля (GND), красный — питание (5V) и желтый — управление (PWM). Соответственно подцепим контакты питания к одноименным контактам Arduino Nano, а желтый провод подключим к любому пину с ШИМ (PWM), в моем случае это D5. На данный пин будем выводить сигнал широтно-импульсной модуляции, управляющий сервоприводом. Угол поворота сервопривода определяется длительностью положительной части импульса и обычно лежит в пределах от 500 до 2500 микросекунд.

Теперь для проверки собранного Вами девайса, подключите Arduino Nano к компьютеру через разъем USB и прошейте простенькую программу, использующую библиотеки «Servo.h» и «TM1637.h».

#include <Servo.h>
#include "TM1637.h"

#define CLK 2 //Подключаем пины соответственно лапкам модуля
#define DIO 3
TM1637 tm1637(CLK,DIO);// создаем экземпляр класса TM1637
Servo S1; //объявляем экземпляр класса сервопривод
byte angle = 90;//переменная, которая будет хранить угол поворота сервы
byte old_angle = 91;//
int V = 0;
void setup()
{
  tm1637.init();//инициализируем дисплей, (хотя что мешало создателям библиотеки инициализировать его в конструкторе TM1637 tm1637(CLK,DIO);?)
  tm1637.set(BRIGHT_TYPICAL);//яркость дисплея
  pinMode(A0,INPUT);// объявляем пин А0 как вход, сюда будем подавать аналоговое напряжение с потенциометра
  pinMode(5,OUTPUT); 
  S1.attach (5); //прикручиваем серву к пину D5
}

void loop() {
V=analogRead(A0);//читаем напряжение с потенциометра
angle = map(V, 0, 1023, 0, 180);//преобразуем в угол
if (angle!=old_angle) // если потенциометр крутился, то...
  {
  tm1637.display(angle);// ... отображаем значение угла на дисплее
  S1.write(angle);// ... крутим сервопривод
  old_angle=angle;//... запоминаем последнее значение угла, чтобы в следующий раз не дергать серву и дисплей, если потенциометр не крутился
  }
}

Если после прошивки на экране отображается какое-либо число от 0 до 180, то значит всё собрано верно. Можно подключать сервопривод и тестировать его.

Стоимость данного тестера:
китайский клон Arduino Nano — от 120 до 200 рублей;
дисплей ТМ1637 — 85-150 рублей;
макетная плата — 100-300 рублей;
резистор — от 10 до 100 рублей;

ИТОГО: 315-715 рублей. А если учесть, что ничего не запаяно и если тестер не нужен, то его можно разобрать, то стоимость вообще падает до нуля. Обычно все эти компоненты уже имеются в арсенале любого микроконтроллерщика (ардуинщика).

Управление семисегментным индикатором через ТМ1637.

2016-04-06_23-31-157segs
В этой короткой статье я поясню как именно микросхема TM1637 управляет семисегментным индикатором. Управление происходит путем записи в регистры ТМ1637 однобайтовых слов. Каждый регистр отвечает за отображение одного знака, состоящего из семи сегментов ABCDEF и десятичной точки dp — итого 8 сегментов, как раз по числу битов в байте. Зажигает нужный нам сегмент установленный в «1» разряд байта. Сегменту А соответствует самый младший разряд, десятичной точке dp — самый старший.
2016-04-14_22-03-31

Таким образом, записывая байты в соответствующие регистры можно управлять каждым сегментом в отдельности. Запишем в регистр знакоместа байт 00000110 — он зажжет сегменты В и С, которые образуют знак «1», запишем 00000111 — включатся сегменты А, В и С, образуя цифру «7».  Если же отправить 01100011, то загорятся сегменты A, B, F и G, образуя маленький нолик, который можно использовать как значок градусов Цельсия при отображении температуры.Пример программы в прилагаемом файле, а на видео — пример работы этой программы.

Управление TM1637. AVR (Си)

2016-04-06_23-31-15TM1637Краткое содержание предыдущей серии: купил модули, очень красивые, вроде работают, а вроде и не очень. К Ардуине вроде как прикручиваются, но не совсем. То ли библиотека не очень подходящая, то ли модули недоработанные. Написал продавцу, так мол и так, или не работает ваше добро как надо или библиотеку пришлите нормальную. Писал, как надо, на английском, мозг аж вспотел, а продавец возьми да и ответь на русском, типа: «всё нормально, дядя, модули исправные, зуб даю, а вот библиотеки у нас нет…».
Ладно, думаю, разберемся… Взял и напрямую прозвонил выводы — пожалуй, продавец был не прав — похоже, что на индикаторе просто нет светодиодов в десятичных точках. Ну да и ладно — первый блин комом, если эти модули нужны для индикации целых чисел, то не пугайтесь — покупайте. А вот если нужно отображать, что-то с плавающей точкой, то увы — ничего не выйдет.Итак, как я уже говорил, библиотеки для микроконтроллеров AVR в среде AtmelStudio я не нашел, а потому был вынужден переписать ардуиновскую. Для этого надо было переписать с C++ на Си полиморфную функцию Display, которая занимается отображением как единичных разрядов, так и всех четырех, а также выводит на дисплей целые и дробные (ха-ха, три раза) числа.

Библиотеку переписывать в полном объеме я не стал, а реализовал лишь служебные функции, а также несколько публичных функций — Display(), осуществляющих вывод знаков на экран. Дисплей применялся мной для отображения текущей скорости вращения двигателей двухколесного шасси для робота. Особенностью этого шасси является то, что ровно оно не едет из-за того, что колеса имеют разные угловые скорости. Для того, чтобы заставить ездить её ровно, я решил управлять с помощью ШИМ (широтно-импульсной модуляции) скоростью вращения каждого колеса. Установленный на машинку дисплейчик в левых двух символах показывает скорость левого колеса, а правые два символа — скорость правого.

Общение с ТМ637 происходит по протоколу I2C. В библиотеке он реализован программно, тоже самое я сделал и для себя. Это нужно для того, чтобы цеплять дисплей на любые выводы контроллера, так как аппаратная поддержка данного протокола в микроконтроллерe Atmega8 поддерживается только на двух пинах — SDA и SCL. Использовать мой перевод библиотеки можно путем копирования из файла описанных ниже функций:

//СЛУЖЕБНЫЕ ФУНКЦИИ (секция Private)
void TM1637_writeByte(int8_t wr_data) // служебная функция для общения с дисплеем по протоколу I2C
void TM1637_start(void) // служебная функция для общения с дисплеем по протоколу I2C
void TM1637_stop(void) // служебная функция для общения с дисплеем по протоколу I2C
void TM1637_coding_all(uint8_t DispData[])// служебная функция — шифратор
int8_t TM1637_coding(uint8_t DispData) //служебная функция — шифратор

//ПОЛЕЗНЫЕ ФУНКЦИИ (секция Public)
void TM1637_display_all(uint8_t DispData[]) // полезная функция, пожалуй даже самая полезная. Выводит содержимое массива на индикаторы
void TM1637_display(uint8_t BitAddr,uint8_t DispData) // выводит один знак на указанное место на индикаторе
void TM1637_display_int_decimal(int16_t Decimal) //выводит целое десятичное число
void TM1637_display_float_decimal(double Decimal) // должна выводить десятичное число с плавающей точкой
void TM1637_clearDisplay(void) // чистит дисплей
void TM1637_init(uint8_t DispType,uint8_t Clk, uint8_t Data) // инициализация глобальных переменных: DispType — Тип дисплея как правило равна 1, Clk — номер пина SCL, Data — номер пина SDA. Порт по умолчанию — PORTC, но можете поставить тот, который Вам нужен, кое-что поменяв в коде
void TM1637_set(uint8_t brightness)// ставим яркость от 0 до 7

Собственно, скачать эти функции можно здесь. Там же заодно и пример работы с ними.
На видео как раз и показана работа программы.

«TM1637.h» — Библиотека для Arduino
Аналог библиотеки на Си для AVR
Управление TM1637. AVR (Си).— предыдущая статья на эту же тему