Sunday, January 31, 2016

URSA: Heartbeat-Delay-PWM code

Now to some coding! I'm using Microchip's free IDE (MPLAB X) and the free version of their C compiler XC8

As a first step I always like to code the basic 'blinking LED' in a new PIC project. I use interrupts instead of delays because in addition to having a visual indication that the PIC is alive, I also use the same interrupt routine to handle any delays I may need down the road. XC8 does have built-in delay functions however, XC8 delay functions actually inserts NOP instructions into the code and you can't pass a variable into the XC8 delay function. You need to know all your static only delays ahead of time! Not very robust or efficient, so I use an interrupt routine to handle all delays instead of the built in XC8 functions. In addition to this basic 'heartbeat' I also configured two CCP peripherals as PWM channels for the control of the two drive motors. 

First we configure the PIC 16F1829 fuses:
/* configuration_bits.c */
#include <xc.h>         /* XC8 General Include File */
#include <xc.h>

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)

#pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)

Now the system config:
/* system.h */
#define SYS_FREQ        1600000L
#define FCY             SYS_FREQ/4

/******************************************************************************/
/* System Function Prototypes                                                 */
/******************************************************************************/


void ConfigureOscillator(void); /* Handles clock switching/osc initialization */

...and

/* system.c */
#include <xc.h>         /* XC8 General Include File */
#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */
#include "system.h"

void ConfigureOscillator(void)
{
    OSCCON=0b01111010;
    while (!HFIOFS) continue;// wait for stable osc

}

The code below contains the configuration of both the CCP and Timer 2 for use as a 1ms interrupt timer.

/* user.c */
#include <xc.h>         /* XC8 General Include File */
#include <stdint.h>     /* For uint8_t definition */
#include <stdbool.h>    /* For true/false definition */
#include "user.h"

void InitApp(void)
{
    /* Heartbeat config */
    TRISAbits.TRISA5 = 0; // Heartbeat LED
    TMR2=0;// clear TMR2
    T2CON = 0b00000110;//post 0, timer on, pre 16
    PR2=0xF9; // sets up timer2 for 1ms interrupts
 
    /* PWM Left config */
    TRISCbits.TRISC6 = 0; // Left PWM output
    T4CON = 0b00000110;//post 0, timer on, pre 16
    PR4 = 0xF9; // 1000 Hz PWM freq
    CCP4CON = 0x3C;// PWM mode
    CCPR4H = 0;
    CCPR4L = 0x3E; // Duty cycle count 1% = 0x9 to 100% = 0xF9
    CCPTMRS = 0x40; // use Timer 4
 
    /* PWM Right config */
    TRISAbits.TRISA2 = 0; // Right PWM output
    T6CON = 0b00000110;//post 0, timer on, pre 16
    PR6 = 0xF9; // 1000 Hz PWM freq
    CCP3CON = 0x1C; // PWM mode
    CCPR3H = 0;
    CCPR3L = 0x7C; // Duty cycle count 1% = 0x9 to 100% = 0xF9
    CCPTMRS = 0x20; // use Timer 6
 
    /* Enable interrupts */
    PIR1bits.TMR2IF=0; // clear TMR2 interrupt flag
    PIE1bits.TMR2IE=1;//enable TMR2 interrupts
    INTCONbits.PEIE=1;//enable Peripheral interrupts
    INTCONbits.GIE=1;// enable global interrupts

}
void msDelay(unsigned int d){
    // load up the msec delay variable
    ms_delay=d;
    // loop execution here until msec delay completes via 1 ms interrupts
    while(ms_delay)continue;
}

We will be manipulating the CCPR3L and CCPR4L registers to vary the duty cycles for the two motors. For now however, they are hard-coded to generate a 25% duty cycle on the Left motor and 50% on the right motor as a test. Note the msDelay function I wrote to execute any delays needed. It simply sets a global variable with the amount of msecs needed and the 1ms interrupt routine decrements that variable until it reaches zero! The delay routine just loops checking for that variable to reach zero.

The O'scope shot below shows the two outputs from the PIC with the top trace being the Left and the bottom trace being the Right. 


The interrupt routine:
/* interrupts.c */
#include <xc.h>         /* XC8 General Include File */
#include <stdint.h>         /* For uint8_t definition */
#include <stdbool.h>        /* For true/false definition */
#include "user.h"

