Return to Stillwater Home

Stillwater Embedded Engineering

Micro-controller Programming Example Joe Wronski 11/30/2010


The following example is large, however it demonstrates several programming techniques learned over years of micro-controller programming with a range of processors and compilers.

The code could used to run a “whole house” power monitor to learn how much power each circuit in a house uses, both instantaneously, and over a programmable interval (hour, day, week). Not all files are included in the example.


Header File:
#ifndef MAIN_H
#define MAIN_H

#ifdef MAIN_C
#define _EXTERN
#else
#define _EXTERN extern
#endif

#include <htc.h>

_EXTERN unsigned long adc_val;
_EXTERN bit bLogging;

// store a starting timestamp as 64 bits
_EXTERN unsigned long ts_hi;
_EXTERN unsigned long ts_lo;

// more...

#define INIT_MAIN_EXTERNS() dbg_bit=0; ee_address=0
// does compiler define true, false?
#ifndef FALSE
#define TRUE 1 == 1
#define FALSE !TRUE
#endif

typedef struct {
    unsigned int tm;
    unsigned int data;
} PRecordT;

// compiler is a pain in the butt about const character string arrays
typedef  struct {
    char str[10];
} ConstStrT;


// CSAFE commands
#define CMD_SET_TIME    1
#define CMD_GET_TIME    2

enum  {MSIPower, MSAPower, MSKwh, MSLog, MSMem, MSAbout, MSLast} ;

void delay20(void);
// ...
void ShowData(int idx);

#undef _EXTERN
#endif // MAIN_H


Main C file:

/*
    PMETER
    o Sums KWX in defineable increments X; Calculates KWH

    20100807
    o Total 8192 words used 6355 free 1837
*/

/* Setup chip configuration */
//#device PIC16F886

#define MAIN_C
#include "main.h"
#undef MAIN_C

__CONFIG(DEBUGEN & XT & WDTDIS ...);

#include <math.h>
// …
#include "misc.h"
//#pragma inline (SER_IORx)
#define LED RB4
static char IntroString[] = "Joe's PMeter";
const ConstStrT MenuStrings[] = {
    {"Inst Pwr"},
    // …
    {"About"}
};   
char gbuf[17];

//#define DBG_INTS
#ifdef    DBG_INTS
#warning DBG_INTS is on
#define INT_NUM(a) n=a
#else
#define INT_NUM(a)
#endif

void interrupt isr(void)
{
    unsigned int b,i;
#ifdef DBG_INTS
    unsigned int n;
    n = 20;    // default to large value
#endif // DBG_INTS   
   
    PEIE = 0;
//    GIE = 0;
    if(TMR1IF)
    {
        INT_NUM(1);
        // ...
//        T1Interrupt();
        ms2++;
        // ...
        // take an adc reading
        if((adc_pend == 0) && (GODONE == 0))
        {
            adc_pend = 1;
            GODONE = 1;
        }
    }
    // ...
    };
   
    PEIE = 1;
//    GIE = 1;
}

//-----------------------------------------------------

void main( void )
{
    unsigned char mask, b;
    // ...
    static bit btn_used = 0;

    OSCCON |= 0x71;    // 8 MhZ, internal
    SBOREN = 0;    // no brownout reset
    // more init ...

    // port inits   
    PORTB=0b00111111;   
    TRISB=0b11001111;   //0 = Output, 1 = Input

// set up Timer1 so we can have some background timing
    T1Val = 65535 - 4000;    //267;    //2 mS = 4000, 4 mS = 8000
    TMR1GE = 0;    // always counting
    // ...
    // other initialization
    SER_IOInit(TRUE);    // sets b_use_esc to desired
    INIT_MAIN_EXTERNS();   
    InitLCD();

    //Send Text
    lcd_puts(IntroString);

    ms50 = 20;
    do
    {
        if(ms50 >= 50)
        {         
            button = btn_scan(); // nb
            if(button != BUTTON_NONE)
            {
                ms50 = 0;    // give time to display menu
            }
            switch (button)
            {
                case BUTTON_UP :
                    MenuState++;    // display next
                    lcd_puts((char *) MenuStrings[MenuState].str);
                break;
                case BUTTON_DOWN :
                    MenuState--;    // display prev
                    lcd_puts((char *) MenuStrings[MenuState].str);
                break;
   
                case BUTTON_LEFT :    // down
                    if(MenuState == MSMem)
                    {
                        DataIdx--;
                        // display mem records
                    }
                break;
   
                case BUTTON_RIGHT :    // up
                    // ...
                break;
   
                case BUTTON_ENTER :
                    if(MenuState == MSMem)
                    {
                        // display memory
                    }
                    break;
                // more button cases   
                default :
                break;
            };
            ms50 = 0;
        }

        if(out_buf.frame.flags & SIO_FULL_FLAG)
        {
            // Serial input; do something
            SIO_OUTBUF_FREE();
        }

        if(ms2 >= 1)
        {
            ms2 = 0;

            if(ms1000 >= 1000)    // every second
            {
                // do ones second task...               
                switch (MenuState) {
                    case MSIPower :
                        ShowPower(InstPower);
                        break;
                    // other cases
                    default :
                        break;
                };
               
            }           
        }
    } while (run != 0);
}

unsigned int CalcPower(unsigned int v)
{
    // empirically defined breakpoints 30 88 140 203.5 278.5 342.5 423 to scale current to non linear xformer
    unsigned int k;

    unsigned int retval;
    k = v < 20 ? 0 :
        v < 30 ? 160 :
        v < 88 ? 116 :
        v < 140 ? 102 :
        v < 203 ? 79 :
        v < 278 ? 74 :
        v < 342 ? 69 :
        v < 423 ? 64 : 64;
       
        retval = (v * k) / 100;
        return retval;
}
       

// write record of lenth len to next ee location
void ee_write_rec(PRecordT *rec, int len)
{
    // ...
}

// more functions