week ten Header Img

Week 11: Teaching the Fab-Pic to read

In week 11 we had the task to measure something with an microcontroller. Well, we also should create our one board for this, so i used the fab-pic again. Lucky for you ;)
This way you will get some code-snipletts more for your first steps in the PIC-World...

My first idea was to realise a kind of touch-screen.

Touch raw Img

Touch folie Img

The theory behind this is a capacitive-touch-sensor, made out of a piece of FR4 or any other metal and a resistor. The sensor (the piece of metal) is connected with an input-pin equiped with a schmitt-trigger. The sensor is also connected to ground by a very high resistor, 1 mega-ohm by example.
The touch-sense itself is all made by the software in a loop:

  • Config the pin as output and make it high. The selfmade capacitor = the metal piece is charged up
  • wait a little bit for the charge to finish (5µs or so)
  • config the pin as an input
  • wait until the pin is a 0
  • repeat

  • So, what is happening here? The metal-piece is building a capacitor with the air around it. In the moment the Output-Pin is configed as an input the charge of this capacitor flows thru the very high resistance of the input-pin and thru the 1 mega-ohm resistor. Like every capacitor discharging this will happen in a inversed e-function.
    The schmitt-trigger on the input will tell a 0 for the input at a defined voltage of this function. If we combine this with a timer we can measure very exact the time of this discharge.

    But what happens if you put your finger on the metal-piece? Right, you change the capacitor and the discharge-time...
    The PIC-Code for this loop is quite easy:

    temp = 0;
    for(i=0; i<5; i++)
         TRISCbits.TRISC0 = 0;     //Set B0 to output
         PORTCbits.RC0 = 1;     //load the sensor-element
         __delay_ms(1);     //wait for the cap to load
         WriteTimer1(0x0);     //Reset Timer 1
         T1CONbits.TMR1ON = 1;     //Start Timer1
         TRISCbits.TRISC0 = 1;     //Set C0 to to input
         while (PORTCbits.RC0 == 1);     //wait for discharge
         T1CONbits.TMR1ON = 0;     //Stop Timer1
         temp = temp + ReadTimer1();
    button_1 = temp / 5;

    This works suprisingly good, but not on a bread board. I tried it out and there is no chance to get it working, because of all the capacitance in the board, emv into the cables between the bread-board and the sensor-board and so on.


    If you still want to try it on your own (maybe without the breadboard!), here are the eagle-files for the sensor-board:

    Ok, after the review with Barcelona i learnd, that Neil explained such a sensor circuit in the conference last week.
    But there is still one big difference: Neil uses the ADC to convert the whole discharging-curve. In my circuit, i use the Schmitt-trigger-inputs and the timer to measure the lenght of the discharging-curve. This way i can use an interrupt for the measurement and the conversion will be much faster.
    The schmitt-tiggers are needed to compare the curve to a defined voltage-level.
    Here is a simple sketch to explain it graphicaly:

    discharging-curves Img

    Again, lucky for you ;)
    After the touch-sensor-sh** i decided to make a short AD-Conversion, and give it out via the USART of the pic. I split this into two sections:

    The USART

    The XC8-Compiler comes with some peripheral functions, also a few for the USART. The Fab-Pic has 2 of these USARTs, we will use the first one. As you can see in the datasheet this is connected to pin RC7(receive) and pin RC6(transmit), on the Fab-Pic Pin 18 and 17.
    So these are the pins you can hook up your FTDI-Cable onto.
    As every peripheral-function we have to config the USART before we can use it.
    To make it simple, here is the commented code:

    Open1USART     //Init USART 1
        (USART_TX_INT_OFF &     //Send-Interrupt off
         USART_RX_INT_OFF &     //Receivce-Interrupt off
         USART_EIGHT_BIT &
         USART_CONT_RX &
         USART_BRGH_HIGH, 16);      //Baud 16 => 115.200

    Now you can use some USART-Commands like getch. To make it more comfortable you can add a subroutine to your program:

    //needed by printf, used to configure the destination
    void putch(char data) {
         while( ! TXIF)      // check buffer
         continue;      // wait till ready
         TXREG = data;      // send data

    This routine is used by the printf-function. Here you define were the printf should print to, USART1 or USART2 or LCD or or or...

    In this case the printf is patched to the USART1, the one we use. The usage is easy and standard c:

    printf("This is Fab-Pic!\n"); //Say hello to the world!

    To read something you have to wait until a character is received. The flag for the USART1 can be found PIR1bits.RC1IF. Take a look in the datasheet under "interrupts" to find the flags for other interfaces. As soon as a character is recieved, you can read it with "getc1USART()". If you want to read in a string use "gets1USART(destination, length)", so b.e. "gets1USART(char string[5], 5)".

        while (PIR1bits.RC1IF != 1);     //wait until USART received data
        some_number = getc1USART();     //read in the received char

    The ADC

    The Fab-Pic has 8 ADC-Channels with a resolution of 12-Bit.
    The init of the ADC is not very complex, i will show it by hand and not by using one of the plibs.
    In my oppinion it is easyer to write the registers by hand so you know exactly what happens

    In this example we will use only the AN0 input pin. The config:

        ANCON1 = 0b00000000;     // Just AN0 is an analog input, all other pins are digital
        ADCON1 = 0b00000000;     //No special triggers, Ref is Vdd and Vss
        ADCON2 = 0b10111100;     //result is right justified, 8 Tads, Clock = 32Mhz / 4
        ADCON0 = 0b00000001;     //Select AD0 for all AD-Operations

    After the init we can use the ADC by the following code:

        ADCON0 = 0b00000001;     //Select AD-Channel 0
        ConvertADC();     //start AD-Conversion
        while(BusyADC());     //wait for conversion to finish
        adc_result = ((ADRESH)<<8)|(ADRESL);    //read in the result
        adc_result = (adc_result * 1.22);    //make mV out of the adc-value

    For a test i connected a potentiometer to AN0, Vdd and Vss. Then i put the USART and the ADC snipletts together
    You can find the main.c on the bottom of this page as a download

    Breadboard Img

    And this is the result in a terminal:

    Terminal Img


    As a little extra here is some uncommented Code for using the I2C-Interface:

    // I2C-Init
    OpenI2C(MASTER, SLEW_OFF);     // Initialize I2C module
    SSPADD = 19;     //100kHz = 79, 400khz = 19

    And the for code to read out a MCP3221 ADC from Microchip:

    // Routine for readout of ADC MCP3221 by I2C
    unsigned int adchanger(void) {
         unsigned int highbyte, lowbyte, adc;
         highbyte = lowbyte = adc = 0;
         StartI2C();    //Send a start-condition
         while (SSPCON2bits.SEN);    //Wait for finishing the start-condition
         WriteI2C(0b10011011);    //Send the MCP the command to start a conversion
         while (SSPCON2bits.ACKSTAT);    //Wait for the ACK from the ADC
         highbyte = ReadI2C();     //Read in the highbyte
         IdleI2C();    //wait the readout to finish
         AckI2C();    //send a ACK to the ADC
         IdleI2C();    //wait for finish
         lowbyte = ReadI2C();    //read in the lowbyte
         IdleI2C();    //Wait for finisch
         StopI2C();    //Send the stop-condition
         while (SSPCON2bits.PEN);     //Wait for finishing the stop-condition
         highbyte = (highbyte << 8);     //Converse the High and Lowbyte into one unsigned int
         adc = (highbyte | lowbyte);
         return (adc);

    I hope this make some things for you easier...

    To download the main.c: CLICK

    In Week 13 i will show you how to make some output...

    Till then, have fun!