void interrupt isr(void)
{
    INTCONbits.GIE=0;// disable all interrupts

    /* Determine which flag generated the interrupt */
    if(PIR1bits.TMR2IF)// 1ms interrupt via Timer 2
    {
        if(hb_count++>500)// if counted 500ms
        {
            LED = ~LED; // toggle the 'heartbeat' LED
            hb_count=0; // and reset the count to 0
        }
        if (ms_delay){ // if  msec delay variable is not zero
            ms_delay--; // decrement the delay
        }
        PIR1bits.TMR2IF=0; // clear Timer 2 interrupt flag
    }
    else
    {
        /* Unhandled interrupts */
    }
    INTCONbits.GIE=1;// enable global interrupts


}

General definitions:
/* user.h */
#define _XTAL_FREQ 16000000                  // Fosc  frequency for _delay()  library
#define LED LATAbits.LATA5

/******************************************************************************/
/* User Function Prototypes                                                   */
/******************************************************************************/
void msDelay(unsigned int d);
void InitApp(void);         /* I/O and Peripheral Initialization */

/* global variables */
unsigned int hb_count=0;// Heartbeat counter

unsigned long ms_delay=0;

The main routine is a bit bare since not much is going on yet!
/* main.c */
#include <xc.h>         /* XC8 General Include File */
#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */
#include "system.h"        /* System funct/params, like osc/peripheral config */
#include "user.h"          /* User funct/params, such as InitApp */

void main(void)
{
    /* Configure the oscillator for the device */
    ConfigureOscillator();

    /* Initialize I/O and Peripherals for application */
    InitApp();

    while(1)
    {

    }


}

Saturday, January 30, 2016

URSA: Encoders

One of the specs I wanted for URSA is accurate movements to navigate it's unknown environment. Using stepper motors would certainly do that, but they are quite power hungry! The answer is using 'standard' motors with axle encoders mounted that will allow fairly accurate tracking. Dagu has a relatively inexpensive Simple Encoder kit consisting of an 8 pole magnetic disc and a hall-effect sensor to read the pole changes as the axle rotates. I have attached the disc to each of URSA's drive axles:

...and the sensors (making sure you have the smaller side of the sensor facing the disc!) were attached to the robot body using wire ties:
Finally, a quick test to insure the sensors are picking up the pole changes on the disc as it rotates by connecting the motors and sensors to a power source and an LED to the hall-effect sensor's output: Mount and Test Dagu Simple Encoders

EE40LX

I recently finished an edx course (EE40LX Electronic Interfaces) where I needed to construct a robot to certain specification. It had to be able to emit sounds, respond to sounds, respond to light, and of course move. The course used an MSP430G2 Launchpad for the microcontroller, but any microcontroller could be used. I used a PIC 16F1705 on two small breadboards and using counter-weight motors to build out a 'hopper' robot.
I then coded in C using Microchip's MPLABX IDE. Here is my final submission: https://youtu.be/o60SYDt0F4o

It was a great course! I highly recommend edx courses for anyone.
There are hundreds of courses presented by many institutions like Berkeley, Columbia, MIT, Austin, etc... Most are free and you can take them as 'Verified' (they validate your identity and certify your passing by Certificate) by paying a small fee, usually in the $50-$150 range.

Thursday, January 28, 2016

URSA

URSA (Urban Robotic Sensor Array) is a project I started a few years ago and placed on the back-burner. I have brought the robot out of mothballs and starting again from scratch! My thinking here is to incorporate the BEAM ideas (a core environment response) with a separate processor operating as a higher level 'brain'. So to start, I stripped out all the existing Arduino components and building a cortex response processor to handle the 'core' movement and responses of the platform. These core movements and responses would be things like 'move forward', 'spin CCW', if right front crash bumper is triggered: move back a few centimeters, spin CCW a few millisecs, then again proceed forward, etc. As can be seen in the picture:
I have added a breadboard mounted inside the base with a PIC 16F1829 and a Sparkfun motor controller. Also installed on the inside wheel axles are Dagu Simple encoder sensors to track the axle speeds. Next: some coding!

Here we go...

I needed a place to document my projects, and so this blog... I work on several projects at once and during the many years I've been doing this, I have accumulated many, many projects to document. I will document those as well. Please feel free to ask questions and/or comment!