brooksbp / pic16f690-examples

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

pic16f690-examples

The PIC16F690 is an 8-bit microcontroller from Microchip. Here are some examples:

PIC16F631/677/685/687/689/690 datasheet

Install tools

We'll use sdcc to compile code and pk2cmd to load it into flash program memory.

Build SDCC from source since Debian does not package a version of SDCC that supports pic14 and pic16 ports because of license requirements by Microchip.

sudo apt-get install bison flex libboost-dev g++ gputils texinfo zlib1g-dev automake autoconf-archive libtool
svn checkout https://svn.code.sf.net/p/sdcc/code/trunk sdcc-code
cd sdcc-code/sdcc
./configure
make -j
sudo make install

Build pk2cmd from source since there is no Debian package.

sudo apt-get install libusb-dev
wget http://ww1.microchip.com/downloads/en/DeviceDoc/pk2cmdv1.20LinuxMacSource.tar.gz
extract pk2cmdv1.20LinuxMacSource.tar.gz
cd pk2cmdv1.20LinuxMacSource
make linux
sudo make install
sudo cp /usr/share/pk2/PK2DeviceFile.dat /usr/local/bin

Run a program

Wire up the PICkit 2 to the PIC16F690.

Note: a decoupling capacitor should be placed between Vdd and Vss.

Write a program (empty.c) in C:

#include <pic16f690.h>

void main(void)
{
        // Does nothing!
}

Compile and run it!

$ sdcc -mpic14 -p16f690 --stack-size 8 --use-non-free empty.c
message: Using default linker script "/usr/share/gputils/lkr/16f690_g.lkr".

$ pk2cmd -PPIC16f690 -Fempty.hex -M -A5.0 -T
PICkit 2 Program Report
26-11-2019, 19:49:07
Device Type: PIC16F690

Program Succeeded.

Operation Succeeded

Note: --stack-size 8 is used because the PIC16F690 can store 8 addresses in its stack:

System clock

Let's use the internal oscillator at 8 MHz and output it on the CLKOUT pin (system-clock-intosc.c):

#include <pic16f690.h>
#include <stdint.h>

static __code uint16_t __at(_CONFIG) config =
        _INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF &
        _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF;

void main(void)
{
        IRCF2 = 1; IRCF1 = 1; IRCF0 = 1;  // Fosc = 8 MHz
        SCS = 1;
}

FOSC<2:0> of the configuration word register is set to internal oscillator with CLKOUT (_INTOSC). The 8 MHz high frequency oscillator is configured via IRCF<2:0> of the OSCCON register. And, then the internal oscillator is configured by setting the SCS bit of the OSCCON register.

Note: take a couple minutes to read pic16f690.h. It can be found here /usr/local/share/sdcc/non-free/include/pic14/pic16f690.h.

Investigate: The datasheet is somewhat inconsistent about the difference between FOSC<2:0> and SCS. SCS appears to control the MUX that selects between external and internal oscillator, despite its definition, while functionality such as CLKOUT as configured in FOSC<2:0> still takes effect.

If we probe CLKOUT, we'll see a 2 MHz square wave since CLKOUT is defined to be Fosc/4:

How long does it take to switch to 8 MHz?

Holy smokes! 380 us from power up!

GPIO

The PIC16F690 has general purpose I/O pins which are grouped into three ports. TRISA, TRISB, and TRISC can be configured to specify whether a pin is input or output, and PORTA, PORTB, PORTC can be read to get the value of an input pin or written to set the value of an output pin.

Note: When using PORTA pins, ANSEL and ANSELH must be set to 0 to configure the pins as digital I/O. If the pin is configured as analog input, the digital I/O circuitry is disabled.

How fast can we toggle a GPIO pin?

gpio1.c

#include <pic16f690.h>
#include <stdint.h>

static __code uint16_t __at(_CONFIG) configword1 =
        _INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF &
        _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF;

void main(void)
{
        IRCF2 = 1; IRCF1 = 1; IRCF0 = 1;  // Fosc = 8 MHz
        SCS = 1;

        // Disable analog input mode
        ANSEL = 0x00;
        ANSELH = 0x00;

        // Configure PORT I/O pins as output
        TRISA = 0x00;
        TRISC = 0x00;
        TRISB = 0x00;

        PORTA = 0x00;
        PORTB = 0x00;
        PORTC = 0x00;

        for (;;) {
                RC2 = 1;
                RC2 = 0;
        }
}

One loop iteration takes 6 instructions. Let's check the assembly (gpio1.asm):

_00106_DS_:
;       .line   27; "gpio1.c"   RC2 = 1;
        BANKSEL _PORTCbits
        BSF     _PORTCbits,2
;       .line   28; "gpio1.c"   RC2 = 0;
        BCF     _PORTCbits,2
        GOTO    _00106_DS_

If we look at section 2.2 Data Memory Organization of the PIC16F690 datasheet, we see that data memory is partitioned into four banks. The bank is selected by configuring RP<1:0> of the STATUS register. For example, to modify the OSCCON register, we must switch to Bank 1 first, and then write to address 0x8F.

BANKSEL is not an instruction, but an assembler directive. To see the complete disassembly, see the list file (gpio1.lst):

                                           _00106_DS_:
                                           ;    .line   27; "gpio1.c"   RC2 = 1;
