PICkit2 programming with MPlabX – PIC16F690 UART ADC

Some years ago when, I started to work with microprocessor programming, I bought the PICkit2 starter kit from Microchip.
I had not much experience and people told me that PIC micro-controllers (MCU) are a good starting point to learn embedded system programming. It turned out, that this PIC starter kit was not my thing. First the development IDE MPLAP was only running on Windows, and I was using mainly Linux. This made it not very nice to work with this IDE, since one had to start a Windows OS.
Second there was no real C/C++ compiler available for these chips. There was a commercial compiler, but nothing very user friendly and free. The recommended compiler from MicroChip was an assembler compiler, not the most beginner friendly way to get started either, specially not when coming from the C/C++ corner. As a beginner it is important to get something working quickly and easily. Therefore I put the kit away and started with the Arduino, which is much more beginner friendly and specially has an IDE running on Linux!

Some years later I have now taken the starter kit again to give it a kind of second chance.

* MicroChip has now a IDE MPLabX based on Netbeans and running on Windows, Linux and MacOS.
* They have a free version of C compiler XC for 8, 16 and 32 bit MCU
* A large variety of low cost MCUs exist which have build in periphery like USB, ADC, DAC, and are provided in DIL packages. Specially USB is one thing I miss in the low pin count ATMEL MCUs.

My main focus was on how difficult it is with the PIC MCUs to get serial communication working (UART), how to read out the ADCs and use the digital pins.
For the PIC16 family there are unfortunately very limited libraries available so I started to write some own UART library.
The MCU on the PICkit2 is a PIC16F690 [1].

PICkit2 programmer connected with the evaluation board and an USB-to-Serial converter:
PICkit2WithUART

The main program looks the following:

/*
 * File:   main.c
 * Author: digibird1
 * (c) 2015
 * https://digibird1.wordpress.com/
 http://www.microchip.com/forums/m827267.aspx#828385
 
 It seems for the output registers it is good to have a shadow 
 * register to avoid read-modify-write-effect
 
 */


#define _XTAL_FREQ 8000000

#include <xc.h>
#include <pic16f690.h>
#include "UART.h"
#include "myPicTools.h"


// BEGIN CONFIG
// CONFIG
#pragma config FOSC = INTRCIO         // Oscillator Selection bits (EC: I/O function on RA4/OSC2/CLKOUT pin, CLKIN on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF        // Watchdog Timer Enable bit (WDT enabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select bit (MCLR pin function is MCLR)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Selection bits (BOR enabled)
#pragma config IESO = ON        // Internal External Switchover bit (Internal External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)
//END CONFIG

void interrupt ISR(void)
{
    //Turn ON/OFF RC3 in order to indicate 
    //that an interrupt occured
    //useful for debugging
    if(checkBit(ShadowPortC,3)>0){
        SetBitReg(Reg_PORTC,3,LOW);
    }
    else{
        SetBitReg(Reg_PORTC,3,HIGH);
    }
    
    //UART interrupt
    if(RCIF && RCIE){
        UART_Interrupt();
        
        return;
    }
}

//Read ADC
void Get_Inputs(void)
{
    __delay_ms(1);
    //Start conversion by setting the GO/DONE bit.
    ADCON0bits.GO_nDONE = 1;
    //Wait for ADC conversion to complete
    //Polling the GO/DONE bit
    // 0 = ADC completed
    // 1 = ADC in progress
    while(ADCON0bits.GO_nDONE == 1);
}

The first part of the main.c defines some variables and function and includes some headers.
The interrupt function and the ADC read function can be found.


int main()
{
    
  initShadowRegisters();  
    
  TRISC = 0; //RC0 as Output PIN
  //TRISC1 = 0;
  
  
  
  //Clear Analog Inputs and make pins digital pins
  ANSEL=0b00000001;//set RA0 as analog pin
  ANSELH=0;
  
  IRCF0 = 0b111;//set prescaler
  
  UART_Init();
  
  UART_writeString("Startup ... done\n");
  
  
  
  //ADC
    //Select ADC conversion clock Frc
    ADCON1bits.ADCS = 0b111;
     //Configure voltage reference using VDD
    ADCON0bits.VCFG = 0;
    //Select ADC input channel (RA0/AN0)
    ADCON0bits.CHS = 0;
    //Select result format right justified
    //right=1 is good when using all 10 bits the two bytes can be concatenated 
    //easily into an integer
    //left=0 is good when using the 8 most significant bits
    ADCON0bits.ADFM = 1;
    //Turn on ADC module
    ADCON0bits.ADON = 1;
  

In the beginning of the main() function the MCU is initialized, PortC is made as an output port.
And the analog pins are made digital except RA0 which is used as ADC input. The analog pins are after reset by default configured as analog, which makes problems when using them as digital inputs. I had trouble with this when I was trying to receive data from the UART! So analog function should be disabled when the pins are used digitally!
Further the ADC is configured with sample rate, Ref voltage and which pin to use.

  while(1)
  {
//---------------------------------------------    
    //Blink LED on RC0 and RC1
    SetBitReg(Reg_PORTC,0,HIGH);
    SetBitReg(Reg_PORTC,1,LOW);

    __delay_ms(100); // 100ms Delay

    SetBitReg(Reg_PORTC,0,LOW);
    SetBitReg(Reg_PORTC,1,HIGH);

    __delay_ms(100); // 100ms Delay
//---------------------------------------------        

    
//--------------------------------------------- 
    //UART communication
    while(UART_DataAvailable()>0){
        //char a=UART_ReadByte();
        //UART_writeByte(a);
        //UART_writeByte('\n');
        
        UART_writeNumber(UART_DataAvailable());
        UART_writeByte('\n');  
        
        UART_writeString(UART_ReadString());
        UART_writeByte('\n');
    }
    
    if(UART_DataAvailable()<0){
        UART_writeString("Data Lost Reset UART\n");
        UART_Reset();
    }
    
    //Print a Register
 // UART_writeBitPattern(ANSELH);
 // UART_writeByte('\n');
//---------------------------------------------        

    
//---------------------------------------------    
        
    //Read ADC
    Get_Inputs();
    unsigned int ADC_Value=0;
    
    ADC_Value=ADRESH<<8 | ADRESL;
 
    UART_writeNumber(ADC_Value);
    UART_writeByte('\n');
    
    if(checkBit(ShadowPortC,1)>0)
       SetBitReg(Reg_PORTC,2,HIGH); 
    

 //---------------------------------------------    
  }
  return 0;
  
 
}

Next comes the infinite loop which is running all the time. Here several different functions are called to test different functionality of the MCU. Digital pins connected to the LED of the kit are turned ON and OFF, serial communication RX/TX is performed and the ADC connected to the potentiometer of the kit is read out. This gives a good feeling what is doable with this kind of MCU.

The serial communication functions is placed in a header file, providing some handling of the serial port. Like read/write of a byte, writing a string, converting a number into a string and returning it on the serial port.
Non of the functions aims for high performance it is more a proof of concept implementation. The serial communications helps to understand what is going on the MCU and gives some control connection.

The UART.h header file:

/*
 * File:   main.c
 * Author: digibird1
 * (c) 2015
 * https://digibird1.wordpress.com/
 http://www.microchip.com/forums/m827267.aspx#828385
 
 It seems for the output registers it is good to have a shadow 
 * register to avoid read-modify-write-effect
 
 */


#define _XTAL_FREQ 8000000

#include <xc.h>
#include <pic16f690.h>
#include "UART.h"
#include "myPicTools.h"


// BEGIN CONFIG
// CONFIG
#pragma config FOSC = INTRCIO         // Oscillator Selection bits (EC: I/O function on RA4/OSC2/CLKOUT pin, CLKIN on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF        // Watchdog Timer Enable bit (WDT enabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select bit (MCLR pin function is MCLR)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Selection bits (BOR enabled)
#pragma config IESO = ON        // Internal External Switchover bit (Internal External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)
//END CONFIG

void interrupt ISR(void)
{
    //Turn ON/OFF RC3 in order to indicate 
    //that an interrupt occured
    //useful for debugging
    if(checkBit(ShadowPortC,3)>0){
        SetBitReg(Reg_PORTC,3,LOW);
    }
    else{
        SetBitReg(Reg_PORTC,3,HIGH);
    }
    
    //UART interrupt
    if(RCIF && RCIE){
        UART_Interrupt();
        
        return;
    }
}

//Read ADC
void Get_Inputs(void)
{
    __delay_ms(1);
    //Start conversion by setting the GO/DONE bit.
    ADCON0bits.GO_nDONE = 1;
    //Wait for ADC conversion to complete
    //Polling the GO/DONE bit
    // 0 = ADC completed
    // 1 = ADC in progress
    while(ADCON0bits.GO_nDONE == 1);
}


