Saturday, November 22, 2008

Small Nixie Tube

I have a plan to build a Nixie Clock so I search for its information and blog it here for further reference.

IN-16 may be not the smallest nixie tube but it's small enough to make a nice desk top nixie clock. The character height is about 13mm. I will use IN-16 for my upcoming nixie clock. There are some nixie clock kits that use IN-16 nixie tube so I am thinking about buying a kit or building it from scratch.

The cheapest PIC with Internal Osc and I2C


I just got the above FM receiver module from SparkFun. It requires I2C interface. Actually, I2C can be implemented in software but I need I2C hardware so I can do something else in software. So, I went to microchip.com and searched for the cheapest PIC Microcontroller that comes with internal oscillator and I2C. Based on my search, PIC16F722 is the cheapest PIC with 16MHz internal osc. and I2C interface. Its price is $0.91 for 1000 pcs. I also found that PIC16F505 is the cheapest PIC that comes with internal oscillator (4MHz, $0.56/1000pcs.).

Anyway, I will develop my FM radio with PIC16F887 as I have it in hand.

Wednesday, November 19, 2008

PCB for 7-Segment PIC Digital Clock

I have designed a single-sided PCB for the PIC Digital Clock. Because the autorouter is not good for routing single-sided PCB so I have to route by hand. It was my first hand routed PCB design and it was a time consuming task. I haven't tested the PCB yet. Use it by your own risk. I will produce this PCB and test it later.

As I don't have server to upload the Eagle file, please contact me if you want the file or PDF version of the image below.

PCB for 7-Segment PIC Digital Clock

Components side of the PCB. The red lines are jumpers.
Component placement on the PCB of 7-Segment PIC Digital Clock

Sunday, November 16, 2008

7-Segment PIC Digital Clock : The photographs

The prototype of the 7-Segment PIC Digital Clock. Check out 7-Segment Digital Clock for the schematic and source code.
7-Segment Digital Clock
7-Segment Digital Clock PIC Microcontroller
7-Segment Digital Clock PIC16F627A
7-Segment Digital Clock PIC16F628

Saturday, November 15, 2008

Using 11.2896 MHz with TMR1 (timer1 module)

11.2896 MHz Rubidium Frequency Standard

A precision clock is my ultimate goal of making clocks. I know that cesium oscillator is to most precise clock on earth. However, a cesium clock is too expensive to play with. My option is using Rubidium oscillator which is the second most accurate oscillator after the cesium (I don't have reference about this) and its cost is more accessible. Searching on eBay about Rubidium Frequency Oscillator brought me to Ultra low jitter 11.2896Mhz Rubidium Frequency Clock . I am thinking about buying this item.
The question is 'How to make use of 11.2896 MHz frequency oscillator with my clock?'. If I run my MCU with this frequency, the internal frequency will be 11.2896MHz/4 = 2.8224 MHz. How to use this frequency to drive timer1 module for making a precision clock? TMR1 is a 16bit timer so it can count from 0 to 65535 before overflow. If I use TMR1 with this frequency without any setting, the TMR1 will send interrupt 2822400/65536= 43.06640625Hz or every 0.023219955 second which is unusable.
I have to find the initial value of the TMR1 for the best precision . The conditions are
1. The number of interrupts per second must be integer so I can count it precis
2. The initial value must be in form 0x##00. That means I have to set only the first 2 bits of TMR1 (TMR1H) as setting the lower bits takes some time and effects the clock precision.

The Solution
Expanding 2822400 results
2822400 = 256 x 49 x 25 x 9 = 57600 x 49
The meaning
If I set TMR1 to count upto 57600, it will send interrupt 49 times/second. Wow! It meets my first condition. To set TMR1 to count up to 57600, I have to set the initial value of TMR1 to 65536-57600 = 7936 = 0x1F00. Wow again!! It meets the second condition.

Now, I have to set TMR1H = 0x1F and count the number of interrupts to 49 to get 1 second time interval. Just perfect!

Thursday, November 13, 2008

Making a Digital Clock (Updated)

My fist Microcontroller project, PIC Digital Clock, is shown below.
Prototype digital clock on Breadbord

I have added time setting feature to make it to be a usable clock. The updated version is shown below:
7-Segment Digital Clock

More features will be added in the future.
There is no fast or slow time setting as in normal/simple digital clocks. In this clock, each digit of the clock display can be set one by one via 2 setting buttons.
Features
1. Bright Led 7-Segment display without Multiplexing
2. Display: Hour, Minute, Second
3. Set time via 2 buttons
4. Set time Digit by Digit
5. Use internal oscillator of PIC16F627a or PIC16F628
6. Use 32.768KHz crystal for better clock accuracy


Setting Time:
1. The clock shows 12:34:56 when power the clock on. The first digit of hour (it's number 1 in this case) will be blinking to notify that the time is not correct and need to be set.
2. Press SET button to count up the digit.
3. Press MODE button when the digit is the correct time. The next digit will be blinking.
4. Repeat step 2 and 3 for setting minute and second.
5. Make sure that pressing MODE button for setting the second digit of second at the correct time.


Shecmatic of the Digital Clock
Schematic of a Digital Clock using PIC16F627a or PIC16F628 and Led 7-Segment with showing second

Example of a PCB design of the Digital Clock
PCB of a Digital Clock using PIC16F627a or PIC16F628 and Led 7-Segment with showing second

The firmware ( source code in C ) written in MikroC
//6 digit  clock
//Using timer1 16bit counter interrupt
// PIC16F627A or PIC16F628
// Internal Clock 4MHz
// PUNKKY@gmail.com
#define MODE PORTB.F4
#define SET PORTB.F5
#define Sec_port_l PORTA.F6
#define Sec_port_h PORTA.F4
#define Min_port_l PORTA.F3
#define Min_port_h PORTA.F2
#define Hr_port_l PORTA.F1
#define Hr_port_h PORTA.F0
#define Blink PORTA.F7
#define HTMR1 0x80
#define LTMR1 0x00
typedef unsigned short uns8;
uns8 i;
uns8 hr_h;
uns8 hr_l;
uns8 min_h;
uns8 min_l;
uns8 sec_h;
uns8 sec_l;
uns8 tick;
uns8 myTimer;
uns8 setting_time;
void setup ();
void set_time ();
void show_time ();
void display (uns8 digit);
void blink_digit (uns8 digit);
void check_bt ();

//void check_bt(); //chech button
void interrupt ()
{
        PIR1.TMR1IF = 0;
        // clears TMR1IF
        TMR1H = HTMR1;
        tick = 1;
        Blink = 1;
        sec_l ++;
        if(sec_l>9){
            sec_l = 0;
            sec_h++;
        }
        if(sec_h>5){
            sec_h=0;
            min_l++;
        }
        if(min_l>9){
            min_l = 0;
            min_h++;
        }
        if(min_h>5){
            min_h = 0;
            hr_l++;
        }
        if(hr_l>9){
            hr_l = 0;
            hr_h++;
        }
        if(hr_h >2){
            hr_h = 0;
        }
        if(hr_h >=2 && hr_l>3){
           hr_h = 0;
           hr_l = 0;
        }
}
void main ()
{
        setup ();
        
        //Set time
        hr_h = 1;
        hr_l = 2;
        min_h = 3;
        min_l = 4;
        sec_h = 5;
        sec_l = 6;
        show_time ();
        setting_time = 1;
        set_time();
        while (1)
        {
                //blink_digit();
                if (tick)
                {
                        tick = 0;
                        show_time ();
                        Delay_ms (300);
                        Blink = 0;
                }
                check_bt ();
        }
}
void setup ()
{
        tick = 0;
//Digital output on PORTA
        CMCON = 0x07;
        //Input buttons + external clock
        TRISB = 0xB0;

        PORTB = 0x00;
        TRISA = 0x00;
        PORTA = 0x00;
        //Internal Clock 4MHz
        PCON.OSCF = 1;
        // Prescaler 1:1   external clock
        T1CON = 0x0F;

        PIE1.TMR1IE = 0;  // disable interupt to stop the clock

        INTCON = 0xC0;
        // Set GIE, PEIE
        TMR1L = LTMR1;
        TMR1H = HTMR1;
        // TMR1 starts at 0x0BDC = 3036 to make TMR1 counts to 62500 and
        // overclows in every 0.1 sec
        // Math: 1/500000*8*62500 = 0.1
        // 1/5000000 : time for 20MHz crystal (internal clock will be 20/4 = 5MHz)
        // 8: prescaler
        // 62500: TMR1 counts to 62500
        // Counting number of overflows to 10 will get 1 sec.

}

void show_time ()
{
        display (1);
        display (2);
        display (3);
        display (4);
        display (5);
        display (6);
}
void display (uns8 digit)
{
        switch (digit)
        {
                case 1 :
                PORTB = hr_h;
                Hr_port_h = 1;
                Hr_port_h = 0;
                break;
                case 2 :
                PORTB = hr_l;
                Hr_port_l = 1;
                Hr_port_l = 0;
                break;
                case 3 :
                PORTB = min_h;
                Min_port_h = 1;
                Min_port_h = 0;
                break;
                case 4 :
                PORTB = min_l;
                Min_port_l = 1;
                Min_port_l = 0;
                break;
                case 5 :
                PORTB = sec_h;
                Sec_port_h = 1;
                Sec_port_h = 0;
                break;
                case 6 :
                PORTB = sec_l;
                Sec_port_l = 1;
                Sec_port_l = 0;
                break;
        }
}
void blink_digit (uns8 digit)
{
        switch (digit)
        {
                case 1 :
                PORTB = 0xFF;
                Hr_port_h = 1;
                Hr_port_h = 0;
                Delay_ms (100);
                display (1);
                Delay_ms (100);
                break;
                case 2 :
                PORTB = 0xFF;
                Hr_port_l = 1;
                Hr_port_l = 0;
                Delay_ms (100);
                display (2);
                Delay_ms (100);
                break;
                case 3 :
                PORTB = 0xFF;
                Min_port_h = 1;
                Min_port_h = 0;
                Delay_ms (100);
                display (3);
                Delay_ms (100);
                break;
                case 4 :
                PORTB = 0xFF;
                Min_port_l = 1;
                Min_port_l = 0;
                Delay_ms (100);
                display (4);
                Delay_ms (100);
                break;
                case 5 :
                PORTB = 0xFF;
                Sec_port_h = 1;
                Sec_port_h = 0;
                Delay_ms (100);
                display (5);
                Delay_ms (100);
                break;
                case 6 :
                PORTB = 0xFF;
                Sec_port_l = 1;
                Sec_port_l = 0;
                Delay_ms (100);
                display (6);
                Delay_ms (100);
                break;
        }
}
void set_time ()
{

        i = 1;
        while (setting_time)
        {
                blink_digit (i);
                while (SET == 0)
                {
                        Delay_ms (5);
                        switch (i)
                        {
                                case 1 :
                                hr_h ++;
                                if (hr_h > 2)
                                {
                                        hr_h = 0;
                                }
                                break;
                                case 2 :
                                hr_l ++;
                                if (hr_l > 9)
                                {
                                        hr_l = 0;
                                }
                                if (hr_h >= 2 && hr_l > 3)
                                {
                                        hr_l = 0;
                                }
                                break;
                                case 3 :
                                min_h ++;
                                if (min_h > 5)
                                {
                                        min_h = 0;
                                }
                                break;
                                case 4 :
                                min_l ++;
                                if (min_l > 9)
                                {
                                        min_l = 0;
                                }
                                break;
                                case 5 :
                                sec_h ++;
                                if (sec_h > 5)
                                {
                                        sec_h = 0;
                                }
                                break;
                                case 6 :
                                sec_l ++;
                                if (sec_l > 9)
                                {
                                        sec_l = 0;
                                }
                                break;
                        }
                        while (SET == 0)
                        {
                                Delay_ms (5);
                        }
                }
                while (MODE == 0)
                {
                        Delay_ms (5);
                        i ++;
                        if (i > 6)
                        {
        sec_l--;
        TMR1H = 0x80;
        TMR1L = 0x00;
        PIE1.TMR1IE = 1;
        setting_time = 0;
                                break;
                        }
                        while (MODE == 0)
                        {
                                Delay_ms (5);
                        }
                }
        }
}
void check_bt ()
{
        myTimer = 0;
        if (setting_time == 0)
        {
                while (MODE == 0)
                {
                        Delay_ms (5);
                        myTimer ++;
                        if (myTimer > 200)
                        {
                                setting_time = 1;
                                myTimer = 0;
                                break;
                        }
                }
        }
        while (MODE == 0)
        {
                PIE1.TMR1IE = 0;
                //Stop clock
                Delay_ms (5);
                blink_digit (1);
        }
        set_time ();
}

I don't have the picture of the prototype as I haven't made it yet. But I have tested the circuit and firmware with proteus already.

--- Update ---
- The prototype of this updated version is done. Please see its photographs at 7-Segment PIC Digital Clock : The photographs
- The PCB is ready: please check out PCB for PIC Digital Clock

Thursday, November 6, 2008

10-Second Counter

I have made a 10-Second Counter by using a PIC16F627a and a CD4543 BCD to 7-Segment decoder. This circuit and source code are just a demonstration of using MCU to drive an LED 7-Segment Display via CD4543. The 7-Segment displays from 0 to 9 and the number will roll over to 0 again. The interval between change is 1 second. So, this circuit counts 10 seconds between the roll over. I use TIMER1 module and 32.768KHz crystal to make 1 second timebase (see Clock with 32.768KHz Crystal for more info ).

The schematic of the 10-Second counter
Schematic of 10-Second counter
Project setting of MikroC for using internal oscillator of the PIC16f627a.
Setting MikroC for Internal Oscillator

The firmware is written in MikroC and you can find it below.

//PIC16F627A
//4MHz Internal OSC
// One Digit Counter
// 06/11/2008
//Punkky
unsigned short counter;
unsigned short tick;
void interrupt ()
{

        PIR1.TMR1IF = 0;        // clears TMR1IF
        TMR1H = 0x80;
        tick = 1;
}

void main(){
        CMCON = 0x07;   //Digital I/O for PORTA
  TRISA = 0x00;
  PORTA = 0x00;
  TRISB = 0x00;
  PORTB = 0x00
  T1CON = 0x0F
         
  // Prescaler 1:1   external clock 
  PIE1.TMR1IE = 1;  // enable interupt to start the clock 
  INTCON = 0xC0;   // Set GIE, PEIE 
  TMR1L = 0x00
  TMR1H = 0x80
  PCON.OSCF = 1//Internal Clock 4MHz
  //PCON.OSCF = 0; //Internal Clock 48KHz doesn't work well
  counter = 0;
  tick = 0;
  PORTA.F0 = 1// Enable 4345 BCD to 7-Segment
  while(1){
    if(tick){
       tick = 0;
       counter++;
       PORTB = counter;
       if(counter>9){
       counter = 0;
    }
   }
  }
}