ATtiny13 + 0,91′ I2C 128×32 OLED (Arduino IDE)

Для простых и компактных проектов разработанных в среде Ardino IDE, таких как например простое реле времени, логичней применять простые и недорогие микроконтроллеры. Так как Arduino IDE поддерживает микроконтроллеры серии ATtiny, мной для этого проекта был выбран микроконтроллер ATtiny13.

ATtiny13 — низкопотребляющий 8 битный КМОП микроконтроллер с AVR RISC архитектурой. Выполняя команды за один цикл, ATtiny13 достигает производительности 1 MIPS при частоте задающего генератора 1 МГц, что позволяет разработчику оптимизировать отношение потребления к производительности.

Микроконтроллер ATtiny13 отлично подходит для маленьких и дешевых проектов, а поддержка средой программирования Arduino IDE заметно упрощает работу с микроконтроллером.

Далее в статье будет рассмотрено несколько простых проектов с применением микроконтроллера ATtiny13 и 0,91′ I2C 128×32 OLED дисплея.

Для поддержки ATtiny13 в Arduino IDE необходимо выполнить несколько простых операций:

  • Добавление поддержки платы

Откройте в Arduino IDE вкладку Файл > Настройки и добавьте ссылку для менеджера плат

https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json

Далее перейдите во вкладку Инструменты > Плата > Менеджер плат

Выберите и установите новую плату MicroCore by MCUdude.

Далее в Инструменты > Плата выберите плату ATtiny13.

  • Для прошивки скетча  Вам понадобится программатор USBAsp

В моем случае я использую микроконтроллер который установлен на плату переходник, схема подключения достаточно простая:

Распиновка программатора USBAsp

В настройках платы нужно выбрать поддержку Attiny13 и установить частоту  9.6 MHz internal, в пункте EEPROM выберите EEPROM not retanied,  в пункте ‘Расчет времени’ выберите Micros disabled .

Далее необходимо выставить нужные фьюзы для микроконтроллера, чтобы он всегда работал на выбранной Вами частоте. Для этого в настройках Arduino IDE выберите программатор USBasb и нажмите Инструменты > Записать загрузчик. Эту операцию необходимо проводить всего один и снова повторить если Вы будете менять частоту работы микроконтроллера.

Для загрузки скетча в настройках Arduino IDE выберите программатор USBasb и во вкладке Скетч нажмите на Загрузить через программатор (или просто нажать кнопку — Загрузить).

Термометр на DS18B20

Так как OLED экраны имеют свойство выгорать при постоянном свечении символов, то в схему термометра добавлена кнопка, при нажатии на которую загорается экран на 10 секунд а потом гаснет до следующего нажатия кнопки.

#include <avr/io.h>
#include <util/delay.h>
 
// Project Files (Github):  https://github.com/wagiminator/ATtiny13-TinyOLEDdemo
// License: http://creativecommons.org/licenses/by-sa/3.0/
 
#define I2C_SDA         PB3                   
#define I2C_SCL         PB4                  
#define TEMP            PB0
#define BUTTON          PB2
 
#define I2C_SDA_HIGH()  DDRB &= ~(1<<I2C_SDA) 
#define I2C_SDA_LOW()   DDRB |=  (1<<I2C_SDA) 
#define I2C_SCL_HIGH()  DDRB &= ~(1<<I2C_SCL) 
#define I2C_SCL_LOW()   DDRB |=  (1<<I2C_SCL) 
 
const char Message1[] PROGMEM = "TEMPERATURE";
 
int main(void) {
  PORTB |=(1 << BUTTON);
  OLED_init();                           
 // OLED_clear(); 
 
  while(1) { 
    if(((PINB >> BUTTON) & 1) == 0){
    int temp = read_temp();
    OLED_cursor(28, 0);                  
    OLED_printP(Message1);  
    OLED_cursor(45, 2);  
    byte a0 = temp/100;
    OLED_num(a0);
    a0 = temp/10%10;
    OLED_num(a0);
    OLED_num(-2);
    a0 = temp%10;
    OLED_num(((PINB >> BUTTON) & 1));
    OLED_num(-9);
    OLED_num(19);
    _delay_ms(10000);}
    OLED_clear();                   
}}
 
///// I2C ///////////////////////////////////////////////////////
void I2C_init(void) {DDRB  &= ~((1<<I2C_SDA)|(1<<I2C_SCL)); PORTB &= ~((1<<I2C_SDA)|(1<<I2C_SCL));}
void I2C_write(uint8_t data) {
  for(uint8_t i = 8; i; i--) {I2C_SDA_LOW();                        
    if (data & 0x80) I2C_SDA_HIGH();I2C_SCL_HIGH();data<<=1;I2C_SCL_LOW();}
  I2C_SDA_HIGH();I2C_SCL_HIGH();asm("nop");I2C_SCL_LOW();                         
}
void I2C_start(uint8_t addr) {I2C_SDA_LOW();I2C_SCL_LOW();I2C_write(addr);}
void I2C_stop(void) {I2C_SDA_LOW();I2C_SCL_HIGH();I2C_SDA_HIGH();}
 
///// OLED ///////////////////////////////////
#define OLED_ADDR       0x78                  
#define OLED_CMD_MODE   0x00                  
#define OLED_DAT_MODE   0x40                  
#define OLED_INIT_LEN   12                 
 
// OLED init settings
const uint8_t OLED_INIT_CMD[] PROGMEM = {
  0xA8, 0x1F,       //  0x1F for 128x32, 0x3F for 128x64 
  0x22, 0x00, 0x03, 
  0x20, 0x00,       
  0xDA, 0x02,       
  0x8D, 0x14,       
  0xAF,             
  0xA1, 0xC8 
};
 