int main()
{
    
  initShadowRegisters();  
    
  TRISC = 0; //RC0 as Output PIN
  //TRISC1 = 0;
  
  
  
  //Clear Analog Inputs and make pins digital pins
  ANSEL=0b00000001;//set RA0 as analog pin
  ANSELH=0;
  
  IRCF0 = 0b111;//set prescaler
  
  UART_Init();
  
  UART_writeString("Startup ... done\n");
  
  
  
  //ADC
    //Select ADC conversion clock Frc
    ADCON1bits.ADCS = 0b111;
     //Configure voltage reference using VDD
    ADCON0bits.VCFG = 0;
    //Select ADC input channel (RA0/AN0)
    ADCON0bits.CHS = 0;
    //Select result format right justified
    //right=1 is good when using all 10 bits the two bytes can be concatenated 
    //easily into an integer
    //left=0 is good when using the 8 most significant bits
    ADCON0bits.ADFM = 1;
    //Turn on ADC module
    ADCON0bits.ADON = 1;
  

        
  
  while(1)
  {
//---------------------------------------------    
    //Blink LED on RC0 and RC1
    SetBitReg(Reg_PORTC,0,HIGH);
    SetBitReg(Reg_PORTC,1,LOW);

    __delay_ms(100); // 100ms Delay

    SetBitReg(Reg_PORTC,0,LOW);
    SetBitReg(Reg_PORTC,1,HIGH);

    __delay_ms(100); // 100ms Delay
//---------------------------------------------        

    
//--------------------------------------------- 
    //UART communication
    while(UART_DataAvailable()>0){
        //char a=UART_ReadByte();
        //UART_writeByte(a);
        //UART_writeByte('\n');
        
        UART_writeNumber(UART_DataAvailable());
        UART_writeByte('\n');  
        
        UART_writeString(UART_ReadString());
        UART_writeByte('\n');
    }
    
    if(UART_DataAvailable()<0){
        UART_writeString("Data Lost Reset UART\n");
        UART_Reset();
    }
    
    //Print a Register
 // UART_writeBitPattern(ANSELH);
 // UART_writeByte('\n');
//---------------------------------------------        

    
//---------------------------------------------    
        
    //Read ADC
    Get_Inputs();
    unsigned int ADC_Value=0;
    
    ADC_Value=ADRESH<<8 | ADRESL;
 
    UART_writeNumber(ADC_Value);
    UART_writeByte('\n');
    
    if(checkBit(ShadowPortC,1)>0)
       SetBitReg(Reg_PORTC,2,HIGH); 
    

 //---------------------------------------------    
  }
  return 0;
  
 
}

Further some help tools were implemented. I had issues with manipulating the I/O registers. When reading and writing quickly after each other, previous changes got lost. This is due to the read-modify-write-effect [2]. Therefore I introduced shadow registers, a register which holds the bit configuration of the I/O port.

/* 
 * File:   myPicTools.h
 * Author: digibird1
 * (c) 2015
 * https://digibird1.wordpress.com/
 *
 * Created on July 18, 2015, 11:12 AM
 */

#ifndef MYPICTOOLS_H
#define	MYPICTOOLS_H

#ifdef	__cplusplus
extern "C" {
#endif

#define HIGH 1
#define LOW 0
    
    
#include <pic16f690.h>
#include <xc.h>  
#include <stdlib.h>
    
volatile unsigned char ShadowPortA; 
volatile unsigned char ShadowPortB;
volatile unsigned char ShadowPortC;

/*
 This is to avoid read-modify-write-effect
 * http://www.microchip.com/forums/m478014.aspx
 */

//enum for the I/O registers which get shadow registers
typedef enum {Reg_PORTA, Reg_PORTB, Reg_PORTC} RegisterType;


void initShadowRegisters(){
    ShadowPortA=PORTA;
    ShadowPortB=PORTB;
    ShadowPortC=PORTC;
}


    
//Use the shadow registers to set or clear a bit
//useful to avoid the read-modify-write-effect
void SetBitReg(RegisterType Register, unsigned Bit, unsigned Value){
    
    
   volatile unsigned char* ShadowReg;
   volatile unsigned char* HardWareReg;
    //Map the Hardware Port and Shadow ports to a pointer
    //for universal code
    switch(Register){
        case Reg_PORTA :
            ShadowReg=&ShadowPortA;
            HardWareReg=&PORTA;
            break;
        case Reg_PORTB  :
            ShadowReg=&ShadowPortB;
            HardWareReg=&PORTB;
            break;
        case Reg_PORTC  :
            ShadowReg=&ShadowPortC;
            HardWareReg=&PORTC;
            break;
            
        default : 
            ShadowReg=NULL;
            HardWareReg=NULL;
    }
    
    if(Value==HIGH){//Set bit
        *ShadowReg |= 1 << Bit;
    }
    if(Value==LOW){//clear bit
        *ShadowReg &= ~(1 << Bit);
    }
    //Write the shadow register to the hardware port
    *HardWareReg=*ShadowReg;
}

//Help function to help to check if an individual bit is set or not
unsigned int checkBit(volatile unsigned char Register, unsigned int Bit){
    return (Register >> Bit) & 1;
}


#ifdef	__cplusplus
}
#endif

#endif	/* MYPICTOOLS_H */

To conclude after all the tests I did, the PIC has for sure a high potential. And with the IDE running on Linux it is for sure a very interesting MCU. The programming of the PIC16 series is a bit more involving compared to the Arduino language.
But if one wants more performance in with the Atmel Mega MCUs one also has to go into lower level programming and things get similar as with the PICs.

High potential I see in the PIC18 series, this series has much more library functionality, and has a code generator in MPLabX. So they are on my to test list for the future.

[1] http://www.microchip.com/wwwproducts/Devices.aspx?product=PIC16F690
[2] http://www.microchip.com/forums/m478014.aspx

The code can be downloaded from GitHub
https://github.com/digibird1/PICkit2_WithMPLabX

It can be checked out with:
git clone https://github.com/digibird1/PICkit2_WithMPLabX

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: