Tuesday 29 November 2011

Elmer 160 -  PIC introduction

Want to get started with PIC microcontrollers? If so, here's a link to a nice introductory course:

Elmer 160 - Course Lessons

This course will get you started with programming controllers using Microchip's MPLAB development suite, available as a free download from the Microchip website.

So now it's a stopwatch.

After watching the numbers flash by, produced by the code in my previous article, i now tweaked the program to do something useful for a change. The display will now count to 59.9 seconds, and resets when a button is pressed. It still isn't very sophisticated, and a $2.00 wristwatch will actually do a much better job, but that wasn't the point :-)

Tuesday 22 November 2011

A small step for mankind, a giant leap for... me!

Finally, last night I had some time to spare... enough to fire up MPLAB, Microchip's microcontroller development studio. I'd spent some time trying to learn to program in assembler, then in C, but gave up in frustration. But, as any true geek knows, projects like this keep on nagging in the back of your mind. So, I decided to give it another go, this time trying to make a counter on the LED display of my homemade controller board, using a PIC16F630:


For some reason, this 16F630 controller never, ever does what it's supposed to do, at least not in my world. This time however, I was determined to get the bastard to submit to my will, instead of the other way around! After an hour or so, I came up with the following:

--------------------------------------------
//Includes & references
#include <htc.h>

//Processor configuration (look in processor header file for definitions)
__CONFIG(FOSC_INTRCIO & WDTE_OFF & PWRTE_OFF & BOREN_OFF &
    MCLRE_OFF & CPD_OFF & CP_OFF);

#define _XTAL_FREQ    4000000
#define DIGIT0        RA0
#define DIGIT1        RA1
#define    DIGIT2        RA2
#define    BUTTON1        RA5

//Global variables
//int counter = 0;

//Put initialization code here
void init (void)
{
    //Disable interrupts
    INTCON        = 0b00000000;
    PIE1        = 0b00000000;
    //Set options
    OPTION_REG    = 0b10000111;
    //Switch off comparator to enable digital I/0
    CMCON        = 0b00000111;
    //Set port directions
    TRISA        = 0b00101000;
    TRISC        = 0b00000000;
    //T1CON        = 0b00110101;
}

/**** Function prototypes ****/
void ShowDigit (int Number);

/**** Main function block ****/
void main (void)
{
    init();
    int count = 0;
    int i = 0;
    for (;;)
    {
        //Blank display before digit switch
        ShowDigit(11);
       
        switch (i)
        {
            case (0):
            //Digit 2 (MSB)
            //PORTA = 0b11111011;
            DIGIT0 = 1;
            DIGIT1 = 1;
            DIGIT2 = 0;
            break;
            case (1):
            //Digit 1
            //PORTA = 0b11111101;
            DIGIT0 = 1;
            DIGIT1 = 0;
            DIGIT2 = 1;           
            break;
            case (2):
            //Digit 0 (LSB)
            //PORTA = 0b11111110;
            DIGIT0 = 0;
            DIGIT1 = 1;
            DIGIT2 = 1;
            break;
            default:
            //PORTA = 0b11111111;
            ;
        }
       
        //Show next number
        ShowDigit(count);
        __delay_ms(500);
        i++;
        if (i > 2)
            i = 0;
        count++;
        if (count > 9)
            count = 0;
           
        //Reset counters when sw1 is pressed
        if (!BUTTON1)
        {
            i = 0;
            count = 0;
        }
    }
}

/**** Functions ****/

//Call with 0 - 9 to show digit. All other values blank the display.
void ShowDigit (int Number)
{
    switch (Number)
    {
        case (0):
        PORTC = 0b000000;
        RA4 = 1;
        break;
        case (1):
        PORTC = 0b111001;
        RA4 = 1;
        break;
        case (2):
        PORTC = 0b100100;
        RA4 = 0;
        break;
        case (3):
        PORTC = 0b110000;
        RA4 = 0;
        break;
        case (4):
        PORTC = 0b011001;
        RA4 = 0;
        break;
        case (5):
        PORTC = 0b010010;
        RA4 = 0;
        break;
        case (6):
        PORTC = 0b000010;
        RA4 = 0;
        break;
        case (7):
        PORTC = 0b111000;
        RA4 = 1;
        break;
        case (8):
        PORTC = 0b000000;
        RA4 = 0;
        break;
        case (9):
        PORTC = 0b010000;
        RA4 = 0;
        break;
        default:
        //Defaults to blank
        PORTC = 0b111111;
        RA4 = 1;   
    }
}

-------------------------------------------- 

Predictably, this did not work... at first. The output ports would not react the way I wanted, resulting in strange characters on the display. As a last resort, i turned to the 16F630 manual. And Eureka! Because of the fact that many of the controller's pins have multiple functions, it is very important to set up the peripherals correctly. Switching off the internal comparator by including the command:

CMCON        = 0b00000111;

did the trick! So now i've got a 3-digit counter of sorts, which counts from 0 to 9 across the 3 digits, and resets when a button is pressed. Next step is to implement multiplexing, and make this into a useful counter / stopwatch. Also, the button should activate an interrupt, making it more responsive. The way things are going, this may take another year.. but hey, at least now it works!

Update: Let's watch the result: