Разработка «OLED часы на arduino», начинается с подбора комплектующих:

  • Часы реального времени (Real Time Clock (RTC) DS1307
  • Микроконтроллер Arduino Pro Mini 5В 16МГц ATMega328
  • OLED дисплей 0.96 «I2C 128×64 (желто-синий)
  • 3 тактовые кнопки
  • 3 резистора на 10кОм.
  • USB кабель для питания устройства.

Составляем схему подключения модулей:

(Рис. 1.1.)

На рис. 1.1. приведена нумерация модулей.

  • Под номером 1, находится USB кабель для питания нашего устройства.
  • Под номером 2, находится микроконтроллер Arduino Pro Mini 5В 16МГц ATMega328. Arduino (Ардуино) — аппаратно-вычислительная платформа, основными компонентами которой являются плата ввода-вывода и среда разработки (IDE). Arduino может использоваться как для создания автономных интерактивных объектов, так и подключаться к программному обеспечению, что выполняется на компьютере. Платформа arduino содержит 14 цифровых входов и выходов, 6 аналоговых входов, резонатор, кнопку перезагрузки и отверстия для монтажа контактов.
    Характеристики:

    • Рабочее напряжение: 5В,
    • Входное напряжение: 5-12В,
    • Цифровые входы / выходы: 14
    • Аналоговые входы: 6,
    • Постоянный ток через вход / выход: 40мА,
    • Флэш память: 16 Кб,
    • ОЗУ: 1 Кб,
    • EEPROM: 512 байт (энергонезависемая память),
    • Тактовая частота: 16МГц,
  • Под номерами 3-5, находятся подтягивающие (pull-down) резисторы номинала 10кОм. Подтягивающие резисторы используются в схемах рядом с входными контактами логических компонентов, которым важен только факт: подается ноль вольт (логический ноль) или НЕ ноль (логическая единица). Резисторы нужны, чтобы не оставить вход в «подвешенном» состоянии. Возьмем такую схему:

    (Рис. 1.2)

    Мы хотим, чтобы когда кнопка ни была нажатой (цепь разомкнута), микроконтроллер фиксировал отсутствие напряжения. Но в данном случае вход находится в «никакому» состоянии. Он может срабатывать и не срабатывать хаотично, непредсказуемым образом. Причина тому — шумы, которые образуются вокруг: провода действуют как маленькие антенны и производят электричество из электромагнитных волн среды. Чтобы гарантировать отсутствие напряжения при разомкнутой цепи, рядом с входом относится подтягивающий резистор (рис. 1.3).

    (Рис. 1.3)

    Теперь нежелательный ток будет идти через резистор на землю. Для подтягивания используются резисторы больших сопротивлений (10 кОм и более), при малых номиналах резисторов произойдет короткое замыкание.
  • Под номерами 6-8, находятся обычные тактовые кнопки.
  • Под номером 9, находится OLED-дисплей. Дисплее в основном доступны на чипе SSD1306, работающих на интерфейсе I2C, для работы которого необходимо только 2 провода, позволяет быстро подключить и начать использовать.
    Технические характеристики:

    • Технология дисплея: OLED,
    • Разрешение дисплея: 128 на 64 точек,
    • Диагональ дисплея: 0,96 дюйма,
    • Угол обзора: 160 °,
    • Напряжение питания: 2.8В ~ 5.5В,
    • Мощность: 0,08Вт,
    • Габариты: 27.3мм х 27.8мм х 3.7мм,
    • Рабочая температура: -30°С ~ +70°С.

    Технология OLED расшифровывается как Organic Light-Emitting Diode, состоит дисплей с большого числа органических светодиодов. Главное отличие от LCD дисплея, в том, что каждый светодиод светится сам и не требует отдельного подсветки. Благодаря этому, дисплей обладает значительными преимуществами по сравнению с обычными LCD, такими как контрастность, углом обзора и малая потребляемая мощность, конечно есть и недостатки, небольшой срок службы и дороговизна.

  • Под номером 10 находится часы реального времени на микросхеме DS1307.

Микросхема DS1307 — часы реального времени с последовательным интерфейсом, которая содержит часы-календарь с представлением информации в двоично-десятичном коде и 56 байт энергонезависимого статического ОЗУ. Адрес и данные передаются по двунаправленной двухпроводных последовательной шине. Информация о реальном времени и календарь представляется в секундах, минутах, часах, днях недели, даты, месяцев и лет. Если текущий месяц содержит менее 31 дня, то микросхема автоматически определит количество дней в месяце с учетом високосного текущего года. Часы работают или в 24-часовом или 12-часовом формате с индикатором AM/PM (до полудня/после полудня). DS1307 содержит встроенную схему контроля уровня основного источника питания и при его недопустимом значении автоматически переключается в резервной батареи.
Модули Real Time Clock (RTC) DS1307 и OLED дисплей 0.96 «I2C 128×64 работают на общей шине I2C, томе подключаем их на одни и те же выходы Arduino.
Распайка ранее созданную схему на макетной плате (рис. 1.4, Рис. 1.5). Подключаем модули в отведенные под них места (рис. 1.6).


(Рис. 1.4)


(рис. 1.5)


(Рис. 1.6)

Принципы программирования контроллеров Arduino

Контроллер программируется с интегрированной среды программного обеспечения Arduino (IDE). Скачиваем его с официального сайта и устанавливаем. Среда является полностью бесплатной и универсальной для различных видов микроконтроллеров Arduino. Язык программирования среды — C++. Структура программы (скетч) Arduino достаточно простая и в минимальном варианте состоит из двух частей setup() и loop().

void setup() {
 // код выполняется один раз при запуске программы
}
void loop() {
 // основной код, выполняется в цикле
}

Функция setup() выполняется один раз, при подключении питания или сброса контроллера. Обычно в ней происходят начальные установки переменных, регистров. Функция должна присутствовать в программе, даже если в ней ничего нет.
После завершения setup() управление переходит к функции loop(). Она в бесконечном цикле выполняет команды, записанные в ее теле (между фигурными скобками). Именно эти команды и делают все алгоритмические действия контроллера.
Все переменные должны быть объявлены до того как будут использоваться. Переменную можно объявлять ​​в любой части программы, но от этого зависит, какие блоки программы могут ее использовать. То есть в переменных есть области видимости.
Переменные, объявленные в начале программы, в функции void setup() считаются глобальными и доступны в любом месте программы.
Локальные переменные объявляются внутри функций или таких блоков, как цикл for, и могут использоваться только в объявленных блоках. Возможны несколько переменных с одним именем, но разными областями видимости.

int mode; // переменная доступна всем функциям
void setup() {
 // пустой блок, начальные установки не нужны
}
void loop() {
 long count; // переменная count доступна только в функции loop ()
 for (int i = 0; i<10;) {// переменная i доступна только внутри цикла
  i++;
 }
}

Подключение библиотек происходит в самом начале программы.

Программирование

Для начала — нужно подключить библиотеки:

#include //подключаем библиотеку, для работы с дисплеем через шину соединения I2C
#include //для удобства работы с I2C шиной, подключаем библиотеку OneWire
#include //подключение библиотеки DS3231

Подключаем дисплей и часы реального времени:

OLED  myOLED(SDA, SCL, 8); // Указываем микроконтроллеру , к которым портов мы подключили OLED дисплей 
DS3231  rtc(SDA, SCL); // Указываем микроконтроллеру , к которым портов мы подключили Real Time Clock (RTC) 

Подключаем шрифты:

extern uint8_t MegaNumbers[]; 
extern uint8_t MediumNumbers[];
extern uint8_t BigNumbers[];
extern uint8_t TinyFont[];
extern uint8_t SmallFont[];
extern uint8_t UkrFont[];

Объявляем переменную, которая в себе будет держать значение режима ввода

uint8_t VAR_mode_SET = 0;

Объявляем переменную типа Дата/Время

 
Time t;

Объявления массива дней недели на английском языке и на украинском языке

String ukr[] = {"","GJYTLSKJR","DSDNJHJR","CTHTLF","XTNDTH","G'ZNYBWZ","CE<JNF","YTLSKKZ"};
String eng[] = {"","MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"};

Объявляем константу, которая в себе содержать значение, к которому пену мы подключили тактовые кнопки:

const int buttonPin1 = 2;// подключение тактовой кнопки "SET" к пину под номером 2
const int buttonPin2 = 3;// подключение тактовой кнопки "UP" к пину под номером 3
const int buttonPin3 = 4;// подключение тактовой кнопки "DOWN" к пину под номером 4

Объявляем различные счетчики:

int i=0;
int s=0;
int m=0;
int h=0;
int z=1;
int dd,mm,yy;

Инициализирует модули в теле функции setup():

void setup() 
{
pinMode(buttonPin1, INPUT);// инициализация кнопки SET
pinMode(buttonPin2, INPUT);// инициализация кнопки UP
pinMode(buttonPin3, INPUT);// инициализация кнопки DOWN
myOLED.begin();// Инициализация OLED дисплея
rtc.begin();// Инициализация RS3231 
}

Для того, чтобы отформатировать число во временной вид (цифра 0 изначально), напишем функцию zeropad(), которая будет возвращать строку в виде 0 + число.

String zeropad(int num){
  String str;
  if ((num>=0)&&(num<10)) {
    str="0"+String(num,DEC);
    return str;
  }
  else return String(num,DEC); 
}

Главную функцию loop() разделим на 5 частей:

  • Вывод времени и даты на экран:
    myOLED.clrScr();// Очищаем экран
    i=0;// очищаем счетчик
    t = rtc.getTime();// получаем значение с RTC
    h=t.hour;// отделяем часа
    m=t.min;// отделяем минуты
    s=t.sec;// отделяем секунды
    dd=t.date;// отделяем день
    mm=t.mon;// отделяем месяц
    yy=t.year;// отделяем год
    z=t.dow; // отделяем день недели (текст)
    if(!VAR_mode_SET){// если не выбран режим ввода, отображается вся информация на экране
    myOLED.setFont(UkrFont);// Выбираем Украинский шрифт
    myOLED.print(ukr[z], CENTER, 0);// Печатаем день недели на украинском языке
    myOLED.setFont(BigNumbers);// Выбираем крупный шрифт для цифр
    myOLED.print(zeropad(h), 5, 15);// Печатаем часы
    myOLED.setFont(SmallFont);// Выбираем малый шрифт для двоеточия
    myOLED.print(":", 38, 23);// Печатаем двоеточие
    myOLED.setFont(BigNumbers);// Выбираем крупный шрифт для цифр
    myOLED.print(zeropad(m), 50, 15);// Печатаем минуты
    myOLED.setFont(SmallFont);// Выбираем малый шрифт для двоеточия
    myOLED.print(":", 83, 23); // Печатаем двоеточие
    myOLED.setFont(BigNumbers); // Выбираем крупный шрифт для цифр
    myOLED.print(zeropad(s), 95, 15);// Печатаем секунды
    myOLED.setFont(SmallFont);// Выбираем малый шрифт для даты
    myOLED.print(zeropad(dd), 30, 57);// Печатаем день
    myOLED.print(".", 45, 57);// Печатаем точку
    myOLED.print(zeropad(mm), 50, 57); // Печатаем месяц
    myOLED.print(".", 63, 57);// Печатаем точку
    myOLED.print(zeropad(yy), 70, 57); // Печатаем год
    myOLED.update();// Обновляем экран
    }
    
  • Нажатие кнопки SET:
    switch (VAR_mode_SET){
    /* д.н. */ case 1: myOLED.setFont(UkrFont); myOLED.print(ukr[z], CENTER, 0);myOLED.update(); break;
    /* сек */ case 2: myOLED.setFont(BigNumbers); myOLED.print(zeropad(s), 95, 15);myOLED.update(); break;
    /* мин */ case 3: myOLED.setFont(BigNumbers); myOLED.print(zeropad(m), 50, 15);myOLED.update(); break;
    /* час */ case 4: myOLED.setFont(BigNumbers);myOLED.print(zeropad(h), 5, 15);myOLED.update(); break;
    /* дни */ case 5: myOLED.setFont(SmallFont);myOLED.print(zeropad(dd), 30, 57);myOLED.update(); break;
    /* мес */ case 6: myOLED.setFont(SmallFont);myOLED.print(zeropad(mm), 50, 57);myOLED.update(); break;
    /* год */ case 7: myOLED.setFont(SmallFont);myOLED.print(zeropad(yy), 70, 57);myOLED.update(); break;
    }
    
  • Нажатие кнопки UP:
    //  Если нажата кнопка UP
        if(digitalRead(buttonPin2)){
          while(digitalRead(buttonPin2)){delay(50);}// ждём пока мы не отпустим кнопку UP
          switch (VAR_mode_SET){// инкремент (увеличение) устанавливаемого значения
            case 1: rtc.setDOW(z==8?1:z+1);myOLED.setFont(UkrFont);myOLED.print(ukr[z], CENTER, 0); myOLED.update(); break;
            case 2: rtc.setTime(h, m,(s==59?0:s+1));myOLED.setFont(BigNumbers);myOLED.print(zeropad(s), 95, 15);myOLED.update(); break;
            case 3: rtc.setTime(h, (m==59?0:m+1), s);myOLED.setFont(BigNumbers);myOLED.print(zeropad(m), 50, 15);myOLED.update(); break;
            case 4: rtc.setTime((h==23?0:h+1), m, s);myOLED.setFont(BigNumbers);myOLED.print(zeropad(h), 5, 15);myOLED.update(); break;
            case 5: rtc.setDate((dd==31?0:dd+1), mm,yy); myOLED.setFont(SmallFont);myOLED.print(zeropad(dd), 30, 57);myOLED.update(); break;
            case 6: rtc.setDate(dd, (mm==12?0:mm+1),yy); myOLED.setFont(SmallFont);myOLED.print(zeropad(mm), 50, 57);myOLED.update(); break;
            case 7: rtc.setDate(dd, mm,(yy==2100?1900:yy+1)); myOLED.setFont(SmallFont);myOLED.print(zeropad(yy), 70, 57);myOLED.update();break;
          }
        }
    
  • Нажатие кнопки DOWN
        if(digitalRead(buttonPin3)){
          while(digitalRead(buttonPin3)){delay(50);}// ждём пока мы её не отпустим
          switch (VAR_mode_SET){ // декремент (уменьшение) устанавливаемого значения
            case 1: rtc.setDOW(z==8?1:z-1);myOLED.setFont(UkrFont);myOLED.print(ukr[z], CENTER, 0); myOLED.update(); break;
            case 2: rtc.setTime(h, m,(s==59?0:s-1));myOLED.setFont(BigNumbers);myOLED.print(zeropad(s), 95, 15);myOLED.update(); break;
            case 3: rtc.setTime(h, (m==59?0:m-1), s);myOLED.setFont(BigNumbers);myOLED.print(zeropad(m), 50, 15);myOLED.update(); break;
            case 4: rtc.setTime((h==23?0:h-1), m, s);myOLED.setFont(BigNumbers);myOLED.print(zeropad(h), 5, 15);myOLED.update(); break;
            case 5: rtc.setDate((dd==0?31:dd-1), mm,yy); myOLED.setFont(SmallFont);myOLED.print(zeropad(dd), 30, 57);myOLED.update(); break;
            case 6: rtc.setDate(dd, (mm==0?12:mm-1),yy); myOLED.setFont(SmallFont);myOLED.print(zeropad(mm), 50, 57);myOLED.update(); break;
            case 7: rtc.setDate(dd, mm,(yy==2100?1900:yy-1)); myOLED.setFont(SmallFont);myOLED.print(zeropad(yy), 70, 57);myOLED.update();break;
          }
        }
    
  • Если кнопка SET содержалась более 2 секунд, возвращаемся в основной режим просмотра:
    if(digitalRead(buttonPin1)){// Если нажата клавиша SET
    while(digitalRead(buttonPin1)){// Пока нажата клавиша SET
    delay(10);// Делаем задержку в 10мс
    if(i<200){i++;}else{// Ждем, пока кнопка будет нажата дольше 2 секунд
    myOLED.clrScr();// Очищаем экран
    myOLED.update(); // Обновляем экран
    }
    }
    if(i<200){// Если кнопка SET содержалась менее 2 секунд
    VAR_mode_SET++;// переходим к следующему параметру
    if(VAR_mode_SET>7){VAR_mode_SET=1;}// возвращаемся к первому параметру
    }else{// Если кнопка SET содержалась более 2-х секунд, то необходимо выйти из режима установки даты/времени
    VAR_mode_SET=0;// Выходим из режима установки даты/времени
    }
    }
    

Для загрузки данного кода в микроконтроллер Arduino Pro Mini, нужно его подключить к компьютеру. В устройстве не предусмотрена схема для взаимодействия с USB портами, поэтому для прошивки необходим USB-TTL преобразователь. Для нашей версии Arduino Pro Mini подойдет кабель FTDI TTL-232R USB-TTL Level Serial Converter. Подключаем его, как показано на схеме (рис. 1.7), запускаем Arduino IDE, выбираем номер COM-порта, к которому подключена Arduino Pro Mini, правильно указываем тип Arduino платы, процессор, на какой частоте он работает (рис. 1.8). После чего, нажимаем кнопку «Загрузка», и программа будет загружена в микроконтроллер.


(Рис. 1.7)


(Рис. 1.8)

P.S. Возможно Вам будет интересна статья Создания универсального ИК пульта из Arduino.