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 » A BCD counter, some questions

January 06, 2011
by SpaceGhost
SpaceGhost's Avatar

Okay, this is what I would like to put together -

I need a count up / count down program that uses two inputs (one to count up, the other to count down), that counts 0 - 15, and displays the count number on the LCD screen as its decimal value.

Four output pins would at the same time provide negative voltages to four LEDs, with the four LED's having common anode connections to the + rail, to provide a BCD output display.

I have some experience in programmable logic controller (PLC) programming. With a PLC it is quite easy to code and implement such a program by using a count up counter paired with a count down counter on each input (UP button and DOWN button) and a "convert to BCD" function.

Is there a similar "convert to BCD" command in C language that can be used with the ATMEGA 168 to turn on the four LEDs by sinking a current to four outputs of the MCU in relationship to the decimal value that would be displayed by the LCD?

In essence, I want to make a 0-15 "BCD switch" that displays the BCD output value in decimal on the LCD, while providing a "negative" output(s) to light the LEDs... No LEDs lit for 0 (default position on power up) LED 1 lit for a count of one, LED 2 lit for a count of 2, LEDs 1 and 2 lit for a count of 3, etc. Of course, I am looking for the simplest programming method for doing this.

Any and all suggestions welcomed and appreciated. Thanks!

Dave

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

Okay, I've made an attempt to code the 0 - 15 up/down counter portion of the program. Naturally, it doesn't work! This is what I have put together:

#define F_CPU 14745600

#include <stdio.h>

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

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

// PIN DEFINITIONS:
// PC3 -- PUSHBUTTON PB3
// PC4 -- PUSHBUTTON PB3

 int main() {
    // fire up the LCD
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

// Set the 2 pins to input mode - Two Pushbuttons
  DDRC &= ~(1<<PC3); // set PC3 as input
  DDRC &= ~(1<<PC4); // set PC4 as input

// turn on the internal resistors for the pins
  PORTC |= (1<<PC3); // turn on internal pull up resistor for PA3
  PORTC |= (1<<PC4); // turn on internal pull up resistor for PA4

// declare the variables to represent each pushbutton input
  uint8_t count_up;
  uint8_t count_down;

    uint16_t i;
    for(i = 0; i <= 15; i++){
        lcd_home();
        fprintf_P(&lcd_stream, PSTR("%i"), i);
        delay_ms(2000);

while(1) {

count_up = (PINC & (1<<PC3)) >> PC3;

    if (count_up == 0)
    {
        for(i = 0; i <= 15; i++); //debounce count up pushbutton
        if (count_up == 0)
        {
            count_up = count_up + 1; // if count up pressed, increase count by one
            if (count_up >= 15)  //set 15 as highest allowed count
            count_up = 15; 
        }   
    }   
    count_down = (PINC & (1<<PC4)) >> PC4;
    if (count_down == 0)
    {
        for(i = 0; i <= 15; i++); //debounce count down pushbutton
        if (count_down == 0)
        {   
            count_down = count_down - 1; // if count down pressed, lower count by one
            if (count_down <= 0) //set 0 as lowest allowed count
            count_down = 0;
        }
    }

    }

    return 0;

    }

When I attempt to upload the above code to the MCU, I get the error "expected declaration or statement at end of input" (for line 71).

I am wondering - do I need to have a while loop for this sort of program? I had looked at another counting program here, and used pbfy0's simple example as a basis to get started with. I notice there is no while loop in his example.

Any suggestions greatly appreciated.

Dave

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

Okay, I removed line #41, took out the while loop. My counter counts, 0-15, but does so with no input. Can anyone suggest what mistake(s) I have made in trying to incorporate the push buttons into the project?

January 08, 2011
by Rick_S
Rick_S's Avatar

You don't have a closing bracket on your if statement on line 36. That is probably what is causing the error.

As for turning on the LED's, just take your variable with the count (i in your example) and do something like this (Untested code)

// Set 4 output pins
DDRB |= (1<<PB1);
DDRB |= (1<<PB2);
DDRB |= (1<<PB3);
DDRB |= (1<<PB4);

//Check each bit in i and sink corrosponding pin if high

// First turn off all LED's
PORTB |= (1<<PB1);
PORTB |= (1<<PB2);
PORTB |= (1<<PB3);
PORTB |= (1<<PB4);

// Now sink the LED's for each corrosponding bit in i
if( i && 1 )
  PORTB &= ~(1<<PB1);
if( i && (1<<1))
  PORTB &= ~(1<<PB2);
if( i && (1<<2))
  PORTB &= ~(1<<PB3);
if( i && (1<<3))
  PORTB &= ~(1<<PB4);

I think that would work... but it's early and may need some fixing... but it should at the lease give you an idea for one option.

The other question I have, is why did you define i as a 16 bit unsigned integer? That would hold a decimal value to 65,535 way larger than the 15 you are counting to.

Rick

January 08, 2011
by Rick_S
Rick_S's Avatar

You posted while I was thinking... :D My post refers to your original posted code...

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

Hey Rick, thanks for posting. I put a closing statement after the "for" statement that began on line #36 (is this actually another form of an "if" statement, or should my "for" be an "if"??), put the "while" statement back in, and the code works the same way it did when I merely removed the "while" statement - it counts 0 - 15, but on its own. The push buttons seem to have no effect to the count. Oh, and I changed the definition of the unsigned integer that I had on line #35 to 8 bit.

So, here is what I'm working with now -

#define F_CPU 14745600

#include <stdio.h>

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

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

// PIN DEFINITIONS:
// PC3 -- PUSHBUTTON PB3
// PC4 -- PUSHBUTTON PB3

 int main() {
    // fire up the LCD
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

// Set the 2 pins to input mode - Two Pushbuttons
  DDRC &= ~(1<<PC3); // set PC3 as input
  DDRC &= ~(1<<PC4); // set PC4 as input

// turn on the internal resistors for the pins
  PORTC |= (1<<PC3); // turn on internal pull up resistor for PA3
  PORTC |= (1<<PC4); // turn on internal pull up resistor for PA4

// declare the variables to represent each pushbutton input
  uint8_t count_up;
  uint8_t count_down;

    uint8_t i;
    for(i = 0; i <= 15; i++){
        lcd_home();
        fprintf_P(&lcd_stream, PSTR("%i"), i);
        delay_ms(2000);
        }

while(1) {

count_up = (PINC & (1<<PC3)) >> PC3;

    if (count_up == 0)
    {
        for(i = 0; i <= 15; i++); //debounce count up pushbutton
        if (count_up == 0)
        {
            count_up = count_up + 1; // if count up pressed, increase count by one
            if (count_up >= 15)  //set 15 as highest allowed count
            count_up = 15;
        }  
    }  
    count_down = (PINC & (1<<PC4)) >> PC4;
    if (count_down == 0)
    {
        for(i = 0; i <= 15; i++); //debounce count down pushbutton
        if (count_down == 0)
        {  
            count_down = count_down - 1; // if count down pressed, lower count by one
            if (count_down <= 0) //set 0 as lowest allowed count
            count_down = 0;
        }
    }

    }

    return 0;

    }

Hey, thanks for the tip on converting the counter's outputs to BCD!! I'll need to study that a bit though when it comes to adding that part of the program. First things first though, I gotta try to figure out the basic counter...

Dave

January 08, 2011
by Rick_S
Rick_S's Avatar

Had a chance to wake up a bit... I goofed on my operators for setting the LED's.

This:

// Now sink the LED's for each corrosponding bit in i
if( i && 1 )
  PORTB &= ~(1<<PB1);
if( i && (1<<1))
  PORTB &= ~(1<<PB2);
if( i && (1<<2))
  PORTB &= ~(1<<PB3);
if( i && (1<<3))
  PORTB &= ~(1<<PB4);

Should be changed to this:

// Now sink the LED's for each corrosponding bit in i
if( i & 1 )
  PORTB &= ~(1<<PB1);
if( i & (1<<1))
  PORTB &= ~(1<<PB2);
if( i & (1<<2))
  PORTB &= ~(1<<PB3);
if( i & (1<<3))
  PORTB &= ~(1<<PB4);

That will make it work correctly.

Your de-bounce doesn't really do anything because, you are checking against a set variable, not the button. You could eliminate that. Then, change the variable you are incrmenting/decrimenting to i instead of the two (count_up/count_down).

This code will work... (Tested)

#define F_CPU 14745600

#include <stdio.h>

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

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

// PIN DEFINITIONS:
// PC3 -- PUSHBUTTON PB3
// PC4 -- PUSHBUTTON PB3

 int main() {
    // fire up the LCD
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    // Set 4 output pins
DDRB |= (1<<PB1);
DDRB |= (1<<PB2);
DDRB |= (1<<PB3);
DDRB |= (1<<PB4);

// Set the 2 pins to input mode - Two Pushbuttons
  DDRC &= ~(1<<PC3); // set PC3 as input
  DDRC &= ~(1<<PC4); // set PC4 as input

// turn on the internal resistors for the pins
  PORTC |= (1<<PC3); // turn on internal pull up resistor for PA3
  PORTC |= (1<<PC4); // turn on internal pull up resistor for PA4

// declare the variables to represent each pushbutton input
  uint8_t count_up;
  uint8_t count_down;
//  uint8_t temp;

    uint16_t i;
    for(i = 0; i <= 15; i++){
            PORTB |= (1<<PB1);
            PORTB |= (1<<PB2);
            PORTB |= (1<<PB3);
            PORTB |= (1<<PB4);

            // Now sink the LED's for each corrosponding bit in i
            if( i & 1 )
                PORTB &= ~(1<<PB1);
            if( i & (1<<1))
                PORTB &= ~(1<<PB2);
            if( i & (1<<2))
                PORTB &= ~(1<<PB3);
            if( i & (1<<3))
                PORTB &= ~(1<<PB4);
        lcd_home();
        fprintf_P(&lcd_stream, PSTR("%i"), i);
        delay_ms(100);
        }

while(1) {

count_up = (PINC & (1<<PC3)) >> PC3;

    if (count_up == 0)
    {
        //for(temp = 0; temp <= 15; temp++); //debounce count up pushbutton
        //if (count_up == 0)
        //{
            i = i + 1; // if count up pressed, increase count by one
            if (i >= 15)  //set 15 as highest allowed count
            i = 15; 
            //Check each bit in i and sink corrosponding pin if high
            delay_ms(100); // a bit of a delay so we can see what is happening

            // First turn off all LED's
            PORTB |= (1<<PB1);
            PORTB |= (1<<PB2);
            PORTB |= (1<<PB3);
            PORTB |= (1<<PB4);

            // Now sink the LED's for each corrosponding bit in i
            if( i & 1 )
                PORTB &= ~(1<<PB1);
            if( i & (1<<1))
                PORTB &= ~(1<<PB2);
            if( i & (1<<2))
                PORTB &= ~(1<<PB3);
            if( i & (1<<3))
                PORTB &= ~(1<<PB4);
        lcd_home();
        fprintf_P(&lcd_stream, PSTR("%i"), i);

        //}   
    }   
    count_down = (PINC & (1<<PC4)) >> PC4;
    if (count_down == 0)
    {
      //  for(temp = 0; temp <= 15; temp++); //debounce count down pushbutton
      //  if (count_down == 0)
      //  {   
            if(i>0)//set 0 as lowest allowed count
            i = i - 1; // if count down pressed, lower count by one

            // unsigned ints do not go below zero so the code below would not have worked.
            //if (count_down <= 0) //set 0 as lowest allowed count
            //count_down = 0;
            //Check each bit in i and sink corrosponding pin if high
            delay_ms(100);// a bit of a delay so we can see what is happening

            // First turn off all LED's
            PORTB |= (1<<PB1);
            PORTB |= (1<<PB2);
            PORTB |= (1<<PB3);
            PORTB |= (1<<PB4);

            // Now sink the LED's for each corrosponding bit in i
            if( i & 1 )
                PORTB &= ~(1<<PB1);
            if( i & (1<<1))
                PORTB &= ~(1<<PB2);
            if( i & (1<<2))
                PORTB &= ~(1<<PB3);
            if( i & (1<<3))
                PORTB &= ~(1<<PB4);
          lcd_home();
        fprintf_P(&lcd_stream, PSTR("%i"), i);
      //}
    }

    }

    return 0;

    }
January 08, 2011
by Rick_S
Rick_S's Avatar

Actually, I have to apologise, the output technically isn't BCD it's really just the binary equivalent. I believe it is correct for zero thru fifteen however for numbers greater than 15, you would have to do some other math to create the nibbles.

January 08, 2011
by Rick_S
Rick_S's Avatar

Whoops, after reading a bit more, I found that I'm incorrect for BCD. It stores a max of zero thru nine in a nibble so an accurate-- from what I read would mean you need 2 nibbles to represent anything greater than 9 so BCD for 15 would be 0001 0101 and would require a 5th LED.

This shouldn't be too hard to accomplish.. you do want a BCD switch not a Binary one right??

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

Wow Rick, I sure do appreciate the input!!

I'll try out the code sometime today - right now my wife's wanting me to do stuff AND unfortunately work wants me in at noon today for inventory (yuck!).

If I understand BCD correctly, it is a base 16 number system represented by four binary (1 or 0) bits. 0 - 9 is represented by decimal expression, and 10 - 15 is represented usually by the letters A - F.

What I actually need is a unique "code" for a 4 bit output - 16 unique possibilities (on/off states) are possible with 4 outputs, of course.

I plan to use this concept to set four of the eight address pins on an encoder chip (a HT-12E), to provide 16 unique code combinations for this chip. The chip also has 4 input "control" bits...

I would be happy to explain more about this project when I have some more time. I am sure I will be posting more to this thread soon.

In the meantime, Thank you Rick for your excellent help and interest in this project! I am learning stuff here, this is really great.

Dave

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

Nope, BCD representation for the number 15 (F) would be 1111.

The least significant bit represents one. The next bit represents two. The third bit represents four, and the most significant bit represents eight.

So, with all four bits "high", ("on" equaling a 1), you have 1+2+4+8, which equals 15.

0000 = 0, of course.

0001 = 1 (0+0+0+1)

0010 = 2 (0+0+2+0)

0011 = 3 (0+0+2+1)

0100 = 4 (0+4+0+0)

0101 = 5 (0+4+0+1)

and so on... With the 4 bits, there are 16 unique combinations. 0-15, or 0-F as it's commonly referred as.

So BCD is a good way of keeping track of these possibilities of the 16 possible combinations of four control bits. That's the gist of why I need this program, and the decimal number displayed on the LCD will let me know what address, or "mode" my controller is in.

Wife's been hollering at me, I gotta get going before I get divorced, fired, or both!!

Dave

January 08, 2011
by Rick_S
Rick_S's Avatar

I believe that Binary is base 2 (0-1), Octal is base 8(0-7), Decimal is Base 10 (0-9), Hexidecimal is base 16 (0-F), and BCD or Binary Coded Decimal is the binary representation of each Decimal digit in binary form. So binary 0000 thru 1001 ( Binary equivalent of Decimal 0 thru 9)are valid BCD digits and each nibble (4 bit set) would represent 1 decimal digit. So the decimal number 539 would be 0101 0011 1001 in BCD. Unless I'm totally off base ... Which wouldn't be the 1st time :D

I think what you are looking for is simply the binary representation of the number which is good because that's what the program does ;)

January 08, 2011
by mongo
mongo's Avatar

BCD is Binary Coded Decimal. It counts from 0 to 9. (1001) afterwhich, it goes back to zero and starts again. Hexadecimal is the full 0000 thru 1111 or 0-F that we see in data. Octal uses only three bits and really isn't very useful unless you are doing something that only needs 8 of something. I have a BCD counter circuit that takes the raw binary to several digits at 16 bits input which was a neat little project. It tops out at 65K but I never needed to count any higher than that. I found it was a lot easier to do a BCD ripple counter that decoding in logic.

January 08, 2011
by Rick_S
Rick_S's Avatar

Mongo, you just confirmed what my on the fly research was telling me. But from the looks of things, what I gave Dave was what he was truly looking for anyway, the binary representation of the numbers 0 thru 15 on 4 LED's.

Yeah after I played with the code and posted it I had a little nagging memory of BCD being different and after doing some research, found out it was.

January 08, 2011
by mongo
mongo's Avatar

Right... You didn't have anything incorrect per say, I just thought I would fill in the details a little. I see a lot of hex and BCD getting mixed up. I have a few chips that decode both. I thing one is the CD4511, which is BCD and the CD4311 which is the hex, (I might have the two mixed up however). The BCD decoder gives different output to different segments. They both drive 7 segment displays. 0 thru 9 are the same. I can't find data sheets on the 4311 any more but they are pin for pin interchangeable if you aren't worried about the different output from 1011 to 1111.

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

I stand corrected. You guys are right, it is the BINARY equivalent of 0-15 that I want the LEDs to display... OOPS, my bad!

Rick, you are correct too that the code that you provided was exactly what I was looking for. I need to play around with it some though - 1 - 9 is displayed on the LCD as 10 - 90... I kinda would like to either loose the 0's or swap them around.

I'm going to play around with it too to see if I can get the count to "wrap around" - go to 0 with an up count above 15, and go to 15 with a down count below 0. This should be possible, right?

One odd thing with the set up is that it counts rapidly to 15 when the circuit powers up. Makes a neat light display with the LEDs flickering. I don't think this is from switch bounce - it's pretty consistent on power up - or is it? If it's not switch bounce this might make the counter more difficult to wrap, I think...

All in all it does what I want it to do - especially the binary counting part. That's what impresses me the most, and I figured would be the hardest part to accomplish.

Rick, once again I thank you for your excellent help!

Dave.

January 08, 2011
by Rick_S
Rick_S's Avatar

At the beginning of your original program you had the count up there with a 2 second delay between counts. I left it there, just cut the delay down to 100ms. So it counts up in a couple of seconds. Then it goes to the while loop that allows for the up/down with the button presses...

Wrap around should be easy to do with some simple playing around with the if statements that prevent the move above 15 or below 0.

As for the help, I don't know how excellent it was, but I was glad to do it.

Rick

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

What about the LCD displaying digits 1 thru 9, as 10 thru 90? Is there a way to correct that?

January 08, 2011
by Rick_S
Rick_S's Avatar

Just put a space or two after the %d and the closing quotation mark in the line that prints to the LCD. This will overwrite the errant zero that is left over from the number 10 with a space.

January 08, 2011
by Rick_S
Rick_S's Avatar

I mean the %i not %d...

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

Got it! Thank you much.

I think counters are great fun. Like mongo, I've played around with the 4511 which is often described as a "BCD to seven-segment decoder" (just google 4511), and the 4510, an actual decade counter that I've seen described as a "BCD counter". This site here, for example. No wonder people (like me!) get hex and BCD confused.

Anyway, I have a one digit up/down counter based on the 4510 and 4511 on one of my larger breadboards done up on my desk. With a 4066 quad bilateral switch ic wired to the 4510's A,B,C & D outputs, does almost the same thing as what I've been trying to do with the MCU... Except, I'm no longer limited to a 0-9 (10) address scheme - six extra addresses for my project now. COOL! (kinda sad what excites me, huh?) That will give me 24 more control bits now...

I'd love to see more counter projects offered up here on the forum. A simple presettable counter program would be nice to study and tinker with if it were to be included with the example downloads, especially for us beginners (IMO). Simple counters can be the basis of many much more complex projects.

I think I'm starting to get some of this code stuff, but I still have a long way to go. I have to say, that mixed with the occasional aggravation I am having a lot of fun (sick, huh?)!

Thanks a lot guys, for helping me out

Dave

January 08, 2011
by mongo
mongo's Avatar

OOPS! I am glad you caught that difference... Yeah, I meant decoder but did say counter. I still can't find anything on the 4311.

January 08, 2011
by SpaceGhost
SpaceGhost's Avatar

Mongo, there is a 4516 chip described as an "up/down 4-bit (0-15) counter with preset." I'm pretty sure that I even have a couple. I remember playing with a chip, probably was that one, that displayed A - F characters when driving a decoder ic (don't remember if I used a 4511, but I'm guessing that I might had).

Or tried to display A -F I should say; the letters were lowercase on a 7 segment display - looked pretty crappy. Looked more like a 7 seg. display trying to make some number but with burned out segments.

This chip paired with a CMOS decoder and a 4066 would give me 16 addresses too... But I couldn't live with the display! Yuck!!

January 08, 2011
by mongo
mongo's Avatar

I have a few display circuits here somewhere. One type is the 7 segment display with all the decoding built right in, It has been around for about 35 years now. Then there is the little ones that have a dot matrix for true alphanumeric displays. Also with decoding built right in. I think I'll dig them up just for fun.

January 09, 2011
by Rick_S
Rick_S's Avatar

I know the 7447 would take BCD input and ouput to a 7 segment display. The 7490 paired well with it as it would take pulses and count them with a BCD output. The combo made for a nice little counter circuit. I built one on perfboard about 25 years ago based on a schematic in a Forest Mims book. I drove it with a 555 timer. It would count from 0 to 99 then back to zero again. I still have that board. I came across it about a year ago when I was digging through some of my old junk for my son. He was taking an electronics class in High School. It brought back a bunch of old memories of my earlier tinkering days :D.

January 09, 2011
by mongo
mongo's Avatar

Here is what I have... They are MAN 2A displays, link

Another is the TIL 308 link to datasheet

I have one other type but it's in the garage at the moment. Not really in the mood to trudge out in the snow to get the numbers...

January 09, 2011
by Rick_S
Rick_S's Avatar

I love those little dot matrix displays. They are pretty slick and still go for a pretty good price on e-bay. I have one of the 7 segment style displays that does hexidecimal output for binary in. I think I paid around $8 for it just to play with.

I always keep my eye out when in the resale shops in case I can find some of those old jewels in the rough.

January 09, 2011
by SpaceGhost
SpaceGhost's Avatar

I tried modifying the code to wrap the count. Part of it works.

I modified this part of the code -

if (count_up == 0)
    {
        //for(temp = 0; temp <= 15; temp++); //debounce count up pushbutton
        //if (count_up == 0)
        //{
            i = i + 1; // if count up pressed, increase count by one
            if (i > 15)  //set 15 as highest allowed count
            i = 0;          
            //Check each bit in i and sink corrosponding pin if high
            delay_ms(100); // a bit of a delay so we can see what is happening

to go from 15 to 0 while counting up. This part works. However, when I modified the down-count portion of the code in a similar way -

    if (count_down == 0)
    {
      //  for(temp = 0; temp <= 15; temp++); //debounce count down pushbutton
      //  if (count_down == 0)
      //  {  
            i = i - 1; // if count down pressed, lower count by one     
            if (i < 0) //set 0 as lowest allowed count
            i = 15;
            //Check each bit in i and sink corrosponding pin if high
            delay_ms(100); // a bit of a delay so we can see what is happening

the count goes into negative numbers.

Why does the if statement "if (1 < 0) i = 15" cause the count to go into negative numbers, while the if statement "if (1 > 15) 1 = 0" caused the count to go back to 0 after 15...

January 09, 2011
by bretm
bretm's Avatar

Where are you observing these negative numbers?

And even if they are going negative, -1 would look just like 15 as far as which of the lower four bits are set. -2 would look just like 14, etc. You could just get rid of the boundary checks altogether. You'd need them if you were doing anything that wasn't a power of 2, but with 16 choices it works fine without the limits:

i incrementing   with limits    without limits
      14          14 = 1110        14 = 1110
      15          15 = 1111        15 = 1111
      16           0 = 0000        16 = (1)0000   (x) = don't care
      17           1 = 0001        17 = (1)0001
      18           2   0010        18 = (1)0010
January 10, 2011
by SpaceGhost
SpaceGhost's Avatar

The negative numbers are appearing below the count of 0 on the LCD screen. You are correct bretm, with the four bit LED output, negative 1 appears as 15 (1111), negative 2 appears as 14 (1110), etc... The binary part "wraps" correctly while counting in either direction.

What I don't understand is why the count-down if statement "if (i < 0) i = 15" doesn't cause the count displayed on the LCD to be 15 when counting down, after reaching 0? I do get the correct binary equivalent of 15.

Using the count-up if statement "if (i > 15) i = 0" works - the count display on the LCD returns to 0 when counting up and passing 15...

I would like for it to work that way in the other direction too. Why doesn't it, and what am I doing wrong??

January 10, 2011
by Rick_S
Rick_S's Avatar

Try this. I would guess that i isn't even going negaive since it's really defined as unsigned. Somehow, the LCD routine must be making it that. In reality it is probably wrapping over to a very large number so it is never less than zero.

// Add Boundry Check
if(i==0)
  i=16;
if(i>0)//set 0 as lowest allowed count
  i = i - 1; // if count down pressed, lower count by one

That should make it wrap correctly

Rick

January 10, 2011
by SpaceGhost
SpaceGhost's Avatar

Thanks Rick... On my way to work, will try it as soon as I get home.

Thanks,

Dave

January 10, 2011
by SpaceGhost
SpaceGhost's Avatar

Works sweet!

That negative number thing still has me confused - You said that you think it could be the "LCD routine" that caused that - could you explain a little what that means?

One last question - how in the heck do you get it start at 0 on power up?

January 10, 2011
by SpaceGhost
SpaceGhost's Avatar

Also, can anyone recommend any good books on C and microcontroller programming?

January 10, 2011
by Rick_S
Rick_S's Avatar

If you trace through the LCD.c file in the libnerdkits folder, you'll see that the lcd_write_data function is passed a char variable type. I believe the compiler will re-cast the variable in the fuction which makes it go negative on the LCD even though the original is still unsigned.

As for making it start at zero, simply put an i=0; before going into the while loop. If you don't want the initial count up, just comment out the for loop and all lines right after the variable declaration up to the while loop.

January 10, 2011
by SpaceGhost
SpaceGhost's Avatar

Works Great!!

I want to play around a little with it before I go to bed - I'm already up past my bed time. I'll post the complete code tomorrow, with a few questions probably.

Rick, you ought to be teaching a class in this stuff. I'd sign up for sure! Better yet, I wish ya was my next door neighbor... I'd bug the hell outta ya! (only because I like you though).

Thanks again,

Dave

January 11, 2011
by Rick_S
Rick_S's Avatar

I don't think I'm quite good enough to teach :D... But I am more than happy to help out when I can. I've found that often I learn as much, or more, through figuring out other peoples questions here on the forum, as I do tinkering with my own stuff. So I should be thanking you :)

Thanks for the compliments however, they are appreciated. The freindliness of this community is rare and one of the things that keeps me coming back multiple times a day. I think in the time since I've been a member, I don't recall seeing one thread go into flames and that speaks volumes for the character of the people here.

Rick

January 11, 2011
by SpaceGhost
SpaceGhost's Avatar

Here's the code, if anyone sees anything that should be cleaned, tightened up, edited or added I welcome any and all suggestions - I hope that it can help someone else out too:

//0 - 16 decimal (LCD) and binary (LED) up-down counter
//LEDs wired with common anode connections
//Two-pushbutton operation for counting. Switches close to -5vdc rail

#define F_CPU 14745600

#include <stdio.h>

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

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

// PIN DEFINITIONS:
// PC3 -- UP PUSHBUTTON (pin 26)
// PC4 -- DOWN PUSHBUTTON (pin 27)

 int main() {
    // fire up the LCD
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    // Set 4 output pins
DDRB |= (1<<PB1);
DDRB |= (1<<PB2);
DDRB |= (1<<PB3);
DDRB |= (1<<PB4);

// Set the 2 pins to input mode - Two Pushbuttons
  DDRC &= ~(1<<PC3); // set PC3 as input
  DDRC &= ~(1<<PC4); // set PC4 as input

// turn on the internal resistors for the pins
  PORTC |= (1<<PC3); // turn on internal pull up resistor for PA3
  PORTC |= (1<<PC4); // turn on internal pull up resistor for PA4

// declare the variables to represent each pushbutton input
  uint8_t count_up;
  uint8_t count_down;

    uint16_t i;
    for(i = 0; i <= 15; i++){
            PORTB |= (1<<PB1);
            PORTB |= (1<<PB2);
            PORTB |= (1<<PB3);
            PORTB |= (1<<PB4);

        lcd_home();
        fprintf_P(&lcd_stream, PSTR("ADDRESS- 0 "), i);

        }
 i=0;
while(1) {

    count_up = (PINC & (1<<PC3)) >> PC3;
    if (count_up == 0)
    {
            i = i + 1;   //if count up pressed, increase count by one
            if (i > 15)  //set 15 as highest allowed count and, if i is greater than 15,
            i = 0;       //count loops back to zero
            //Check each bit in i and sink corrosponding pin if high        
            delay_ms(100); // a bit of a delay so we can see what is happening

            // First turn off all LED's
            PORTB |= (1<<PB1);
            PORTB |= (1<<PB2);
            PORTB |= (1<<PB3);
            PORTB |= (1<<PB4);

            // Now sink the LED's for each corrosponding bit in i
            if( i & 1 )
                PORTB &= ~(1<<PB1); // 1's LED (pin 15)
            if( i & (1<<1))
                PORTB &= ~(1<<PB2); // 2's LED (pin 16)
            if( i & (1<<2))
                PORTB &= ~(1<<PB3); // 4's LED (pin 17)
            if( i & (1<<3))
                PORTB &= ~(1<<PB4); // 8's LED (pin 18)
        lcd_home();
        fprintf_P(&lcd_stream, PSTR("ADDRESS- %i "), i); //leave a space after %i, to cover trailing 0
    }  
    count_down = (PINC & (1<<PC4)) >> PC4;
    if (count_down == 0)
    {  
        // Add Boundry Check to prevent count going negative
            if(i==0)
            i=16;
            if(i>0)//set 0 as lowest allowed count
            i = i - 1; // if count down pressed, lower count by one
            //Check each bit in i and sink corrosponding pin if high
            delay_ms(100); // a bit of a delay so we can see what is happening

            // First turn off all LED's
            PORTB |= (1<<PB1);
            PORTB |= (1<<PB2);
            PORTB |= (1<<PB3);
            PORTB |= (1<<PB4);

            // Now sink the LED's for each corrosponding bit in i
            if( i & 1 )
                PORTB &= ~(1<<PB1); // 1's LED (pin 15)
            if( i & (1<<1))
                PORTB &= ~(1<<PB2); // 2's LED (pin 16)
            if( i & (1<<2))
                PORTB &= ~(1<<PB3); // 4's LED (pin 17)
            if( i & (1<<3))
                PORTB &= ~(1<<PB4); // 8's LED (pin 18)
          lcd_home();
        fprintf_P(&lcd_stream, PSTR("ADDRESS- %i "), i); //leave a space after %i, to cover trailing 0
      //I have ADDRESS there, because that's what the counter will identify for me
    }

    }

    return 0;

    }

A few things that puzzle me about the code: (ahhh, more bloody questions!!)

On line 56 the declaration of "i=0" falls after the closing } of the beginning program (am I interpreting that right?), and before the beginning of the while(1) loop and its { . I though most code instructions had to be inbetween brackets somewhere in the code? If that's true, why isn't it in this circumstance?

I've noticed too that in parenthesied equasions, sometimes there is one comparison symbol, sometimes too. I figure seeing >= means greater than or equal to, but why are two symbols sometimes used? (==, <<, etc.)

" FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);" appears twice in the code. What is the 0 representing? What would happen if it was changed to a 1?

Sorry if my questions are kind of goofy. I'll be getting a book from my wife for my birthday at the end of the month. What's a good one to get that I can buy and give to her, so she can give it me? (That's how we do all my birthday and Christmas presents, lol).

Rick, I'm pretty new here and I have to agree - this is a very friendly forum. I've been browsing through a lot of posts and haven't seen anything meant to belittle, criticise or flame anyone. All good and lots of interesting stuff.

I like reading about other people's thoughts, ideas and projects too... A lot of original ideas get born that way. I hope to contribute here when I can. This has been a lot of fun! The counter is a dandy, does EVERYTHING I want it to do. Got me wanting to learn more too.

Best regards,

Dave

January 11, 2011
by Rick_S
Rick_S's Avatar

When your code runs, the program reads everything that is not whithin a function first, then runs the main function. The i=0 is within the braces of the main function just not in the for loop above it. (Which you could do away with altogether by deleting lines 46 thru 55)

As to the "symbols", or operators as they are called, single vs. double versions perform different tasks.

For instance:

i=0; // Assign value of zero to the variable i

if(i=0) // This would not work because it assigns the value of zero to i instead of 
        // comparing it to zero.
if(i==0)// This is the correct syntax as this compares the value currently in i to zero.
  i=15; // This then assigns the value 15 to i

So = is for assignment while == is for comparison. The other operators work similarly.

<< and >> are bitwise operators and do bit shifting left or right < and > work as expected.

" FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);" simply sets up a stream for LCD output calling the function lcd_putchar which you can find in the lcd.c file in your libnerdkits folder. Follow the functions there and you'll see what the zero is for.

As for books, I really don't have any good suggestions. Any basic "C" language book will be helpful but there will be a lot you won't use. It will however get you familiar with the operators available and what they do. I have fallen back on an old book I have (don't even know if it is still published or not) called "C/C++ Programmers Reference" by Herbert Schildt.

Rick

January 11, 2011
by bretm
bretm's Avatar

The i=0 on line 56 does not fall outside the closing } of the beginning of the program. The { after main() on line 21 is matched by the } on line 121. All of the statements are inside int main() { ... }. It's just the way it's indented that makes it look otherwise.

== is the equality comparison. != is the "not equal to" comparison. You know about >= and <=. But the >> and << operators have nothing whatsoever to do with comparisons. These are shift operators. 1<<PC4 means take "1" in binary (which is still "1") and add four "0" bits on the end (since PC4 equals the value "4"), to get 10000 in binary.

The 0 in FDEV_SETUP_STREAM is a pointer to a function for reading characters from the stream. It's set to 0 because you can't read from the LCD, you can only write to it. If you use a 1, nothing would happen until you attempted to read from it with one of the scanf type of functions, in which case it would jump to address 1 in memory which probably contains nonsense instructions because it's in the middle of a jump statement in the interrupt vector table, and what happens then is anybody's guess.

January 14, 2011
by BobaMosfet
BobaMosfet's Avatar

Speaking of vintage 7-Segment Displays, anyone got any of these?

HP5082-7415

I have one. I'd love to have a few more, and had all but forgotten about them. The cool thing is, I remember wearing one of the very first digital wrist-watches, using these kinds of HP displays. All the TI Calculators used to have them. It was so cutting edge back then....

How time flies.

BM

January 15, 2011
by SpaceGhost
SpaceGhost's Avatar

I have something that looks very similar. I have a Litronix DL-34M, it's a mini common cathode four digit-seven segment "bubble" display. I got it a couple months ago and only paid a dollar for it... Very cool "retro" looking piece - haven't really done anything with it yet, but had to snag it!

I remember seeing bubble displays like these in calculators and watches waaay back when also (1970's?)... I remember building a clock for my mom's car from a Radio Shack kit that I think had a display like that (memory a little fuzzy that far back) when I was a teenager. I do remember Radio Shack stocking bubble displays back then though... Nixie tubes also!

It's too bad some of that stuff had to go away and get harder to find. I'll bet that you um, paid a pretty penny for that fancy watch when they first came out?

Post a Reply

Please log in to post a reply.

Did you know that you can follow NerdKits on Facebook, YouTube, and Twitter? Learn more...