// Standard ASCII 5x8 font (adapted from Neven Boyanov and Stephen Denne)
const uint8_t OLED_FONT[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, //   0  
  0x00, 0x1c, 0x22, 0x41, 0x00, // ( 8 
  0x00, 0x41, 0x22, 0x1c, 0x00, // ) 9 
  0x14, 0x08, 0x3E, 0x08, 0x14, // * 10
  0x08, 0x08, 0x3E, 0x08, 0x08, // + 11
  0x00, 0x00, 0xA0, 0x60, 0x00, // , 12
  0x08, 0x08, 0x08, 0x08, 0x08, // - 13
  0x00, 0x60, 0x60, 0x00, 0x00, // . 14
  0x20, 0x10, 0x08, 0x04, 0x02, // / 15
  0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 16
  0x00, 0x42, 0x7F, 0x40, 0x00, // 1 17
  0x42, 0x61, 0x51, 0x49, 0x46, // 2 18
  0x21, 0x41, 0x45, 0x4B, 0x31, // 3 19
  0x18, 0x14, 0x12, 0x7F, 0x10, // 4 20
  0x27, 0x45, 0x45, 0x45, 0x39, // 5 21
  0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 22
  0x01, 0x71, 0x09, 0x05, 0x03, // 7 23
  0x36, 0x49, 0x49, 0x49, 0x36, // 8 24
  0x06, 0x49, 0x49, 0x29, 0x1E, // 9 25
  0x00, 0x36, 0x36, 0x00, 0x00, // : 26
  0x00, 0x56, 0x36, 0x00, 0x00, // ; 27
  0x08, 0x14, 0x22, 0x41, 0x00, // < 28
  0x14, 0x14, 0x14, 0x14, 0x14, // = 29
  0x00, 0x41, 0x22, 0x14, 0x08, // > 30
  0x02, 0x01, 0x51, 0x09, 0x06, // ? 31
  0x32, 0x49, 0x59, 0x51, 0x3E, // @ 32
  0x7C, 0x12, 0x11, 0x12, 0x7C, // A 33
  0x7F, 0x49, 0x49, 0x49, 0x36, // B 34
  0x3E, 0x41, 0x41, 0x41, 0x22, // C 35
  0x7F, 0x41, 0x41, 0x22, 0x1C, // D 36
  0x7F, 0x49, 0x49, 0x49, 0x41, // E 37
  0x7F, 0x09, 0x09, 0x09, 0x01, // F 38
  0x3E, 0x41, 0x49, 0x49, 0x7A, // G 39
  0x7F, 0x08, 0x08, 0x08, 0x7F, // H 40
  0x00, 0x41, 0x7F, 0x41, 0x00, // I 41
  0x20, 0x40, 0x41, 0x3F, 0x01, // J 42
  0x7F, 0x08, 0x14, 0x22, 0x41, // K 43
  0x7F, 0x40, 0x40, 0x40, 0x40, // L 44
  0x7F, 0x02, 0x0C, 0x02, 0x7F, // M 45
  0x7F, 0x04, 0x08, 0x10, 0x7F, // N 46
  0x3E, 0x41, 0x41, 0x41, 0x3E, // O 47
  0x7F, 0x09, 0x09, 0x09, 0x06, // P 48
  0x3E, 0x41, 0x51, 0x21, 0x5E, // Q 49
  0x7F, 0x09, 0x19, 0x29, 0x46, // R 50
  0x46, 0x49, 0x49, 0x49, 0x31, // S 51
  0x01, 0x01, 0x7F, 0x01, 0x01, // T 52
  0x3F, 0x40, 0x40, 0x40, 0x3F, // U 53
  0x1F, 0x20, 0x40, 0x20, 0x1F, // V 54
  0x3F, 0x40, 0x38, 0x40, 0x3F, // W 55
  0x63, 0x14, 0x08, 0x14, 0x63, // X 56
  0x07, 0x08, 0x70, 0x08, 0x07, // Y 57
  0x61, 0x51, 0x49, 0x45, 0x43, // Z 58
};
 
void OLED_init(void) {
  I2C_init();                             
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  for (uint8_t i = 0; i < OLED_INIT_LEN; i++) I2C_write(pgm_read_byte(&OLED_INIT_CMD[i])); 
  I2C_stop();                            
}
 
void OLED_printC(char ch) {
  uint16_t offset = ch - 32 -7;              
  offset += offset << 2;                  
  I2C_write(0x00);                       
  for(uint8_t i=5; i; i--) I2C_write(pgm_read_byte(&OLED_FONT[offset++])); 
}
 
void OLED_printP(const char* p) {
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_DAT_MODE);               
  char ch = pgm_read_byte(p);            
  while (ch != 0){OLED_printC(ch);ch = pgm_read_byte(++p);}
  I2C_stop();                   
}
 
void OLED_cursor(uint8_t xpos, uint8_t ypos) {
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  I2C_write(xpos & 0x0F);                 
  I2C_write(0x10 | (xpos >> 4));     
  I2C_write(0xB0 | (ypos & 0x07));    
  I2C_stop();                           
}
 
void OLED_clear(void) {
  OLED_cursor(0, 0);                      
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_DAT_MODE);              
  for(uint16_t i=512; i; i--) I2C_write(0x00); 
  I2C_stop();  
}
 
void OLED_num(byte num){
  I2C_start(OLED_ADDR);                 
  I2C_write(OLED_DAT_MODE);    
  OLED_printC(num+48); 
  I2C_stop();    
  }
 
//// DS18B20 //////////////////////////////////////
uint8_t therm_reset(){
    uint8_t i;
    PORTB &= ~(1 << TEMP);
    DDRB |= (1 << TEMP);
    _delay_us(480);  
    DDRB &= ~(1 << TEMP);
    _delay_us(60);
    i=((PINB >> TEMP) & 1);
    _delay_us(420);
    return i;
}
void therm_write_bit(uint8_t bit){
    DDRB |= (1 << TEMP);
    if(bit) DDRB &= ~(1 << TEMP);
    _delay_us(60);
    DDRB &= ~(1 << TEMP);
}
uint8_t therm_read_bit(void){
    uint8_t bit=0;
    DDRB |= (1 << TEMP);
    DDRB &= ~(1 << TEMP);
    _delay_us(14);
    if(PINB & (1 << TEMP)) bit=1;
    _delay_us(45);
    return bit;
}
uint8_t therm_read_byte(void){
    uint8_t i=8, n=0;
    while(i--){n>>=1;n|=(therm_read_bit()<<7);}
    return n;
}
void therm_write_byte(uint8_t byte){
    uint8_t i=8;
    while(i--){therm_write_bit(byte&1);byte >>= 1;
    }}
int read_temp(){
    uint8_t temperature[2];
    int temper;
    therm_reset();
    therm_write_byte(0xCC);
    therm_write_byte(0x44);
    while(!therm_read_bit());
    therm_reset();
    therm_write_byte(0xCC);
    therm_write_byte(0xBE);
    temperature[0]=therm_read_byte();
    temperature[1]=therm_read_byte();
    therm_reset();
    return temper = (temperature[1] << 8 | temperature[0])*10/16;
}

Скетч использует 1002 байт (97%) памяти устройства. Всего доступно 1024 байт.
Глобальные переменные используют 0 байт (0%) динамической памяти, оставляя 64 байт для локальных переменных. Максимум: 64 байт.

Термометр на DS18B20 с большими цифрами

Так как OLED экраны имеют свойство выгорать при постоянном свечении символов, то в схему термометра добавлена кнопка, при нажатии на которую загорается экран на 10 секунд а потом гаснет до следующего нажатия кнопки.

#include <avr/io.h>
#include <util/delay.h>
 
// Project Files (Github):  https://github.com/wagiminator/ATtiny13-TinyOLEDdemo
// License: http://creativecommons.org/licenses/by-sa/3.0/
 
#define I2C_SDA         PB3                   
#define I2C_SCL         PB4                  
#define TEMP            PB0
#define BUTTON          PB2
 
#define I2C_SDA_HIGH()  DDRB &= ~(1<<I2C_SDA) 
#define I2C_SDA_LOW()   DDRB |=  (1<<I2C_SDA) 
#define I2C_SCL_HIGH()  DDRB &= ~(1<<I2C_SCL) 
#define I2C_SCL_LOW()   DDRB |=  (1<<I2C_SCL) 
 
int main(void) {
    PORTB |=(1 << BUTTON);
    OLED_init();
    OLED_clear();                    
 
while(1) { 
    if(((PINB >> BUTTON) & 1) == 0){   
    int temp = read_temp();
    byte a0 = temp/100;
    byte a1 = temp/10%10;
    byte a2 = temp%10;
    uint8_t buffer[8] = {19, a0, a1, 16, a2, 19, 12, 19};                                               
    OLED_printB(buffer);
    _delay_ms(10000);}
    OLED_clear();                   
}}
 
///// I2C ///////////////////////////////////////////////////////
void I2C_init(void) {DDRB  &= ~((1<<I2C_SDA)|(1<<I2C_SCL)); PORTB &= ~((1<<I2C_SDA)|(1<<I2C_SCL));}
void I2C_write(uint8_t data) {
  for(uint8_t i = 8; i; i--) {I2C_SDA_LOW();                        
    if (data & 0x80) I2C_SDA_HIGH();I2C_SCL_HIGH();data<<=1;I2C_SCL_LOW();}
  I2C_SDA_HIGH();I2C_SCL_HIGH();asm("nop");I2C_SCL_LOW();                         
}
void I2C_start(uint8_t addr) {I2C_SDA_LOW();I2C_SCL_LOW();I2C_write(addr);}
void I2C_stop(void) {I2C_SDA_LOW();I2C_SCL_HIGH();I2C_SDA_HIGH();}
 
///// OLED ///////////////////////////////////
#define OLED_ADDR       0x78                  
#define OLED_CMD_MODE   0x00                  
#define OLED_DAT_MODE   0x40                  
#define OLED_INIT_LEN   15 
 
// OLED init settings
const uint8_t OLED_INIT_CMD[] PROGMEM = {
  0xA8, 0x1F,       
  0x22, 0x00, 0x03, 
  0x20, 0x01,       
  0xDA, 0x02,       
  0x8D, 0x14,       
  0xAF,             
  0x00, 0x10, 0xB0, 
  0xA1, 0xC8        
};
 
// Simple reduced 3x8 font
const uint8_t OLED_FONT[] PROGMEM = {
  0x7F, 0x41, 0x7F, // 0  0
  0x00, 0x00, 0x7F, // 1  1
  0x79, 0x49, 0x4F, // 2  2
  0x41, 0x49, 0x7F, // 3  3
  0x0F, 0x08, 0x7E, // 4  4
  0x4F, 0x49, 0x79, // 5  5
  0x7F, 0x49, 0x79, // 6  6
  0x03, 0x01, 0x7F, // 7  7
  0x7F, 0x49, 0x7F, // 8  8
  0x4F, 0x49, 0x7F, // 9  9
  0x7F, 0x09, 0x7F, // A 10
  0x7F, 0x48, 0x78, // b 11
  0x7F, 0x41, 0x63, // C 12
  0x78, 0x48, 0x7F, // d 13
  0x7F, 0x49, 0x41, // E 14
  0x7F, 0x09, 0x01, // F 15
  0x00, 0x60, 0x00, // . 16
  0x00, 0x36, 0x00, // : 17
  0x08, 0x08, 0x08, // - 18
  0x00, 0x00, 0x00  //   19
};
 
void OLED_cursor(uint8_t xpos, uint8_t ypos) {
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  I2C_write(xpos & 0x0F);                 
  I2C_write(0x10 | (xpos >> 4));     
  I2C_write(0xB0 | (ypos & 0x07));    
  I2C_stop();                           
}
 
void OLED_clear(void) {
  OLED_cursor(0, 0);                      
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_DAT_MODE);              
  for(uint16_t i=512; i; i--) I2C_write(0x00); 
  I2C_stop();  
}
 
void OLED_init(void) {
  I2C_init();                             
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  for (uint8_t i = 0; i < OLED_INIT_LEN; i++) I2C_write(pgm_read_byte(&OLED_INIT_CMD[i])); 
  I2C_stop();                            
}
 
uint8_t OLED_stretch(uint8_t b){b=((b & 2)<<3)|(b&1);b|=b<<1;b|=b<<2;return b;}
 
void OLED_printD(uint8_t ch) {
  uint8_t i, j, k, b;                     
  uint8_t sb[4];                          
  ch += ch << 1;                          
  for(i=8; i; i--) I2C_write(0x00);       
  for(i=3; i; i--) {                      
    b = pgm_read_byte(&OLED_FONT[ch++]);  
    for(j=0; j<4; j++, b >>= 2) sb[j] = OLED_stretch(b);  
    j=4; if(i==2) j=6;               
    while(j--) {                    
      for(k=0; k<4; k++) I2C_write(sb[k]);
}}}
 
void OLED_printB(uint8_t *buffer) {
  I2C_start(OLED_ADDR);      
  I2C_write(OLED_DAT_MODE);              
  for(uint8_t i=0; i<8; i++) OLED_printD(buffer[i]); 
  I2C_stop();                            
}
 
//// DS18B20 //////////////////////////////////////
uint8_t therm_reset(){
    uint8_t i;
    PORTB &= ~(1 << TEMP);
    DDRB |= (1 << TEMP);
    _delay_us(480);  
    DDRB &= ~(1 << TEMP);
    _delay_us(60);
    i=((PINB >> TEMP) & 1);
    _delay_us(420);
    return i;
}
void therm_write_bit(uint8_t bit){
    DDRB |= (1 << TEMP);
    if(bit) DDRB &= ~(1 << TEMP);
    _delay_us(60);
    DDRB &= ~(1 << TEMP);
}
uint8_t therm_read_bit(void){
    uint8_t bit=0;
    DDRB |= (1 << TEMP);
    DDRB &= ~(1 << TEMP);
    _delay_us(14);
    if(PINB & (1 << TEMP)) bit=1;
    _delay_us(45);
    return bit;
}
uint8_t therm_read_byte(void){
    uint8_t i=8, n=0;
    while(i--){n>>=1;n|=(therm_read_bit()<<7);}
    return n;
}
void therm_write_byte(uint8_t byte){
    uint8_t i=8;
    while(i--){therm_write_bit(byte&1);byte >>= 1;
    }}
int read_temp(){
    uint8_t temperature[2];
    int temper;
    therm_reset();
    therm_write_byte(0xCC);
    therm_write_byte(0x44);
    while(!therm_read_bit());
    therm_reset();
    therm_write_byte(0xCC);
    therm_write_byte(0xBE);
    temperature[0]=therm_read_byte();
    temperature[1]=therm_read_byte();
    therm_reset();
    return temper = (temperature[1] << 8 | temperature[0])*10/16;
}

Скетч использует 860 байт (83%) памяти устройства. Всего доступно 1024 байт.
Глобальные переменные используют 0 байт (0%) динамической памяти, оставляя 64 байт для локальных переменных. Максимум: 64 байт.

Секундомер 

Дискретность отсчета 0,1 секунда, максимальное время 999 минут. Первое нажатие кнопки BUTTON запускает секундомер, второе нажатие останавливает его, третье сбрасывает показания.

#include <avr/io.h>
#include <util/delay.h>
 
// Project Files (Github):  https://github.com/wagiminator/ATtiny13-TinyOLEDdemo
// License: http://creativecommons.org/licenses/by-sa/3.0/
 
#define I2C_SDA         PB3                   
#define I2C_SCL         PB4                  
#define BUTTON          PB2
 
#define I2C_SDA_HIGH()  DDRB &= ~(1<<I2C_SDA) 
#define I2C_SDA_LOW()   DDRB |=  (1<<I2C_SDA) 
#define I2C_SCL_HIGH()  DDRB &= ~(1<<I2C_SCL) 
#define I2C_SCL_LOW()   DDRB |=  (1<<I2C_SCL) 
 
byte msec,sec;
int min;
byte w;
 
 
int main(void) {
    PORTB |=(1 << BUTTON);
    OLED_init();
    OLED_clear();                    
 
while(1) { 
    if(((PINB >> BUTTON) & 1) == 0 && w==0){w=1;_delay_ms(200);msec=200;}  
    if(((PINB >> BUTTON) & 1) == 0 && w==1){w=2;_delay_ms(200);} 
    if(((PINB >> BUTTON) & 1) == 0 && w==2){w=0;_delay_ms(200);msec=0;sec=0;min=0;}  
    if(w==1){
    _delay_ms(100);
    msec++;
    if(msec>9){sec++;msec=0;}
    if(sec>59){min++;sec=0;}
    if(min>999){min=0;sec=0;min=0;msec=0;}
    }
    uint8_t buffer[8] = {min/100, min/10%10, min%10,17, sec/10, sec%10, 16, msec};                                               
    OLED_printB(buffer);               
}}
 
///// I2C ///////////////////////////////////////////////////////
void I2C_init(void) {DDRB  &= ~((1<<I2C_SDA)|(1<<I2C_SCL)); PORTB &= ~((1<<I2C_SDA)|(1<<I2C_SCL));}
void I2C_write(uint8_t data) {
  for(uint8_t i = 8; i; i--) {I2C_SDA_LOW();                        
    if (data & 0x80) I2C_SDA_HIGH();I2C_SCL_HIGH();data<<=1;I2C_SCL_LOW();}
  I2C_SDA_HIGH();I2C_SCL_HIGH();asm("nop");I2C_SCL_LOW();                         
}
void I2C_start(uint8_t addr) {I2C_SDA_LOW();I2C_SCL_LOW();I2C_write(addr);}
void I2C_stop(void) {I2C_SDA_LOW();I2C_SCL_HIGH();I2C_SDA_HIGH();}
 
