[Advice needed] Building a small BCD clock.

Introductions are awkward, straight to business:

I need some advice on how to control 14 leds, with the smallest possible microprocessor. I'd rather not buy an arduino mini just for this project, as this is a school project. I haven't written any code yet, but I was thinking I could just count the time within the loop.

I discovered that it is possible to shrink your projects, by uploading your sketches into single chips, such as the ATtiny85. Now that's small enough, but it only has 6 I/O pins, and I need to control 14 leds. Unless I can find a 14 I/O chip that's relatively small, I'm going to need some help from you guys. I googled around and read something about an LED matrix, but I really don't get the idea, since it seems to require the same amount of outputs anyway. I guess it makes the code clearer.

TL;DR: What's the simplest way of controlling 14 LEDs with the smallest possible configuration?

atmega328, 16 mhz crystal, two 22pf caps, three 100nF caps. 14 resistors, 14 LEDs.

CrossRoads: atmega328, 16 mhz crystal, two 22pf caps, three 100nF caps. 14 resistors, 14 LEDs.

A board to mount the stuff on. And access to someone else's system with a serial line to program it. Easier if the atmega328 already has a bootloader programmed.

CrossRoads: atmega328, 16 mhz crystal, two 22pf caps, three 100nF caps. 14 resistors, 14 LEDs.

Thanks, but isn't the the ATmega series quite large, compared to Attiny? ATtiny 40 seems to have a max 18 I/O pins and it's definetely smaller. Do I need the crystal and caps if I'll end up using this one? What about the program to turn the leds on, can you suggest anything "smoother" than a bunch of if - structures?

Msquare: A board to mount the stuff on. And access to someone else's system with a serial line to program it. Easier if the atmega328 already has a bootloader programmed.

Right, that's pretty obvious. I've got a friend who has an Uno, so I can program the chip with it, right?

iDroid:

CrossRoads: atmega328, 16 mhz crystal, two 22pf caps, three 100nF caps. 14 resistors, 14 LEDs.

Thanks, but isn't the the ATmega series quite large, compared to Attiny? ATtiny 40 seems to have a max 18 I/O pins and it's definetely smaller. Do I need the crystal and caps if I'll end up using this one? What about the program to turn the leds on, can you suggest anything "smoother" than a bunch of if - structures?

The smallest one with 14 pins would be an ATtiny4313.

PS: You don't really need the crystal, they work just fine with their internal clock. The only time you need an external crystal is when you need accurate timing for, eg. serial communications.

If you want a smaller AVR chip (Tiny84 or Tiny85) then you'll need an external 16 channel LED driver. I think the smallest 16 channel chip has 24 pins (eg. TLC5925).

iDroid: Right, that's pretty obvious. I've got a friend who has an Uno, so I can program the chip with it, right?

You can do it that way So long as he doesn't mind you pulling it apart. The best way to work with bare chips is to get an "ISP Programmer". They cost about $12 and when you have one you don't lose any flash memory to bootloaders, etc.

PS: The ATtiny85 only really has five usable pins. You can use six pins but after you reconfigure the reset pin for I/O you can't upload any more software without a special device to configure it back again.

PPS: You could also do it with an ATtiny84 and a technique called "charlieplexing". It allows you to control (eg.) six LEDs with only three pins but it's tricky to get right.

See: https://en.wikipedia.org/wiki/Charlieplexing

Using charleplexing, you can control up to 20 LEDs with 5 pins. This would be your best bet. I recently put some code up on Github that makes it relatively painless: GitHub - marcuserronius/ChuckPlex at 1.0

… as for controlling the LEDs, I’d make arrays organizing the relevant LEDs. Split the time up into separate decimal components, and extract the extract the place values of the ON bits from each, use them as indices for the LEDs array, i.e.:

