Last updated:
$Date: 2002/12/04 19:42:10 $
PIC are microcontroller made by Microchip, not very expensive, easy to use and quite popular.
In this document, the only PIC we present is the PIC16F877:
In a minimal tool chain you need:
GPASM is a part of the gputils package. To install it under Linux download the tgz file (I use the version 0.10.2) and follow the instructions
of the INSTALL file (./configure, make, and make install).
Then, to build a .hex file from an .asm file:
gpasm file.asmthe result is a file named file.hex ready to be transfer on the PIC.
Unfortunately, to this date, there is no efficient free and open-source C compiler for PIC. But C2C is an efficient postcardware. In fact, the new version of C2C for Linux is no more a postcardware: it's a shareware without evaluation period (44.76 Euro) available here: C2C (http://www.picant.com/c2c/c.html). I have never test this new shareware version.
To install the free postcardware version under Linux, you download a binary archive C2C Linux Version 3.27 (http://botzilla.free.fr/pic/c2cl327e_version_linux.zip).
This application is distributed as postcardware for non-profit use and as shareware for commercial use. For non-profit use you may use the compiler as long as you wish. If you like it I ask you to send me any nice postcard. For commercial use you have to register the compiler. To register run the 'register.exe' application under MS Windows or go to the compiler home page and follow the instructions.For this Linux version there is no code size, date, or target restriction. It's a fully operational C compiler. C2C is not very big (735Ko) and could be put in /usr/local/bin to be usable for all users. It's distributed with a small help file, and some examples.
C2C is not GCC, there are some major limitations that you have to known:
To compile a program for a PIC16F877, try for instance:
c2c -SRC -PPIC16F877 file.cThe -SRC option is used to insert C-code as comments into assembler file (file.ASM) (it's far more easy to read).
;========================================== list p=16f877 #include <p16f877.inc> __config _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _WRT_ENABLE_ON & _LVP_ON & _DEBUG_OFF & _CPD_OFF ;==========================================In an assembler file, use ; to comment a line. And every line have to start with a tabulation. Then, go to the GPASM chapter to build an hex file ready to be programmed to your PIC.
The choice of a programmer closely depends on the hardware you want to use. We use Picprog1.1 to program the PIC via a PC regular serial port (see chapter 18 for the hardware design). Picprog is simple, efficient and open source. This programmer has been design for PIC16F84 but it works fine with the whole PIC16xx microcontroller family. For more informations go to Picprog 1.1 (http://gyre.weather.fi/ jaakko/pic/picprog.html).
Since, you use UNIX, all these commands can be put in a Makefile like this:
# ===================================================================== C_FILE=file.c # ===================================================================== ASM_FILE=$(addsuffix .asm, $(basename $(notdir $(C_FILE)))) ASM_COMPLET_FILE=$(addsuffix _complet.asm, $(basename $(notdir $(C_FILE)))) HEX_FILE=$(addsuffix _complet.hex, $(basename $(notdir $(C_FILE)))) all: c2c -SRC -PPIC16F877 $(C_FILE) cat header.asm $(ASM_FILE) > $(ASM_COMPLET_FILE) gpasm --dos -w1 $(ASM_COMPLET_FILE) prog: picprog --erase --burn --device=pic16f877 --input $(HEX_FILE) --pic /dev/ttyS0 clean: rm -f $(ASM_FILE) rm -f $(ASM_COMPLET_FILE) rm -f $(HEX_FILE) # =====================================================================
Of course, you HAVE TO read the data sheet from Microchip. But, it's not easy to understand without some examples, and unfortunately the examples provided by Microchip are poor and not very clear (and written in assembler). In this chapter, I'll just give you some tips to start.
Port A is a 6 bits bi-directional port. It's not the easiest IO port
because pins RA0-RA3 and RA5 are multiplexed with analog inputs, and pin RA4
is multiplexed with Timer 0 module clock input.
To use IO rather than AN module use the ADCON1 register:
ADCON1=ADCON1|0x06To switch pins as Output or Input use the TRSIA register (0 for output, 1 for input).
// Example1 from http://botzilla.free.fr/ // Code design for PIC16F877 // To compile with c2c -SRC -PPIC16F877 example1.c char ADCON1@0x9; //(bank 1) // C2C automaticaly define some classical register (PORTA, TRISA,...) but not ADCON1 // so you have to define it (go to C2C doc for more informations about this syntax) void init_portA() { PORTA=0;//No need to switch to bank 1, C2C do it. TRISA=0;//using PORTA as output ADCON1=ADCON1|0x06;//using IOPort instead of AN module } void wait() { char i; for(i=0;i<255;++i) nop();// generate an empty instruction (a busy wait) } main() { init_portA(); PORTA=0; while(1) { PORTA=1;//0001 wait(); PORTA=3;//0011 wait(); PORTA=7;//0111 wait(); PORTA=15;//1111 wait(); PORTA=0; wait(); } } /* EOF *****************/To test this program:
c2c -SRC -PPIC16F877 example1.c
cat header.asm example1.ASM > example1_with_header.ASM
gpasm --dos -w1 example1_with_header.ASM
picprog --erase --burn --device=pic16f877 --input example1_with_header.ASM --pic /dev/ttyS0
For the other IO ports, it's more or less the same thing: use the TRISB, TRISC, TRISD or TRISE to choose input or output for each bits. But, PORTB pins RB7:RB4 could be use for Interrupt on change feature. This kind of interrupt inputs are very precious so keep it in stock for later. For the PORTC, pins RC4:RC3 could be use for I2C bus communication, and pins RC2:RC1 could be use for PWM commands. etc,... As you see, you always have to check the data sheet to choose the right initialize for each IO port.
An important feature of this PIC16F877 is the PWM modules.
Pulse Width Modulation (PWM) is an efficient light dimmer or DC motor speed controller.
In our robot, we use these 2 modules to control the two motors.
The important steps to configure the PWM modules are:
void init_pwm_and_timer2() { CCP1CON=0; CCP2CON=0; T2CON=0;// Timer2 control register (PostScale|Off|PreScale) set_bit(T2CON,1);//PreScale 1:16 TMR2=0; // Reset timer2 // set the pwm period by writing to the PR2 register PR2=0xFF;// period is ((PR2)+1)x(prescale)x(50x4)ns=0.81ms // set the PWM1 duty cycle by writing to the CCPR1L register an CCP1CON<4:5> CCP1CON=0x0C; // 0 -> 2 LSbs of the PWM duty cycle // C -> PWM Mode //CCPR1L=0xDF;// duty cycle -> 1101 1111 00 = 892x50xprescale=0.71ms (87%) //CCPR1L=0x80;// duty cycle -> 1000 0000 00 = 512x50xprescale=0.4ms (50%) CCPR1L=0xFF;// duty cycle -> 1111 1111 00 = 1020x50xprescale=0.8ms (100%) // set the PWM2 duty cycle by writing to the CCPR2L register an CCP2PON<4:5> CCP2CON=0x0C; // 0 -> 2 LSbs of the PWM duty cycle // C -> PWM Mode //CCPR2L=0xDF;// duty cycle -> 1101 1111 00 = 892x50xprescale=0.71ms (87%) //CCPR2L=0x80;// duty cycle -> 1000 0000 00 = 512x50xprescale=0.4ms (50%) CCPR2L=0xFF;// duty cycle -> 1111 1111 00 = 1020x50xprescale=0.8ms (100%) //make the CCP2, CCP1 pin an output by clearing the TRISC<1:2> bits TRISC=TRISC&0xF9; set_bit(PIE1,1);//Timer2 IE clear_bit(PIR1,1);//Timer2 clear IF //set_bit(T2CON,1);// Prediviseur a 16 set_bit(T2CON,2);// Turn ON Timer2 }
With C2C, the interrupt dispatch is done in a special function with this reserved prototype:
void interrupt(void);The context is automaticly saved, and restored. Keep in mind that an efficient interrupt routine has to be short to avoid overflow interrupt problems. And don't forget that you often have to reset an interrupt flag (for timer, I2C, or an input interrupt).
void interrupt(void) { // context is automaticaly saved, and restored at the end of interrupt if (INTCON&00000100b) timer0_interrupt(); if (PIR1 &00000001b) timer1_interrupt(); if (PIR1 &00000010b) timer2_interrupt(); if (INTCON&00000001b) sensor_interrupt(); if (PIR1 &00001000b) i2c_slave_interrupt(); } void timer0_interrupt() { clear_bit(INTCON,2);//reset the interrupt flag } void timer1_interrupt() { clear_bit(PIR1,0);//reset the interrupt flag } void timer2_interrupt() { clear_bit(PIR1,1);//reset the interrupt flag } void sensor_interrupt() { clear_bit(INTCON,0);//reset the interrupt flag } void i2c_slave_interrupt() { if (SSPSTAT&0x04) { //I2C Slave Transmission SSPBUFF=0; set_bit(SSPCON,4);// CKP, SSPBUFF must be write before clear_bit(PIR1,3);//reset the interrupt flag } else { //I2C Slave Reception tempI2C=SSPBUFF; clear_bit(PIR1,3);//reset the interrupt flag } }
As you could see in the init_i2c_slave function, to initialize I2C on the PIC, the major steps are:
void init_i2c_slave() // I2C IE : PIE1<3> // I2C IF : PIR1<3> { set_bit(SSPSTAT,7);// standard speed mode clear_bit(SSPSTAT,6);// input level conform to I2C spec clear_bit(SSPSTAT,0);// BF buffer full bit //SCL and SDA as input set_bit(TRISC,3); set_bit(TRISC,4); //SSPCON<5> enable bit (1) //SSPCON<3:0> 0110 (slave, 7 bits address) clear_bit(SSPCON,7);// WCOL Write Collision Detect bit clear_bit(SSPCON,6);// SSPOV Receive Overflow Indicator bit set_bit(SSPCON,5);// Synchronous Serial Port Enable set_bit(SSPCON,4);// CKP clear_bit(SSPCON,3);//0110: I2C slave mode: 7 bits address set_bit(SSPCON,2); set_bit(SSPCON,1); clear_bit(SSPCON,0); SSPADD=0x0E;// 0000 1110 -> 7 bits address= 0000 111 //SSP IE set_bit(PIE1,3); clear_bit(PIR1,3); }Then, we test if it's an incoming message, or a request for a transmission with SSPSTAT register. If it's a reception you have to read the SSPBUFF register (if you don't it will stop the I2C interrupt handler). If it's a transmission you have to write in the SSPBUFF register. In all the case, at the end, don't forget to reset the interrupt flag (here PIR1:3).
void i2c_slave_interrupt() { if (SSPSTAT&0x04) { //I2C Slave Transmission SSPBUFF=0; set_bit(SSPCON,4);// CKP, SSPBUFF must be write before clear_bit(PIR1,3);//reset the interrupt flag } else { //I2C Slave Reception tempI2C=SSPBUFF; clear_bit(PIR1,3);//reset the interrupt flag } }
Microchip (http://www.microchip.com):
For all the device data-sheets.
GPutils (http://gputils.sourceforge.net):
Picprog 1.1 (http://gyre.weather.fi/ jaakko/pic/picprog.html):
A simple way to program PIC microcontroller (Hardware and Open source soft for Linux).
A PIC development Tutorial (http://www.mastincrosbie.com/mark/electronics/pic/pic.html):
A good way to start with some advises and examples for using libraries and Makefile.
Fribotte: PIC et Linux (http://fribotte.free.fr):
A good way to start (in French).
botzilla@free.fr , http://botzilla.free.fr/