Простые часы на ATtiny2313 (Arduino IDE)

На микроконтроллере ATtiny2313 можно собрать простые часы, в качестве индикатора часов будет использоваться модуль TM1637, который представляет собой 4-х разрядный семисегментный дисплей на базе драйвера TM1637 с двоеточием для индикации такта секунд.

Схема часов очень простая, а использование модуля TM1637 упрощает сборку часов. Для коррекции времени служат две кнопки (для минут и часов).

Перед загрузкой скетча рекомендую ознакомится со статьей — ATtiny2313 + Arduino IDE

// tm1637 PB0 === CLK
// tm1637 PB1 === DIO
 
int i,i1,hh,mm,bb;
 
void setup() {
  DDRB = 0b00000000; 
  PORTB |= (1 << 2) | (1 << 3);
   cli();  
  TCCR1A = 0;   
  TCCR1B = 0;   
  OCR1A = 18750; // 0.1 s
  TCCR1B |= (1 << WGM12); 
  TCCR1B |= (1 << CS11) | (1 << CS10);  // 64  
  TIMSK |= (1 << OCIE1A);  
   sei(); 
}
 
void loop() {
  print_time(hh * 100 + mm, bb, 7);// int число выводимое на индикатор, двоеточие, яркость от 0 до 7
  if(i1 <= 5){bb = 1;}else{bb = 0;}
 
  if(((PINB >> 2) & 1) == 0){mm++; if(mm>59){mm = 0;} delay(200);}
  if(((PINB >> 3) & 1) == 0){hh++; if(hh>23){hh = 0;} delay(200);}
}
 
void tm_dec(byte dig){
       for(int i = 0; i < 8; i++) {
         DDRB |= (1 << 0);del();
       if (dig & 0x01)
         DDRB &= ~(1 << 1);
       else
         DDRB |= (1 << 1);del();
         DDRB &= ~(1 << 0);del();
         dig = dig >> 1;
  }
         DDRB |= (1 << 0);
         DDRB &= ~(1 << 1);del();
         DDRB &= ~(1 << 0);del();
 
       if (((PINB >> 1) & 1) == 0)
         DDRB |= (1 << 1);del();
         DDRB |= (1 << 0);del();
  }  
 
void tm_stop(){
         DDRB |= (1 << 1);del();
         DDRB &= ~(1 << 0);del();
         DDRB &= ~(1 << 1);del();
  }  
 
void tm_start(){
         DDRB |= (1 << 1);del();
  }
 
void print_time(int t, bool pd_t, int br){
        tm_start();tm_dec(0b10001000 + br);//tm_stop();tm_start();
        tm_dec(0x40);tm_stop();tm_start();
 
        int data0 = t / 1000;
        int data1 = t / 100 % 10;
        int data2 = t / 10 % 10;
        int data3 = t % 10;
 
      for(byte n = 0; n < 4; n++){
        int data;
      switch(n){
        case 0: data = data0;break;
        case 1: data = data1;break;
        case 2: data = data2;break;
        case 3: data = data3;break;
        }
 
      switch(data){  // XGFEDCBA
        case 0:  data = 0b00111111;break;     // 0
        case 1:  data = 0b00000110;break;     // 1
        case 2:  data = 0b01011011;break;     // 2
        case 3:  data = 0b01001111;break;     // 3
        case 4:  data = 0b01100110;break;     // 4
        case 5:  data = 0b01101101;break;     // 5
        case 6:  data = 0b01111101;break;     // 6
        case 7:  data = 0b00000111;break;     // 7
        case 8:  data = 0b01111111;break;     // 8
        case 9:  data = 0b01101111;break;     // 9
        }
 
        if(n == 0){data0 = data;}
        if(n == 1){data1 = data;}
        if(n == 2){data2 = data;}
        if(n == 3){data3 = data;}
        }
      if(pd_t == 1){data1+= 0b10000000;}
      tm_dec(0xC0);tm_dec(data0);tm_dec(data1);tm_dec(data2);tm_dec(data3);tm_stop();
}  
 
void del(){delayMicroseconds(100);}
 
ISR(TIMER1_COMPA_vect){
     i++;i1++;
     if(i1 > 9){i1 = 0;}
     if(i > 599){mm++;i = 0;}
     if(mm > 59){hh++;mm = 0;}
     if(hh > 23){hh = 0;}
     }

Скетч использует 1330 байт (64%) памяти устройства. Всего доступно 2048 байт.
Глобальные переменные используют 19 байт (14%) динамической памяти, оставляя 109 байт для локальных переменных. Максимум: 128 байт.

Так как в ATtiny2313 еще остается немного памяти и я решил дополнить часы будильником.

Управление часами осуществляется при помощи 4-х кнопок, кнопки НН и ММ для корректировки часов и минут, кнопка SET ALARM служит для установки времени срабатывания будильника, при нажатии на кнопку SET ALARM мигание двоеточия прекращается, что свидетельствует о активации режима корректировки времени будильника, кнопками НН и ММ устанавливается время будильника. При повторном нажатии кнопки SET ALARM происходит переход в режим часов. Кнопка ALARM ON/OFF предназначена для активации режима будильника, при нажатии на кнопку загорается светодиод и при совпадении времени часов и времени будильника будет слышен прерывисты звуковой сигнал, который будет звучать в течении 1 минуты, прервать сигнал можно нажав на кнопку ALARM ON/OFF.

Время будильника сохраняется в энергонезависимой памяти.

// tm1637       PB0 === CLK
// tm1637       PB1 === DIO
// zummer       PD5
// MM+          PB2
// HH+          PB3
// ALARM/TIME   PB4
// ALARM ON/OFF PD4
// led ALARM    PD3
 
int i,i1,hh,mm,bb,b_hh,b_mm,bud,blok;
 
void setup() {
  DDRB = 0b00000000; 
  DDRD = 0b00101000;
  PORTB |= (1 << 2) | (1 << 3) | (1 << 4);
  PORTD |= (1 << 4);
   cli();  
  TCCR1A = 0;   
  TCCR1B = 0;   
  OCR1A = 18750; // 0.1 s
  TCCR1B |= (1 << WGM12); 
  TCCR1B |= (1 << CS11) | (1 << CS10);  // 64  
  TIMSK |= (1 << OCIE1A);  
   sei(); 
   b_hh = EEPROM_read(1);
   b_mm = EEPROM_read(0);
}
 
void loop() {
  if(bud == 0){print_time(hh * 100 + mm, bb, 7);}// int число выводимое на индикатор, двоеточие, яркость от 0 до 7
  if(bud == 1){print_time(b_hh * 100 + b_mm, bb, 7);}// int число выводимое на индикатор, двоеточие, яркость от 0 до 7
  if(i1 <= 5){bb = 1;}else{bb = 0;} 
  if(bud == 1){bb = 1;}
  // button set time
  if(((PINB >> 2) & 1) == 0 && bud == 0){mm++; if(mm>59){mm = 0;} delay(200);}
  if(((PINB >> 3) & 1) == 0 && bud == 0){hh++; if(hh>23){hh = 0;} delay(200);}
  // button set alarm
  if(((PINB >> 4) & 1) == 0 && bud == 0){bud = 1; delay(200);}
  if(((PINB >> 4) & 1) == 0 && bud == 1){bud = 0; delay(200);}
  // button set time alarm
  if(((PINB >> 2) & 1) == 0 && bud == 1){b_mm++;  if(b_mm>59){b_mm = 0;}EEPROM_write(0,b_mm); delay(200);}
  if(((PINB >> 3) & 1) == 0 && bud == 1){b_hh++;  if(b_hh>23){b_hh = 0;}EEPROM_write(1,b_hh); delay(200);}
  // led alarm | button on/off
  if(((PIND >> 4) & 1) == 0 && blok == 1){blok = 0; delay(200);}
  if(((PIND >> 4) & 1) == 0 && blok == 0){blok = 1; delay(200);}
  if(blok == 1){PORTD &= ~(1 << 3);}else{PORTD |= (1 << 3);}
  // zummer
  if((hh * 100 + mm) == (b_hh * 100 + b_mm) && blok == 0){if(i1 <= 5){DDRD |= (1 << 5);pic();} else {DDRD &= ~(1 << 5);}}  
} // loop
 
void pic(){
  for(int g=0;g<1000;g++){
  PORTD |= (1 << 5);
  delayMicroseconds(150);
  PORTD &= ~(1 << 5);
  delayMicroseconds(100);}
  }
 
void tm_dec(byte dig){
       for(int i = 0; i < 8; i++) {
         DDRB |= (1 << 0);del();
       if (dig & 0x01)
         DDRB &= ~(1 << 1);
       else
         DDRB |= (1 << 1);del();
         DDRB &= ~(1 << 0);del();
         dig = dig >> 1;
  }
         DDRB |= (1 << 0);
         DDRB &= ~(1 << 1);del();
         DDRB &= ~(1 << 0);del();
 
       if (((PINB >> 1) & 1) == 0)
         DDRB |= (1 << 1);del();
         DDRB |= (1 << 0);del();
  }  
 
void tm_stop(){
         DDRB |= (1 << 1);del();
         DDRB &= ~(1 << 0);del();
         DDRB &= ~(1 << 1);del();
  }  
 
void tm_start(){
         DDRB |= (1 << 1);del();
  }
 
void print_time(int t, bool pd_t, int br){
        tm_start();tm_dec(0b10001000 + br);//tm_stop();tm_start();
        tm_dec(0x40);tm_stop();tm_start();
 
        int data0 = t / 1000;
        int data1 = t / 100 % 10;
        int data2 = t / 10 % 10;
        int data3 = t % 10;
 
      for(byte n = 0; n < 4; n++){
        int data;
      switch(n){
        case 0: data = data0;break;
        case 1: data = data1;break;
        case 2: data = data2;break;
        case 3: data = data3;break;
        }
 
      switch(data){  // XGFEDCBA
        case 0:  data = 0b00111111;break;     // 0
        case 1:  data = 0b00000110;break;     // 1
        case 2:  data = 0b01011011;break;     // 2
        case 3:  data = 0b01001111;break;     // 3
        case 4:  data = 0b01100110;break;     // 4
        case 5:  data = 0b01101101;break;     // 5
        case 6:  data = 0b01111101;break;     // 6
        case 7:  data = 0b00000111;break;     // 7
        case 8:  data = 0b01111111;break;     // 8
        case 9:  data = 0b01101111;break;     // 9
        }
 
        if(n == 0){data0 = data;}
        if(n == 1){data1 = data;}
        if(n == 2){data2 = data;}
        if(n == 3){data3 = data;}
        }
      if(pd_t == 1){data1+= 0b10000000;}
      tm_dec(0xC0);tm_dec(data0);tm_dec(data1);tm_dec(data2);tm_dec(data3);tm_stop();
}  
 
void del(){delayMicroseconds(100);}
 
ISR(TIMER1_COMPA_vect){
     i++;i1++;
     if(i1 > 9){i1 = 0;}
     if(i > 599){mm++;i = 0;}
     if(mm > 59){hh++;mm = 0;}
     if(hh > 23){hh = 0;}
     }
 
unsigned char EEPROM_read(unsigned int uiAddress){
  while(EECR & (1<<EEPE));
    EEAR = uiAddress;
    EECR |= (1<<EERE);
    return EEDR;
}
 
void EEPROM_write(unsigned int uiAddress, unsigned char ucData){
    while(EECR & (1<<EEPE));
      EEAR = uiAddress;
      EEDR = ucData;
      EECR |= (1<<EEMPE);
      EECR |= (1<<EEPE);
}

Следующая версия часов аналогична первым часам, но в них в качестве индикатора используется LCD1602.

// ATTINY2313 V1.25
// D4 = PB0
// D5 = PB1
// D6 = PB2
// D7 = PB3
// E  = PB4
// RS = PD6
// button HH = PD3
// button MM = PD4
 
int i,hh,mm,ss;
int a[8];
 
void setup() {
    DDRB = 0b00011111;
    DDRD = 0b01000000;
    PORTD |= (1 << 4) | (1 << 3);
    cli();  
    TCCR1A = 0;   
    TCCR1B = 0;   
    OCR1A = 18750; // 0.1 s
    TCCR1B |= (1 << WGM12); 
    TCCR1B |= (1 << CS11) | (1 << CS10);  // 64  
    TIMSK |= (1 << OCIE1A);  
    sei();
         delay(100);
    lcd(0x03);delayMicroseconds(4500);
    lcd(0x03);delayMicroseconds(4500);
    lcd(0x03);delayMicroseconds(200);
    
    lcd(0b00000010);del();
    lcd(0b00001100);del();
    lcd(0b00000001);
}
 
void loop() {    
  if(((PIND >> 4) & 1) == 0){mm++; if(mm>59){mm = 0;}}
  if(((PIND >> 3) & 1) == 0){hh++; if(hh>23){hh = 0;}}
    a[0] = hh/10+48;
    a[1] = hh%10+48;
    a[2] = mm/10+48;
    a[3] = mm%10+48;
    a[4] = ss/10+48;
    a[5] = ss%10+48;
 
    lcd(0b10000000+6); // 0
    lcdString("TIME");
    lcd(0b11000000+4); // 1
    lcdChar(a[0]);lcdChar(a[1]);lcdString(":");lcdChar(a[2]);lcdChar(a[3]);lcdString(":");lcdChar(a[4]);lcdChar(a[5]);
 
    delay(100);
}
 
void lcdSend(bool isCommand, uint8_t data) {
    if(isCommand==0){PORTD |= (1 << 6);}//RS
    if(isCommand==1){PORTD &= ~(1 << 6);}//RS
    del();
    if(((data >> 7) & 1) ==1){PORTB |= (1 << 3);}else{PORTB &= ~(1 << 3);}
    if(((data >> 6) & 1) ==1){PORTB |= (1 << 2);}else{PORTB &= ~(1 << 2);}
    if(((data >> 5) & 1) ==1){PORTB |= (1 << 1);}else{PORTB &= ~(1 << 1);}
    if(((data >> 4) & 1) ==1){PORTB |= (1 << 0);}else{PORTB &= ~(1 << 0);}
    rs();
    if(((data >> 3) & 1) ==1){PORTB |= (1 << 3);}else{PORTB &= ~(1 << 3);}
    if(((data >> 2) & 1) ==1){PORTB |= (1 << 2);}else{PORTB &= ~(1 << 2);}
    if(((data >> 1) & 1) ==1){PORTB |= (1 << 1);}else{PORTB &= ~(1 << 1);}
    if(((data >> 0) & 1) ==1){PORTB |= (1 << 0);}else{PORTB &= ~(1 << 0);}
    rs();
}
 
void lcd(uint8_t cmd) {lcdSend(true, cmd);}
void lcdChar(const char chr) {lcdSend(false, (uint8_t)chr);}
void lcdString(const char* str) {while(*str != '\0') {lcdChar(*str);str++;}}
void del(){delay(5);}
void rs(){PORTB |= (1 << 4);del();PORTB &= ~(1 << 4);}
ISR(TIMER1_COMPA_vect){
     i++;
     if(i > 9){ss++;i=0;}
     if(ss > 59){mm++;ss = 0;}
     if(mm > 59){hh++;mm = 0;}
     if(hh > 23){hh = 0;}
     }

Скетч использует 1316 байт (64%) памяти устройства. Всего доступно 2048 байт.
Глобальные переменные используют 41 байт (32%) динамической памяти, оставляя 87 байт для локальных переменных. Максимум: 128 байт.

Форум — http://forum.rcl-radio.ru/viewtopic.php?pid=3185#p3185

Прошивка проводилась при помощи программатора USBasp в среде Arduino IDE 1.8.9, версия платы 1.25

Comments

  1. Скетч первых часов реален ? Повторил проект ,нет только конденсаторов на кварце. Часы не работают , только нули. Точки мигают с интервалом 5 секунд.

    1. Если есть нули и точки мигают, значит часы работают. Нужен кварц на 12 МГц или на другую частоту с небольшой коррекцией кода. При включении на часах всегда будут нули, нужно кнопками установить время.

  2. Кварц стоит как по схеме 12 мгц. Но при включении часов разделитель мигает с интервалом около 5 секунд а должно 1 сек. При попытке выставить минуты надо долго ждать переключения с 1 до 2 итд. Какие полные установки в ардуино ide ?
    1 Чип 2313
    2 Внешний кварц 16
    3
    4
    Если рабочий скетч ,чудес не должно быть.

  3. Привет. Сегодня для надежности проверил работу кварца, работает. Впечатление такое что чип прошивается не полностью , при включении запускается дисплей и очень медленно мигают разделительные точки. Чипы брал у китайцев они оказались промаркированные . Есть возможность считать с чипа часов фьюзы ?

    Да для справки мигание не пропадает при удалении кварца.

  4. Привет. Купил сегодня новый чип и прошил ,результата нет. Библиотека 1.2.5 ,версия ide 1.8.13
    Частота внешнего кварца при прошивке 12 мгц , емкости поставил. Прошиваю через программатор
    usbasp плюс платка переходник но без кварца.
    Программой avrdude 3.3 чип определяется.

  5. Вопрос решился перепрограммированием , включением фьюза ckdiv8 в программе avrdude_prog 3.3
    Скетч залил через arduino ide , коррекцию фьюзов делал avrdude_prog 3.3.

    1. Я прошивал немного по другому, собрал всю схему с кварцем и конденсаторами, в Arduino IDE выбрал контроллер attiny2313 и установил нужную частоту кварца (12MHz external), подключил USBasp, выбрал USBasp в качестве программатора и нажал «Прошить через программатор».
      Без кварца даже если выбрать 8MHz internal (внутренний генератор) прошивка не удавалась.

    1. Точность часов зависит только от точности кварца.

      Таймер генерирует прерывания с частотой 10 Гц, для создания интервала 0,1 сек
      Предделитель равен 64, регистр сравнения OCR1A = 18750
      12000000 / 64 / 18750 = 10 Гц

      Если Вы подсчитаете погрешность, то можно изменить значение регистра OCR1A, тогда точность часов будет очень высокой.

      Например, за час часы отстали на 2 секунды
      то есть за час таймер совершил 35980 прерываний (10 прерываний за 1 сек)
      18750 / 36000 * 35980 = 18739
      OCR1A = 18739

  6. Я могу и если да,на какой ноге посмотреть прерывания ?
    При установке времени (минут) при достижении (10минут-пример) ход идет с 00 или 59 секунд.

    Пожелание. После 23 часов минимальная яркость, в 6-7 нормальная.
    Типа кнопки старта ,я выставил 13-40 (пример) жду по интернет часам достижения этого времени
    и отпускаю или что то аналогичное Как ?

    1. Я могу и если да,на какой ноге посмотреть прерывания ?
      — просто замерьте на сколько секунд отстают часы в течении 1 часа, прерывания программные.
      При установке времени (минут) при достижении (10минут-пример) ход идет с 00 или 59 секунд.
      — секунды не корректируются, на ход секунд корректировка минут или часов не отражается. Можно доделать, но тогда надо еще одну кнопку добавить (обнуление секунд)
      Типа кнопки старта ,я выставил 13-40 (пример) жду по интернет часам достижения этого времени
      и отпускаю или что то аналогичное Как ?
      — не совсем понял вопрос

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

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