int digit1[] = {1,2};
int digit2[] = {3,4,5,6};
int digit3[] = {7,8,9};
int digit4[] = {10,11,12,13};
[…]
for(i=0;i<4;i++){
  if(bitRead(timeDigit2,i){
    // turn on LED digit2[i]
  }
}

Something like that, maybe. If you go with multiplexing the LEDs, this might end up causing too much flicker, in which case you could build a list of LEDs to turn on and cycle them until the next time the time changes.

As you are building a BCD clock I would use an ATTiny with at least 8 outputs and arrange your leds on a 4x4 grid (with gaps where you dont need LEDs)

Then you can use 4 of your outputs for your rows and four for you columns and you have an easier solution than charlieplexing. You would need to row scan the rows of the 4x4 matrix but there are loads of tutorials on how to do that on these forums and on the net.

Something like X X X X X X X X X X X X X Hours Minutes

HTH

An RC oscillator isn't going to make a very accurate clock... Not at all. Go read the notes on RC frequency vs temperature and supply voltage variations. What you propose to do would be neat... as long as it didn't have to keep time.

Bob

Alright, hope this post isn't too old yet, because OP is here to deliver... Somewhat.

So I got this set up on a breadbord with the Arduino Uno, just to see how the code works. I'm using the millis() function to count milliseconds from the time when the program started. Every 1000 milliseconds, it adds one to seconds, every 60 seconds it adds one to minutes and so on. Each LED is connected to it's own digital pin on the Uno and lights up accordingly with basic if functions. Right now there's only one button to set minutes on digital pin 0, but I plan to add one for hours as well when its time to shrinkify this project to the ATtiny2313. The reference page says that this millis() function overflows after approximately 50 days, how badly does this affect the clocks accuracy?

I'm building the schematic with Eagle now. Do I need any other components, suchs as caps, in order for the ATtiny to work properly?

Thanks for your help guys!

EDIT: Will the buttons work with the ATtiny? I wired them (Or "it" at this time) as instructed on this page.

I tried something with using millis() to run a clock. That had a longterm drift (a minute or so every day). That was with an (older) Arduino with a crystals - the newers ones have something else that is not as regular.

Get an RTC chip, maybe on on a breakoutboard w/battery, and use it. (I dont know if you can get something that will interface easily with the ATTiny). By the way, I could set the 24 hour clock using just one push button (and some patientence)

Okay, I’ll try to get one of those in my hands.

Well patience… doesn’t quite belong in my vocabulary. Besides, it isn’t very convenient if you have room for two buttons. I’ll be setting the time in the code anyway before I upload it. I’m adding the button mainly for demonstration.

You will need to (re)set the time sometime. Daylightsaving f.ex. Or a powerloss, battery change. The "patience" was that my little onebutton algorithm involved waiting for a few secs for it to advance to the next digit and entering 9 required - yes, nine pushes.

iDroid: The reference page says that this millis() function overflows after approximately 50 days, how badly does this affect the clocks accuracy?

Not at all. Search the forum, discussions on why this is not a problem constitute approximately 42% of the posts on the forum ;)

Msquare: You will need to (re)set the time sometime. Daylightsaving f.ex. Or a powerloss, battery change. The "patience" was that my little onebutton algorithm involved waiting for a few secs for it to advance to the next digit and entering 9 required - yes, nine pushes.

Oh, I thought you meant pushing the minute button 60 times to advance one hour :D Well, that's convenient enough. May I ask how did you do it?

[quote author=Jack Christensen link=topic=124484.msg1038080#msg1038080 date=1355932203]

iDroid: The reference page says that this millis() function overflows after approximately 50 days, how badly does this affect the clocks accuracy?

Not at all. Search the forum, discussions on why this is not a problem constitute approximately 42% of the posts on the forum ;) [/quote]

Okay, thanks!

Msquare: You will need to (re)set the time sometime.

Yes.

Daylightsaving f.ex.

I humbly submit my fix for that, for your consideration: http://arduino.cc/forum/index.php/topic,96891.0.html

Best way to explain is the code :)

The setuploop is called in the loop() code, when we are setting the clock (initiated by a long press of the button, as a short press was used to do something else), ie. it is called repeatedy and often (until the SetClock is false)

Instead of LEDs I have a meter, you just do some visual feedback on your LEDs instead at the SetNeedle()

void setuploop() {
/* This is a small state machine, ensuring we can enter current HH:MI with one button.
  The needle is waved a bit, then wait for number button pushes that sets a single digit. */

  static byte Entry = 0 ; // State machine
  static byte N ; static byte H = 0 ; static byte T = 0 ; static byte M = 0 ;

  switch (Entry) {
    case 0: case 2: case 4: case 6: wiggle() ; Entry++ ; break ;
    case 1: if (ButtonUpd(&N)) Entry++ ; if (N>2) N=0 ; break ;
    case 3: if (ButtonUpd(&H)) Entry++ ; if (H>9 || (N==2&&H>3)) H=0 ; break ;
    case 5: if (ButtonUpd(&T)) Entry++ ; if (T>5) T=0 ; break ;
    case 7: if (ButtonUpd(&M)) Entry++ ; if (M>9) M=0 ; break ;
    case 8: SetClock = false ;
            //Store a new value in the RTC chip. Date is ignored (random)
            RTC.stopClock();
            RTC.fillByHMS(N*10+H,T*10+M,0);
            RTC.setTime();
            RTC.startClock();
            break ;
  }
}

boolean ButtonUpd(byte *Pdig) {
/* return true when no button change for several seconds. Increment argument for every button push
  put needle at value for feedback. (Wrap is handled by calling function) */

  static byte PrvBtn = HIGH ;
  byte Button ;

  SetNeedle( *Pdig, 10 ) ;
  if ( millis() - Timer > 5 && (Button=digitalRead(BUTN1)) != PrvBtn) {
    // button change, increment digit if push
    if (Button==LOW) (*Pdig)++ ;
    Timer = millis() ; PrvBtn = Button ;
  }
  return millis() - Timer >5000L ;
}

Msquare: Best way to explain is the code :)

The setuploop is called in the loop() code, when we are setting the clock (initiated by a long press of the button, as a short press was used to do something else), ie. it is called repeatedy and often (until the SetClock is false)

Instead of LEDs I have a meter, you just do some visual feedback on your LEDs instead at the SetNeedle()

void setuploop() {
/* This is a small state machine, ensuring we can enter current HH:MI with one button.
  The needle is waved a bit, then wait for number button pushes that sets a single digit. */

  static byte Entry = 0 ; // State machine   static byte N ; static byte H = 0 ; static byte T = 0 ; static byte M = 0 ;

  switch (Entry) {     case 0: case 2: case 4: case 6: wiggle() ; Entry++ ; break ;     case 1: if (ButtonUpd(&N)) Entry++ ; if (N>2) N=0 ; break ;     case 3: if (ButtonUpd(&H)) Entry++ ; if (H>9 || (N==2&&H>3)) H=0 ; break ;     case 5: if (ButtonUpd(&T)) Entry++ ; if (T>5) T=0 ; break ;     case 7: if (ButtonUpd(&M)) Entry++ ; if (M>9) M=0 ; break ;     case 8: SetClock = false ;             //Store a new value in the RTC chip. Date is ignored (random)             RTC.stopClock();             RTC.fillByHMS(N*10+H,T*10+M,0);             RTC.setTime();             RTC.startClock();             break ;   } }

boolean ButtonUpd(byte Pdig) { / return true when no button change for several seconds. Increment argument for every button push   put needle at value for feedback. (Wrap is handled by calling function) */

  static byte PrvBtn = HIGH ;   byte Button ;

  SetNeedle( *Pdig, 10 ) ;   if ( millis() - Timer > 5 && (Button=digitalRead(BUTN1)) != PrvBtn) {     // button change, increment digit if push     if (Button==LOW) (*Pdig)++ ;     Timer = millis() ; PrvBtn = Button ;   }   return millis() - Timer >5000L ; }

Nice, may I use it?

Alright guys, I'm back to school and now I have it all set up: Arduino uno as ISP, trying to program ATtiny2313. All connections are triple checked, the heartbeat led is beating, the cap is between reset and ground. I uploaded the patched ArduinoISP sketch to the arduino, connected the cap and all four wires from the tiny, plus 5 volts and ground. Also connected a resistor and a led to attiny pin 2. Then I tried uploading the sketch to the tiny. No errors, but nothing happens. Tried pins 2 and 6, when it was pin 2 I set it to pin 1 in the sketch. Also tried 2. This is my second processor, since the last one gave the same error every time, so I don't think this one would be broken.

I don't need a bootloader for the tiny when I'm using the arduino as a programmer, right?

iDroid:
Nice, may I use it?

Of course, as long as you do not use it for nuclear powerstations, monitoring medical lifesupport, yada da da…

Thanks.

Alright, I got it working and now I'm experimenting button usage with the ATTiny. Pin 12 is where the button goes and pin 11 is where an LED should light up when the input is high.

int setm;

void setup() {                
  // initialize the digital pin as an output.
  pinMode(9, INPUT);
  pinMode(8, OUTPUT);  
}

// the loop routine runs over and over again forever:
void loop() {
setm = digitalRead(9);    // Read state
    if(setm == HIGH){
      digitalWrite(8, HIGH);
      delay(250);
}
else
{
  digitalWrite(8,LOW);
  delay(250);
    }
}

However, now the LED is low when the button is pressed and when it's not, the LED just blinks. Any advice here?