0000e7   1283     bcf     0x03, 0x5             BANKSEL _PORTCbits
0000e8   1303     bcf     0x03, 0x6
0000e9   1507     bsf     0x07, 0x2             BSF     _PORTCbits,2
                                           ;    .line   28; "gpio1.c"   RC2 = 0;
0000ea   1107     bcf     0x07, 0x2             BCF     _PORTCbits,2
0000eb   28e7     goto    0x00e7                GOTO    _00106_DS_

Here we see that BANKSEL clears both RP bits in the STATUS register, which selects Bank 0. Notice the STATUS register is always at address 0x3 in each bank.

So... we don't need to switch banks every time the loop body executes... let's rewrite the loop in assembly gpio2.c:

__asm
        banksel PORTC
loop:
        bsf PORTC, 2
        bcf PORTC, 2
        goto loop
__endasm ;
}

Now we should expect the body of the loop to take 4 cycles since BSF and BCF take 1 cycle each and GOTO takes 2 cycles:

With a couple extra NOPs we can get a 50% duty cycle square wave. However, it has a jitter of ~10ns and is ~1.1kHz faster than expected:

Timer with interrupt

Timer0 is an 8-bit counter that can be configured to increment according to either an internal or external clock source. When the timer overflows from 0xFF to 0x00, an interrupt will be generated.

Example timer-interrupt.c:

#include <pic16f690.h>
#include <stdint.h>

static __code uint16_t __at(_CONFIG) configword1 =
        _INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF &
        _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF;

void isr(void) __interrupt(0)
{
        T0IF = 0;  // Clear timer interrupt flag.

        RC2 = 0;
        RC2 = 1;   // Set RC2 high for one cycle.
        RC2 = 0;
}

void main(void)
{
        IRCF2 = 1; IRCF1 = 1; IRCF0 = 1;  // Fosc = 8 MHz
        SCS = 1;

        TRISC = 0x00;  // Configure PORTC pins as digital output.
        PORTC = 0x00;  // Output logic zero on PORTC pins.

        T0CS = 0;      // Use the internal instruction cycle clock (Fosc/4) as TMR0 clock source.
        PSA = 1;       // Assign the prescaler to the WDT so that it doesn't affect TMR0.

        INTCON = 0;    // Disable all interrupt enables and flags.
        GIE = 1;       // Global interrupt enable.
        T0IE = 1;      // Enable Timer0 interrupt.

        TMR0 = 0;      // Reset counter to 0.

        for (;;) {
                // Do nothing!
        }
}

From the list file we see the interrupt service routine located at address 0x4 in program memory with quite a few instructions for saving/restoring a some special function registers:

                                           ;    .line   8; "timer-interrupt.c"  void isr(void) __interrupt(0)
000004   00f2     movwf   0x72                  MOVWF   WSAVE
000005   0e03     swapf   0x03, 0x0             SWAPF   STATUS,W
000006   0183     clrf    0x03                  CLRF    STATUS
000007   00f1     movwf   0x71                  MOVWF   SSAVE
000008   080a     movf    0x0a, 0x0             MOVF    PCLATH,W
000009   018a     clrf    0x0a                  CLRF    PCLATH
00000a   00f0     movwf   0x70                  MOVWF   PSAVE
00000b   0804     movf    0x04, 0x0             MOVF    FSR,W
00000c   1283     bcf     0x03, 0x5             BANKSEL ___sdcc_saved_fsr
00000d   1303     bcf     0x03, 0x6
00000e   00ac     movwf   0x2c                  MOVWF   ___sdcc_saved_fsr
                                           ;    .line   10; "timer-interrupt.c" T0IF = 0;  // Clear timer interrupt flag.
00000f   1283     bcf     0x03, 0x5             BANKSEL _INTCONbits
000010   1303     bcf     0x03, 0x6
000011   110b     bcf     0x0b, 0x2             BCF     _INTCONbits,2
                                           ;    .line   12; "timer-interrupt.c" RC2 = 0;
000012   1107     bcf     0x07, 0x2             BCF     _PORTCbits,2
                                           ;    .line   13; "timer-interrupt.c" RC2 = 1;  // Set RC2 high for one cycle.
000013   1507     bsf     0x07, 0x2             BSF     _PORTCbits,2
                                           ;    .line   14; "timer-interrupt.c" RC2 = 0;
000014   1107     bcf     0x07, 0x2             BCF     _PORTCbits,2
                                           ;    .line   15; "timer-interrupt.c" }
000015   1283     bcf     0x03, 0x5             BANKSEL ___sdcc_saved_fsr
000016   1303     bcf     0x03, 0x6
000017   082c     movf    0x2c, 0x0             MOVF    ___sdcc_saved_fsr,W
000018   1283     bcf     0x03, 0x5             BANKSEL FSR
000019   1303     bcf     0x03, 0x6
00001a   0084     movwf   0x04                  MOVWF   FSR
00001b   0870     movf    0x70, 0x0             MOVF    PSAVE,W
00001c   008a     movwf   0x0a                  MOVWF   PCLATH
00001d   0183     clrf    0x03                  CLRF    STATUS
00001e   0e71     swapf   0x71, 0x0             SWAPF   SSAVE,W
00001f   0083     movwf   0x03                  MOVWF   STATUS
000020   0ef2     swapf   0x72, 0x1             SWAPF   WSAVE,F
000021   0e72     swapf   0x72, 0x0             SWAPF   WSAVE,W
                                           END_OF_INTERRUPT:
000022   0009     retfie                        RETFIE

About

License:MIT License


Languages

Language:C 100.0%