Микроконтроллер ATtiny13 отлично подходит для маленьких и дешевых проектов, а поддержка средой программирования Arduino IDE заметно упрощает работу с микроконтроллером.
Как использовать ATtiny13 в среде Arduino подробно описано в http://rcl-radio.ru/?p=98729.
В этой статье показаны несколько простых примеров работы микроконтроллера ATtiny13 совместно с 4-х разрядным 7-сегментным индикатором (BTE21—30) на MAX7219 0,56′.
MAX7219 – драйвер восьми разрядного индикатора с последовательным интерфейсом. Драйвер может управлять восемью семисегментными индикаторами с точкой, либо отдельно 64 светодиодами в LED панелях с общим катодом.
Драйвер MAX7219 управляется по трехпроводной последовательной шине Microwire (3-Wire). Каждый из разрядов индикатора имеет независимую адресацию и его содержимое может быть обновлен без необходимости перезаписи всего индикатора.
Более подробно о работе драйвера можно узнать из — http://rcl-radio.ru/?p=98749.
Тестовый скетч
Тестовый скетч просто демонстрирует работу индикатора (отсчет секунд).
// MAX7219 #define DIN PB0 #define CLK PB1 #define CS PB2 int ccc; #include <avr/io.h> #include <util/delay.h> int main(void) { DDRB |=(1<<DIN)|(1<<CLK)|(1<<CS); PORTB |=(1<<DIN)|(1<<CLK)|(1<<CS); WriteBit16(0x0F, 0);// тест выкл. WriteBit16(0x0C, 1);// вкл. индик. WriteBit16(0x0A, 2);// яркость WriteBit16(0x09, 0xFF);// дешифраторы вкл. WriteBit16(0x0B, 3);// кол-во разрядов while(1) { ccc++; WriteBit16(1, ccc/1000%10); WriteBit16(2, ccc/100%10 + 0xF0);// запятая WriteBit16(3, ccc/10%10); WriteBit16(4, ccc%10); _delay_ms(1000); } } void WriteBit16(byte reg, byte data){ PORTB &=~(1<<CLK);PORTB &=~(1<<CS); for(int i = 7; i >= 0; i--){ if(((reg >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } for(int i = 7; i >= 0; i--){ if(((data >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } PORTB |=(1<<CS);PORTB &=~(1<<CLK);PORTB &=~(1<<DIN); }
Термометр DS18B20
Используя цифровой датчик DS18B20 можно собрать простой термометр
// MAX7219 #define DIN PB0 #define CLK PB1 #define CS PB2 #define TEMP PB4 int ccc; bool minus; #include <avr/io.h> #include <util/delay.h> int main(void) { DDRB |=(1<<DIN)|(1<<CLK)|(1<<CS); PORTB |=(1<<DIN)|(1<<CLK)|(1<<CS); WriteBit16(0x0F, 0);// тест выкл. WriteBit16(0x0C, 1);// вкл. индик. WriteBit16(0x0A, 2);// яркость WriteBit16(0x09, 0xFF);// дешифраторы вкл. WriteBit16(0x0B, 3);// кол-во разрядов while(1) { int temp = read_temp(); if(temp<0){minus=1;temp=-temp;}else{minus=0;} if(minus==0){WriteBit16(1, 15);}else{WriteBit16(1, 10);} WriteBit16(2, temp/100%10); WriteBit16(3, temp/10%10+0xf0); WriteBit16(4, temp%10); _delay_ms(3000); } } void WriteBit16(byte reg, byte data){ PORTB &=~(1<<CLK);PORTB &=~(1<<CS); for(int i = 7; i >= 0; i--){ if(((reg >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } for(int i = 7; i >= 0; i--){ if(((data >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } PORTB |=(1<<CS);PORTB &=~(1<<CLK);PORTB &=~(1<<DIN); } //// 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; }
Простые часы на DS3231
Время в часах устанавливается по времени компиляции, для этого раскомментируйте строку:
// set_time(24,3,8,15,9,55,0);// год 00-99, ДН 1-7 (1=ВС), месяц 1-12, дата 1-31, час 0-23, минуты 0-59, секунды 0-59
Укажите текущее время и загрузите скетч, далее закомментируйте эту строку и повторно загрузите скетч.
// MAX7219 #define DIN PB0 #define CLK PB1 #define CS PB2 #define TEMP PB4 int ccc; bool minus; #include <avr/io.h> #include <util/delay.h> int main(void) { DDRB |=(1<<DIN)|(1<<CLK)|(1<<CS); PORTB |=(1<<DIN)|(1<<CLK)|(1<<CS); WriteBit16(0x0F, 0);// тест выкл. WriteBit16(0x0C, 1);// вкл. индик. WriteBit16(0x0A, 2);// яркость WriteBit16(0x09, 0xFF);// дешифраторы вкл. WriteBit16(0x0B, 3);// кол-во разрядов while(1) { int temp = read_temp(); if(temp<0){minus=1;temp=-temp;}else{minus=0;} if(minus==0){WriteBit16(1, 15);}else{WriteBit16(1, 10);} WriteBit16(2, temp/100%10); WriteBit16(3, temp/10%10+0xf0); WriteBit16(4, temp%10); _delay_ms(3000); } } void WriteBit16(byte reg, byte data){ PORTB &=~(1<<CLK);PORTB &=~(1<<CS); for(int i = 7; i >= 0; i--){ if(((reg >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } for(int i = 7; i >= 0; i--){ if(((data >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } PORTB |=(1<<CS);PORTB &=~(1<<CLK);PORTB &=~(1<<DIN); } //// 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; }
Простые часы на DS3231 + термометр
Часы реального времени содержат датчик температуры, ниже показан скетч который выводит не только текущее время, но и температуру.
// MAX7219 #define DIN PB0 #define CLK PB1 #define CS PB2 #define SDA PB3 #define SCL PB4 #include <avr/io.h> #include <util/delay.h> int dp; int main(void) { DDRB |=(1<<DIN)|(1<<CLK)|(1<<CS); PORTB |=(1<<DIN)|(1<<CLK)|(1<<CS); WriteBit16(0x0F, 0);// тест выкл. WriteBit16(0x0C, 1);// вкл. индик. WriteBit16(0x0A, 2);// яркость WriteBit16(0x09, 0xFF);// дешифраторы вкл. WriteBit16(0x0B, 3);// кол-во разрядов // set_time(24,3,8,15,9,55,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); int temper = (ds_read(0x11)*100 + ((ds_read(0x12) & 0b11000000) >> 6)*25)/10 ; if(sec<50){ WriteBit16(1, hour/10%10); if(dp==0){ WriteBit16(2, hour%10+0xf0);}else{WriteBit16(2, hour%10);} WriteBit16(3, min/10%10); WriteBit16(4, min%10); } else{ WriteBit16(1, 15); WriteBit16(2, temper/100%10); WriteBit16(3, temper/10%10+0xf0); WriteBit16(4, temper%10); } _delay_ms(500); dp++; if(dp>1){dp=0;} } } void WriteBit16(byte reg, byte data){ PORTB &=~(1<<CLK);PORTB &=~(1<<CS); for(int i = 7; i >= 0; i--){ if(((reg >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } for(int i = 7; i >= 0; i--){ if(((data >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } PORTB |=(1<<CS);PORTB &=~(1<<CLK);PORTB &=~(1<<DIN); } bool i2c_read_bit() { bool i2c_bit = 1; DDRB &= ~(1 << SDA); _delay_us(10); DDRB &= ~(1 << SCL); if((PINB >> SDA) & 1) i2c_bit=0; _delay_us(10); DDRB |= (1 << SCL); return i2c_bit; } byte i2c_write_byte(byte data){ for (byte i=0; i<8; i++){i2c_write_bit((data&0x80)==0);data<<=1;} return i2c_read_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_bit(a);return data; } void i2c_write_bit(byte b){ _delay_us(5); if(b){DDRB |= (1 << SDA);}else{DDRB &= ~(1 << SDA);} _delay_us(5); DDRB &= ~(1 << SCL); _delay_us(10); DDRB |= (1 << SCL); } void i2c_start(){ _delay_us(10); DDRB &= ~(1 << SDA); DDRB &= ~(1 << SCL); _delay_us(10); DDRB |= (1 << SDA); PORTB &= ~(1 << SDA); _delay_us(10); DDRB |= (1 << SCL); PORTB &= ~(1 << SCL); _delay_us(10); } void i2c_stop() { DDRB |= (1 << SDA); _delay_us(10); DDRB &= ~(1 << SCL); _delay_us(10); DDRB &= ~(1 << SDA); } byte ds_read(byte reg){ byte data = 0; i2c_start(); i2c_write_byte(0b11010000); i2c_write_byte(reg); i2c_start(); i2c_write_byte(0b11010001); data = i2c_read_byte(0); i2c_stop(); return data; } void ds_write(byte reg, byte data){ i2c_start(); i2c_write_byte(0b11010000); i2c_write_byte(reg); i2c_write_byte(data); i2c_stop(); } void set_time(byte years, byte days, byte monts, byte datas, byte hours ,byte minute, byte second){ if(second < 255){ds_write(0x00,(second/10<<4)+second%10);} if(minute < 255){ds_write(0x01,(minute/10<<4)+minute%10);} if(hours < 255){ds_write(0x02,(hours/10<<4)+hours%10);} if(days < 255){ds_write(0x03,days);} if(datas < 255){ds_write(0x04,(datas/10<<4)+datas%10);} if(monts < 255){ds_write(0x05,(monts/10<<4)+monts%10);} if(years < 255)ds_write(0x06,(years/10<<4)+years%10); }
ATtiny13+DHT11+DS18B20 — мини погодная станция
В этом примере используется два датчика, это датчик температуры DS18B20, для получения данных о температуре на улице и датчик DHT11 для получения данных о влажности и температуры в помещении.
Вывод информации на индикатор происходит поочередно через каждые 3 секунды.
![]() |
![]() |
![]() |
// MAX7219 #define DIN PB0 #define CLK PB1 #define CS PB2 #define TEMP PB4 #define DHT PB3 bool minus; byte data_dht[5]; #include <avr/io.h> #include <util/delay.h> int main(void) { DDRB |=(1<<DIN)|(1<<CLK)|(1<<CS); PORTB |=(1<<DIN)|(1<<CLK)|(1<<CS); WriteBit16(0x0F, 0);// тест выкл. WriteBit16(0x0C, 1);// вкл. индик. WriteBit16(0x0A, 2);// яркость WriteBit16(0x09, 0xFF);// дешифраторы вкл. WriteBit16(0x0B, 3);// кол-во разрядов while(1) { int temp = read_temp(); if(temp<0){minus=1;temp=-temp;}else{minus=0;} if(minus==0){WriteBit16(1, 15);}else{WriteBit16(1, 10);} WriteBit16(2, temp/100%10); WriteBit16(3, temp/10%10+0xf0); WriteBit16(4, temp%10); _delay_ms(3000); dht_read(); WriteBit16(1, 15); WriteBit16(2, 15); WriteBit16(3, data_dht[2]/10%10); WriteBit16(4, data_dht[2]%10); _delay_ms(3000); WriteBit16(1, 12); WriteBit16(2, 15); WriteBit16(3, data_dht[0]/10%10); WriteBit16(4, data_dht[0]%10); _delay_ms(3000); } } void WriteBit16(byte reg, byte data){ PORTB &=~(1<<CLK);PORTB &=~(1<<CS); for(int i = 7; i >= 0; i--){ if(((reg >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } for(int i = 7; i >= 0; i--){ if(((data >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } PORTB |=(1<<CS);PORTB &=~(1<<CLK);PORTB &=~(1<<DIN); } //// 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; } int dht_read(){ byte i = 0,i1 = 0; for(i = 0;i < 5;i++){data_dht[i] = 0;} DDRB |=(1 << DHT); PORTB &= ~(1 << DHT); _delay_ms(18); PORTB |= (1 << DHT); _delay_us(40); DDRB &= ~(1 << DHT); _delay_us(80); while(PINB & (1 << DHT)); for (i = 0; i < 5; i++){ data_dht[i]=0; for (i1=0; i1<8; i1++){ while(!(PINB & (1 << DHT))); _delay_us(30); if (PINB & (1 << DHT)){data_dht[i] |= 1 << (7-i1);} while(PINB & (1 << DHT)); }}return 1;}
Секундомер
Секундомер имеет диапазон счета от 0,0 сек до 999,9 секунд. При нажатии на кнопку происходит запуск секундомера, при повторном нажатии остановка счета, если нажать кнопку еще раз, то произойдет сброс показаний секундомера.
// MAX7219 #define DIN PB0 #define CLK PB1 #define CS PB2 #define BUTTON PB4 int ccc; bool start; #include <avr/io.h> #include <util/delay.h> int main(void) { PORTB |=(1<<BUTTON); DDRB |=(1<<DIN)|(1<<CLK)|(1<<CS); PORTB |=(1<<DIN)|(1<<CLK)|(1<<CS); WriteBit16(0x0F, 0);// тест выкл. WriteBit16(0x0C, 1);// вкл. индик. WriteBit16(0x0A, 2);// яркость WriteBit16(0x09, 0xFF);// дешифраторы вкл. WriteBit16(0x0B, 3);// кол-во разрядов while(1) { if(((PINB >> BUTTON) & 1)==LOW && start==0 && ccc==0){start=1;_delay_ms(200);ccc=2;} if(((PINB >> BUTTON) & 1)==LOW && start==1){start=0;_delay_ms(200);} if(((PINB >> BUTTON) & 1)==LOW && start==0 && ccc>0){ccc=0;_delay_ms(200);} if(start==1){ccc++;} WriteBit16(1, ccc/1000%10); WriteBit16(2, ccc/100%10); WriteBit16(3, ccc/10%10 + 0xF0); WriteBit16(4, ccc%10); _delay_ms(100); } } void WriteBit16(byte reg, byte data){ PORTB &=~(1<<CLK);PORTB &=~(1<<CS); for(int i = 7; i >= 0; i--){ if(((reg >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } for(int i = 7; i >= 0; i--){ if(((data >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } PORTB |=(1<<CS);PORTB &=~(1<<CLK);PORTB &=~(1<<DIN); }
Таймер с звуковым оповещением с фиксированными отметками времени
Таймер имеет несколько фиксированных отметок времени 1, 2, 5, 7, 10, 15 , 20, 30, 45, 60 и 90 минут. Но Вы можете изменить их или добавить новые. Таймер имеет две кнопки, одна для выбора отметки времени, другая для запуска таймера. В таймере используется активный зуммер (при подаче GND на управляющий вход звучит звуковой сигнал), который издает звуковой сигнал при завершении отсчета таймера.
// MAX7219 #define DIN PB0 #define CLK PB1 #define CS PB2 #define BUTTON_1 PB4 // start/stop #define BUTTON_2 PB3 // set time int ccc,set,min,sec; bool start; #include <avr/io.h> #include <util/delay.h> int main(void) { PORTB |=(1<<BUTTON_1)|(2<<BUTTON_2); DDRB |=(1<<DIN)|(1<<CLK)|(1<<CS); PORTB |=(1<<DIN)|(1<<CLK)|(1<<CS); WriteBit16(0x0F, 0);// тест выкл. WriteBit16(0x0C, 1);// вкл. индик. WriteBit16(0x0A, 2);// яркость WriteBit16(0x09, 0xFF);// дешифраторы вкл. WriteBit16(0x0B, 3);// кол-во разрядов while(1) { if(((PINB >> BUTTON_2) & 1)==LOW){start=0;_delay_ms(200);set++;if(set>11){set=0;}} if(start==0){ switch(set){ case 0: ccc=300;break; case 1: ccc=900;break; case 2: ccc=1500;break; case 3: ccc=2100;break; case 4: ccc=3000;break; case 5: ccc=3600;break; case 6: ccc=4500;break; case 7: ccc=6000;break; case 8: ccc=9000;break; case 9: ccc=13500;break; case 10: ccc=18000;break; case 11: ccc=27000;break; }} if(((PINB >> BUTTON_1) & 1)==LOW && start==0){start=1;_delay_ms(100);} if(start==1){ccc--;if(ccc<0){start=0; DDRB |=(1<<BUTTON_1); for(int n=0;n<10;n++){PORTB|=(1<<BUTTON_1);_delay_ms(200);PORTB&=~(1<<BUTTON_1);_delay_ms(200);} DDRB &=~(1<<BUTTON_1); }} min = ccc/300; sec = (ccc*2-(min*600))/10; WriteBit16(1, min/10%10); WriteBit16(2, min%10 + 0xF0); WriteBit16(3, sec/10%10); WriteBit16(4, sec%10); _delay_ms(200); } } void WriteBit16(byte reg, byte data){ PORTB &=~(1<<CLK);PORTB &=~(1<<CS); for(int i = 7; i >= 0; i--){ if(((reg >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } for(int i = 7; i >= 0; i--){ if(((data >> i) & 1) == 1){PORTB |=(1<<DIN);} else{PORTB &=~(1<<DIN);} PORTB |=(1<<CLK);PORTB &=~(1<<CLK); } PORTB |=(1<<CS);PORTB &=~(1<<CLK);PORTB &=~(1<<DIN); }