Feb 06

Getting Started with PIC Microcontrollers – The ADC

This time, we shall take a look at the Analogue to Digital Converter (ADC) This peripheral is the link between the analogue world and the digital domain of the microcontroller.

It can be used for many things, e.g. (analogue) sensor/transducer readings such as microphones, thermistors, light sensors, pressure sensors, accelerometers, and much more. In this entry we will take a look at one of the simplest uses, reading the voltage from a potentiometer. We will look at how to set the ADC peripheral up, and how to take the reading and use it to display this information roughly on a line of LEDs.

If you are completely new to the ADCs and how they work, or want to learn a little more about the different types and theory behind them, then I suggest taking a look at these links:

Wiki ADC page

Decent Sparkfun page on ADC basics

Microchip app note on ADCs

ABCs of ADCs

At its most basic, the ADC takes a voltage level, compares it with a reference voltage, and turns it into a corresponding binary value. The PIC ADC is a 10-bit ADC, and we will be using the Vdd (+V power pin, it is possible to use a dedicated Vref pin if a different voltage to Vdd is required as reference) as the reference so the range will be from 0 – 5V (assuming you are running your PIC at 5V) The resolution, which at 10-bits equates to \( 2^{10} = 1024 \) steps means that each step equals \( \frac{5V }{1024} = 4.88mV \). For example an input of 0.5V would convert to \( \frac {0.5V}{0.00488V} = 102 \) decimal or  \( 0001100110 \) binary. An input of 2.5V would convert to  \( \frac {2.5V} {0.00488V} = 512 \) decimal or \( 1000000000 \) binary.

Okay, now to setup the PIC. We will use the PIC16F690 again with just a few additions to the original “Getting Started with Microcontrollers” schematic, namely a potentiometer and a few extra LEDs. The potentiometer can be pretty much anything you have to hand, preferably in the range of \( 1k \Omega\  –\  100k \Omega \). The LEDs used are standard 5mm red LEDs, but you can swap these if you need to (make sure you size the resistors correctly if the Vf / max current is different – see here if you need help) Also, don’t worry if you don’t have enough LEDs, you can use less if you alter the #define value accordingly in the code below and comment out the references to unused pins e.g. for 5 LEDs then the LED_INT value should be \( \frac{1024}{5} = 204 \).

Here is the schematic:

Blog_ADC_SCH

And the code:

/* * File: comet2.c * Author: Oli Glaser * * Created on 31 Jan 2015, 07:37 */ #include <xc.h> #define LED_INT 146 // Interval for ADC reading to light another LED (approx. 1024/7) #pragma config FOSC = INTRCIO // Internal RC Oscillator #pragma config WDTE = OFF // Turn watchdog off #pragma config BOREN = OFF // Brown out enable off // Set which pin controls which LED, change if you wish to use // different pin // Define which pin each LED is on #define LED1 PORTAbits.RA2 #define LED2 PORTCbits.RC0 #define LED3 PORTCbits.RC1 #define LED4 PORTCbits.RC2 #define LED5 PORTBbits.RB4 #define LED6 PORTBbits.RB5 #define LED7 PORTBbits.RB6 //#define LED8 PORTBbits.RB7 //#define LED9 PORTCbits.RC7 //#define LEDD PORTCbits.RC6 // Function definitions void init(void); void delay(unsigned int ms); // Global variables volatile unsigned int result = 0; int main(int argc, char** argv) { init(); while(1) { delay(10); unsigned int temph, templ; if(ADCON0bits.GO_DONE == 0) { temph = (ADRESH << 2); templ = (ADRESL >> 6); result = temph + templ; if(result > LED_INT) { LED1 = 1; } else { LED1 = 0; } if(result > LED_INT*2) { LED2 = 1; } else { LED2 = 0; } if(result > LED_INT*3) { LED3 = 1; } else { LED3 = 0; } if(result > LED_INT*4) { LED4 = 1; } else { LED4 = 0; } if(result > LED_INT*5) { LED5 = 1; } else { LED5 = 0; } if(result > LED_INT*6) { LED6 = 1; } else { LED6 = 0; } if(result > LED_INT*7) { LED7 = 1; } else { LED7 = 0; } ADCON0bits.GO_DONE = 1; // Start ADC conversion } } } // Basic delay routine void delay(unsigned int ms) { unsigned int i = 1000; while(ms) { i = 1000; while(i) i--; ms--; } } // Set up various things - I/Os, interrupts, etc void init(void) { // Set internal oscillator to 8MHz OSCCON = 0x70; // Turn analog inputs off ANSEL = 0; ANSELH = 0; // Set all pins to outputs TRISA = 0; TRISB = 0; TRISC = 0; // Setup ADC to read potentiometer voltage TRISCbits.TRISC6 = 1; // Set RC6/AN8 as input for ADC ANSELHbits.ANS8 = 1; // ADCON0bits.CHS = 0x08; // Set AN8 as the analog input pin ADCON1bits.ADCS = 0b010; // Set conversion clock as Fosc / 32 = 1(8MHz / 32) = 4us ADCON0bits.ADON = 1; // Turn ADC on }

Jan 17

PWM LED Comet Effect using a PIC Micro 2

This is a short follow up to the previous post on creating a comet effect with a PIC. The previous post used an 18F4550 but

this time I will use a 16F690, to demonstrate how relatively easy it is to change between different microcontrollers in the PIC

family. I have also used the preprocessor statements #ifdef and #endif which enable us to include/remove certain chunks of code

depending on a #define setting. The colouring is not correct for the 18F preprocessor sections below (they should be greyed out in MPLABX)

Have a read about these and other preprocessor statements in the XC8 (or another C) compiler manual.

Anyway, here is the code:

/*
 * File:   comet2.c
 * Author: Oli Glaser
 *
 * Created on 9 Jan 2014, 17:11
 */

// Uncomment one or the other depending on which chip used
//#define _18F4550_SETTING
#define _16F690_SETTING

#include <xc.h>

// Define a rotate speed - set higher for slower rotate
#define ROTATE_SPEED 1023

#ifdef _18F4550_SETTING
#define _XTAL_FREQ 24000000  
#pragma config FOSC = HS        // 24MHz crystal
#pragma config WDT = OFF        // Turn watchdog off
#pragma config BOR = OFF
#pragma config PWRT = ON
#pragma config IESO = ON
#endif

#ifdef _16F690_SETTING
#pragma config FOSC = INTRCIO   // Internal RC Oscillator
#pragma config WDTE = OFF       // Turn watchdog off
#pragma config BOREN = OFF
#endif

#ifdef _18F4550_SETTING
// Define which pin each LED is on
#define LED1    PORTDbits.RD7
#define LED2    PORTDbits.RD6
#define LED3    PORTDbits.RD5
#define LED4    PORTDbits.RD4
#define LED5    PORTCbits.RC7
#define LED6    PORTDbits.RD3
#define LED7    PORTDbits.RD2
#define LED8    PORTDbits.RD1
#define LED9    PORTDbits.RD0
#endif

// Set which pin controls which LED, change if you wish to use
// different pins
#ifdef _16F690_SETTING
// Define which pin each LED is on
#define LED1    PORTAbits.RA2
#define LED2    PORTCbits.RC0
#define LED3    PORTCbits.RC1
#define LED4    PORTCbits.RC2
#define LED5    PORTBbits.RB4
#define LED6    PORTBbits.RB5
#define LED7    PORTBbits.RB6
#define LED8    PORTBbits.RB7
#define LED9    PORTCbits.RC7
#endif

#define LEDD    PORTCbits.RC6

// Function definitions
void init(void);

// Global variables
unsigned int rot = 0;
unsigned char i = 0;
// Set on/off times for each LED in the "comet", for head to tail
unsigned char a = 0;
unsigned char b = 0;
unsigned char c = 0;
unsigned char d = 2;
unsigned char e = 5;
unsigned char f = 15;
unsigned char g = 50;
unsigned char h = 150;
unsigned char j = 240;
unsigned char j2 = 0;

int main(int argc, char** argv) {

    init();

    while(1)
    {

    // Reset PWM variable
    if(i == 255) i = 1;

    // Rotate LEDs after desired period and reset rotate variable
    if(rot >  ROTATE_SPEED)
    {
        j2 = j;
        j = h;
        h = g;
        g = f;
        f = e;
        e = d;
        d = c;
        c = b;
        b = a;
        a = j2;
        rot=0;
    }
    }
}


// Interrupt Service Routine (keyword "interrupt" tells the compiler it's an ISR)
void interrupt int_routine(void)
{
      // Check it's the timer that has interrupted
    if (PIE1bits.TMR2IE && PIR1bits.TMR2IF)
    {
        PIR1bits.TMR2IF = 0;    // Clear interrupt flag

        // Toggle LEDs if their interval has elapsed
        (i < a) ? LED1=1 : LED1=0;
        (i < b) ? LED2=1 : LED2=0;
        (i < c) ? LED3=1 : LED3=0;
        (i < d) ? LED4=1 : LED4=0;
        (i < e) ? LED5=1 : LED5=0;
        (i < f) ? LED6=1 : LED6=0;
        (i < g) ? LED7=1 : LED7=0;
        (i < h) ? LED8=1 : LED8=0;
        (i < j) ? LED9=1 : LED9=0;

        // Increment PWM and rotate variables
        i++;
        rot++;
    }

    // process other interrupt sources here, if required

}

// Set up various things - I/Os, interrupts, etc
void init(void)
{
#ifdef _18F4550_SETTING
    OSCCON = 0x70;  // If using internal oscillator set it to 8 MHz
    SPPCON = 0x00;
    ADCON1 = 0x0F;      // Turn analog inputs off
    CMCON = 0x07;       // Turn comparators off
    CCP1CON = 0x00;
    TRISD = 0x00;
    TRISCbits.TRISC7 = 0;
    PORTD = 0x00;
    TRISA = 0x00;
#endif

#ifdef _16F690_SETTING
    // Set internal oscillator to 8MHz
    OSCCON = 0x70;
    // Turn analog inputs off
    ANSEL = 0;
    ANSELH = 0;
    // Set all pins to outputs
    TRISA = 0;
    TRISB = 0;
    TRISC = 0;
#endif

    // Use Timer 2 for PWM timing
    T2CON = 0b00000000;
    TMR2 = 0x00;
    PR2 = 0x7F;             // Timer 2 period register
    T2CONbits.TMR2ON = 1;

    // Setup interrupt for Timer 2
    INTCONbits.GIE = 1;     // Interrupts on
    INTCONbits.PEIE = 1;    // Peripheral interrupts on
    PIE1bits.TMR2IE = 1;    // Timer 2 interrupt enable
}
 

Sep 02

PWM LED Comet Effect using a PIC Micro

This is a response to a comment on Getting Started with PIC Microcontrollers – the PWM Peripheral from Jayanath,

which requested some example code for a “12 LED chaser with comet tail  effect”. I thought since this would be a

pretty good example of using PWM on multiple pins I would post a rough example. I used 9 LEDs as the number is

not so important – the code can easily be scaled to allow for more/less. To keep it simple I drive the LEDs direct from

individual pins – for larger numbers we would want to be looking at using multiplexing and extra components

such as shift registers, possibly even a CPLD/FPGA. We will save this for the future.

Due to the number of pins we need PWM on, we cannot use the PWM peripheral, as it only does 4 pins maximum.

So we need to do the PWM in software – this is not so difficult, we just pick a period and slice it into bits – the more bits

the higher resolution our PWM has. For example, if we pick 1ms for our period, and slice it into 1024 bits (roughly 1us each)

we would have Log2(1024) = 10 bit resolution (Log2 is the logarithm of base 2 – another way to look at it is 2^10 = 1024)

If we want a 50% duty cycle with the above, we simply turn a pin on for 512us and off for the other 512us.

The main problems we face are the fact that the human eye’s response to light is not linear, and we can detect extremely

small amounts of light. Also an LED responds very quickly (unlike an incandescent bulb it turns on/off very fast) so can

appear to flicker unless the frequency is high enough. These two facts work against us, as the higher the resolution,

the faster the “time slices” we need. Anyway, studying the code should prove informative, and testing/improving it

even more so. For this reason I have made it as simple as possible (I hope) so the concept is clear.

I used a PIC18F4550 for this test, so be sure to make any necessary changes to pins/registers in your initialisation

routine if you are using a PIC from a different family.

/* 
 * File:   comet.c
 * Author: Oli
 *
 * Created on 23 August 2013, 22:14
 */


#include <xc.h>

#define _XTAL_FREQ 24000000

#pragma config FOSC = HS        // 24MHz crystal
#pragma config WDT = OFF        // Turn watchdog off
//#pragma config PLLDIV = 1       // Divide by 5 (20MHz Crystal input)
#pragma config BOR = OFF
#pragma config PWRT = ON
#pragma config IESO = ON

// Define which pin each LED is on
#define LED1    PORTDbits.RD7
#define LED2    PORTDbits.RD6
#define LED3    PORTDbits.RD5
#define LED4    PORTDbits.RD4
#define LED5    PORTCbits.RC7
#define LED6    PORTDbits.RD3
#define LED7    PORTDbits.RD2
#define LED8    PORTDbits.RD1
#define LED9    PORTDbits.RD0

// Function definitions
void init(void);

int main(int argc, char** argv) {

    init();

    while(1)
    {
        // do nothing...
    }
}


// Interrupt Service Routine (keyword "interrupt" tells the compiler it's an ISR)
void interrupt int_routine(void)
{
    static unsigned char i = 0;
    static unsigned int rot = 0;
    static unsigned char a = 0;
    static unsigned char b = 0;
    static unsigned char c = 0;
    static unsigned char d = 2;
    static unsigned char e = 5;
    static unsigned char f = 15;
    static unsigned char g = 50;
    static unsigned char h = 150;
    static unsigned char j = 240;
    static unsigned char j2 = 0;

      // Check it's the timer that has interrupted
    if (PIE1bits.TMR2IE && PIR1bits.TMR2IF)
    {
        PIR1bits.TMR2IF = 0;    // Clear interrupt flag

        // Toggle LEDs if their interval has elapsed
        (i < a) ? LED1=1 : LED1=0;
        (i < b) ? LED2=1 : LED2=0;
        (i < c) ? LED3=1 : LED3=0;
        (i < d) ? LED4=1 : LED4=0;
        (i < e) ? LED5=1 : LED5=0;
        (i < f) ? LED6=1 : LED6=0;
        (i < g) ? LED7=1 : LED7=0;
        (i < h) ? LED8=1 : LED8=0;
        (i < j) ? LED9=1 : LED9=0;
    }

    i++;
    rot++;

    // Reset PWM variable
    if(i == 255) i = 1;

    // Rotate LEDs after desired period and reset rotate variable
    if(rot > 2550)
    {
        j2 = j;
        j = h;
        h = g;
        g = f;
        f = e;
        e = d;
        d = c;
        c = b;
        b = a;
        a = j2;
        rot=0;
    }
    
    // process other interrupt sources here, if required
}

// Set up various things - I/Os, interrupts, etc
void init(void)
{
    OSCCON = 0x70;  // If using internal oscillator set it to 8 MHz
    SPPCON = 0x00;
    ADCON1 = 0x0F;      // Turn analog inputs off
    CMCON = 0x07;       // Turn comparators off
    CCP1CON = 0x00;
    TRISD = 0x00;
    TRISCbits.TRISC7 = 0;
    PORTD = 0x00;
    TRISA = 0x00;

    // Use Timer 2 for PWM timing
    T2CON = 0b00000000;
    TMR2 = 0x00;
    PR2 = 0x2F;             // Timer 2 period register
    T2CONbits.TMR2ON = 1;

    // Setup interrupt for Timer 2
    INTCONbits.GIE = 1;     // Interrupts on
    INTCONbits.PEIE = 1;    // Peripheral interrupts on
    PIE1bits.TMR2IE = 1;    // Timer 2 interrupt enable
}

