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.

Microcontroller Programming » A Couple Clock Program Questions:

January 05, 2012
by Zen
Zen's Avatar

Hi Everyone, I have been experimenting with the clock program. I have modified the example code with the intent of displaying elapsed minutes below the seconds on the LCD and to turn on an LED once 2, 4 or 6 seconds have elapsed. I have two problems with my code that I'm hoping to get some help with:

1) I figured out how to display minutes and partially do the second=>minute conversion. How do I shift the decimal point over in my calculation? right now when the code is executed, 1 minute is displayed as 10.0. Also, in future versions, it would be cool to display the minutes elapsed in 1 min. increments...

2) I am trying to set a condition using the IF command. example:

if(( the_time == 2.00, 4.00, 6.00)

I think I am not declaring the correct variable or time conversion. Any pointers would be appreciated. My code is pasted below:

// PIN DEFINITIONS:
//
// PC4 -- Red LED anode

void realtimeclock_setup() {
  // setup 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);
}

// 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;

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

int main() {
  realtimeclock_setup();

  // LED as output
  DDRC |= (1<<PC4);

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

  // init serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // turn on interrupt handler
  sei();

  while(1) {
    lcd_home();
    fprintf_P(&lcd_stream, PSTR("%16.2f sec"), (double) the_time / 100.0);
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("%16.2f min"), (double) the_time / 600.0);

    }

    if(( the_time == 2.00, 4.00, 6.00) {
    // turn on LED if the_time = 2,4 or 6 seconds
    PORTC |= (1<<PC4);

    //delay for 1000 milliseconds to let the light stay on
    delay_ms(1000);

    // turn off LED
    PORTC &= ~(1<<PC4);

  }

  return 0;
}
January 05, 2012
by pcbolt
pcbolt's Avatar

Hi Zen -

I think you can change this statement:

fprintf_P(&lcd_stream, PSTR("%16.2f min"), (double) the_time / 600.0);

To:

fprintf_P(&lcd_stream, PSTR("%16d min"), the_time / 6000);

The %16d will print a 16 character output (and fill unused spaces with blanks) and by specifying 'd' it will print an integer. Using 6000 as a divisor should give correct minutes.

For the second question, the code:

if(( the_time == 2.00, 4.00, 6.00)

I'm pretty sure this will work:

if(( the_time == 200) || ( the_time == 400) || ( the_time == 600)) {

This reads as "If the time = 200 OR if the time = 400 OR if the time = 600 then..."

The use of == is correct in your code but remember the_time is counted in 100ths of a second. This is a really good project to expand on. It helps explain timer interrupts and how you can build off of them. Good show.

January 06, 2012
by Zen
Zen's Avatar

Pcbolt,

Thank you for the helpful coding tweaks and enthusiasm. The syntax change to the 'fprint_P' did the trick. I decided to throw in an extra line of code to display the elapsed hours! How would I learn more about the different libraries we are referencing in our Nerdkits? Is there a place I can go to look at examples of syntax? Can someone explain what the components of "%16.2f min" actually means? Obviously, I understand that 'min' is our text tag for the units. I think that '%' means: 'display the integer that follows' and I think '16.2' tells the display how many characters over from home to print.

As for the tweek to my 'If' command, I'm still not getting the LED to illuminate. So far, I confirmed that I have all the required libraries, I have the pin's initial value set and the if command seems to be correct. Any suggestions? The code is as follows:

  int main() {
  // LED as output
  DDRC |= (1<<PC4);

  realtimeclock_setup();

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

  // init serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // turn on interrupt handler
  sei();

  while(1) {
    lcd_home();
    fprintf_P(&lcd_stream, PSTR("%16.2f sec"), (double) the_time / 100.0);
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("%16d min"), the_time / 6000);
    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR("%16d hr"), the_time / 3600000);

    }

    // turn on LED if the_time = 2,4 or 6 seconds
if(( the_time == 200) || ( the_time == 400) || ( the_time == 600)){

    PORTC |= (1<<PC4);

    //delay for 1000 milliseconds to let the light stay on
    delay_ms(1000);

    // turn off LED
    PORTC &= ~(1<<PC4);

  }
January 06, 2012
by pcbolt
pcbolt's Avatar

Zen -

There's a really good video tutorial on this site about the printf() functions. Most of the C functions used are pretty standard, so any "C tutorial" website will get you going.

As far as the code above, I think it may be a timing issue. The LCD functions can be a little slow sometimes, so while your using them, the interrupt routine may have incremented "the_time" from 200 to 201 and the tests later on will fail. You could try this:

if ((the_time > 395) && (the_time < 405)) {  //blink the LED

I realize this will work only near the 4 second mark, but if it works you can add more code. To keep the code readable, you can add another variable, say "blink_led". Declare it after main() but before while(1) using:

uint8_t blink_led = 0;

Then you can add more tests:

if ((the_time > 195) && (the_time < 205)) blink_led = 1;
if ((the_time > 395) && (the_time < 405)) blink_led = 1;
if ((the_time > 595) && (the_time < 605)) blink_led = 1;

Then just add a final test to enter your blink code:

if (led_blink == 1) { //turn on LED etc....
January 08, 2012
by Zen
Zen's Avatar

Well, I tried the tips above. Great suggestion about the LCD functions being slow and I liked how you created the blink_led variable to keep the code streamlined. Still, no luck on getting the LED to blink. I tried playing with the structure of the code a bit and even played with the time ranges in the 'If' commands. what am I missing?

int main() {

  uint8_t blink_led = 0;

  // LED as output
  DDRC |= (1<<PC3);
  DDRC |= (1<<PC4);

  realtimeclock_setup();

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

  // init serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // turn on interrupt handler
  sei();

  while(1) {
    lcd_home();
    fprintf_P(&lcd_stream, PSTR("%16.2f sec"), (double) the_time / 100.0);
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("%16d min"), the_time / 6000);
    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR("%16d hr"), the_time / 360000);
    }

if ((the_time > 195) && (the_time < 205)) blink_led = 1;
if ((the_time > 395) && (the_time < 405)) blink_led = 1;
if ((the_time > 595) && (the_time < 605)) blink_led = 1;

 if (blink_led == 1) {
    // turn on LED
    PORTC |= (1<<PC4);

    //delay for 1000 milliseconds to let the light stay on
    delay_ms(1000);

    // turn off LED
    PORTC &= ~(1<<PC4);

    }

  return 0;
January 08, 2012
by Rick_S
Rick_S's Avatar

Look at your while statement for the LCD output.

while(1) {    
 lcd_home();
 fprintf_P(&lcd_stream, PSTR("%16.2f sec"), (double) the_time / 100.0);
 lcd_line_two();
 fprintf_P(&lcd_stream, PSTR("%16d min"), the_time / 6000);
 lcd_line_three();
 fprintf_P(&lcd_stream, PSTR("%16d hr"), the_time / 360000);
}
January 08, 2012
by Rick_S
Rick_S's Avatar

Whoops, hit the post button my mistake....

Anyway to continue. Once your code gets there it never leaves. while(1) is always a true statement.

Try putting your blink inside the while statement like this.

  while(1) {

    // THIS IS ADDED TO KEEP THE LED FROM BLINKING CONTINUOUSLY ONCE SET
    blink_led=0;

    lcd_home();
    fprintf_P(&lcd_stream, PSTR("%16.2f sec"), (double) the_time / 100.0);
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("%16d min"), the_time / 6000);
    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR("%16d hr"), the_time / 360000);

    if ((the_time > 195) && (the_time < 205)) blink_led = 1;
    if ((the_time > 395) && (the_time < 405)) blink_led = 1;
    if ((the_time > 595) && (the_time < 605)) blink_led = 1;

    if (blink_led == 1) {
     // turn on LED
     PORTC |= (1<<PC4);
     //delay for 1000 milliseconds to let the light stay on
     delay_ms(1000);
     // turn off LED
     PORTC &= ~(1<<PC4);
    }
  }

Rick

Post a Reply

Please log in to post a reply.

Did you know that interrupts can be used to trigger pieces of code when events happen? Learn more...