Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Arduino code for Alarm Clock with TSA1605A 8x14 segments micro leds display
- // Goebish 2013 (twitter: @goebish)
- //
- // This code is distributed in the hope that it will be useful for everyone but it is distributed
- // without any warranty. It is provided "as is" without warranty of any kind, either expressed or
- // implied, including, but not limited to, the implied warranties of merchantability and fitness for a
- // particular purpose. The entire risk as to the quality and performance of the code is with you.
- // Should the code prove defective, you assume the cost of all necessary servicing, repair or
- // correction. In no event will the author of the code be liable to you for damages, including
- // any general, special, incidental or consequential damages arising out of the use of the code,
- // including but not limited to loss of life, money, the loss of data or data being rendered
- // inaccurate, or any other losses sustained by you or third parties or a failure of the code to
- // operate with any other device(s), even if the author has been advised of the possibility of such
- // possibilities.
- //
- // pictures:
- // http://imgur.com/10iBfZd
- // http://imgur.com/CL1NXCh
- //
- // parts:
- // - atmega168 or atmega328 Arduino or compatible dev board (2009, UNO, nano, mini ...) this has not been tested with the Leonardo and shouldn't work on the MEGA
- // - DS1307 RTC + 32.768kHz chrystal + 3V coin battery cell (CR2032) or DS1307 breakout module
- // - TSA1605A 8x14 segments micro LEDs display
- // - 14x 68 ohms resistors (one for each segments anode, they don't appear in my pictures, but you should really use them ;))
- // - 3x 74HC595 shift registers
- // - 3x momentary pushbuttons (MODE/SET, PLUS, MINUS) + 3x 0.1µF capacitors
- // - 10k ohms resistor (HC595s OE pullup)
- // - piezzo buzzer + 100 ohms resistor
- // - led + 8k ohms resistor (alarm enabled indicator)
- //
- // schematics:
- // http://imgur.com/2fvOBml
- //
- // read through the code for usage description :)
- #include <Wire.h> // I2C for DS1307 RTC
- #include <EEPROM.h> // eeprom used to store alarm settings
- #include <TimerOne.h> // get it from http://code.google.com/p/arduino-timerone/
- #define NO_PORTC_PINCHANGES
- #define NO_PORTD_PINCHANGES
- #include <PinChangeInt.h> // get it from http://code.google.com/p/arduino-pinchangeint/
- #if (ARDUINO >= 100)
- #define WireWrite Wire.write
- #define WireRead Wire.read()
- #else
- #define WireWrite Wire.send
- #define WireRead Wire.receive()
- #endif
- // One side of the pushbuttons is connected to ground. The other sides connect to the Arduino pins.
- // Put a 0.1µF cap between pushbuttons legs to provide hard debouncing
- const int setButton = 9;
- const int minusButton=10;
- const int plusButton =11;
- const int buzzer=2; // buzzer w/ 100ohms resistor
- const int alarmLed=13; // led + 8k ohms resistor, use an high enough resistor value so the led isn't brighter than the display
- // HC595s wiring:
- // HC595-1, 2 & 3 pin 8 to GND
- // HC595-1, 2 & 3 pin 10 to +5V
- // HC595-1, 2 & 3 pin 16 to +5V
- // HC595-1 QA to QH outputs --> TSA1605A cathodes 1 to 8 (pins 1, 22, 3, 19, 8, 15, 11, 12)
- // HC595-2 QA to QH outputs --> TSA1605A G to N segments (pins 6, 13, 20, 18, 17, 2, 7, 10)
- // HC595-3 QA to QF outputs --> TSA1605A A to F segments (pins 14, 16, 5, 4, 9, 21)
- // place one 68 ohms resistor between each display segment anodes and it's HC959 output.
- // do not modify the following pins assignments ! (direct port access in ISR, no digitalWrite)
- const int dataPin1 = 5; // to HC595-1 (pin 14) cathodes driver
- const int dataPin2 = 4; // to HC595-2 (pin 14) segments GHIJKLMN anodes driver
- const int dataPin3 = 6; // to HC595-3 (pin 14) segments ABCDEF anodes driver
- const int latchPin = 7; // to HC595-1, 2 & 3 (pin 12)
- const int clockPin = 8; // to HC595-1, 2 & 3 (pin 11)
- const int oePin = 3; // to HC595-1, 2 & 3 (pin 13) + 10k pull up resistor to +5V
- #define DISPLAY_ISR_PERIOD 500 // 1/DISPLAY_ISR_PERIOD/1000 = display frequency, lower value produce brighter display. Don't go to low or the main loop won't have enough (if any) CPU cycles for itself.
- //#define DISPLAY_ISR_PERIOD 2000 // for use without resistors on display anodes, USE AT YOUR OWN RISK !
- // DS1307 Real Time Clock
- // SDA (pin 5) to Arduino A4
- // SCL (pin 6) to Arduino A5
- #define DS1307_ADDRESS 0x68
- typedef struct {
- byte second;
- byte minute;
- byte hour;
- byte weekDay;
- byte monthDay;
- byte month;
- int year;
- }
- s_dateTime;
- volatile s_dateTime curDateTime;
- typedef struct {
- byte enabled;
- byte hour;
- byte minute;
- } s_alarmSettings;
- volatile s_alarmSettings alarm = {false,0,0};
- // states
- typedef enum e_states{
- CLOCK,
- DATE, // return to CLOCK if SET button is not pushed for 5 seconds
- SET_ALARM, // is SET button is pushed while "SET ALARM" is displayed, go directly to "SET_DATE", or wait 2s to enter SET_ALARM_ENABLED
- SET_ALARM_ENABLED, // if OFF is choosen, go to WRITE_ALARM
- SET_ALARM_HOUR,
- SET_ALARM_MINUTE, // go to WRITE_ALARM when done
- SET_DATE, // if SET button is pushed while "SET DATE" is displayed, go directly to "SET TIME", or wait 2s to enter SET_YEAR
- SET_YEAR,
- SET_MONTH,
- SET_DAY, // go to WRITE_DATETIME after setting day
- SET_TIME, // if SET button is pushed while "SET TIME" is displayed, return to CLOCK mode
- SET_HOUR,
- SET_MINUTE,
- WRITE_DATETIME, // returns to CLOCK when done
- WRITE_ALARM // returns to CLOCK when done
- };
- volatile int currentState = CLOCK;
- volatile unsigned long mode_start=0;
- volatile unsigned long last_button_push=0;
- const char* clock_format_no_tick = "%02d %02d %02d";
- const char* clock_format_tick = "%02d-%02d-%02d";
- const char* clock_format_date = "%02d %s";
- const char* set_hour_tick = " %02d ";
- const char* set_hour_no_tick = "%02d %02d ";
- const char* set_minute_tick = "%02d ";
- const char* set_minute_no_tick = set_hour_no_tick;
- const char* set_day_tick = " %s";
- const char* set_month_tick = set_minute_tick;
- const char* set_year_tick = " %04d ";
- const char* month[] = {"JANV ","FEV ","MARS ","AVRIL","MAI ","JUIN ","JUIL ","AOUT ","SEPT ","OCT ","NOV ","DEC "}; // french months, change them as you like but keep 5 characters per month
- const int daysInMonth[12]={31,28,31,30,31,30,31,31,30,31,30,31};
- char displayBuffer[9]= "aaaaaaaa";
- // credits go to http://huinink.info/8-x-14-segment/ for most of the ascii table font, I only modified a few numbers to take advantage of the 14 segments
- const byte ascii[] PROGMEM={
- 0x00, 0x00, // space
- 0x01, 0x4A, // !
- 0x22, 0x00, // "
- 0x0E, 0x55, // #
- 0x39, 0x55, // euro. for dollar use 0x2D, 0x55
- 0x24, 0x88, // %
- 0x1D, 0x2B, // &
- 0x00, 0x08, // '
- 0x39, 0x00, // {
- 0x0F, 0x00, // )
- 0x00, 0xFF, // *
- 0x00, 0x55, // +
- 0x00, 0x80, // ,
- 0x00, 0x11, //
- 0x08, 0x00, // dot?
- 0x00, 0x88, // /
- 0x3F, 0x00, // 0
- 0x06, 0x08, // 1
- 0x1B, 0x11, // 2
- 0x0F, 0x10, // 3
- 0x26, 0x11, // 4
- 0x2D, 0x11, // 5
- 0x3D, 0x11, // 6
- 0x01, 0x88, // 7
- 0x3F, 0x11, // 8
- 0x2F, 0x11, // 9
- 0x01, 0x11, // :
- 0x01, 0x80, // ;
- 0x00, 0x28, // <
- 0x00, 0x82, // >
- 0x08, 0x11, // =
- 0x03, 0x50, // ?
- 0x1F, 0x41, // @
- 0x37, 0x11, // A
- 0x0F, 0x54, // B
- 0x39, 0x00, // C
- 0x0F, 0x44, // D
- 0x39, 0x01, // E
- 0x31, 0x01, // F
- 0x3D, 0x10, // G
- 0x36, 0x11, // H
- 0x09, 0x44, // I
- 0x1E, 0x00, // J
- 0x30, 0x29, // K
- 0x38, 0x00, // L
- 0x36, 0x0A, // M
- 0x36, 0x22, // N
- 0x3F, 0x00, // O
- 0x33, 0x11, // P
- 0x3F, 0x20, // Q
- 0x33, 0x31, // R
- 0x2D, 0x11, // S
- 0x01, 0x44, // T
- 0x3E, 0x00, // U
- 0x30, 0x88, // V
- 0x36, 0xA0, // W
- 0x00, 0xAA, // X
- 0x00, 0x4A, // Y
- 0x09, 0x88, // Z
- 0x39, 0x00, // [
- 0x00, 0x88, // /
- 0x0F, 0x00, // ]
- 0x00, 0xA0, // ^
- 0x08, 0x00, // _
- 0x00, 0x02, // `
- 0x3F, 0xFF // a --> test character
- };
- void buttonsManager()
- {
- if(millis()-last_button_push>150) {
- switch(PCintPort::arduinoPin) {
- case setButton:
- if(currentState == SET_ALARM)
- currentState = SET_DATE;
- else if(currentState == SET_DATE)
- currentState = SET_TIME;
- else if(currentState==SET_TIME)
- currentState = CLOCK;
- else if(currentState==SET_MONTH)
- {
- if( curDateTime.monthDay > daysInMonth[curDateTime.month-1])
- curDateTime.monthDay = daysInMonth[curDateTime.month-1];
- currentState++;
- }
- else if(currentState == SET_DAY)
- currentState = WRITE_DATETIME;
- else if(currentState == SET_MINUTE)
- {
- curDateTime.second=0;
- currentState = WRITE_DATETIME;
- }
- else if((currentState==SET_ALARM_ENABLED && alarm.enabled==false) || currentState==SET_ALARM_MINUTE)
- currentState = WRITE_ALARM;
- else
- currentState++;
- mode_start=millis();
- break;
- case minusButton:
- switch(currentState) {
- case SET_HOUR:
- if(curDateTime.hour<=0)
- curDateTime.hour = 23;
- else
- curDateTime.hour--;
- break;
- case SET_MINUTE:
- if(curDateTime.minute<=0)
- curDateTime.minute = 59;
- else
- curDateTime.minute--;
- break;
- case SET_ALARM_ENABLED:
- alarm.enabled = !alarm.enabled;
- digitalWrite(alarmLed, alarm.enabled);
- break;
- case SET_ALARM_HOUR:
- if(alarm.hour<=0)
- alarm.hour = 23;
- else
- alarm.hour--;
- break;
- case SET_ALARM_MINUTE:
- if(alarm.minute<=0)
- alarm.minute = 59;
- else
- alarm.minute--;
- break;
- case SET_MONTH:
- if(curDateTime.month<=1)
- curDateTime.month = 12;
- else
- curDateTime.month--;
- break;
- case SET_DAY:
- if(curDateTime.monthDay <= 1)
- if(isLeapYear(curDateTime.year) && curDateTime.month==2)
- curDateTime.monthDay = 29;
- else
- curDateTime.monthDay = daysInMonth[curDateTime.month-1];
- else
- curDateTime.monthDay--;
- break;
- case SET_YEAR:
- if(curDateTime.year<=2000)
- curDateTime.year = 2099;
- else
- curDateTime.year--;
- break;
- }
- break;
- case plusButton:
- switch(currentState) {
- case SET_HOUR:
- if(curDateTime.hour>=23)
- curDateTime.hour = 0;
- else
- curDateTime.hour++;
- break;
- case SET_MINUTE:
- if(curDateTime.minute>=59)
- curDateTime.minute = 0;
- else
- curDateTime.minute++;
- break;
- case SET_ALARM_ENABLED:
- alarm.enabled = !alarm.enabled;
- digitalWrite(alarmLed, alarm.enabled);
- break;
- case SET_ALARM_HOUR:
- if(alarm.hour>=59)
- alarm.hour = 0;
- else
- alarm.hour++;
- break;
- case SET_ALARM_MINUTE:
- if(alarm.minute>=59)
- alarm.minute = 0;
- else
- alarm.minute++;
- break;
- case SET_MONTH:
- if(curDateTime.month>=12)
- curDateTime.month = 1;
- else
- curDateTime.month++;
- break;
- case SET_DAY:
- if(isLeapYear(curDateTime.year) && curDateTime.month==2 && curDateTime.monthDay>=29)
- curDateTime.monthDay = 1;
- else if(curDateTime.monthDay >= daysInMonth[curDateTime.month-1])
- curDateTime.monthDay = 1;
- else
- curDateTime.monthDay++;
- break;
- case SET_YEAR:
- if(curDateTime.year>=2099)
- curDateTime.year = 2000;
- else
- curDateTime.year++;
- break;
- }
- break;
- }
- last_button_push = millis();
- }
- }
- void setup()
- {
- digitalWrite(oePin, HIGH); // disable output
- pinMode(oePin, OUTPUT);
- pinMode(dataPin1, OUTPUT);
- pinMode(dataPin2, OUTPUT);
- pinMode(dataPin3, OUTPUT);
- pinMode(latchPin, OUTPUT);
- pinMode(clockPin, OUTPUT);
- pinMode(buzzer, OUTPUT);
- digitalWrite(buzzer, LOW);
- pinMode(alarmLed, OUTPUT);
- // init buttons interrupt
- pinMode(setButton, INPUT);
- digitalWrite(setButton, HIGH);
- PCintPort::attachInterrupt(setButton, &buttonsManager, FALLING);
- pinMode(minusButton, INPUT);
- digitalWrite(minusButton, HIGH);
- PCintPort::attachInterrupt(minusButton, &buttonsManager, FALLING);
- pinMode(plusButton, INPUT);
- digitalWrite(plusButton, HIGH);
- PCintPort::attachInterrupt(plusButton, &buttonsManager, FALLING);
- // init I2C bus for DS1307 RTC
- Wire.begin();
- // init display interrupt
- Timer1.initialize(DISPLAY_ISR_PERIOD);
- Timer1.attachInterrupt(displayISR);
- getAlarmSettings();
- delay(1000);
- }
- bool isLeapYear(const int year) {
- return year%4==0; // ok for 2000-2099 because 2000 is leap
- }
- void printDate() {
- sprintf(displayBuffer,clock_format_date,curDateTime.monthDay, month[curDateTime.month-1]);
- }
- void printTime() {
- sprintf(displayBuffer,curDateTime.second%2?clock_format_tick:clock_format_no_tick,curDateTime.hour,curDateTime.minute,curDateTime.second);
- }
- void buzz() {
- unsigned long alarmStart=millis();
- while(digitalRead(setButton==HIGH) && millis()-alarmStart < 60000) { // one minute should be enough to wake up ;)
- updateCurDateTime();
- printTime();
- for(int j=0; j<4; j++) {
- digitalWrite(buzzer,HIGH);
- delay(90);
- digitalWrite(buzzer,LOW);
- delay(45);
- if(digitalRead(setButton)==LOW) {
- currentState = CLOCK;
- return;
- }
- }
- sprintf(displayBuffer," ALARM ");
- delay(400);
- }
- currentState = CLOCK;
- }
- void loop(){
- bool tick=millis()%600<300 && millis()-last_button_push > 400;
- if(millis()-last_button_push>200 && (digitalRead(minusButton)==LOW || digitalRead(plusButton)==LOW)) { // buttons auto-repeat
- buttonsManager();
- }
- updateCurDateTime();
- switch(currentState) {
- case CLOCK:
- printTime();
- if(alarm.enabled && curDateTime.second==0 && curDateTime.hour==alarm.hour && curDateTime.minute==alarm.minute)
- buzz();
- break;
- case DATE:
- printDate();
- if(millis()-mode_start>5000) // return to clock after 5s
- currentState = CLOCK;
- break;
- case SET_ALARM:
- if(millis()%1000<500)
- sprintf(displayBuffer, "SET ALAR");
- else
- sprintf(displayBuffer, "ET ALARM");
- if(millis()-mode_start>2000) // wait 2s to set alarm
- currentState++;
- break;
- case SET_ALARM_ENABLED:
- if(alarm.enabled)
- sprintf(displayBuffer," ON ");
- else
- sprintf(displayBuffer," OFF ");
- break;
- case SET_ALARM_HOUR:
- if(tick)
- sprintf(displayBuffer, set_hour_tick, alarm.minute);
- else
- sprintf(displayBuffer, set_hour_no_tick, alarm.hour, alarm.minute);
- break;
- case SET_ALARM_MINUTE:
- if(tick)
- sprintf(displayBuffer, set_minute_tick, alarm.hour);
- else
- sprintf(displayBuffer, set_minute_no_tick, alarm.hour, alarm.minute);
- break;
- case SET_TIME:
- sprintf(displayBuffer,"SET TIME");
- if(millis()-mode_start>2000) // wait 2s to set time
- currentState++;
- break;
- case SET_HOUR:
- if(tick)
- sprintf(displayBuffer, set_hour_tick, curDateTime.minute);
- else
- sprintf(displayBuffer, set_hour_no_tick, curDateTime.hour, curDateTime.minute);
- break;
- case SET_MINUTE:
- if(tick)
- sprintf(displayBuffer, set_minute_tick, curDateTime.hour);
- else
- sprintf(displayBuffer, set_minute_no_tick, curDateTime.hour, curDateTime.minute);
- break;
- case SET_DATE:
- sprintf(displayBuffer,"SET DATE");
- if(millis()-mode_start>2000) // wait 2s to set date
- currentState++;
- break;
- case SET_MONTH:
- if(tick)
- sprintf(displayBuffer, set_month_tick, curDateTime.monthDay);
- else
- printDate();
- break;
- case SET_DAY:
- if(tick)
- sprintf(displayBuffer, set_day_tick, month[curDateTime.month-1]);
- else
- printDate();
- break;
- case SET_YEAR:
- if(tick)
- sprintf(displayBuffer, " ");
- else
- sprintf(displayBuffer, set_year_tick, curDateTime.year);
- break;
- case WRITE_DATETIME:
- setDateTime();
- currentState=CLOCK;
- break;
- case WRITE_ALARM:
- setAlarmSettings();
- currentState=CLOCK;
- break;
- }
- }
- void shiftBits (byte cathodes, byte segments1, byte segments2){
- PORTD |= B10000000; // HC595s latch high
- for (int k=0; k < 8; k++){
- PORTB &= ~1; // HC595s clock low
- if ( cathodes & (0b10000000 >> k) )
- PORTD |= 0b100000; // HC595-1 data high
- else
- PORTD &= ~0b100000; // HC595-1 data low
- if ( segments2 & (0b10000000 >> k) )
- PORTD |= 0b10000; // HC595-2 data high
- else
- PORTD &= ~0b10000; // HC595-2 data low
- if ( segments1 & (0b10000000 >> k) )
- PORTD |= 0b1000000; // HC595-3 data high
- else
- PORTD &= ~0b1000000; // HC595-3 data low
- PORTB |= 1; // HC595s clock high
- }
- PORTD &= ~0b10000000; // HC595s latch low
- }
- void displayISR(){
- PORTD &= ~B1000; // HC595s output enabled
- for (int cathode = 0; cathode <8; cathode++){
- shiftBits(0xFF-(1<<cathode),pgm_read_byte(&ascii[(2*(displayBuffer[cathode]-0x20))]), pgm_read_byte(&ascii[(1+2*(displayBuffer[cathode]-0x20))]));
- }
- PORTD |= B1000; // HC595s output disabled
- }
- byte bcdToDec(byte val) {
- return ((val/16*10)+(val%16));
- }
- byte decToBcd(byte val){
- return ((val/10*16)+(val%10));
- }
- void updateCurDateTime() {
- static unsigned long last_update = -200;
- if((currentState<SET_HOUR || currentState>SET_MINUTE) && currentState != WRITE_DATETIME) {
- if(millis()-last_update>100) {
- Timer1.detachInterrupt(); // disable display ISR while using i2c bus
- Wire.beginTransmission(DS1307_ADDRESS);
- WireWrite(0);
- Wire.endTransmission();
- Wire.requestFrom(DS1307_ADDRESS, 7);
- curDateTime.second = bcdToDec(WireRead);
- curDateTime.minute = bcdToDec(WireRead);
- curDateTime.hour = bcdToDec(WireRead & 0b111111);
- curDateTime.weekDay = bcdToDec(WireRead);
- if(currentState>SET_DAY || currentState<SET_YEAR) {
- curDateTime.monthDay = bcdToDec(WireRead);
- curDateTime.month = bcdToDec(WireRead);
- curDateTime.year = bcdToDec(WireRead)+2000;
- }
- if(curDateTime.second & 0b1000000) {
- curDateTime.second = 0; // disable Clock Halt bit on fresh ds1307 chip
- setDateTime();
- }
- Timer1.attachInterrupt(displayISR);
- }
- }
- }
- void setDateTime() {
- Timer1.detachInterrupt();
- Wire.beginTransmission(DS1307_ADDRESS);
- WireWrite(0);
- WireWrite(decToBcd(curDateTime.second));
- WireWrite(decToBcd(curDateTime.minute));
- WireWrite(decToBcd(curDateTime.hour));
- WireWrite(decToBcd(curDateTime.weekDay));
- WireWrite(decToBcd(curDateTime.monthDay));
- WireWrite(decToBcd(curDateTime.month));
- WireWrite(decToBcd(curDateTime.year-2000));
- WireWrite(0);
- Wire.endTransmission();
- Timer1.attachInterrupt(displayISR);
- }
- void getAlarmSettings()
- {
- if(EEPROM.read(0)==0x13 && EEPROM.read(1)==0x37) // check signature
- {
- alarm.enabled = EEPROM.read(2);
- alarm.hour = EEPROM.read(3);
- alarm.minute = EEPROM.read(4);
- }
- digitalWrite(alarmLed, alarm.enabled);
- }
- void setAlarmSettings()
- {
- EEPROM.write(0, 0x13);
- EEPROM.write(1, 0x37);
- EEPROM.write(2, alarm.enabled);
- EEPROM.write(3, alarm.hour);
- EEPROM.write(4, alarm.minute);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement