Для простых и компактных проектов разработанных в среде 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 байт.
Привет. Повторил проект термометра с большими цифрами. Показания с интервалом около 5 сек. выключаются на 1 сек и включаются и так постоянно.
Кнопку добавили, программатор отключили?
Программатор отключен. Вместо кнопки перемычка на землю.
Попробуйте убрать перемычку и сделать кнопку, при нажатии на кнопку экран должен загораться на 10 секунд а потом гаснуть. Как поведут себя часы при постоянно замкнутой кнопке я не проверял.
Я хотел сказать что если держать кнопку замкнутой то с определенным интервалом идет отключение и включение экрана.
Ну так и должно работать. Если Вы хотите чтобы экран работал когда нажата кнопка, то надо изменить код.
Добрый день. Повторил термометр с большими цифрами, но не работает 🙁 при нажатии кнопки или «00.0 С» или абракодабра какая-то. Прошивал hex и через ИДЕ.
Единственное отличие — добавил резистор 4,7к с выхода DS18B20 на +5В, без него вообще не заводилось.
В чем может быть дело? Грешу на фьюзы, которые ИДЕ мог не корректно установить.
Проверьте версию установленной платы Attiny13, должно быть 2.0.0
В проекте использован модуль ds18b20, если у Вас датчик без платы, то конечно нужно устанавливать резистор 4,7 К
Перед заливкой скетча, нужно нажать кнопку Записать загрузчик (фьюзы)
Спасибо за оперативный ответ! К сожалению, не работает как надо: то 88,4 градуса показывает, то абракадабру. Если не сложно, напишите фьюзы для AVRDUDE, попробую прошить hex.
У Вас точно датчик рабочий? Вы можете его проверить на плате Arduino?
Фьюзы правильные выставляет Arduino IDE.
Да, скорее всего датчики все дохлые, хотя и новые.
Ещё раз спасибо!
Два вопроса:
1. Прочитал что у датчика можно повысить точность(разрядность). Возможно ли это в Вашей разработке сделать?
2. Диапазон измерения температур Вашей разработки какой, — 50…+125?
1. Скетч и так поддерживает 12 бит
2. Датчик должен работать на минус, но индикатор нет, нужна правка кода
Не подскажите где и что поправить что б отображался минус? ? Про 12бит — Ваша разработка ещё лучше чем я думал! Класс!
Не проверял
http://forum.rcl-radio.ru/viewtopic.php?pid=5710#p5710
Заработало! Нашел датчик другой, подключил — всё отлично!
Спасибо за разработку!
Похоже мои пять датчиков никуда не годны 🙁
Корпус взял от курительной фиговины. Там аккумуляторы толковые. Всё спаял на макетке.
Отлично получилось!!!
Ага:) меряю температуру. Правда на 0,5гр, по сравнению с ртутным, занижает.
Или ртутный врет или датчик такой попался с «приветом».
точность 0,5 гр не плохой результат, но при желании можно сделать коррекцию:
int temp = read_temp()+5; // +0.5 гр.Цельсия
Спасибо! Попробую.
Автор я уже умаялся, что только не пробовал, но никак не выходит прошить тиньку с этим скетчем из ардуины, а прошить из другого софта — не знаю какие фьюзы выставлять.
1. Скетч компилится и HEX файл создается без ошибок.
2. USBAsp не хочет ни в какую прошивать тиньку из ардуины. AVRDUDESS видит тиньку и детектит её, определеяет. Более того разный софт тиньку спокойно читает, вижу что тинька пустая везде FF. Установлены сейчас биты: LowF: 0x6A, HighF: 0xEB (где-то показывает что 0xFF), ExtendedF: 0xFF, LockF: 0xFF, Calibration 0x4D4D4D49.
3. Справедливости ради тиньку другой софт видит с перемычкой JP3 т.е. пониженная частота.
Что делать?
avrdude: Device signature = 0x1e9007 (probably t13a)
avrdude: erasing chip
avrdude: set SCK frequency to 16000 Hz
avrdude: reading input file «0x3f»
avrdude: writing lock (1 bytes):
Ошибка при записи загрузчика.
Версия платы 2.0.0? Перед загрузкой скетча нажимали — Записать загрузчик?
Закон подлости какой-то, когда уже потратил уйму времени почитал/посмотрел/попробовал всё что только можно должно помочь, подумал ну всё нужна помощь зала. Как решил сдампить несколько раз саму прошивку посмотреть что там совпадает ли, потому как верификацию не проходит и моему удивлению не было предела — прошивка при каждом чтении не совпадала с предыдущей. Закрались сомнения, отпаял датчик температуры — всё сразу прошивается включая и из ардуины! Кто бы мог подумать!
Залил первый скетч где слово температура и цифры (ничего не менял, но добавил резистор 4,7 кОм на шину датчика температуры). К сожалению после пробы двух датчиков цифры по прежнему 00.0 С на экране(( не одно так другое.
Прямо сейчас накинул на ножку цифровой анализатор — вижу что идут 2 раза очень похожие посылки команд.
Забыл сказать: Версия платы 2.0.0 — да. Перед загрузкой скетча нажимали — Записать загрузчик — да.
Это вся посылка команд к датчику после нажатия на кнопку.
Раньше понедельника не смогу проверить. Но при написании статьи у меня все работало. Датчик правда были не оригинальные в виде модулей.
Хорошо, на картинке видно что анализатор не видит в потоке валидных команд. Датчик припаян почти в упор (не дальше 1 см) к микроконтроллеру, а резистор ровно на 4,7 кОм согласно спецификации. С прошивкой проблема решилась. Саму прошивку не менял. В итоге какие фъюзы должны быть выставлены (картинку из ардуино я вижу, а поточнее можно)? Порт PB2 висит в воздухе — мне чет не нравиться, завтра резистор подтяжки подпаяю посмотрю результать.
Перейдите с PB0 на PB1
#define TEMP PB0
на
#define TEMP PB1
«Порт PB2 висит в воздухе — мне чет не нравиться, завтра резистор подтяжки подпаяю посмотрю результать.»
Резистор не надо, включен внутренний подтягивающий к +5В
Пока перепроверить не смогу, в наличии нет датчика, на днях должен получить с алиэкспрес.