NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » Christmas Countdown Clock

December 04, 2015
by edSky
edSky's Avatar

Here is my Christmas Countdown Clock version 1.0

I use interrupts to process the button click that sets the current date/time. I modelled the setting of the time after a current LED watch called the Blink Time Watch. Hold the button for a few seconds and it enters "Set Year" mode. Then short presses increment the year. Hold long again and we go to "Set Month". Short clicks set the month. Repeat with Day, Hour, Minute and Seconds. The last long hold goes back to Run mode.

I initially wanted to store the date/time as Year|DDD|Seconds where Seconds was Hours36+Minutes60+Seconds. I figured I could get the difference between current time and target time (i.e. Now and Christmas) by subtracting the two and using MODulus arithmetic to convert back to hh:mm:ss for display. This was to me the most intuitive from a mainframe or modern PC CPU point of view. It would simplify the code and I wouldn't have to "borrow" from minutes and hours and so on when subtracting.

Turns out the code needed to do the "simple" math was over 4x as large as the long-hand accounting for time differences.

I have been busy at my store (a toy store and this is prime time) so I haven't posted this until now. And I have a few to-do items. One is to automatically increment the year of the target (Christmas) when we actually count down to 0. Two is to do something fancy for the 24 hours of Christmas (or just display Merry Christmas). Three is to code in changes to and from Daylight Savings Time. I wanted to do this up front but figured I need to reset the time every other day or so because of drift. Five is to capture the moment of button-press and determine time-down so when you release the final mode change back to seconds it will negate the delay (i.e. it is 17:24:00 and I push the button now and hold for two seconds - when I release it is 17:24:03 not 17:24:00, but if I can add that time in...) (Four is something I can't remember or haven't even thought of yet.)

// realtimeclock1.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>
#include <stdlib.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
//#include "../libnerdkits/uart.h"

#include "countdownClock.h"

/// Timer Macros to help understanding

#define TIMER_TICKSPERSECONDS           100
#define TIMER_SECONDS2TICKS(et)         ((et)*TIMER_TICKSPERSECONDS)
#define TIMER_TICKS2SECONDS(et)         ((double)(et)/TIMER_TICKSPERSECONDS)
#define TIMER_TICKS2MILLISECONDS(et)    ((double)(et)/(TIMER_TICKSPERSECONDS/1000.0))
#define TIMER_TICKS2MICROSECONDS(et)    ((double)(et)/(TIMER_TICKSPERSECONDS/1000000.0))

// Helper Defines

#define BTN_1       PC5
#define BTN_1_PORT  PORTC
#define BTN_1_DDR   DDRC
#define BTN_1_PIN   PINC
#define BTN_1_PCIE  PCIE1
#define BTN_1_PCMSK PCMSK1
#define BTN_1_PCINT PCINT13

#define LED_1       PC4
#define LED_1_PORT  PORTC
#define LED_1_DDR   DDRC

#define LED_2       PC3
#define LED_2_PORT  PORTC
#define LED_2_DDR   DDRC

#define LED_3       PC2
#define LED_3_PORT  PORTC
#define LED_3_DDR   DDRC

/*
    Interrupt Info
    PCIFR needs to have bit PCIF1 - Pin Change Interrupt Flag 1 - set for interrupt on PCINT13 (or PC5)
    PCMSK1 – Pin Change Mask Register 1 - needs PCINT13 set
    ISR (PCINT1_vect) {} will be called and we will look at PINC and the bit for PC5
*/

// Global variables

// Time Collection data.
int32_t     tEvent[16];     // An array of time events.
uint16_t    tState;         // A 16-bit set of state values
uint8_t     eventCount;     // A count of the # of entries in tEvent[]
int8_t      displayRow[4];  // An array of indexes into tEvent, where -1 is an empty row

volatile uint8_t    monthDays[12] = {31,28,31,30,31,30,31,31,30,31,30,31};  // Standard days per month. Leap will be calculated

// PIN DEFINITIONS:

// the_time will store the elapsed time
// in hundredths of a second.
// (100 = 1 second)
// 
// note that this will overflow in approximately 248 days!
//
//
// This variable is marked "volatile" because it is modified
// by an interrupt handler.  Without the "volatile" marking,
// the compiler might just assume that it doesn't change in 
// the flow of any given function (if the compiler doesn't
// see any code in that function modifying it -- sounds 
// reasonable, normally!).
//
// But with "volatile", it will always read it from memory 
// instead of making that assumption.

volatile int32_t    the_time;
volatile int32_t    btnDownStart;   // The time the button was pressed
volatile int32_t    btnEventTime;   // The time in ticks of the last button event
volatile uint8_t    qtrSecTimer;    // a small timer used to count quarter-seconds
volatile uint8_t    clockMode;      // 
volatile uint8_t    prevClockMode;

volatile DayTime    theDayTime;     // The Day/Time value
volatile DayTime    tgtDayTime;     // The Target Day/Time value
volatile DayTime    difDayTime;     // The countdown Day/Time value!!!

volatile uint8_t    setYY;          // Set Mode Copy of YY
volatile uint8_t    setMM;          // Set Mode Copy of MM, zero-based
volatile uint8_t    setDD;          // Set Mode Copy of DD, one-based
volatile uint8_t    setHour;        // Set Mode Copy of Hour
volatile uint8_t    setMins;        // Set Mode Copy of Mins
volatile uint8_t    setSecs;        // Set Mode Copy of Secs

void Timer0_Setup() {
  // setup 8 bit Timer0:
  // CTC (Clear Timer on Compare Match mode)
  // TOP set by OCR0A register
  TCCR0A |= (1<<WGM01);
  // clocked from CLK/1024
  // which is 14745600/1024, or 14400 increments per second
  TCCR0B |= (1<<CS02) | (1<<CS00);
  // set TOP to 143
  // because it counts 0, 1, 2, ... 142, 143, 0, 1, 2 ...
  // so 0 through 143 equals 144 events
  OCR0A = 143;
  // enable interrupt on compare event
  // (14400 / 144 = 100 per second)
  TIMSK0 |= (1<<OCIE0A);
}

void Timer1_Setup() 
{ 
    // setup 16 bit Timer1: TCCR1A and TCCR1B
    // Insure all of bits in 1A are reset to default

    TCCR1A = 0x00;

    // CTC (Clear Timer on Compare Match mode): Set WGM12 to "1"
    // Set prescale to 1024: Set CS12, CS11 and CS10 to "101"
    // Insure all other Time/Count1 Control Register B bits are reset: thus "00001101" -> 0x0D

    TCCR1B = ((1<<WGM12) | (1<<CS12) | (1<<CS10)) /* 0x0D */;

   // set TOP to 3599 
    // because it counts 0, 1, 2, ... 3598, 3599, 0, 1, 2 ... 
    // so 0 through 3599 equals 3600 events (approx interrrupt 4/sec)

    OCR1A = 3599;

    // enable interrupt on compare event

    TIMSK1 |= (1<<OCIE1A); 
}

ISR(TIMER0_COMPA_vect) {
  // when Timer0 gets to its Output Compare value,
  // one one-hundredth of a second has elapsed (0.01 seconds).
  the_time++;
}

ISR(TIMER1_COMPA_vect) 
{ 
    // when Timer1 gets to its Output Compare value, approx 1/4 second has elapsed. 
    qtrSecTimer++;

    // Have we gone for a second?
    if (qtrSecTimer > 3)
    {
        qtrSecTimer -= 4;

        if (CLK_RUN == clockMode)
        {
            CountDayTimeUp(&theDayTime);
            CountDayTimeDown(&difDayTime);
        }

        Toggle_LED_3();
    }

    Toggle_LED_2();
}

ISR(PCINT1_vect)
{
    int32_t btnTotalTime;   // The time in ticks that the button was down
    uint8_t btnIsDown;      // Indicates whether the button is pressed (true) or not;

    // Capture the time of the now most-recent event and
    // get the state of the button. In our implementation
    // The button is normally closed and pushing will open it.

    btnEventTime = the_time;
    btnIsDown =  !(BTN_1_PIN & (1<<BTN_1)); //sets which this module listens to

    if (btnIsDown)          // Is the button open (pushed)?
    {
        Set_LED_1(1);
        btnDownStart = btnEventTime;
    }
    else
    {
        Set_LED_1(0);
        btnTotalTime = btnEventTime - btnDownStart;

        if ((btnTotalTime/TIMER_TICKSPERSECONDS) >= 1)      // Was it a long press?
        {
            switch(clockMode)
            {
                case CLK_RUN :
                    setYY = theDayTime.yy;
                    DDDtoMMDD(theDayTime.ddd, &setMM, &setDD);
                    setHour = theDayTime.hour;
                    setMins = theDayTime.mins;
                    setSecs = theDayTime.secs;
                    clockMode = CLK_SET_YR;
                    break;
                case CLK_SET_YR  :
                    clockMode = CLK_SET_MO;
                    break;
                case CLK_SET_MO  :
                    clockMode = CLK_SET_DAY;
                    break;
                case CLK_SET_DAY  :
                    clockMode = CLK_SET_HR;
                    break;
                case CLK_SET_HR  :
                    clockMode = CLK_SET_MIN;
                    break;
                case CLK_SET_MIN  :
                    clockMode = CLK_SET_SEC;
                    break;
                case CLK_SET_SEC  :
                    theDayTime.yy = setYY;
                    MMDDtoDDD(setMM, setDD, &theDayTime.ddd);
                    theDayTime.hour = setHour;
                    theDayTime.mins = setMins;
                    theDayTime.secs = setSecs;

                    DateDiff(&theDayTime, &tgtDayTime, &difDayTime);
                    clockMode = CLK_RUN;
                    break;
                default :
                    clockMode = CLK_RUN;
            }
        }
        else
        {
            switch(clockMode)
            {
                case CLK_RUN :
                    clockMode = CLK_UNDEF;
                    break;
                case CLK_SET_YR  :
                    setYY++;
                    if (setYY > 99)
                        setYY = 0;
                    if ((setYY % 4) == 0)
                        monthDays[1] = 29;
                    else
                        monthDays[1] = 28;
                    break;
                case CLK_SET_MO  :
                    setMM++;
                    if (setMM > 11)
                        setMM = 0;
                    // When the month changes, cap the day to the max in the month
                    if (setDD > monthDays[setMM])
                        setDD = monthDays[setMM];
                    break;
                case CLK_SET_DAY  :
                    setDD++;
                    // When the day changes, cap the day to the max in the month
                    if (setDD > monthDays[setMM])
                        setDD = 1;
                    break;
                case CLK_SET_HR  :
                    setHour++;
                    if (setHour > 23)
                        setHour = 0;
                    break;
                case CLK_SET_MIN  :
                    setMins++;
                    if (setMins > 59)
                        setMins = 0;
                    break;
                case CLK_SET_SEC  :
                    setSecs++;
                    if (setSecs > 59)
                        setSecs = 0;
                    break;
                default :
                    break;
            }
        }
    }
}

/*
    Params:
        *pDayTime       The
*/
void CountDayTimeUp(DayTime *pDayTime)
{
    // For now we aren't going to worry about ddd year overflow
    // If so we would need to pass in the #days/year and check,
    // and if we did roll into a new year we would have to reset
    // February if a leap year.

    pDayTime->secs++;
    if (60 == pDayTime->secs)
    {
        pDayTime->secs = 0;
        pDayTime->mins++;
        if (60 == pDayTime->mins)
        {
            pDayTime->mins = 0;
            pDayTime->hour++;
            if (24 == pDayTime->hour)
            {
                pDayTime->hour = 0;
                pDayTime->ddd++;
            }
        }
    }

}

/*
    Params:
        *pDayTime       The
*/
void CountDayTimeDown(DayTime *pDayTime)
{
    // For now we aren't going to worry about ddd year overflow
    // If so we would need to pass in the #days/year and check,
    // and if we did roll into a new year we would have to reset
    // February if a leap year.

    pDayTime->secs--;
    if (0 > pDayTime->secs)
    {
        pDayTime->secs = 59;
        pDayTime->mins--;
        if (0 > pDayTime->mins)
        {
            pDayTime->mins = 59;
            pDayTime->hour--;
            if (0> pDayTime->hour)
            {
                pDayTime->hour = 23;
                pDayTime->ddd--;
            }
        }
    }

}

/*
    Params:
        *pCurDayTime    The
        *pTgtDayTime    The 
        *pDifDayTime    The 
*/
void DateDiff(DayTime *pCurDayTime, DayTime *pTgtDayTime, DayTime *pDifDayTime)
{
    int8_t  minsAdj = 0;
    int8_t  hourAdj = 0;
    int8_t  daysAdj = 0;

    pDifDayTime->secs = (pTgtDayTime->secs - pCurDayTime->secs);
    if (pDifDayTime->secs < 0)
    {
        pDifDayTime->secs += 60;
        minsAdj = 1;
    }

    pDifDayTime->mins = (pTgtDayTime->mins - pCurDayTime->mins - minsAdj);
    if (pDifDayTime->mins < 0)
    {
        pDifDayTime->mins += 60;
        hourAdj = 1;
    }

    pDifDayTime->hour = (pTgtDayTime->hour - pCurDayTime->hour - hourAdj);
    if (pDifDayTime->hour < 0)
    {
        pDifDayTime->hour += 24;
        daysAdj = 1;
    }

    pDifDayTime->ddd = (pTgtDayTime->ddd - pCurDayTime->ddd - daysAdj);
}

/*
    Params:
        dayOfYear       The one-based day of the year
        *mm             The zero-based corresponding month
        *dd             The corresponding day of the month
*/
void DDDtoMMDD(int16_t dayOfYear, int8_t *mm, int8_t *dd)
{
    int8_t  i = 0;
    int16_t ddd = dayOfYear;

    while ((ddd > monthDays[i]) && (i < 11))
    {
        ddd -= monthDays[i];
        i++;
    }
    *dd = ddd;
    *mm = i;
}

/*
    Params:
        mm              The zero-based corresponding month
        dd              The corresponding day of the month
        *dayOfYear      The one-based day of the year
*/
void MMDDtoDDD(int8_t mm, int8_t dd, int16_t *dayOfYear)
{
    int8_t  i = 0;
    int16_t ddd = dd;

    while ((i < mm) && (i <= 11))
    {
        ddd += monthDays[i];
        i++;
    }
    *dayOfYear = ddd;
}

void Set_LED(uint8_t *ledPort, uint8_t ledState)
{
    // REflect the button state in the LED
    if (ledState != 0) {
        _MMIO_BYTE(ledPort) |= (1<<LED_1); // turn on LED_1
    } else {
        _MMIO_BYTE(ledPort) &= ~(1<<LED_1); // turn off LED_1
    }
}

void Set_LED_1(uint8_t ledState)
{
    // REflect the button state in the LED
    if (ledState != 0) {
        LED_1_PORT |= (1<<LED_1); // turn on LED_1
    } else {
        LED_1_PORT &= ~(1<<LED_1); // turn off LED_1
    }
}

void Toggle_LED_2()
{
    LED_2_PORT ^= (1<<LED_2); // Toggle LED_1
}

void Toggle_LED_3()
{
    LED_3_PORT ^= (1<<LED_3); // Toggle LED_1
}

void ClearLCDLine()
{
    uint8_t i;
    for (i = 0; i < 20; i++)
        lcd_write_data(' ');
}

void DisplaySetData(FILE lcd_stream, const char *fmt, uint8_t value)
{
    lcd_line_two();
    ClearLCDLine();
    lcd_line_two();
    fprintf_P(&lcd_stream, fmt, value);
}

int main() {
    uint8_t lastQtrSecTimer;        // a small timer used to count quarter-seconds
     uint8_t btnPrev;               // The current button state
    uint8_t btnCurr;                // the previous button state

    uint8_t lastSetYY;              // Set Mode Copy of YY
    uint8_t lastSetMM;              // Set Mode Copy of MM, zero-based
    uint8_t lastSetDD;              // Set Mode Copy of DD, one-based
    uint8_t lastSetHour;            // Set Mode Copy of Hour
    uint8_t lastSetMins;            // Set Mode Copy of Mins
    uint8_t lastSetSecs;            // Set Mode Copy of Secs

    Timer0_Setup();
    Timer1_Setup();

    theDayTime.yy   = 15;           // theDayTime.hms will be 0
    theDayTime.ddd  = 305;
    theDayTime.hour = 17;
    theDayTime.mins = 23;
    theDayTime.secs = 0;

    tgtDayTime.yy   = 15;           // theDayTime.hms will be 0
    tgtDayTime.ddd  = 359;
    tgtDayTime.hour = 0;
    tgtDayTime.mins = 0;
    tgtDayTime.secs = 0;

    DateDiff(&theDayTime, &tgtDayTime, &difDayTime);

    // init lcd
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    // setup input for the Button
    BTN_1_DDR  &= ~(1<<BTN_1);      // set BTN_1 as input
    BTN_1_PORT |= (1<<BTN_1);       // turn on pullup resistor for BTN_1

    //Enable PIN Change Interrupt for BTN_1 - This enables interrupts on corresponding pins
    //Set PCMSKn for corresponding interrupt for BTN_1 - see p70 of datasheet
    PCICR  |= (1 << BTN_1_PCIE);
    PCMSK1 |= (1 << BTN_1_PCINT);

    // setup output for the LED_1
    LED_1_DDR  |=  (1<<LED_1);      // set PC4 as output
    LED_1_PORT &= ~(1<<LED_1);      // turn off PC4

    // setup output for the LED_2
    LED_2_DDR  |=  (1<<LED_2);      // set PC3 as output
    LED_2_PORT &= ~(1<<LED_2);      // turn off PC3

    // setup output for the LED_3
    LED_3_DDR  |=  (1<<LED_3);      // set PC2 as output
    LED_3_PORT &= ~(1<<LED_3);      // turn off PC2

    // Set states initially the same so no change is indicated
    btnCurr = btnPrev = !(BTN_1_PIN & (1<<BTN_1));
    Set_LED_1(btnCurr);

    // turn on interrupt handler
    sei();

    delay_ms(50);
    clockMode = CLK_RUN;
    prevClockMode = CLK_UNDEF;

    lastQtrSecTimer = qtrSecTimer;              // prime last_time

    while(1)
    {
        // Has the mode just changed? Change the display accordingly

        if ((clockMode == CLK_RUN) || (clockMode == CLK_UNDEF))
        {
            if (clockMode != prevClockMode)
            {
                lcd_clear_and_home();
                lcd_write_string(PSTR("Christmas Countdown!"));
            }
            if ((clockMode != prevClockMode) || (lastQtrSecTimer != qtrSecTimer))
            {
                lcd_line_two();

                fprintf_P(&lcd_stream, PSTR("  %3d days & %2d:%.2d")    ,difDayTime.ddd
                                                                        ,difDayTime.hour
                                                                        ,difDayTime.mins);

                lcd_line_four();
                fprintf_P(&lcd_stream, PSTR("      %2d:%.2d:%.2d"), theDayTime.hour
                                                                    ,theDayTime.mins
                                                                    ,theDayTime.secs);
                lastQtrSecTimer = qtrSecTimer;
            }
            clockMode = CLK_RUN;
            prevClockMode = clockMode;

            lastSetYY = lastSetMM = lastSetDD = lastSetHour = lastSetMins = lastSetSecs = 255;

        }
        else
        {
            if (clockMode == CLK_SET_YR)
            {
                if ((clockMode != prevClockMode) || (lastSetYY != setYY))
                {
                    DisplaySetData(lcd_stream, PSTR(" Year 20%.2d"), setYY);
                    lastSetYY = setYY;
                    prevClockMode = clockMode;
                }
            }
            else if (clockMode == CLK_SET_MO)
            {
                if ((clockMode != prevClockMode) || (lastSetMM != setMM))
                {
                    DisplaySetData(lcd_stream, PSTR("Month %2d  "),(setMM+1));
                    lastSetMM = setMM;
                    prevClockMode = clockMode;
                }
            }
            else if (clockMode == CLK_SET_DAY)
            {
                if ((clockMode != prevClockMode) || (lastSetDD != setDD))
                {
                    DisplaySetData(lcd_stream, PSTR("  Day %2d  "),setDD);
                    lastSetDD = setDD;
                    prevClockMode = clockMode;
                }
            }
            else if (clockMode == CLK_SET_HR)
            {
                if ((clockMode != prevClockMode) || (lastSetHour != setHour))
                {
                    DisplaySetData(lcd_stream, PSTR(" Hour %2d  "),setHour);
                    lastSetHour = setHour;
                    prevClockMode = clockMode;
                }
            }
            else if (clockMode == CLK_SET_MIN)
            {
                if ((clockMode != prevClockMode) || (lastSetMins != setMins))
                {
                    DisplaySetData(lcd_stream, PSTR(" Mins %2d  "),setMins);
                    lastSetMins = setMins;
                    prevClockMode = clockMode;
                }
            }
            else if (clockMode == CLK_SET_SEC)
            {
                if ((clockMode != prevClockMode) || (lastSetSecs != setSecs))
                {
                    DisplaySetData(lcd_stream, PSTR(" Secs %2d  "),setSecs);
                    lastSetSecs = setSecs;
                    prevClockMode = clockMode;
                }
            }           
        }

    }

  return 0;
}

image image image image

December 04, 2015
by sask55
sask55's Avatar

That is a very good looking display. Nice neat work on the code and the layout as well.

December 04, 2015
by JKITSON
JKITSON's Avatar

Very neat. The code is easy to follow, thanks to notes. Good work & idea

Jim

December 04, 2015
by edSky
edSky's Avatar

Thanks. I can explain what I did if anyone has any questions. The 74LS14 is used to debounce the switch. I tried the various software debounce code folks were nice enough to share but I had such a hard time with reliability. It would work 90% of the time but 1 outta ten times when something jumped ahead it drove me nuts. Not knowing a lot about EE I had a hard time with this but I wrote another project to time bounces.

The inverter is also used as an ancillary tool to alternate the Christmas lights. It's wasn't really important (but yeah, it is) to have lights, but what the heck. It's Christmas and even us old Jews have the spirit! So I had a second timer firing interrupts every quarter second and using it to control two bulbs - one every 1/4 second toggling on/off and and one every second. To be more festive I took advantage of the extra inverters and had one alternate bulb.

The logic in the main() loop is primarily display logic. What I noticed early on in the earliest (years ago) examples was that values were displayed over and over even when they didn't change in successive passes. I try to check what happened last pass, and if nothing changed then no need to steal cycles and call an expensive routine to display something that is already being displayed (or any other action besides "displayed").

The problems I encountered, however, was that my logic didn't always work. Look, I've been coding since the 70's on and off at machine level so I have an inkling of an idea how to code. But this is the tiniest machine ever! Smaller than an 8080, 6502, etc! I'm not sure it was the compiler or the resources, but things didn't always work. It's a miracle this finally came together!

Anyway, I tried to put as much logic and as little at the same time in the interrupt handler for the button. I think I was able to do that. It's all pretty much straightforward state-logic reacting to the button.

Lastly, this is my first project I've contributed and I feel funny because while I bought my kit early I also changed careers from a life-long developer to a retail store owner. I had a lousy work experience with a big-name defense contractor in the government sector and turned my back on the avocation I loved for 3 decades. So for 5 years my Nerdkit sat collecting dust and I am so thankful this site is still operational. You guys are awesome.

December 04, 2015
by edSky
edSky's Avatar

Thanks. I can explain what I did if anyone has any questions. The 74LS14 is used to debounce the switch. I tried the various software debounce code folks were nice enough to share but I had such a hard time with reliability. It would work 90% of the time but 1 outta ten times when something jumped ahead it drove me nuts. Not knowing a lot about EE I had a hard time with this but I wrote another project to time bounces.

The inverter is also used as an ancillary tool to alternate the Christmas lights. It's wasn't really important (but yeah, it is) to have lights, but what the heck. It's Christmas and even us old Jews have the spirit! So I had a second timer firing interrupts every quarter second and using it to control two bulbs - one every 1/4 second toggling on/off and and one every second. To be more festive I took advantage of the extra inverters and had one alternate bulb.

The logic in the main() loop is primarily display logic. What I noticed early on in the earliest (years ago) examples was that values were displayed over and over even when they didn't change in successive passes. I try to check what happened last pass, and if nothing changed then no need to steal cycles and call an expensive routine to display something that is already being displayed (or any other action besides "displayed").

The problems I encountered, however, was that my logic didn't always work. Look, I've been coding since the 70's on and off at machine level so I have an inkling of an idea how to code. But this is the tiniest machine ever! Smaller than an 8080, 6502, etc! I'm not sure it was the compiler or the resources, but things didn't always work. It's a miracle this finally came together!

Anyway, I tried to put as much logic and as little at the same time in the interrupt handler for the button. I think I was able to do that. It's all pretty much straightforward state-logic reacting to the button.

Lastly, this is my first project I've contributed and I feel funny because while I bought my kit early I also changed careers from a life-long developer to a retail store owner. I had a lousy work experience with a big-name defense contractor in the government sector and turned my back on the avocation I loved for 3 decades. So for 5 years my Nerdkit sat collecting dust and I am so thankful this site is still operational. You guys are awesome.

December 05, 2015
by Ralphxyz
Ralphxyz's Avatar

Nice to see new projects, thanks for posting!! Nice project!!

December 06, 2015
by Rick_S
Rick_S's Avatar

Had you given any thought to using a real time clock module? Just curious on that note. Great looking display and project. Just for curiosity's sake, where are you/your store located?

Rick

December 07, 2015
by edSky
edSky's Avatar

Rick,

I thought about the real-time clock and have looked at some of the projects that use them. I wanted to get this done by Thanksgiving and being a programmer in a previous life everything looks like a software solution to me. Throw in a little fear of the unknown (finding a good two-wire library or whatever other code I would need) and I went the brute force route.

I did look at SparkFun and other sites to see how easily I could source a real-time clock and integrate it painlessly. I made the decision that since it wasn't mission critical I would settle for a periodic clock adjustment. I managed to get this all working on a 168 so I'm happy with that.

Debugging is also another issue. Using printf and the original Serial-USB dongle works but I would love to find an easy-to-read schematic to use the AVR-USB approach. If I can crack that nut then my next version will have that and a real-time clock and be on a 328 for good measure.

Ed

December 08, 2015
by Rick_S
Rick_S's Avatar

I think before I'd try to mess with the AVR-USB (V-USB) I'd just go with a microcontroller with built in USB support like the ATMEGA32U4 found on the Teensy. That way the USB is handled by an on-board peripheral instead of having to emulate it in software. That would leave a lot more overhead available for your program instead of all the dedicated time for the USB. The teensy has pretty good C libraries available and can even be programmed with the Arduino environment if desired.

Also, you didn't mention where your store was. ??

Rick

December 08, 2015
by Ralphxyz
Ralphxyz's Avatar

Have you ever tried Atmel Studio? It has a debugger.

I have only played around with Atmel Studio it has been on my list of things to learn for a long time now.

December 08, 2015
by edSky
edSky's Avatar

Rick,

The store. It is in Little Rock, AR.

Getting back to the Teensy, are the C libraries suitable to drop-in to our existing Nerdkits code/libs? I need to look at it some more and see if it comes in a form factor or dev board that exposes all the pins.

What development environment do you use for it? I'd hate to go the dumbed-down Arduino C++ route if I didn't have to. I'd prefer C on these small machines.

Ed

December 09, 2015
by Rick_S
Rick_S's Avatar

Most of the code you compile for the nerdkit can be easily ported to the ATMEGA32u4 on the the teensy using AVR-GCC just like with the nerdkit. The only changes you may have to make would be the ports used and timing. The Teensy is clocked at 16MHz so the delay library the NK uses would not work without modification. Also, since the LCD library uses the delay library for its timing, it would only work if the NK delay library was modified to suit the AVR-GCC standard delays.

Paul Stoffregen has C libraries for the USB portion of the chip so you don't really have to delve too deeply into that, just link his library and it works. It's really a nice little platform if you want USB serial built in. I built an Industrial inductive joystick to PC mouse control with them for the place I work. We have them running on 4 CNC Milling machines and a CNC Vertical Turret Lathe. You can see the photo's of the prototype (Before I had commercial PCB's made for them) and the writeup I sent Paul on his website HERE. It was all coded in straight C no Arduino at all.

I'd pop in and check out your store if I lived a bit closer, but I live about 80 Miles SE of Chicago, so Little Rock would be a bit of a drive :D.

December 10, 2015
by lnino
lnino's Avatar

Really nice Project, edSky. Thanks a lot for sharing.

Maybe I have the time to make it.

I definately have to share some of my projects when I finsihed them. I started so many projects, but most of them are unfinished.

But the project which is almost finished is my "Text Adventure".

December 14, 2015
by Ralphxyz
Ralphxyz's Avatar

edSky, since you are in Little Rock would you send me your email address.

I have some unfinished business in Little Rock but I live on Long Island, NY.

I'll fill in the details with email.

Thanks,

Ralph

December 29, 2015
by edSky
edSky's Avatar

Ralph,

Sorry I didn't reply sooner, this is my busy time and now it's getting back to normal. My email address is efigarsky at gmail.com

I hope you and everyone on board had a Merry Christmas and wish you all a Happy New Year!

Ed

Post a Reply

Please log in to post a reply.

Did you know that our kits have shipped to hobbyists in 40 countries? Learn more...