Comet3 Comet

Aug 11

Getting Started with PIC Microcontrollers – The PWM Peripheral

In this tutorial, we will take a look at the PWM (Pulse Width Modulation) peripheral, as it is one of the most common and widely used peripherals. Almost every microcontroller has a PWM peripheral in one form or another, usually as part of a larger peripheral. For example in the PIC16F690, the peripheral we are interested in is named the “Enhanced Capture/Compare PWM Module”, and it can be used to time input waveforms (Capture) or compare timing and trigger on a certain event (Compare) as well as output the PWM we are interested in for this tutorial.

So what’s so good about PWM? What can it be used for?

PWM has many uses, the most common being efficient control of power to components (usually driven from an external transistor) such as an LED, Motor, Heating Element, Piezo transducer, and many others. We can also use it as a simple DAC (Digital to Analogue Converter) as we will see in later examples.

Why can’t we just use a varying analogue output signal to control the power (e.g. a DAC) or a potentiometer to control the power?

To answer this we need to go through a couple of examples:

Say we have a medium power 12V / 4W automotive bulb (a standard incandescent type) controlled from our PIC we wish to add dimming capability to. You may be wondering why we are not using an LED for this demonstration – this is due to the fact an LED is not a simple resistive load so it adds complexity which will distract from the main points, we will cover LED driving later.

We know the bulb is rated at 12V and 4W, so using Ohm’s law we can calculate the current through it and it’s on resistance (incandescent bulbs have a lower resistance when cold):

$$ I_{BULB} = \frac{4W}{12V} = 333mA $$

$$ R_{BULB} = \frac{12V}{333mA} = 36\Omega $$

So our supply for the bulb is 12V, and for the PIC it is 5V. Since the average drive capability of a microcontroller output is around a maximum of 20mA and the logic high voltage is only the supply voltage (5V in this case), we need a transistor and a 12V power rail to drive the bulb. Using a transistor as a basic switch to turn the bulb on and off is pretty easy – we will go into more detail on transistor circuits in a future blog, but for now we are focusing on the benefit of PWM. Still, here is a basic switch circuit with a simple explanation:

Blog_Transistor_Switch

R1 limits the current into the base from the microcontroller output. The base current can be calculated by:

$$ I_b = \frac{V_{SUPPLY} – 0.7V}{R1} $$

so in the circuit above

$$ I_b = \frac{5V-0.7V}{10k \Omega} = 0.43mA $$

Since the BC-337-40 has a minimum current gain of 250, this is plenty of base current to switch the load current. We can calculate this easily too:

$$ I_{load} = \frac{V_{supply}}{R_{load}} = 22.7mA $$

minimum current gain required at base:

$$ \frac{22.7mA}{250} = 90.8uA $$

So we have plenty of current to spare – for a switch it’s a good idea to use at least 3 times the minimum current, due to the gain dropping as the transistor turns on fully (don’t worry too much about this now, it’s just a rule of thumb, you’ll get a feel for it very quickly)

Dimming – the analogue solution

Right, lets compare linear to PWM dimming to see how PWM helps us. First we will set up a dimming circuit controlled by a linear voltage (e.g. from a Digital to Analogue Converter or a potentiometer) We will use a common op-amp based constant current circuit (a useful circuit in it’s own right) and the incandescent bulb detailed above with the BC337-40.

Blog_PWM_Analog_Method

Basically the opamp tries to keep the voltages on it’s two inputs equal, so if we have 330mV at CONTROL_IN, then we should have 33mV at the + input and 33mV at the top of R1. This means that 33mV / 100mΩ = 330mA is flowing through R1, the transistor and the bulb (plus a tiny bit of base current which we can ignore for this example) as they are all in series with one another. However, the purpose here is to examine the power dissipation through the transistor, so let’s take a look at the simulation:

Blog_PWM_Analog_Method_Sim

The top trace is the bulb power dissipation, and the middle trace is the transistor power dissipation. The bottom trace is the collector voltage and bulb/collector current.

Now looking at the middle trace, we see that the power consumption is highest at halfway between zero current and the full current and zero and full voltage – 166.6mA and 6V (166.6mA * 6V = 1W). So if we wanted our bulb at half brightness, we need to dissipate 1W in the transistor! For many small general purpose transistors in a SOT23 or TO-92 package, 1W is not achievable without a heat sink. For example, the TO-92 package has a junction to case thermal resistance of 200°C/W – this means without a heat sink it would want to rise 200°C above ambient (obviously it would blow before it got that far…)

We can do much better than this.

Looking again at the above graphs, there is some useful information. We notice that the dissipation drops to (almost) zero at the minimum and maximum points. This is important and can be taken advantage of – for half brightness, what if instead of having the bulb at 50% current all of the time, we have it at 0% current half the time and 100% current half the time? This is where PWM comes in.

Dimming – the PWM solution

Okay, let’s take a look at the PWM version. The first thing you will notice is it’s a nice and simple circuit, using only 1 transistor. Since we are using it as a basic switch we don’t need any precise control with feedback the opamp was used for in the previous circuit, just an on or off signal from our PIC pin into the PWM_IN port on the schematic. R1 is to limit the current into the base as shown in the switch circuit above, though it’s lower this time as the bulb needs more current (equivalent to a 36Ω resistor)

Blog_PWM

Now let’s look at the simulation for this circuit with a PWM input. The duty cycle (this is what we call the percentage of on/off time) is 50%, which means the pin outputs high half the time and low half the time. If the duty cycle were, say, 25%, then the pin would output high 25% of the time and low 75% of the time. As long as the switching frequency is higher than 25Hz or so (>100Hz is better and easy for the PWM peripheral), our eyes cannot detect flicker and the bulb appears to be a steady brightness (see POV – Persistence Of Vision). This 50% duty cycle is half power, at which the analogue circuit transistor was dissipating 1W. Let’s examine these waveforms:

Blog_PWM_Sim2

At the top is our bulb power, in the middle is our transistor power dissipation, and the bottom is the transistor current and collector voltage. The box in the top right displays the average power dissipation for the transistor, only 35mW! This is over 28 times less than the analogue solution, and a power level the small transistor can easily handle.

Hopefully this helps to show one good reason why PWM is very useful. There are plenty of other cool things we can do with PWM, like combining with a low pass filter to generate analogue signals (e.g. play music/sound effects), data transmission (e.g. use as a carrier for IR signals) We will try to take a look at these in future tutorials.

Setting up the PIC for PWM

Okay, enough about why PWM is good, let’s look at setting the PIC PWM peripheral up. We will create a basic routine which fades an LED from zero to full brightness and repeats. We can add to this code later and implement control over the brightness using buttons or a knob, adjust patterns for flashing effects, etc. Be sure to read the section on the ECCP peripheral in the datasheet, and experiment with the code below to get a feel for how it works.

Here is the code:

/* 
 * File:   bulb_pwm.c
 * Author: Oli Glaser
 * License: MIT license
 * Created on 13 August 2013
 */

// main include file - this contains all the "friendly names"
// for the pins and peripherals
#include <xc.h>
// Turn the Watchdog Timer off
#pragma config WDTE = OFF

// Function definitions
void init(void);

int main(int argc, char** argv) {

    // To keep things tidy we put the setup in an initialisation routine this time
    init(); 
    PORTCbits.RC0 = 1;

    while(1) // Infinite loop
    {
        /* Sit and wait for interrupt!
         * We could be doing other things here
         * in the meantime */
    }

}

// Interrupt Service Routine (keyword "interrupt" tells the compiler it's an ISR)
void interrupt int_routine(void)
{
    // Check it's the timer that has interrupted
    if (PIE1bits.TMR1IE && PIR1bits.TMR1IF)
    {
        PIR1bits.TMR1IF = 0;    // Clear interrupt flag
        T1CONbits.TMR1ON = 0;   // Turn timer off to reset count register

        TMR1H = 0xE7;   // Reset timer count - 0xE795 = 20Hz
        TMR1L = 0x95;   //

        T1CONbits.TMR1ON = 1;   // Turn timer back on
        CCPR1L++; // Increment duty cycle
    }

    // process other interrupt sources here, if required
}

// Initialisation routine
void init(void)
{
    ANSEL = 0;
    ANSELH = 0;
    TRISCbits.TRISC5 = 1; // Disable RC5 output driver till PWM configured
    // Setup PWM on pin RC5/CCP1/P1
    PR2 = 0xff; // Set PWM period (PR2+1) * 4 * Tosc * TMR2_Prescale_value
    // period = (255 + 1) * 4 * 0.25us * 16 = 4.1ms
    // frequency = 1 / 4.1ms = 244Hz
    CCP1CON = 0x00; // Reset all bits to 0 
    CCP1CONbits.CCP1M = 0xF; // Set up for PWM mode, with only P1 active
    CCPR1L = 0x80; // Setup for 50% duty cycle
    // Setup Timer 2 for PWM
    PIR1bits.T2IF = 0; // Clear Timer 2 interrupt flag
    T2CONbits.T2CKPS = 0x3; // Set Timer 2 prescaler to 1:16
    T2CONbits.TMR2ON = 1; // Enable Timer 2
    while(PIR1bits.T1IF); // Wait for first overflow
    TRISCbits.TRISC5 = 0; // Sets pin RC5 to output for PWM

    // Setup Timer 1 for incrementing PWM duty cycle and fading bulb
    T1CON = 0;
    T1CONbits.T1CKPS0 = 1;  // T1CKPS = 11 = 1:8 prescaler
    T1CONbits.T1CKPS1 = 1;  // so timer clock = 1MHz / 8 = 125kHz
    /* Calculating for PWM duty cycle increment every 50ms, so period needs
     * to be 20Hz. Timer 1 clock is 125kHz, so
     * for 20Hz, we divide 125kHz by 20 = 6250. Then we subtract this
     * from the rollover value of 65,535, so 65,535 - 6250 = 59,285
     * which is 0xE795 in hex. Timer 1 has two 8-bit high and low
     * registers, so we put 0xE7 in the high and 0x95 in the low */
    TMR1H = 0xE7;           
    TMR1L = 0x95;
    T1CONbits.TMR1ON = 1;   // Turn timer on
    INTCONbits.GIE = 1;     // Enable global interrupts
    INTCONbits.PEIE = 1;    // Enable peripheral interrupts
    PIR1bits.TMR1IF = 0;    // Clear Timer 1 interrupt flag
    PIE1bits.TMR1IE = 1;    // Enable Timer 1 interrupt
}

 

Mar 20

Getting Started with PIC Microcontrollers–The Timer Peripheral and Interrupts

This time we shall take a look at the timer peripheral; one of the most common peripherals, available on just about every microcontroller. We will also have a brief look at interrupts and how useful they are.

An excellent way of demonstrating both is by revising the original LED blink code in Getting Started with Microcontrollers to make it a lot more efficient.

Here is the original code for reference:

/* 
 * File:   led_blink.c
 * Author: Oli Glaser
 *
 * Created on 02 January 2013, 06:59
 */

// main include file - this contains all the "friendly names"
// for the pins and peripherals
#include <xc.h>
// Turn the Watchdog Timer off
#pragma config WDTE = OFF 

void delay(int d); // This is the prototype declaration for the Delay function

int main(int argc, char** argv) {

    TRISBbits.TRISB6 = 0; // This sets pin RB6 to output

    while(1) // Infinite loop
    {
        PORTBbits.RB6 = 1;  // Set RB6 pin to logic high (5V)
        delay(500);         // Delay for roughly 500ms
        PORTBbits.RB6 = 0;  // Set RB6 pin to logic low (0V)
        delay(500);         // Delay for roughly 500ms
    }

}

// Delay routine
void delay(int d)
{
    int i;  // Declare variable to be used in the loop

    while(d)    // While d > 0
    {
        i = 100;    // set i to 100 for inner loop
        while(i)    // while i > 0
        {
            i--;    // decrement i (e.g. i will equal 99, 98, 97,...)
        }

        d--;    // decrement d
    }
}

So what’s so bad about this code? Two main things:

  1. The timing is not very accurate. Even if we calculate and count the cycles properly (which we don’t do in the above code, it’s just a rough estimate based on observation of the LED) we have to factor in the time taken to execute the instructions for the delay() loop and main while(1) loop, which complicates matters, and depending on the compiler or optimisation settings may vary quite a bit. 

  2. It spends 99% of it’s time doing nothing but counting cycles, which is a “criminal” waste of processor power. While it is doing this, the main loop is tied up and cannot do anything else. In this code that’s not an issue as we are not doing anything else, but imagine if we were – for example using serial communications such as SPI, I2C, UART, etc, and a message arrived during the delay routine; it would almost certainly be missed. Since we spend less than 1% of the time in the main loop the chances of this are very high.

How can we fix these issues?

For the first issue, we could always use the same compiler, make sure it is always set to the same optimisation level, use the tools to count exactly how many instructions are executed in the loops, and adjust the delay routine for this.

For the second issue, if were expecting it, during the delay loop we could poll for incoming data on every cycle of the loop and read/act on it if some arrives. This type of technique is used sometimes but it’s still quite an inefficient way of doing things since we are checking continuously even though no data may arrive for long periods. Every check requires processor cycles, most of which would be redundant and also have to factored into the delay timing.

So we come to interrupts. To clarify the concept with an everyday analogy, imagine you (the processor) have a task which involves switching on and off a signal light (our LED) every couple of hours. You have nothing but a basic clock on the wall. To avoid being late for the switching you would have to continually check the clock to see if it’s time yet (i.e. “polling”)

Now imagine instead of this you have a watch which has an alarm function (the peripheral) which will alert you a couple of minutes beforehand (the interrupt). In this case you have freed up your time to do other things instead of continually checking the wall clock. Now, humans are intelligent enough to have a sense of time so in reality we probably wouldn’t need to check the clock e.g. every minute, and could manage to do other things in the meantime (even so, the alarm system is more efficient even for the human). However a processor is “dumb” and has to check repeatedly until it has a match for its delay setting, so we waste a lot of cycles.   

So if we do things correctly, we should end up with >99% of the processors time free, and <1% taken up servicing the interrupt when it arrives. So let’s take a look at the revised code for our LED blink firmware. When debugging, an easy way to check how much time you are spending in a routine is to set a spare pin high on entry and low on exit – then you can watch it on the scope to see the percentage of time it is high. For multiple routines you can use more than one pin (you can add this into the code below and the previous code to compare the differences in timing – I’ll leave this as an exercise for the reader)

NOTEIf you choose to use a pin other than RB6 as your LED pin, then as Chris discovered and kindly took the time to comment, you are likely to run into a problem with the default analogue pin settings. The quick solution is to add:

ANSEL = 0;

ANSELH = 0;

To your init function – The ADCON0 register should default to 0 on reset anyway so you should not need to set this (but it won’t do any harm if you wish to)

Basically, any pins with an analogue function (e.g. marked AN0, AN1, AN2, etc) default to this state on power up, so you need to turn the analogue inputs off in order for them to function as digital pins. The name of the register can vary from part to part (for the PIC16F690 it is as above), but the details are always there in the datasheet under the I/O PORTS section and the ADC section. There are quite a few other things that can cause pins not to function as expected, but the analogue settings are the main one that new users run into first. 

Interrupt based LED blink

/* 
 * File:   led_blink_timer.c
 * Author: Oli Glaser
 *
 * Created on 20 March 2013, 21:31
 */

// main include file - this contains all the "friendly names"
// for the pins and peripherals
#include <xc.h>
// Turn the Watchdog Timer off
#pragma config WDTE = OFF

// Function definitions
void init(void);

int main(int argc, char** argv) {

    // To keep things tidy we put the setup in an initialisation routine this time
    init(); 

    while(1) // Infinite loop
    {
        /* Sit and wait for interrupt!
         * We could be doing other things here
         * in the meantime */
    }
    
}

// Interrupt Service Routine (keyword "interrupt" tells the compiler it's an ISR)
void interrupt int_routine(void)
{
    // Check it's the timer that has interrupted
    if (PIE1bits.TMR1IE && PIR1bits.TMR1IF)
    {
        static char i = 0;
        PIR1bits.TMR1IF = 0;
        T1CONbits.TMR1ON = 0;   // Turn timer off to reset count register
        TMR1H = 0x0B;   // Reset timer count - 0x0BDB = 1Hz
        TMR1L = 0xDB;   //
        T1CONbits.TMR1ON = 1;   // Turn timer back on
        PORTBbits.RB6 = ~PORTBbits.RB6;  // Toggle RB6

    }

    // process other interrupt sources here, if required
}

// Initialisation routine
void init(void)
{
    TRISBbits.TRISB6 = 0;   // This sets pin RB6 to output

    // Setup Timer 1
    T1CON = 0;
    T1CONbits.T1CKPS0 = 1;  // T1CKPS = 11 = 1:8 prescaler
    T1CONbits.T1CKPS1 = 1;  // so timer clock = 1MHz / 8 = 125kHz
    /* Calculating for 1 Hz LED flash, so period needs to be 2Hz as
     * interrupt toggles between on/off. Timer 1 clock is 125kHz, so
     * for 2Hz, we divide 125kHz by 2 = 62,500. Then we subtract this
     * from the rollover value of 65,535, so 65,535 - 62,500 = 3035
     * which is 0x0BDB in hex. Timer 1 has two 8-bit high and low
     * registers, so we put 0x0B in the high and 0xDB in the low */
    TMR1H = 0x0B;           // 65,535 - 62500 = 3035 = 0x0BDB
    TMR1L = 0xDB;           // to count for 0.5 seconds till timer rollover
    T1CONbits.TMR1ON = 1;   // Turn timer on
    INTCONbits.GIE = 1;     // Enable global interrupts
    INTCONbits.PEIE = 1;    // Enable peripheral interrupts
    PIR1bits.TMR1IF = 0;    // Clear Timer 1 interrupt flag
    PIE1bits.TMR1IE = 1;    // Enable Timer 1 interrupt
}

The code is well commented, so hopefully not too much needs to be said about it. I would recommend reading the very detailed Timer 1 section (section 6) thoroughly in the datasheet to get a full idea of what the peripheral is capable of.  If anyone has any questions, just leave a comment.

So how accurate is the timing? Even with the internal RC oscillator it’s just about spot on (for better timing accuracy we would use a crystal oscillator) Here is a scope clip of the results:

Blog - Timer LED Blinkb We can see according to the cursor measurement (in the white box, taken between the two vertical purple dotted lines) it’s 1Hz exactly. The oscilloscope is pretty accurate frequency wise so we can assume it’s very close (it does have a 6 digit frequency counter but this only works for frequencies over 2Hz)

Hopefully this simple introduction has helped to demonstrate the importance of peripherals and interrupts. In future tutorials we will look at combining tasks to get an even better sense of how much more efficient they can make your firmware.

Jan 26

Getting Started with PIC Microcontrollers – The Header File

This time we’ll have an in depth look at a specific area which can present problems for the beginner. Using the simple code presented in Getting Started with Microcontrollers as our example. we will talk a bit about the main xc.h header file, how it works, and how important it is. Understanding this file, and how the registers are mapped to the names we use in the code is very important, will help you form a clearer picture of what is happening behind the scenes, and can save you a lot of debugging time later on. 

Okay, let’s jump right in, here is the previous code for reference:

/* 
 * File:   led_blink.c
 * Author: Oli Glaser
 *
 * Created on 02 January 2013, 06:59
 */

// main include file - this contains all the "friendly names"
// for the pins and peripherals
#include <xc.h>
// Turn the Watchdog Timer off
#pragma config WDTE = OFF 

void delay(int d); // This is the prototype declaration for the Delay function

int main(int argc, char** argv) {
   
    TRISBbits.TRISB6 = 0; // This sets pin RB6 to output
    
    while(1) // Infinite loop
    {
        PORTBbits.RB6 = 1;  // Set RB6 pin to logic high (5V)
        delay(500);         // Delay for roughly 500ms
        PORTBbits.RB6 = 0;  // Set RB6 pin to logic low (0V)
        delay(500);         // Delay for roughly 500ms
    }
    
}

// Delay routine
void delay(int d)
{
    int i;  // Declare variable to be used in the loop

    while(d)    // While d > 0
    {
        i = 100;    // set i to 100 for inner loop
        while(i)    // while i > 0
        {
            i--;    // decrement i (e.g. i will equal 99, 98, 97,...)
        }

        d--;    // decrement d
    }
}

 

Some of you may wonder why the main routine has a return value and argument options, since they are never used in firmware such as this (i.e. the code is executed automatically on power up and never exits) This is simply the default template MPLABX starts you off with when you begin a new project, and conforms to the standard ANSI C entry point. You could also use for example: int main(void) if you wish, but I just leave it as it is.

The main header file

Now for a very important issue, and one which causes many beginners much frustration – the main header file <xc.h>. This header file is a global header that will point to another header file in turn, and eventually point to the header file for the particular part you are using. This contains the friendly names for all the registers (e.g. Ports, Peripherals, etc) in your device, and allows you to type things such as PORTB = 0;

Let’s examine this in detail, and follow the header file “trail” to find out how it does this. To do this, highlight  <xc.h> right click and select Navigate->Go to Declaration as shown below:

MPLABX_GotoImp 

Now, the xc.h file should open in your editor:

#ifndef _XC_H_
#define _XC_H_

#ifdef __XC8
#include <htc.h>
#endif

#endif        //_XC_H

You will notice there is not a lot there, apart from a couple of preprocessor commands, one makes sure that _XC_H_ is defined, and the other includes <htc.h> if __XC8  is defined, which it will be since we are using the XC8 compiler. So let’s follow <htc.h> and see where that leads; use exactly the same method as described above, and you should find this code:

#ifndef _HTC_H_
#define _HTC_H_

#if defined(__CCI__) && !defined(_XC_H_)
#warning "CCI projects should use the top-level support file xc.h\nIncluding xc.h instead"
#include <xc.h>
#endif

/* Definitions for _HTC_EDITION_ values */
#define __LITE__ 0
#define __STD__ 1
#define __PRO__ 2

/* common definitions */

#define    ___mkstr1(x)    #x
#define    ___mkstr(x)    ___mkstr1(x)



/* HI-TECH PICC / PICC-Lite compiler */
#if    defined(__PICC__) || defined(__PICCLITE__)
#include <pic.h>
#endif

/* HI-TECH PICC-18 compiler */
#if    defined(__PICC18__)
#include <pic18.h>
#endif

/* MPLAB C18 Compatibility Header */
#ifdef __18CXX
#include <pic18.h>
#endif

/* HI-TECH dsPICC compiler */
#if    defined(__DSPICC__)
#include <dspic.h>
#endif

/* HI-TECH C for PIC32 */
#if defined(__PICC32__)
#include <pic32.h>
#endif

#endif

Okay, there’s a bit more here, but it still is not the header file we are looking for. Essentially, it is checking to see which compiler is being used and including yet another header based on what it finds. In our case, it is the __PICC__ or __PICC_LITE__ compiler which has already been defined, so we follow <pic.h> (you can tell which code is being used as the unused code will be greyed out, whilst the <pic.h> should be highlighted). Now we get to another file which includes <pic_chip_select.h> and <eeprom_routines.h>. Follow the <pic_chip_select.h>  header, and we are finally getting somewhere – this is a huge file that checks for the particular part you are using (defined automatically when you select the part during project setup) and includes the header that contains all the important definitions mentioned at the start. Depending on your part, you will see one of the related sections highlighted. I used the PIC16F690 for the first tutorial, so scanning the file, about halfway down I come across this highlighted code:

PIC_Include  

Now if we look in pic16f690.h (or whatever the header file for you part is), we will see a very long list of defintions which map the addresses of the registers to the “friendly names” we use in our code. Since we used PORTB in our code, lets take a look at the definition for this in the file:

// Register: PORTB
extern volatile unsigned char           PORTB               @ 0x006;
#ifndef _LIB_BUILD
asm("PORTB equ 06h");
#endif
// bitfield definitions
typedef union {
    struct {
        unsigned                        :4;
        unsigned RB4                    :1;
        unsigned RB5                    :1;
        unsigned RB6                    :1;
        unsigned RB7                    :1;
    };
} PORTBbits_t;
extern volatile PORTBbits_t PORTBbits @ 0x006;
// bitfield macros
#define _PORTB_RB4_POSN                                     0x4
#define _PORTB_RB4_POSITION                                 0x4
#define _PORTB_RB4_SIZE                                     0x1
#define _PORTB_RB4_LENGTH                                   0x1
#define _PORTB_RB4_MASK                                     0x10
#define _PORTB_RB5_POSN                                     0x5
#define _PORTB_RB5_POSITION                                 0x5
#define _PORTB_RB5_SIZE                                     0x1
#define _PORTB_RB5_LENGTH                                   0x1
#define _PORTB_RB5_MASK                                     0x20
#define _PORTB_RB6_POSN                                     0x6
#define _PORTB_RB6_POSITION                                 0x6
#define _PORTB_RB6_SIZE                                     0x1
#define _PORTB_RB6_LENGTH                                   0x1
#define _PORTB_RB6_MASK                                     0x40
#define _PORTB_RB7_POSN                                     0x7
#define _PORTB_RB7_POSITION                                 0x7
#define _PORTB_RB7_SIZE                                     0x1
#define _PORTB_RB7_LENGTH                                   0x1
#define _PORTB_RB7_MASK                                     0x80

Now we can see how it works. Don’t worry if you don’t fully understand it, you don’t need to in order to use it, but it helps to know where these definitions are and why the compiler will complain if it can’t find them. For further reading check the XC8 Compiler User’s Guide (this will also be on your computer under the Microchip->MPLAB XC8 folder). We won’t fully cover all the above code (General C books, PIC specific C books, and the users guide mentioned cover all this), apart from the most important part of it:

// Register: PORTB
extern volatile unsigned char           PORTB               @ 0x006;

Note the @ 0x006; part of this code. This points the PORTB declaration to a specific address in the microcontrollers memory. If we check the PIC16F690 datasheet and look at page 31, we will see a table of the Special Function Registers and their addresses:

PIC16F690Registers

Note that PORTB is at address 06h, as defined in the header file. If you check the other registers you will see they will all correspond with the datasheet table.

Hopefully the above has helped to show how important the main header file is, and how the code connects with the hardware. This brings us on to the last part of this tutorial; making sure your tools know where to look for the header file. Firstly, click on the spanner icon in the “Dashboard” at the bottom left hand of your screen. This brings up the Project Properties dialog box. Select compiler properties, pre-processing and messages in the dropdown and in the include directories box, you need to point to the folder which contains the compiler include files:

IncludeDir

Usually it’s under something like C:\Program Files\Microchip\xc8\v1.12\include as shown in the clip, but it may be different if for example, you chose another install directory or Microchip decides to rearrange things in the near future.

Jan 04

Getting Started with PIC Microcontrollers

How to get started with microcontrollers is a very common question, and can be quite daunting for the newcomer. We will start right from the beginning, assuming almost no prior knowledge of the subject. Although we will probably cover other micros in later tutorials, we will start with the very popular PIC microcontroller range from Microchip.

What tools do I need to get started?

Luckily the answer to this question is “not very much”. At the minimum you will need:

  • A means of programming your microcontroller with the code you have written (generally known as “firmware”) There are hundreds of programmers to choose from out there, ranging from cheap “hobbyist type” to very expensive production programmers. For me, an important feature of the programmer is the ability to debug your code, and be able to be driven from within MPLAB or MPLABX – these are Microchips Integrated Development Environments (IDEs – we will cover these shortly)  Personally, I recommend either the PICkit3 or the ICD3, as these are the two I have experience with. Both have the features mentioned above, the only difference is that the ICD3 does a little more on the debugging side, programs faster and is a  “production programmer”. The is a comparison of their relative features here. If you don’t know what to start with, I’d go for the PICkit3.
  • A tool with which to organise your projects and write your code (an IDE) Microchip provides MPLAB and MPLABX for this purpose. MPLAB was the only IDE from Microchip for a long time, but recently MPLABX has been released and is the new IDE Microchip are focusing on. It’s based on Netbeans, and you get a feature packed tool somewhat like Eclipse/Visual Studio. I would pick MPLABX here, as it has many more features, plus MPLAB will probably be discontinued at some future point. If you are going to follow this tutorial right through to the end, then now would be good time to start downloading and installing MPLABX, and the XC8 compiler (free version – in the left column of the linked page) whilst you are reading the rest. Don’t worry if you have no components yet, if you wish you can use the simulator to run the code presented later on. For this all you need is your computer.
  • Something to test your microcontroller with. This could be a Development Board, or simply a breadboard you can layout yourself. On the dev board side, at $70 the PICkit3 starter kit is a pretty good choice; it’s on this page along with other starter kits. It comes with everything you need to get started, plus some tutorials on basic assembly programming. Olimex and other independent vendors also make some good dev boards. On the breadboard side, it’s a little more complex but more versatile, plus you will probably learn more this way. Which you choose depends on how much of a challenge you feel up to. We will cover the breadboard option here, but the discussion of coding and firmware will be just as relevant whichever option you take.
  • Optional – some prior knowledge of programming or electronics – particularly the C language as this is the language of choice for small micros. Assembler is well worth looking at, but nowadays memory is cheap, micros are fast and making firmware as small and efficient as possible is no longer as important as it used to be (many folk will disagree with this, and you will frequently see Assembly vs C discussions – my view is learn C for 99% of your firmware and use inline assembly when you need to, since they can be used together)

The breadboard route

First you need a breadboard (read link for how they work) and a few components to go with your microcontroller. A breadboard is used to make circuits up quickly and conveniently during prototyping. They all look something like this:

Secondly you need your PIC, and a few components to plug into it. For the PIC, we will choose something from the 16F series. Depending on what you can get hold of, this can be pretty much anything from that range. This is a common problem for newcomers, when they see a circuit on the ‘net which uses e.g. a PIC16F628, they assume that only that part will do. This is not the case – often, all that is involved in using code written for one PIC with another PIC is simply changing a couple of lines of code. Even jumping from say, the 16F series to the 18F/24F/32F series is not too difficult – this is the beauty of the C language: your code is portable, so you don’t have to write the same routine for each different microcontroller (this is one big advantage over using assembly)

I realise it can be difficult to get the newest parts in certain places, so here we will pick the PIC16F690, an oldish but reasonable part that should be widely available. If not, pick something similar – ideally source from places like Mouser, Digikey, Farnell, RS, etc; they have the latest stuff in stock at better prices than the smaller vendors. The downside is there is usually a minimum order for free delivery, but if you plan things you can order a few weeks worth of parts at once. I will mainly link to parts from Farnell here as that is my main source of components, but obviously you can get the same parts from many different places (eBay is good for cheap and cheerful parts, just watch out for dodgy vendors)

Anyway, here is a basic list of parts to start with (many of these are available in kits such as this one, so shop around a bit):

  • Breadboard – preferably select a decent size one with power tie points as shown above.
  • A load of jumper wires to connect your circuit together with. You can either buy some ready made ones such as these or these, or buy a roll of single core wire (I use 0.6mm which is equivalent to ~22.5AWG, but 22AWG or 20AWG should do fine) and cut your own.
  • A header to plug your programmer into during programing/debugging – this depends on which programmer you have. For the PICkit3 you need a simple 6-pin 2.54mm pitch header, whilst for an ICD3 you need either an RJ-11 (6-pin type), an RJ11 to 6-pin header adapter or this very convenient breadboard cable adapter.
  • Some passive components (“passive” roughly means things that don’t require power to perform their function, like resistors, capacitors, inductors)
  • Capacitors – 20 x 100nF ceramic capacitors (also listed as 0.1uF, which is the same value, 1nF is 1/1000th of a uF) – you will use a lot of these. 10pF, 15pF and 22pF ceramic (here are some options). 10uF, 100uF, 220uF, 470uF electrolytic types. There are also starter kits available which combine many of the common values.
  • Resistors – 10Ω, 47Ω, 220Ω, 1kΩ, 2.2kΩ, 10kΩ, 22kΩ, 47kΩ, 100kΩ, 470kΩ, 1MΩ metal film or carbon film type should be enough to get you started (or you can buy a kit of various standard values such as this, though you can often pay a lot more for the convenience of having them selected for you)
  • Crystals – the modern PICs have an internal oscillator (this is the thing that supplies the clock to the internal circuit) but it’s not as accurate timing wise as using an external crystal. Some applications require accurate timing, such as serial communications (e.g. RS232 or USB between PIC and PC) so you may want to grab some of these. Typical handy values are 32.768kHz, 4MHz, 8MHz, 12MHz, 20MHz.
  • LEDs – you need something to indicate events, an LED is a cheap and simple way of doing this. They come in many colours, shapes and sizes, but start off with some simple 5mm red LEDs.
  • General purpose transistors – you will need these for various things along the way, often for driving things that the PIC pins don’t have enough power to handle. There are two main types of transistor, bipolar and FET (Field Effect Transistor) Both have their uses. The choice of parts is overwhelming – some BC337-40 NPN, BC327 PNP, and some BS270 N-ch MOSFETs are a good start.
  • 7-segment displays, LCDs, Buzzers, Sensors, Motors – the list is endless once you get past the basics. Again, there are sensor kits available from places like Sparkfun, Adafruit, and other vendors if you want a basic selection handy for future use.

Okay, assuming you have (at least some of) the basic parts above, how do you wire things up on your breadboard? This is where the vendors datasheets and application notes (“app notes”) come in handy (you should read your first microcontroller datasheet from start to finish – all the information you need is there)

The datasheet, or a related app note will often have a minimum operational diagram. Googling a bit for things like (“PIC schematic”, “PIC16F starter kit schematic”, etc) brings up plenty of useful info. Using the “images” search option is often a good way to find what you are looking for.

Anyway, at the end of the “Low Pin Count Demo Board” app note, we find a very simple schematic, I have snipped out the relevant part of it below and taken the liberty of simplifying it a bit:

PIC16F690 Setup 2

We can see it’s very basic, apart from the PIC16F690 (U1) itself, there is only R1, C1 and P1 in addition. R1 is a 10kΩ resistor mentioned above, C1 is one of the 100nF ceramic capacitors (marked as 0.1uF – get used to this different marking of what is actually the same value, it happens all the time), and P1 is the 6-pin 2.54mm pitch header also mentioned. SW1 is optional, this can be used to reset the microcontroller, but if you have the programmer connected you can do this from within MPLAB (or disable the reset function entirely)

If you are not used to reading schematics, all the symbols marked the same are physically connected to each other (this saves making things messy by having to draw wires everywhere) For example, the little arrow with V+ above it is connected to all the other V+ symbols (V+ is your supply voltage, typically +5V for this PIC which can be supplied from your PICkit3 or ICD3 – not all programmers have this option though). Also the little downwards pointing triangle made from three horizontal lines means circuit ground, and these points are all connected together. Same again with the symbol marked RA3, the point over on the right is connected to pin 4 of the microcontroller which has the same symbol attached to it. 

Breadboarding the circuit

Okay, enough waffle, let’s actually make something happen. Since, we don’t have any display, the standard “Hello world” equivalent for microcontrollers is the LED blinking project, so we’ll go for this (boring I know, but it’s a start…)

This is the same schematic as the previous one (it’s good to get used to seeing schematics in different formats), but we have added an LEDs and resistor in for blinking purposes.

1_LED

Now to setup the circuit on the breadboard, here is the result:

Breadboard_Layout2_Annotated3

Ignore the yellow capacitor at the right middle (it’s part of another circuit on the same board) You can see the 100nF capacitor across the power pins, the anode of the LED connected to RB7, it’s cathode connected to the resistor then the resistor connects to the circuit ground at the top (pin 20 of the microcontroller) Also you can see the 10kΩ resistor between MCLR (pin 4) and the positive supply pin (Vcc – pin 1) The wires plugged into the board are the adapter wires from the ICD3 breadboard cable adapter previously mentioned above – the unlabelled blue wire plugged into the left hand side is the Vpp (programming voltage) connection .

Creating the project in MPLABX

Okay, now everything is setup, it’s time to write the code. Open up MPLABX and click on File->New Project (or Ctrl-Shift-N) This will bring up the following dialog box:

MPLABX_New_Project

Select Standalone Project and click next. You will be presented with the dialog box below.We now need to select the part we will be using, so select Mid Range 8-bit MCUs and PIC16F690 (or if you are using another similar part 20-pin like the PIC16F685, PIC16F687, PIC16F689, etc, then select this instead) and click next.

MPLABX_New_Project_Select_Micro

You will now be asked to select an optional debug header (these parts do not have on chip debug capabilities) which we won’t be using, so just click next.

Now we get a dialog asking to select a tool for programming (e.g. a PICkit3, ICD3, etc) I have my ICD3 plugged in so I have selected this. Select your tool and click next.

MPLABX_New_Project_Select_Programmer

Now we have to select the compiler we will be using, so we choose the XC8 compiler we downloaded before (if you didn’t, here’s the compiler page link again) You may have other compilers installed (as you can see I have) so make sure the XC8 compiler is highlighted and click next.

MPLABX_New_Project_Compiler

Now we finally give the project a name – I named it “LED Blink” and put it in a folder of the same name. Select “Set as main project” also (this just means it will be the active one of you have more than one project in your workspace) and click Finish:

MPLABX_New_Project_Name

Writing the code

Okay, we’re finally done setting up the project, now we can write the code. Under the “Projects” tab, right click on “Source Files” and select “C Main File”. This will create a basic template main file for you. You can rename it if you like from the default “newmain”, I called it “led_blink”.

MPLABX_Add_Source

You should now have something like this in the editor:

MPLABX_Source

Don’t worry if you don’t understand the code, we will cover that in future tutorials. For now, let’s just get the LED blinking. Basically, all we need to add is a loop with two instructions to send a logic 1 to the LED pin, with a delay between them. Here is the updated code with this added:

/* * File: led_blink.c * Author: Oli Glaser * * Created on 02 January 2013, 06:59 */ // main include file - this contains all the "friendly names" // for the pins and peripherals #include <xc.h> // Turn the Watchdog Timer off #pragma config WDTE = OFF void delay(int d); // This is the prototype declaration for the Delay function int main(int argc, char** argv) { TRISBbits.TRISB7 = 0; // This sets pin RB7 to output while(1) // Infinite loop { PORTBbits.RB7 = 1; // Set RB7 pin to logic high (5V) delay(500); // Delay for roughly 500ms PORTBbits.RB7 = 0; // Set RB7 pin to logic low (0V) delay(500); // Delay for roughly 500ms } } // Delay routine void delay(int d) { int i; // Declare variable to be used in the loop while(d) // While d > 0 { i = 100; // set i to 100 for inner loop while(i) // while i > 0 { i--; // decrement i (e.g. i will equal 99, 98, 97,...) } d--; // decrement d } }

  

And that’s it, just copy the above code and replace the entire contents of the file with it. You will notice we changed the intial header files (e.g. #include stdio.h and stlib.h) and added #include <xc.h>. We also added a rather important line, the #pragma WDTE = OFF – this turns the watchdog timer off. The watchdog timer is a timer that needs to be reset before it times out otherwise it will reset your microcontroller. This can be useful in case the microcontroller hangs for whatever reason, but we don’t need it here as it adds complications.

We will cover more on these features in future tutorials. For now, you can experiment with the timing of the LED blink by passing different values to the delay routine. You could also try adding another LED to RB6 (hint – you need to make sure the pin is set to output)

I hope this is of use to a few people. If you spotted any mistakes, have any questions, ideas for improvement, suggestions for future tutorials, then leave a comment and I’ll do my best to answer promptly.

Jun 13

Mole Deterrent Teardown

Having a very healthy population of moles in our vicinity, I logged onto eBay and bought four of the cheaply available (ultra?)sonic mole deterrents for our garden lawns in the hope of persuading them to move elsewhere. Here’s a link to a couple of similar ads: eBay ad, eBay ad2.

MoleRepllerPic

The basic idea is pretty simple and used with other animals too – the gadget emits an frequency that the animals in question (in this case the moles) aren’t keen on, so they move away from the area. Some are advertised as being “ultrasonic mole deterrent”, and some simply say “sonic mole deterrent”.

A few questions spring to mind:

  1. Do they actually work?
  2. Are they sonic or ultrasonic? (ultrasonic means above the range of human hearing  of which the upper limit is around 20kHz) What frequency do they actually work at?
  3. What’s the build quality like? How long are they likely to function properly for?

Do they work?

Unfortunately the particular model purchased didn’t work for long enough (see below) to answer this question fully, but I’d say they did seem to make a difference initially. Further tests will have to be made on this front..

I took a look inside one to try and answer the second and third questions, see how it operates and whether any “improvements” might be made.

Here’s a picture of the various bits:

20120526_155710

We have 2 x 1.2V 600mAh NiMH batteries, a solar panel, a buzzer of some sort and a little circuit board.

Here’s a close up of the circuit board:

20120526_155820

Tracing out the circuit in LTSpice (an excellent free SPICE simulator) produced this schematic:

Mole Repeller Circuit

Note that 2N2222 and 2N907 are used in the SPICE circuit as LTSpice doesn’t have models for 9014 or S8550, which are the actual parts used on the PCB. This matters little to the simulation (see below) as the circuit should work fine with most “general purpose” bipolar transistors.

The function of the circuit is very simple – to briefly turn on the buzzer every 20 or so seconds. Here is a run through of the operation:

Q1 and Q3 are responsible for the on/off pulses. If we start with Q1 turned on (via current through R1 into base), Q3 turned off, and either side of C1 at around 0V. R7 gradually charges up the right side of C1 (Q3b) until the voltage reaches around 500mV at which point Q3 will begin to turn on. When Q3 starts to turn on, Q4 and Q2 (BUZ_V) will also begin to turn on, activating the buzzer. Also the bottom of C2 will be pulled to ground, which will also pull Q1s base (Q1b) low, turning it off – this will raise the voltage on the left (Q1c) and right side of C1, accelerating Q3s turn on. Things will stay like this for around 1 second until C2 charges up through R1 and the base of Q1 (Q1b) reaches ~500mV, at which point Q1 will turn on and  C1 will be pulled low, pulling the base of Q3 to ground and turning Q4 and Q2 (and the buzzer) off again. Then the process repeats….

If we run the simulation of the above circuit in LTSpice we get these waveforms (waveform names below correspond to bracketed names in the description and labels in the schematic above):

Mole Repeller Simulation 3

So, how accurate is the simulation? Pretty accurate as it turns out – here are the waveforms for Q1c (yellow) and Q3b (red) over 30 seconds, we can see they are almost identical to the simulation:

20120613_915639

What frequency does the buzzer run at?

To answer this question and find out how the buzzer operates, I pulled the casing apart and took a look inside:

20120612_000241

It’s a very simple construction, consisting of one resistor, a transistor of some sort, a magnet on an arm and a coil with two windings to produce what is called a “blocking oscillator”. The electromagnetic coil produces an oscillating magnetic field which makes the magnet arm vibrate against a diaphragm producing the sound.

Here is a schematic:

Buzzer

Some guesswork has been applied here as the (unmarked) transistor has 4 pins, not the usual 3. I think there may be a diode (represented by D2, possibly for temperature compensation) included in the package with it’s cathode connected to ground, so pin 4 would be the anode of D2.  The part numbers 1N4148 and BC847C are not the real part numbers, just a random diode and NPN from the LTSpice library.  The coil inductance values are guessed at too, but the resistor is definitely a 1.5k part.

This is not the most common blocking oscillator topology, with the control coil from base to ground (via biasing/compensating diode) rather than from supply to base (see below joule thief example) For further reading, Wiki has good pages on the blocking oscillator, and also the popular Joule Thief which utilises such an oscillator:

For the version I think is used in our buzzer I managed to track down a buzzer patent 4,065,733 which describes the use of a diode subject to the same thermal changes (e.g. ideally part of the same IC) as a compensating bias element.

The links above all have explanations of operation, but here is rough run through. If we begin with Q1 turned on, L_DRIVE_COIL begins to charge. Since it is coupled to L_CONTROL_COIL, the changing current induces a voltage across L_CONTROL_COIL keeping Q1 turned on. As the magnetising current slope levels out on L_DRIVE_COIL, the induced voltage into L_CONTROL_COIL drops. This lowers the voltage across L_CONTROL_COIL, stealing base current and begins to turn Q1 off. As Q1 turns off, L_DRIVE_COIL attempts to keep the current flowing and a voltage of the opposite polarity appears across it. Since it is coupled with L_CONTROL_COIL this accelerates the turn off process, with a voltage of the opposite polarity appearing across L_CONTROL_COIL bringing the base voltage below 0V. Q1 remains turned off until L_DRIVE_COIL discharges (i.e. “blocked”, hence the name) and the voltage on L_CONTROL_COIL drops, allowing the transistor to begin to turn on again.

To answer the frequency question, a quick test with the scope found out it runs at 359 Hz:

20120613_920124

So, it’s not ultrasonic. My suspicion is that they all work around the same frequency (300-1000Hz) but some advertise as ultrasonic, presumably being unaware of the meaning (or maybe as they think it sounds better) Some ads (such as this one) advertise ultrasonic, but then state an operating frequency of 400-1000Hz in the specs, which seems to support this theory. The low frequency is apparently meant to simulate the sound of other moles burrowing. This makes more sense to me than an ultrasonic frequency for repelling moles, but I couldn’t find much data on what frequencies work best, or the hearing range of the mole.

What about the quality?

To put it simply, the quality of this product is pretty terrible. They are not built to withstand continuous operation outdoors. Reasonable proof of this was the fact that out of the 4 purchased, within a few months of use outside, all 4 were no longer working. So what went wrong?

Although there is nothing wrong with the principle of the buzzer operation, unfortunately the actual design of the buzzers in this product is not up to scratch. 3 out of the 4 failed due to the permanent magnet on the end of the armature coming loose as can be seen below (note the magnet is separated from the arm and lying on the white sealant stuff):

20120612_081032

On inspection, it was found that the magnet is simply glued on with what appears to be something like super glue. This is simply not going to stand repeated vibration for long. Another issue is that the enclosure is not well sealed and none of the circuitry is “weatherproofed” (e.g. conformal coating or potting) This fact caused the remaining one to fail:

20120614_081730

20120614_081452

Next time we’ll have a look at designing a better version Winking smile