///// OLED ///////////////////////////////////
#define OLED_ADDR       0x78                  
#define OLED_CMD_MODE   0x00                  
#define OLED_DAT_MODE   0x40                  
#define OLED_INIT_LEN   15 
 
// OLED init settings
const uint8_t OLED_INIT_CMD[] PROGMEM = {
  0xA8, 0x1F,       
  0x22, 0x00, 0x03, 
  0x20, 0x01,       
  0xDA, 0x02,       
  0x8D, 0x14,       
  0xAF,             
  0x00, 0x10, 0xB0, 
  0xA1, 0xC8        
};
 
// Simple reduced 3x8 font
const uint8_t OLED_FONT[] PROGMEM = {
  0x7F, 0x41, 0x7F, // 0  0
  0x00, 0x00, 0x7F, // 1  1
  0x79, 0x49, 0x4F, // 2  2
  0x41, 0x49, 0x7F, // 3  3
  0x0F, 0x08, 0x7E, // 4  4
  0x4F, 0x49, 0x79, // 5  5
  0x7F, 0x49, 0x79, // 6  6
  0x03, 0x01, 0x7F, // 7  7
  0x7F, 0x49, 0x7F, // 8  8
  0x4F, 0x49, 0x7F, // 9  9
  0x7F, 0x09, 0x7F, // A 10
  0x7F, 0x48, 0x78, // b 11
  0x7F, 0x41, 0x63, // C 12
  0x78, 0x48, 0x7F, // d 13
  0x7F, 0x49, 0x41, // E 14
  0x7F, 0x09, 0x01, // F 15
  0x00, 0x60, 0x00, // . 16
  0x00, 0x36, 0x00, // : 17
  0x08, 0x08, 0x08, // - 18
  0x00, 0x00, 0x00  //   19
};
 
void OLED_cursor(uint8_t xpos, uint8_t ypos) {
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  I2C_write(xpos & 0x0F);                 
  I2C_write(0x10 | (xpos >> 4));     
  I2C_write(0xB0 | (ypos & 0x07));    
  I2C_stop();                           
}
 
void OLED_clear(void) {
  OLED_cursor(0, 0);                      
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_DAT_MODE);              
  for(uint16_t i=512; i; i--) I2C_write(0x00); 
  I2C_stop();  
}
 
void OLED_init(void) {
  I2C_init();                             
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  for (uint8_t i = 0; i < OLED_INIT_LEN; i++) I2C_write(pgm_read_byte(&OLED_INIT_CMD[i])); 
  I2C_stop();                            
}
 
uint8_t OLED_stretch(uint8_t b){b=((b & 2)<<3)|(b&1);b|=b<<1;b|=b<<2;return b;}
 
void OLED_printD(uint8_t ch) {
  uint8_t i, j, k, b;                     
  uint8_t sb[4];                          
  ch += ch << 1;                          
  for(i=8; i; i--) I2C_write(0x00);       
  for(i=3; i; i--) {                      
    b = pgm_read_byte(&OLED_FONT[ch++]);  
    for(j=0; j<4; j++, b >>= 2) sb[j] = OLED_stretch(b);  
    j=4; if(i==2) j=6;               
    while(j--) {                    
      for(k=0; k<4; k++) I2C_write(sb[k]);
}}}
 
void OLED_printB(uint8_t *buffer) {
  I2C_start(OLED_ADDR);      
  I2C_write(OLED_DAT_MODE);              
  for(uint8_t i=0; i<8; i++) OLED_printD(buffer[i]); 
  I2C_stop();                            
}

Скетч использует 886 байт (86%) памяти устройства. Всего доступно 1024 байт.
Глобальные переменные используют 5 байт (7%) динамической памяти, оставляя 59 байт для локальных переменных. Максимум: 64 байт.

Простые часы на DS1307

Простые часы работают на часах реального времени DS1307 (модуль).

Для установки времени раскомментируйте строку:

set_time(21,2,5,11,12,28,0);// год 00-99, ДН 1-7 (1=ВС), мес 1-12, дата 1-31, час 0-23, мин 0-59, сек 0-59

Залейте скетч и закомментируйте эту строку:

// set_time(21,2,5,11,12,28,0);// год 00-99, ДН 1-7 (1=ВС), мес 1-12, дата 1-31, час 0-23, мин 0-59, сек 0-59

#include <avr/io.h>
#include <util/delay.h>
 
// Project Files (Github):  https://github.com/wagiminator/ATtiny13-TinyOLEDdemo
// License: http://creativecommons.org/licenses/by-sa/3.0/
 
#define I2C_SDA         PB3                   
#define I2C_SCL         PB4                  
 
#define I2C_SDA_HIGH()  DDRB &= ~(1<<I2C_SDA) 
#define I2C_SDA_LOW()   DDRB |=  (1<<I2C_SDA) 
#define I2C_SCL_HIGH()  DDRB &= ~(1<<I2C_SCL) 
#define I2C_SCL_LOW()   DDRB |=  (1<<I2C_SCL) 
 
#define OLED_ADDR       0x78                  
#define OLED_CMD_MODE   0x00                  
#define OLED_DAT_MODE   0x40                  
#define OLED_INIT_LEN   15 

int main(void) {
    OLED_init();
    OLED_clear(); 
   //  set_time(21,2,5,11,12,28,0);// год 00-99, ДН 1-7 (1=ВС), месяц 1-12, дата 1-31, час 0-23, минуты 0-59, секунды 0-59                   
 
while(1) { 
 
    byte sec =  (ds_read(0) & 0x0F) + (((ds_read(0) & 0x70) >> 4) * 10);
    byte min =  (ds_read(1) & 0x0F) + (((ds_read(1) & 0x70) >> 4) * 10);
    byte hour = ((ds_read(2) & 0x0F) + ((ds_read(2) & 0x70) >> 4) * 10);
    uint8_t buffer[8] = {hour/10,hour%10,17,min/10,min%10,17,sec/10,sec%10};                                               
    OLED_printB(buffer); 
    _delay_ms(100);
 
}}
 
//////  DS1307 I2C
void set_time(byte years, byte days, byte monts, byte datas, byte hours ,byte minute, byte second){
    ds_write(0x00,(second/10<<4)+second%10);
    ds_write(0x01,(minute/10<<4)+minute%10);
    ds_write(0x02,(hours/10<<4)+hours%10);
    ds_write(0x03,days);
    ds_write(0x04,(datas/10<<4)+datas%10);
    ds_write(0x05,(monts/10<<4)+monts%10);
    ds_write(0x06,(years/10<<4)+years%10);
  }  
 
void ds_write(byte reg, byte data){
     I2C_start(0b1101000<<1);
     I2C_write(reg);
     I2C_write(data);
     I2C_stop();
  }   
 
bool i2c_read_bit() {
    bool i2c_bit = 1;        
    I2C_SDA_HIGH();           
    I2C_SCL_HIGH();                
    if((PINB >> I2C_SDA) & 1) i2c_bit=0;                            
    I2C_SCL_LOW();              
    return i2c_bit;  
}
 
byte i2c_read_byte(byte a){
    byte i, data=0;                
    for(i=0; i<8; i++){if (!i2c_read_bit()) data++;if(i!=7) data<<=1;}        
    I2C_write(a);return data;  
}
 
void i2c_write_bit(byte b){
    _delay_us(5);
    if(b){I2C_SDA_LOW();}else{I2C_SDA_HIGH();}
    _delay_us(5);
    I2C_SCL_HIGH();      
    _delay_us(10);
    I2C_SCL_LOW();
}
 
 
byte ds_read(byte reg){
     byte dat=0;
     I2C_start(0b1101000<<1);
     I2C_write(reg);
     I2C_start((0b1101000<<1)|1);
     dat = i2c_read_byte(0);
     I2C_stop();
     return dat;
  }
 
//////////////////
 
///// I2C ///////////////////////////////////////////////////////
void I2C_init(void) {DDRB  &= ~((1<<I2C_SDA)|(1<<I2C_SCL)); PORTB &= ~((1<<I2C_SDA)|(1<<I2C_SCL));}
void I2C_write(uint8_t data) {
  for(uint8_t i = 8; i; i--) {I2C_SDA_LOW();                        
    if (data & 0x80) I2C_SDA_HIGH();I2C_SCL_HIGH();data<<=1;I2C_SCL_LOW();}
  I2C_SDA_HIGH();I2C_SCL_HIGH();asm("nop");I2C_SCL_LOW();                         
}
void I2C_start(uint8_t addr) {
     DDRB &= ~(1 << I2C_SDA); DDRB &= ~(1 << I2C_SCL);
     DDRB |= (1 << I2C_SDA);  PORTB &= ~(1 << I2C_SDA);
     DDRB |= (1 << I2C_SCL);  PORTB &= ~(1 << I2C_SCL);
     I2C_write(addr);}
void I2C_stop(void) {I2C_SDA_LOW();I2C_SCL_HIGH();I2C_SDA_HIGH();}
 
///// OLED ///////////////////////////////////
// OLED init settings
const uint8_t OLED_INIT_CMD[] PROGMEM = {
  0xA8, 0x1F,       
  0x22, 0x00, 0x03, 
  0x20, 0x01,       
  0xDA, 0x02,       
  0x8D, 0x14,       
  0xAF,             
  0x00, 0x10, 0xB0, 
  0xA1, 0xC8        
};
 
// Simple reduced 3x8 font
const uint8_t OLED_FONT[] PROGMEM = {
  0x7F, 0x41, 0x7F, // 0  0
  0x00, 0x00, 0x7F, // 1  1
  0x79, 0x49, 0x4F, // 2  2
  0x41, 0x49, 0x7F, // 3  3
  0x0F, 0x08, 0x7E, // 4  4
  0x4F, 0x49, 0x79, // 5  5
  0x7F, 0x49, 0x79, // 6  6
  0x03, 0x01, 0x7F, // 7  7
  0x7F, 0x49, 0x7F, // 8  8
  0x4F, 0x49, 0x7F, // 9  9
  0x7F, 0x09, 0x7F, // A 10
  0x7F, 0x48, 0x78, // b 11
  0x7F, 0x41, 0x63, // C 12
  0x78, 0x48, 0x7F, // d 13
  0x7F, 0x49, 0x41, // E 14
  0x7F, 0x09, 0x01, // F 15
  0x00, 0x60, 0x00, // . 16
  0x00, 0x36, 0x00, // : 17
  0x08, 0x08, 0x08, // - 18
  0x00, 0x00, 0x00  //   19
};
 
void OLED_cursor(uint8_t xpos, uint8_t ypos) {
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  I2C_write(xpos & 0x0F);                 
  I2C_write(0x10 | (xpos >> 4));     
  I2C_write(0xB0 | (ypos & 0x07));    
  I2C_stop();                           
}
 
void OLED_clear(void) {
  OLED_cursor(0, 0);                      
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_DAT_MODE);              
  for(uint16_t i=512; i; i--) I2C_write(0x00); 
  I2C_stop();  
}
 
void OLED_init(void) {
  I2C_init();                             
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  for (uint8_t i = 0; i < OLED_INIT_LEN; i++) I2C_write(pgm_read_byte(&OLED_INIT_CMD[i])); 
  I2C_stop();                            
}
 
uint8_t OLED_stretch(uint8_t b){b=((b & 2)<<3)|(b&1);b|=b<<1;b|=b<<2;return b;}
 
void OLED_printD(uint8_t ch) {
  uint8_t i, j, k, b;                     
  uint8_t sb[4];                          
  ch += ch << 1;                          
  for(i=8; i; i--) I2C_write(0x00);       
  for(i=3; i; i--) {                      
    b = pgm_read_byte(&OLED_FONT[ch++]);  
    for(j=0; j<4; j++, b >>= 2) sb[j] = OLED_stretch(b);  
    j=4; if(i==2) j=6;               
    while(j--) {                    
      for(k=0; k<4; k++) I2C_write(sb[k]);
}}}
 
void OLED_printB(uint8_t *buffer) {
  I2C_start(OLED_ADDR);      
  I2C_write(OLED_DAT_MODE);              
  for(uint8_t i=0; i<8; i++) OLED_printD(buffer[i]); 
  I2C_stop();                            
}

Скетч использует 714 байт (69%) памяти устройства. Всего доступно 1024 байт.
Глобальные переменные используют 0 байт (0%) динамической памяти, оставляя 64 байт для локальных переменных. Максимум: 64 байт.

Простые часы с гашением экрана

Для предотвращения выгорания OLED экрана в схему часов можно добавить кнопку BUTTON, при нажатии на которую на 10 секунд загорается экран, а потом гаснет.

#include <avr/io.h>
#include <util/delay.h>
 
// Project Files (Github):  https://github.com/wagiminator/ATtiny13-TinyOLEDdemo
// License: http://creativecommons.org/licenses/by-sa/3.0/
 
#define I2C_SDA         PB3                   
#define I2C_SCL         PB4                  
 
#define I2C_SDA_HIGH()  DDRB &= ~(1<<I2C_SDA) 
#define I2C_SDA_LOW()   DDRB |=  (1<<I2C_SDA) 
#define I2C_SCL_HIGH()  DDRB &= ~(1<<I2C_SCL) 
#define I2C_SCL_LOW()   DDRB |=  (1<<I2C_SCL) 
 
#define OLED_ADDR       0x78                  
#define OLED_CMD_MODE   0x00                  
#define OLED_DAT_MODE   0x40                  
#define OLED_INIT_LEN   15 
 
#define BUTTON          PB2
 
byte w;
int tic;
 
 
int main(void) {
    PORTB |=(1 << BUTTON);
    OLED_init();
    OLED_clear(); 
   //  set_time(21,2,5,11,12,28,0);// год 00-99, ДН 1-7 (1=ВС), месяц 1-12, дата 1-31, час 0-23, минуты 0-59, секунды 0-59                   
 
while(1) { 
 
    byte sec =  (ds_read(0) & 0x0F) + (((ds_read(0) & 0x70) >> 4) * 10);
    byte min =  (ds_read(1) & 0x0F) + (((ds_read(1) & 0x70) >> 4) * 10);
    byte hour = ((ds_read(2) & 0x0F) + ((ds_read(2) & 0x70) >> 4) * 10);
    uint8_t buffer[8] = {hour/10,hour%10,17,min/10,min%10,17,sec/10,sec%10};                                               
    if(((PINB >> BUTTON) & 1) == 0){w=1;_delay_ms(300);}
 
    if(w==1 && tic <=100){
    tic++;  
    OLED_printB(buffer); 
    _delay_ms(100);
    if(tic==100){tic=0;w=0;OLED_clear();}
    }           
}}
 
//////  DS1307 I2C
void set_time(byte years, byte days, byte monts, byte datas, byte hours ,byte minute, byte second){
    ds_write(0x00,(second/10<<4)+second%10);
    ds_write(0x01,(minute/10<<4)+minute%10);
    ds_write(0x02,(hours/10<<4)+hours%10);
    ds_write(0x03,days);
    ds_write(0x04,(datas/10<<4)+datas%10);
    ds_write(0x05,(monts/10<<4)+monts%10);
    ds_write(0x06,(years/10<<4)+years%10);
  }  
 
void ds_write(byte reg, byte data){
     I2C_start(0b1101000<<1);
     I2C_write(reg);
     I2C_write(data);
     I2C_stop();
  }   
 
bool i2c_read_bit() {
    bool i2c_bit = 1;        
    I2C_SDA_HIGH();           
    I2C_SCL_HIGH();                
    if((PINB >> I2C_SDA) & 1) i2c_bit=0;                            
    I2C_SCL_LOW();              
    return i2c_bit;  
}
 
byte i2c_read_byte(byte a){
    byte i, data=0;                
    for(i=0; i<8; i++){if (!i2c_read_bit()) data++;if(i!=7) data<<=1;}        
    I2C_write(a);return data;  
}
 
void i2c_write_bit(byte b){
    _delay_us(5);
    if(b){I2C_SDA_LOW();}else{I2C_SDA_HIGH();}
    _delay_us(5);
    I2C_SCL_HIGH();      
    _delay_us(10);
    I2C_SCL_LOW();
}
 
 
byte ds_read(byte reg){
     byte dat=0;
     I2C_start(0b1101000<<1);
     I2C_write(reg);
     I2C_start((0b1101000<<1)|1);
     dat = i2c_read_byte(0);
     I2C_stop();
     return dat;
  }
 
//////////////////
 
///// I2C ///////////////////////////////////////////////////////
void I2C_init(void) {DDRB  &= ~((1<<I2C_SDA)|(1<<I2C_SCL)); PORTB &= ~((1<<I2C_SDA)|(1<<I2C_SCL));}
void I2C_write(uint8_t data) {
  for(uint8_t i = 8; i; i--) {I2C_SDA_LOW();                        
    if (data & 0x80) I2C_SDA_HIGH();I2C_SCL_HIGH();data<<=1;I2C_SCL_LOW();}
  I2C_SDA_HIGH();I2C_SCL_HIGH();asm("nop");I2C_SCL_LOW();                         
}
void I2C_start(uint8_t addr) {
     DDRB &= ~(1 << I2C_SDA); DDRB &= ~(1 << I2C_SCL);
     DDRB |= (1 << I2C_SDA);  PORTB &= ~(1 << I2C_SDA);
     DDRB |= (1 << I2C_SCL);  PORTB &= ~(1 << I2C_SCL);
     I2C_write(addr);}
void I2C_stop(void) {I2C_SDA_LOW();I2C_SCL_HIGH();I2C_SDA_HIGH();}
 
///// OLED ///////////////////////////////////
// OLED init settings
const uint8_t OLED_INIT_CMD[] PROGMEM = {
  0xA8, 0x1F,       
  0x22, 0x00, 0x03, 
  0x20, 0x01,       
  0xDA, 0x02,       
  0x8D, 0x14,       
  0xAF,             
  0x00, 0x10, 0xB0, 
  0xA1, 0xC8        
};
 
// Simple reduced 3x8 font
const uint8_t OLED_FONT[] PROGMEM = {
  0x7F, 0x41, 0x7F, // 0  0
  0x00, 0x00, 0x7F, // 1  1
  0x79, 0x49, 0x4F, // 2  2
  0x41, 0x49, 0x7F, // 3  3
  0x0F, 0x08, 0x7E, // 4  4
  0x4F, 0x49, 0x79, // 5  5
  0x7F, 0x49, 0x79, // 6  6
  0x03, 0x01, 0x7F, // 7  7
  0x7F, 0x49, 0x7F, // 8  8
  0x4F, 0x49, 0x7F, // 9  9
  0x7F, 0x09, 0x7F, // A 10
  0x7F, 0x48, 0x78, // b 11
  0x7F, 0x41, 0x63, // C 12
  0x78, 0x48, 0x7F, // d 13
  0x7F, 0x49, 0x41, // E 14
  0x7F, 0x09, 0x01, // F 15
  0x00, 0x60, 0x00, // . 16
  0x00, 0x36, 0x00, // : 17
  0x08, 0x08, 0x08, // - 18
  0x00, 0x00, 0x00  //   19
};
 
void OLED_cursor(uint8_t xpos, uint8_t ypos) {
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  I2C_write(xpos & 0x0F);                 
  I2C_write(0x10 | (xpos >> 4));     
  I2C_write(0xB0 | (ypos & 0x07));    
  I2C_stop();                           
}
 
void OLED_clear(void) {
  OLED_cursor(0, 0);                      
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_DAT_MODE);              
  for(uint16_t i=512; i; i--) I2C_write(0x00); 
  I2C_stop();  
}
 
void OLED_init(void) {
  I2C_init();                             
  I2C_start(OLED_ADDR);                   
  I2C_write(OLED_CMD_MODE);               
  for (uint8_t i = 0; i < OLED_INIT_LEN; i++) I2C_write(pgm_read_byte(&OLED_INIT_CMD[i])); 
  I2C_stop();                            
}
 
uint8_t OLED_stretch(uint8_t b){b=((b & 2)<<3)|(b&1);b|=b<<1;b|=b<<2;return b;}
 
void OLED_printD(uint8_t ch) {
  uint8_t i, j, k, b;                     
  uint8_t sb[4];                          
  ch += ch << 1;                          
  for(i=8; i; i--) I2C_write(0x00);       
  for(i=3; i; i--) {                      
    b = pgm_read_byte(&OLED_FONT[ch++]);  
    for(j=0; j<4; j++, b >>= 2) sb[j] = OLED_stretch(b);  
    j=4; if(i==2) j=6;               
    while(j--) {                    
      for(k=0; k<4; k++) I2C_write(sb[k]);
}}}
 
void OLED_printB(uint8_t *buffer) {
  I2C_start(OLED_ADDR);      
  I2C_write(OLED_DAT_MODE);              
  for(uint8_t i=0; i<8; i++) OLED_printD(buffer[i]); 
  I2C_stop();                            
}

Скетч использует 834 байт (81%) памяти устройства. Всего доступно 1024 байт.
Глобальные переменные используют 3 байт (4%) динамической памяти, оставляя 61 байт для локальных переменных. Максимум: 64 байт.

Comments

  1. Привет. Повторил проект термометра с большими цифрами. Показания с интервалом около 5 сек. выключаются на 1 сек и включаются и так постоянно.

    1. Попробуйте убрать перемычку и сделать кнопку, при нажатии на кнопку экран должен загораться на 10 секунд а потом гаснуть. Как поведут себя часы при постоянно замкнутой кнопке я не проверял.

  2. Добрый день. Повторил термометр с большими цифрами, но не работает 🙁 при нажатии кнопки или «00.0 С» или абракодабра какая-то. Прошивал hex и через ИДЕ.
    Единственное отличие — добавил резистор 4,7к с выхода DS18B20 на +5В, без него вообще не заводилось.
    В чем может быть дело? Грешу на фьюзы, которые ИДЕ мог не корректно установить.

    1. Проверьте версию установленной платы Attiny13, должно быть 2.0.0
      В проекте использован модуль ds18b20, если у Вас датчик без платы, то конечно нужно устанавливать резистор 4,7 К
      Перед заливкой скетча, нужно нажать кнопку Записать загрузчик (фьюзы)

      1. Спасибо за оперативный ответ! К сожалению, не работает как надо: то 88,4 градуса показывает, то абракадабру. Если не сложно, напишите фьюзы для AVRDUDE, попробую прошить hex.

          1. Да, скорее всего датчики все дохлые, хотя и новые.
            Ещё раз спасибо!
            Два вопроса:
            1. Прочитал что у датчика можно повысить точность(разрядность). Возможно ли это в Вашей разработке сделать?
            2. Диапазон измерения температур Вашей разработки какой, — 50…+125?

Добавить комментарий

Войти с помощью: