Fehler mit einer Uhr auf ATMega48

Hervorragende Uhren auf einem beleuchteten LCD-Display und einem ATMega48-Supermikrokreis. Aber sie haben nicht geklappt.



UPD : Crohn starb, schnitt zwei Drähte ab, legte Schlaf an, 0,3-1,6 mA


Das heißt, die Uhren funktionieren natürlich, aber leider werden sie nicht lange funktionieren.


Es war einmal, als ich in einer Kiste im hinteren Nachttisch ein paar Sieben-Segment-LCD-Anzeigen aufhängte. Und genauso lange wollte ich sie in Umlauf bringen und eine Uhr bauen, die auf einer von ihnen basiert. Es war einmal: Es sind buchstäblich sieben Jahre. 2011 interessierte ich mich dann für Elektronik. Ohne lange nachzudenken, bestellte ich dann alle möglichen Dinge in einem guten Online-Shop (nein, nicht bei Ali; ich bin mir nicht sicher, ob er damals schon da war). Aber irgendwie hat es für mich nicht geklappt, das Ewige zu erschaffen. Nach mehreren geätzten Brettern gab ich diese Unterhaltung auf und vergaß.


Als Ali ein Paket mit Modellen, einem Paket mit 595 Sekunden in Dip-Cases, Tiny RTC auf ds1307 und vor allem USBasp erhielt , war es an der Zeit, zur alten Idee zurückzukehren. Aus dem alten Vorrat hatte ich einen ATMega48 , den mit 28 Beinen, lm7805 , alles kleine Dinge in Form von Widerständen / Kondensatoren / Knöpfen und tatsächlich eine Anzeige auf 40 Beinen.


Im Allgemeinen war ursprünglich geplant, AtTiny13 zu verwenden, das ich auch herumrollte, aber nachdem ich dies und das geschätzt hatte, fand ich nicht heraus, wie ich mich mit seinen 5 Beinen fortbewegen sollte, um die Anzeige anzuzeigen, zwei Tasten zu lesen und mit der i2c-Uhr zu kommunizieren. Mit 28 Mega-Beinen ist das Speichern und Pervertieren mit der Vereinigung der Beine nicht mehr erforderlich. In diesem Fall können Sie natürlich nicht auf die 595 verzichten. Wenn ich jedoch vorhatte, alle 32 Datenbits für den Indikator von einem Teenager in Serie auszugeben, können Sie mit Mega ein Bild auf allen vier Mikroschaltungen gleichzeitig anzeigen.



Alle 595 sind unter der Anzeige versteckt. Das Foto zeigt die Schlussfolgerungen des Gehäuses der Mikroschaltung.


595 genau vier Teile wurden verwendet, da der Indikator, obwohl er 40 Beine hat, nur 32 auf Segmenten anzeigt. Leider ist er nur ein 3,5-Indikator, dh er hat 3 volle Ziffern plus eine. Es gibt ein Symbol für Sekunden, aber keine Bezeichnung für AM / PM. Aber das gibt es wirklich. Ich würde keine fortgeschrittenere Anzeige bestellen wollen, ohne irgendetwas in den LCD-Anzeigen in meinem Leben gesammelt zu haben.



Arbeitsdokumentation für den Indikator. Der Tester musste herausfinden, welches Bein welchem ​​Segment entspricht.


Nun, dann ist es eine Frage der Technologie. Es gab nie ein Schema, aber dort ist alles offensichtlich. Es mussten nur vier Beine für die Eingabe von Puffern ausgewählt werden, ein gemeinsames Bein für den Indikator, ein Bein für SCLK / RCLK, ein Bein für OE, zwei Beine für i2c, zwei Beine für Tasten. Das alles war natürlich falsch. Warum ich beschlossen habe, OE auf dem Controller zu installieren und SCLK mit RCLK zu kombinieren - ich kann mich selbst nicht daran erinnern. Es war notwendig, genau das Gegenteil zu tun. Und das gemeinsame Indikatorkabel am Controller wird überhaupt nicht benötigt, auf eine der Schlussfolgerungen des ersten 595. könnte verzichtet werden (was ich am Ende auch getan habe).



Drähte. Viele von ihnen. Innenansicht.



Der Blick von außen.


Das Interessanteste an diesem ganzen Projekt: der Ausgabecode für den Indikator. Die Feinheit ist, dass Sie nicht einfach Spannung an die LCD-Anzeige anlegen und diese vergessen können. Es ist notwendig, die Polarität zwischen den Segmenten und dem gemeinsamen Kontakt ungefähr hundert Mal pro Sekunde zu ändern, damit alles schön und angenehm für das Auge ist. Als Entwicklungsumgebung habe ich ohne weiteres die Arduino IDE verwendet, nur eine kleine Qual in einigen Punkten. Erstens musste ich das MiniCore-Paket ( https://github.com/MCUdude/MiniCore ) verwenden, weil ich nicht für ein großes fertiges Arduino schreiben musste, sondern für ein schwaches und nacktes ATMega48. Zweitens ist ein direkter Versuch, die integrierte Bibliothek für i2c zu verwenden, fehlgeschlagen. Aus irgendeinem Grund wollte es nicht funktionieren, ich musste eine andere Bibliothek finden und sie dann an meine Bedürfnisse anpassen.


Wie sich herausstellte, sind 4 Kilobyte sehr klein. Vor allem, wenn Sie in C schreiben. Wenn ich mich für einen Wechsel zum Assembler entschieden hätte, hätte diese Einschränkung möglicherweise nicht so stark zugenommen, aber wenn ich mich an meine früheren Auszüge beim Schreiben in Assembler für AVR erinnere, hatte ich keinen solchen Wunsch. Und um in C zu schreiben, wird viel Platz benötigt. Ich habe versehentlich eine kleine Klammer eingefügt - hundert Bytes in die Pipe.


Eigentlich kommentierter Code
#include <avr/io.h>
#include <util/delay.h>
#include <avr/power.h>

#define cbi(sfr, bit)   (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit)   (_SFR_BYTE(sfr) |= _BV(bit))

//      ds1307,    
#define DS3231_I2C_ADDRESS 0x68

#define LCD_PORT PORTD
#define LCD_DDR DDRD
#define LCD_DATA1 PD0
#define LCD_DATA2 PD1
#define LCD_DATA3 PD2
#define LCD_DATA4 PD3
#define LCD_SCLK PD4
#define LCD_OE PD5
#define LCD_COM PD6

#define KEY1_PORT PORTB
#define KEY1_DDR DDRB
#define KEY1_PIN PINB
#define KEY1 PB0
#define KEY2_PORT PORTD
#define KEY2_DDR DDRD
#define KEY2_PIN PIND
#define KEY2 PD7

#define DS1307SQ PB1
#define DS1307SQ_INT PCIE0
#define DS1307SQ_PORT PORTB
#define DS1307SQ_DDR DDRB
#define DS1307SQ_PIN PINB
#define DS1307SQ_VEC PCINT0_vect
#define DS1307SQ_MSK PCMSK0

//  . , -, , -     
#define LM335_PORT PORTC
#define LM335_DDR DDRC
#define LM335 PC3

//  
unsigned char lcd_buf[4];

#define MODE_MAIN 0
#define MODE_CALENDAR 1
#define MODE_YEAR 2
#define MODE_TERMOMETER 3 // 
#define MODE_VOLTMETER 4 // 
#define MODE_SET_MINUTE 5
#define MODE_SET_HOUR 6
#define MODE_SET_DAY 7
#define MODE_SET_MONTH 8
#define MODE_SET_YEAR 9
#define MODE_SECOND 10
#define MODE_DEBUG 11 //  

//        10ms ()
#define MODE_TIMEOUT 20 // 2 
#define MODE_TIMEOUT_SET 100 // 10 
#define KEY_TIMEOUT 10 // 1 

byte mode = MODE_MAIN;
byte mode_timeout = 0;

byte key1_press = 0;
byte key1_time = 0;
byte key2_press = 0;
byte key2_time = 0;

byte cycle_count_10 = 0; //   
byte even_10 = 0; //    1/10 

//    ds1307
byte second;
byte minute;
byte hour;
byte dayOfWeek;
byte dayOfMonth;
byte month;
byte year;

volatile byte need_render_int = 1;
uint8_t porthistory = 0xFF;
volatile uint8_t debug_value = 0;

byte twi_problems = 0;

//    ds1307   SQ
//   ,     
ISR(DS1307SQ_VEC) {
  uint8_t changedbits = DS1307SQ_PIN ^ porthistory;
  porthistory = DS1307SQ_PIN;
  if (changedbits & _BV(DS1307SQ) && porthistory & _BV(DS1307SQ)) {
    need_render_int = 1;
  }
}

//       
void lcd_num(char pos, char num) {
  unsigned char buf = 0b01110110;
  if (pos < 1 || pos > 3) {
    return;
  }
  switch (num) {
    case 0:
      //      3-   ,   ?
      // ,     ,     
      //   :-(
      if (pos == 3) {
        buf = 0b11101110;
      } else {
        buf = 0b11100111;
      }
      break;
    case 1:
      if (pos == 3) {
        buf = 0b10001000;
      } else {
        buf = 0b10000001;
      }
      break;
    case 2:
      buf = 0b11010110;
      break;
    case 3:
      if (pos == 3) {
        buf = 0b11011100;
      } else {
        buf = 0b11010011;
      }
      break;
    case 4:
      if (pos == 3) {
        buf = 0b10111000;
      } else {
        buf = 0b10110001;
      }
      break;
    case 5:
      if (pos == 3) {
        buf = 0b01111100;
      } else {
        buf = 0b01110011;
      }
      break;
    case 6:
      if (pos == 3) {
        buf = 0b01111110;
      } else {
        buf = 0b01110111;
      }
      break;
    case 7:
      if (pos == 3) {
        buf = 0b11001000;
      } else {
        buf = 0b11000001;
      }
      break;
    case 8:
      if (pos == 3) {
        buf = 0b11111110;
      } else {
        buf = 0b11110111;
      }
      break;
    case 9:
      if (pos == 3) {
        buf = 0b11111100;
      } else {
        buf = 0b11110011;
      }
      break;
  }
  lcd_buf[pos] = buf;
}

//  
void lcd_one(bool e) {
  if (e) {
    lcd_buf[0] |= (1 << 0);
  } else {
    lcd_buf[0] &= ~(1 << 0);
  }
}
void lcd_sec(bool e) {
  if (e) {
    lcd_buf[0] |= (1 << 7);
  } else {
    lcd_buf[0] &= ~(1 << 7);
  }
}
void lcd_minus(bool e) {
  if (e) {
    lcd_buf[0] |= (1 << 1);
  } else {
    lcd_buf[0] &= ~(1 << 1);
  }
}
void lcd_plus(bool e) {
  if (e) {
    lcd_buf[0] |= (1 << 6);
  } else {
    lcd_buf[0] &= ~(1 << 6);
  }
}
void lcd_lo(bool e) {
  if (e) {
    lcd_buf[0] |= (1 << 5);
  } else {
    lcd_buf[0] &= ~(1 << 5);
  }
}
void lcd_over(bool e) {
  if (e) {
    lcd_buf[0] |= (1 << 4);
  } else {
    lcd_buf[0] &= ~(1 << 4);
  }
}
void lcd_dot(int pos, bool e) {
  int pos_buf;
  if (pos == 1) {
    pos_buf = 3;
  } else if (pos == 2) {
    pos_buf = 2;
  } else if (pos == 3) {
    pos_buf = 1;
  } else {
    return;
  }
  if (pos_buf == 3) {
    if (e) {
      lcd_buf[pos_buf] |= (1 << 0);
    } else {
      lcd_buf[pos_buf] &= ~(1 << 0);
    }
  } else {
    if (e) {
      lcd_buf[pos_buf] |= (1 << 3);
    } else {
      lcd_buf[pos_buf] &= ~(1 << 3);
    }
  }
}

//     100   
//    595-,    
void lcd_refresh() {
  unsigned char data1 = lcd_buf[0];
  unsigned char data2 = lcd_buf[1];
  unsigned char data3 = lcd_buf[2];
  unsigned char data4 = lcd_buf[3];
  byte reverse = data1 & (1 << 3); //      
  if (reverse) { //    ,  
    data1 = ~data1;
    data2 = ~data2;
    data3 = ~data3;
    data4 = ~data4;
  }
  for (int i = 0; i < 8; i++) {
    //          
    if (data1 & (1 << i)) {
      LCD_PORT |= _BV(LCD_DATA1);
    } else {
      LCD_PORT &= ~_BV(LCD_DATA1);
    }
    if (data2 & (1 << i)) {
      LCD_PORT |= _BV(LCD_DATA2);
    } else {
      LCD_PORT &= ~_BV(LCD_DATA2);
    }
    if (data3 & (1 << i)) {
      LCD_PORT |= _BV(LCD_DATA3);
    } else {
      LCD_PORT &= ~_BV(LCD_DATA3);
    }
    if (data4 & (1 << i)) {
      LCD_PORT |= _BV(LCD_DATA4);
    } else {
      LCD_PORT &= ~_BV(LCD_DATA4);
    }
    // SCLK 595- 
    sbi(LCD_PORT, LCD_SCLK);
    // SCLK 595- 
    cbi(LCD_PORT, LCD_SCLK);
  }
  //    SCLK
  //       SCLK   RCLK     ,    
  //     .
  //   , -, .       
  // SCLK  RCLK,  OE     (.   74HC595)
  //    .
  sbi(LCD_PORT, LCD_SCLK);
  cbi(LCD_PORT, LCD_SCLK);
  //    -   
  // -   -   ,  1  40
  // (-    ;     )
  //      4   595-,     
  //   ,    
  if (reverse) {
    sbi(LCD_PORT, LCD_COM);
  } else {
    cbi(LCD_PORT, LCD_COM);
  }
  //     
  lcd_buf[0] ^= (1 << 3);
}

//        ,     
void do_render() {
  lcd_buf[0] = 0;
  lcd_buf[1] = 0;
  lcd_buf[2] = 0;
  lcd_buf[3] = 0;
  if (twi_problems) {
    lcd_lo(1);
  }
  if (mode == MODE_MAIN) {
    lcd_num(3, minute % 10);
    lcd_num(2, minute / 10);
    byte hour1 = (hour <= 12) ? hour : (hour % 12);
    lcd_num(1, hour1 % 10);
    if (hour1 >= 10) {
      lcd_one(1);
    }
    //     ,   . .
    //         SQ  ,
    //     ,        
    //     
    if (second % 2) {
      lcd_sec(1);
    }
  } else if (mode == MODE_CALENDAR) {
    lcd_num(3, dayOfMonth % 10);
    lcd_num(2, dayOfMonth / 10);
    lcd_num(1, month % 10);
    if (month >= 10) {
      lcd_one(1);
    } else {
      lcd_one(0);
    }
    lcd_dot(2, 1);
  } else if (mode == MODE_YEAR) {
    lcd_num(3, year % 10);
    lcd_num(2, year / 10);
    lcd_buf[1] = 0b10110011; //   y. 
  } else if (mode == MODE_TERMOMETER) {
  } else if (mode == MODE_VOLTMETER) {
  } else if (mode == MODE_SET_MINUTE) {
    if (even_10) {
      lcd_num(3, minute % 10);
      lcd_num(2, minute / 10);
    }
    byte hour1 = (hour <= 12) ? hour : (hour % 12);
    lcd_num(1, hour1 % 10);
    if (hour1 >= 10) {
      lcd_one(1);
    } else {
      lcd_one(0);
    }
    lcd_sec(1);
  } else if (mode == MODE_SET_HOUR) {
    lcd_num(3, minute % 10);
    lcd_num(2, minute / 10);
    if (even_10) {
      byte hour1 = (hour <= 12) ? hour : (hour % 12);
      lcd_num(1, hour1 % 10);
      if (hour1 >= 10) {
        lcd_one(1);
      } else {
        lcd_one(0);
      }
      if (hour > 12) {
        lcd_over(1);
      } else {
        lcd_over(0);
      }
    }
    lcd_sec(1);
  } else if (mode == MODE_SET_DAY) {
    if (even_10) {
      lcd_num(3, dayOfMonth % 10);
      lcd_num(2, dayOfMonth / 10);
    }
    lcd_num(1, month % 10);
    if (month >= 10) {
      lcd_one(1);
    } else {
      lcd_one(0);
    }
    lcd_dot(2, 1);
  } else if (mode == MODE_SET_MONTH) {
    lcd_num(3, dayOfMonth % 10);
    lcd_num(2, dayOfMonth / 10);
    if (even_10) {
      lcd_num(1, month % 10);
      if (month >= 10) {
        lcd_one(1);
      } else {
        lcd_one(0);
      }
    }
    lcd_dot(2, 1);
  } else if (mode == MODE_SET_YEAR) {
    if (even_10) {
      lcd_num(3, year % 10);
      lcd_num(2, year / 10);
    }
    lcd_buf[1] = 0b10110011;
  } else if (mode == MODE_SECOND) {
    lcd_sec(1);
    lcd_num(3, second % 10);
    lcd_num(2, second / 10);
  } else if (mode == MODE_DEBUG) {
    byte d = debug_value;
    lcd_num(3, d % 10);
    d /= 10;
    lcd_num(2, d % 10);
    lcd_num(2, d / 10);
  }
}

int main(void)
{
  // -    .
  // , ,  .
  // ACSR = (1<<ACD);
  ADCSRA = (0<<ADEN);
  PRR = (1<<PRTIM0) | (1<<PRTIM1) | (1<<PRTIM2) | (1<<PRSPI) | (1<<PRADC) | (1<<PRUSART0);

  // 
  DDRB = 0x00;
  PORTB = 0xff;
  DDRC = 0x00;
  PORTC = 0xff;
  DDRD = 0x00;
  PORTD = 0xff;

  lcd_buf[0] = 0x0;
  lcd_buf[1] = 0x0;
  lcd_buf[2] = 0x0;
  lcd_buf[3] = 0x0;

  twi_begin();

  //   .    usbasp,     
  // ,  ,   
  // DDRB |= _BV(PB7);
  // PORTB |= _BV(PB7); // off

  //   -
  LCD_DDR |= (_BV(LCD_DATA1) | _BV(LCD_DATA2) | _BV(LCD_DATA3) | _BV(LCD_DATA4) | _BV(LCD_SCLK) | _BV(LCD_OE) | _BV(LCD_COM));
  cbi(LCD_PORT, LCD_SCLK);
  cbi(LCD_PORT, LCD_OE);

  // 
  KEY1_DDR &= ~(_BV(KEY1));
  KEY1_PORT |= _BV(KEY1);
  KEY2_DDR &= ~(_BV(KEY2));
  KEY2_PORT |= _BV(KEY2);

  //    ds1307   
  DS1307SQ_DDR &= ~_BV(DS1307SQ);
  DS1307SQ_PORT |= _BV(DS1307SQ);
  PCICR |= _BV(DS1307SQ_INT);
  DS1307SQ_MSK |= _BV(DS1307SQ);
  sei();

  // ds1307     ,     ,   
  //    ,    - 
  // setDS3231time(30,40,21,6,11,3,18);

  while(1)
  {
    byte need_render = 0;
    byte need_date_set = 0;

    //    ds1307,   
    // 
    if (need_render_int) {
      byte rc;
      if (mode == MODE_MAIN) {
        rc = readDS3231time_hms(&second, &minute, &hour);
      } else {
        rc = readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
      }
      if ( ! rc && twi_problems) {
        twi_problems -= 1;
      } else {
        twi_problems += rc;
      }
      need_render = 1;
      need_render_int = 0;
    }

    //    1/10 
    if ( ! cycle_count_10) {
      even_10 = ! even_10;

      //    ,   
      if (mode_timeout > 0) {
        mode_timeout -= 1;
      }
      //    ,    
      if (mode != MODE_MAIN && ! mode_timeout) {
        mode = MODE_MAIN;
        need_render = 1;
      }
      //      ,  10   
      if (mode == MODE_SET_MINUTE || mode == MODE_SET_HOUR || mode == MODE_SET_DAY || mode == MODE_SET_MONTH || mode == MODE_SET_YEAR) {
        need_render = 1; 
      }

      //  
      byte key1_down = (KEY1_PIN & _BV(KEY1)) ? 0 : 1;
      byte key2_down = (KEY2_PIN & _BV(KEY2)) ? 0 : 1;
      if (key1_down || key2_down || key1_press || key2_press) {
        need_render = 1;
      }
      if (key1_down && key1_press) {
        key1_time += 1;
      }
      if (key2_down && key2_press) {
        key2_time += 1;
      }
      //     
      if (key1_down && ! key1_press) {
          if (mode == MODE_SET_MINUTE) {
            mode = MODE_SET_HOUR;
            mode_timeout = MODE_TIMEOUT_SET;
          } else if (mode == MODE_SET_HOUR) {
            mode = MODE_SET_DAY;
            mode_timeout = MODE_TIMEOUT_SET;
          } else if (mode == MODE_SET_DAY) {
            mode = MODE_SET_MONTH;
            mode_timeout = MODE_TIMEOUT_SET;
          } else if (mode == MODE_SET_MONTH) {
            mode = MODE_SET_YEAR;
            mode_timeout = MODE_TIMEOUT_SET;
          } else if (mode == MODE_SET_YEAR) {
            mode = MODE_MAIN;
          } else if (mode == MODE_MAIN) {
            mode = MODE_SECOND;
            mode_timeout = MODE_TIMEOUT_SET;
          } else if (mode == MODE_SECOND) {
            mode = MODE_MAIN;
            mode_timeout = 0;
          }
      } else if ( ! key1_down && key1_press) {
        if (key1_time >= KEY_TIMEOUT) {
        } else {
        }
      } else if (key1_down && key1_press) {
        if (key1_time >= KEY_TIMEOUT) {
          if (mode == MODE_MAIN || mode == MODE_SECOND) {
            mode = MODE_SET_MINUTE;
            mode_timeout = MODE_TIMEOUT_SET;
          }
        } else {
        }
      }
      if (key2_down && ! key2_press) {
        if (mode == MODE_MAIN) {
          mode = MODE_CALENDAR;
          mode_timeout = MODE_TIMEOUT;
        } else if (mode == MODE_CALENDAR) {
          mode = MODE_YEAR;
          mode_timeout = MODE_TIMEOUT;
        } else if (mode == MODE_YEAR) {
          mode = MODE_MAIN;
          mode_timeout = 0;
        } else if (mode == MODE_SET_MINUTE) {
          minute += 1;
          mode_timeout = MODE_TIMEOUT_SET;
          need_date_set = 1;
        } else if (mode == MODE_SET_HOUR) {
          hour += 1;
          mode_timeout = MODE_TIMEOUT_SET;
          need_date_set = 1;
        } else if (mode == MODE_SET_DAY) {
          dayOfMonth += 1;
          mode_timeout = MODE_TIMEOUT_SET;
          need_date_set = 1;
        } else if (mode == MODE_SET_MONTH) {
          month += 1;
          mode_timeout = MODE_TIMEOUT_SET;
          need_date_set = 1;
        } else if (mode == MODE_SET_YEAR) {
          year += 1;
          mode_timeout = MODE_TIMEOUT_SET;
          need_date_set = 1;
        } else if (mode == MODE_SECOND) {
            second = 0;
            need_date_set = 1;
            mode_timeout = MODE_TIMEOUT_SET;
        }
      } else if ( ! key2_down && key2_press) {
        if (key2_time >= KEY_TIMEOUT) {
        } else {
        }
      } else if (key2_down && key2_press) {
        if (key2_time >= KEY_TIMEOUT) {
          if (mode == MODE_SET_MINUTE) {
            minute += 1;
            mode_timeout = MODE_TIMEOUT_SET;
            need_date_set = 1;
          } else if (mode == MODE_SET_HOUR) {
            hour += 1;
            mode_timeout = MODE_TIMEOUT_SET;
            need_date_set = 1;
          } else if (mode == MODE_SET_DAY) {
            dayOfMonth += 1;
            mode_timeout = MODE_TIMEOUT_SET;
            need_date_set = 1;
          } else if (mode == MODE_SET_MONTH) {
            month += 1;
            mode_timeout = MODE_TIMEOUT_SET;
            need_date_set = 1;
          } else if (mode == MODE_SET_YEAR) {
            year += 1;
            mode_timeout = MODE_TIMEOUT_SET;
            need_date_set = 1;
          }
        } else {
        }
      }
      key1_press = key1_down;
      if ( ! key1_press) {
        key1_time = 0;
      }
      key2_press = key2_down;
      if ( ! key2_press) {
        key2_time = 0;
      }
    }

    if (need_date_set) {
      //  
      //       ds1307    ,
      // ,  ,    
      if (minute > 59) {
        minute = 0;
      }
      if (hour > 23) {
        hour = 0;
      }
      if (dayOfMonth > 31) {
        dayOfMonth = 1;
      }
      if (month > 12) {
        month = 1;
      }
      if (year > 99) {
        year = 0;
      }
      //    ds1307
      setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
    }

    // ,    
    if (need_render) {
      do_render();
    }

    //  
    cli();
    lcd_refresh();
    sei();

    _delay_ms(10);

    cycle_count_10 += 1;
    if (cycle_count_10 >= 10) {
      cycle_count_10 = 0;
    }
  }

  return 0;
}

//    -   
byte decToBcd(byte val)
{
  return( (val/10*16) + (val%10) );
}
byte bcdToDec(byte val)
{
  return( (val/16*10) + (val%16) );
}

//    ds1307
void setDS3231time(byte second, 
                   byte minute, 
                   byte hour, 
                   byte dayOfWeek, 
                   byte dayOfMonth, 
                   byte month, 
                   byte year)
{
  twi_write((uint8_t) DS3231_I2C_ADDRESS, (uint8_t) 0, (uint8_t) decToBcd(second));
  twi_write((uint8_t) DS3231_I2C_ADDRESS, (uint8_t) 1, (uint8_t) decToBcd(minute));
  twi_write((uint8_t) DS3231_I2C_ADDRESS, (uint8_t) 2, (uint8_t) decToBcd(hour));
  twi_write((uint8_t) DS3231_I2C_ADDRESS, (uint8_t) 3, (uint8_t) decToBcd(dayOfWeek));
  twi_write((uint8_t) DS3231_I2C_ADDRESS, (uint8_t) 4, (uint8_t) decToBcd(dayOfMonth));
  twi_write((uint8_t) DS3231_I2C_ADDRESS, (uint8_t) 5, (uint8_t) decToBcd(month));
  twi_write((uint8_t) DS3231_I2C_ADDRESS, (uint8_t) 6, (uint8_t) decToBcd(year));
  //     SQ 1Hz
  //        ,   ds1307    
  //      8kHz
  twi_write((uint8_t) DS3231_I2C_ADDRESS, (uint8_t) 7, (uint8_t) 0b00010000);
}

//    ds1307
byte readDS3231time(byte *second,
                    byte *minute,
                    byte *hour,
                    byte *dayOfWeek,
                    byte *dayOfMonth,
                    byte *month,
                    byte *year)
{
  byte rc = twi_read(DS3231_I2C_ADDRESS, 0, 7);
  if (rc) return 1;
  *second = bcdToDec(twi_receive() & 0x7f);
  *minute = bcdToDec(twi_receive());
  *hour = bcdToDec(twi_receive() & 0x3f);
  *dayOfWeek = bcdToDec(twi_receive());
  *dayOfMonth = bcdToDec(twi_receive());
  *month = bcdToDec(twi_receive());
  *year = bcdToDec(twi_receive());
}

//   ,   ,  ,   
byte readDS3231time_hms(byte *second, byte *minute, byte *hour) {
  byte rc = twi_read(DS3231_I2C_ADDRESS, 0, 3);
  if (rc) return 1;
  *second = bcdToDec(twi_receive() & 0x7f);
  *minute = bcdToDec(twi_receive());
  *hour = bcdToDec(twi_receive() & 0x3f);
}

/*
 *  ,   ,  : http://dsscircuits.com/articles/arduino-i2c-master-library
 *      ,         4k  .
 */
/*
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#define START           0x08
#define REPEATED_START  0x10
#define MT_SLA_ACK  0x18
#define MT_SLA_NACK 0x20
#define MT_DATA_ACK     0x28
#define MT_DATA_NACK    0x30
#define MR_SLA_ACK  0x40
#define MR_SLA_NACK 0x48
#define MR_DATA_ACK     0x50
#define MR_DATA_NACK    0x58
#define LOST_ARBTRTN    0x38
#define TWI_STATUS      (TWSR & 0xF8)
#define SLA_W(address)  (address << 1)
#define SLA_R(address)  ((address << 1) + 0x01)

#define MAX_BUFFER_SIZE 32

uint8_t twi_bytesAvailable = 0;
uint8_t twi_bufferIndex = 0;
uint8_t twi_totalBytes = 0;
uint16_t twi_timeOutDelay = 0;
uint8_t twi_returnStatus;
uint8_t twi_nack;
uint8_t twi_data[MAX_BUFFER_SIZE];

void twi_begin()
{
  sbi(PORTC, 4);
  sbi(PORTC, 5);
  cbi(TWSR, TWPS0);
  cbi(TWSR, TWPS1);
  TWBR = ((F_CPU / 100000) - 16) / 2;
  TWCR = _BV(TWEN) | _BV(TWEA); 
}
uint8_t twi_read(uint8_t address, uint8_t registerAddress, uint8_t numberBytes)
{
  twi_bytesAvailable = 0;
  twi_bufferIndex = 0;
  if(numberBytes == 0){numberBytes++;}
  twi_nack = numberBytes - 1;
  twi_returnStatus = 0;
  twi_returnStatus = twi_start();
  if(twi_returnStatus){return(twi_returnStatus);}
  twi_returnStatus = twi_sendAddress(SLA_W(address));
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(2);}
    return(twi_returnStatus);
  }
  twi_returnStatus = twi_sendByte(registerAddress);
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(3);}
    return(twi_returnStatus);
  }
  twi_returnStatus = twi_start();
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(4);}
    return(twi_returnStatus);
  }
  twi_returnStatus = twi_sendAddress(SLA_R(address));
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(5);}
    return(twi_returnStatus);
  }
  for(uint8_t i = 0; i < numberBytes; i++)
  {
    if( i == twi_nack )
    {
      twi_returnStatus = twi_receiveByte(0);
      if(twi_returnStatus == 1){return(6);}
      if(twi_returnStatus != MR_DATA_NACK){return(twi_returnStatus);}
    }
    else
    {
      twi_returnStatus = twi_receiveByte(1);
      if(twi_returnStatus == 1){return(6);}
      if(twi_returnStatus != MR_DATA_ACK){return(twi_returnStatus);}
    }
    twi_data[i] = TWDR;
    twi_bytesAvailable = i+1;
    twi_totalBytes = i+1;
  }
  twi_returnStatus = twi_stop();
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(7);}
    return(twi_returnStatus);
  }
  return(twi_returnStatus);
}
uint8_t twi_write(uint8_t address, uint8_t registerAddress, uint8_t data)
{
  twi_returnStatus = 0;
  twi_returnStatus = twi_start(); 
  if(twi_returnStatus){return(twi_returnStatus);}
  twi_returnStatus = twi_sendAddress(SLA_W(address));
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(2);}
    return(twi_returnStatus);
  }
  twi_returnStatus = twi_sendByte(registerAddress);
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(3);}
    return(twi_returnStatus);
  }
  twi_returnStatus = twi_sendByte(data);
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(3);}
    return(twi_returnStatus);
  }
  twi_returnStatus = twi_stop();
  if(twi_returnStatus)
  {
    if(twi_returnStatus == 1){return(7);}
    return(twi_returnStatus);
  }
  return(twi_returnStatus);
}
uint8_t twi_receive()
{
  twi_bufferIndex = twi_totalBytes - twi_bytesAvailable;
  if(!twi_bytesAvailable)
  {
    twi_bufferIndex = 0;
    return(0);
  }
  twi_bytesAvailable--;
  return(twi_data[twi_bufferIndex]);
}
uint8_t twi_start()
{
  unsigned long startingTime = millis();
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  while (!(TWCR & (1<<TWINT)))
  {
    if(!twi_timeOutDelay){continue;}
    if((millis() - startingTime) >= twi_timeOutDelay)
    {
      twi_lockUp();
      return(1);
    }

  }
  if ((TWI_STATUS == START) || (TWI_STATUS == REPEATED_START))
  {
    return(0);
  }
  if (TWI_STATUS == LOST_ARBTRTN)
  {
    uint8_t bufferedStatus = TWI_STATUS;
    twi_lockUp();
    return(bufferedStatus);
  }
  return(TWI_STATUS);
}
uint8_t twi_sendAddress(uint8_t i2cAddress)
{
  TWDR = i2cAddress;
  unsigned long startingTime = millis();
  TWCR = (1<<TWINT) | (1<<TWEN);
  while (!(TWCR & (1<<TWINT)))
  {
    if(!twi_timeOutDelay){continue;}
    if((millis() - startingTime) >= twi_timeOutDelay)
    {
      twi_lockUp();
      return(1);
    }

  }
  if ((TWI_STATUS == MT_SLA_ACK) || (TWI_STATUS == MR_SLA_ACK))
  {
    return(0);
  }
  uint8_t bufferedStatus = TWI_STATUS;
  if ((TWI_STATUS == MT_SLA_NACK) || (TWI_STATUS == MR_SLA_NACK))
  {
    twi_stop();
    return(bufferedStatus);
  }
  else
  {
    twi_lockUp();
    return(bufferedStatus);
  } 
}
uint8_t twi_sendByte(uint8_t i2cData)
{
  TWDR = i2cData;
  unsigned long startingTime = millis();
  TWCR = (1<<TWINT) | (1<<TWEN);
  while (!(TWCR & (1<<TWINT)))
  {
    if(!twi_timeOutDelay){continue;}
    if((millis() - startingTime) >= twi_timeOutDelay)
    {
      twi_lockUp();
      return(1);
    }

  }
  if (TWI_STATUS == MT_DATA_ACK)
  {
    return(0);
  }
  uint8_t bufferedStatus = TWI_STATUS;
  if (TWI_STATUS == MT_DATA_NACK)
  {
    twi_stop();
    return(bufferedStatus);
  }
  else
  {
    twi_lockUp();
    return(bufferedStatus);
  } 
}
uint8_t twi_receiveByte(uint8_t ack)
{
  unsigned long startingTime = millis();
  if(ack)
  {
    TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);

  }
  else
  {
    TWCR = (1<<TWINT) | (1<<TWEN);
  }
  while (!(TWCR & (1<<TWINT)))
  {
    if(!twi_timeOutDelay){continue;}
    if((millis() - startingTime) >= twi_timeOutDelay)
    {
      twi_lockUp();
      return(1);
    }
  }
  if (TWI_STATUS == LOST_ARBTRTN)
  {
    uint8_t bufferedStatus = TWI_STATUS;
    twi_lockUp();
    return(bufferedStatus);
  }
  return(TWI_STATUS); 
}
uint8_t twi_stop()
{
  unsigned long startingTime = millis();
  TWCR = (1<<TWINT)|(1<<TWEN)| (1<<TWSTO);
  while ((TWCR & (1<<TWSTO)))
  {
    if(!twi_timeOutDelay){continue;}
    if((millis() - startingTime) >= twi_timeOutDelay)
    {
      twi_lockUp();
      return(1);
    }

  }
  return(0);
}
void twi_lockUp()
{
  TWCR = 0;
  TWCR = _BV(TWEN) | _BV(TWEA);
}

. , , — . , 1MHz, , 6mA. . , , : USBasp- .


, AVR- " ". ! , 100 . 65ms, , .


, - , , , 7805 , - .


! . , - , , , ? , . STM8/32? MSP430? , -, AVR?




UPD


, , . . , i2c ds1307. , VCC 1.2*VBAT. , i2c. : , +5v - 2.5v, i2c , . VBAT Tiny RTC 3v. .


. , . — , , 595, . , RCLK, , , , . , , . , .


, . - , , , , AVR 65 . , , . , AVR , , , . olartamonov .


, , 6mA , lm7805, (Vout ; Vin ). — 4mA.


, 0.3 1.6 mA. , — , , .


— ! , , 595- 555 , - , 32 595-. - :-)

Source: https://habr.com/ru/post/de410899/


All Articles