UART Troubleshooting / Help - PIC16F689 + CP2102

Thread Starter

matrix7181

Joined Nov 8, 2012
9
Hello,

I've been having some difficulties making the damn serial communication to work.
For 3 days I couldn't sleep, eat, drink .. It's driving me crazy.
(Slightly exaggerated, but for real, I don't know what to do anymore)

So here is the scenario.

I've set up and connected the PIC16F689 with CP2102 (USB to UART Bridge) and with putty I get the output from the MCU but I can't for the love of what is holy send anything to it. It just doesn't cooperate.

Details :
Env : MPLAB X IDE 1.50
Programmer : PICkit3 (used to power the MCU as well, 3,3V)
Compiler Toolchain : XC8 (v1.11)

PIN 10 (RB7/TX/CK) connected to RX pin on CP2102
PIN 12 (RB5/AN11/RX/DT) connected to TR on CP2102
PIN 20 (Vss) connected to the GND on CP2102

I don't know how to draw schematics so here is a picture (just for completeness, I don't know If you can see anything from all the wires, but maybe you will see something else that I did wrong) :



Configuration :
Rich (BB code):
// CONFIG
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#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)
Important Bits / Whats Going On / The Problem :
Rich (BB code):
//Set the bits / registers 

    CREN = 1; //Enable Receiver
    TXEN = 1; //Enable Transmitter
    SPEN = 1;
    TXIE = 0;  //Disable interrupts
    RCIE = 0;
    TX9 = 0; //Transmission Type -  8 bit (Disable 9bit comm)
    RX9 = 0;
    SYNC = 0; //async
    BRGH = 1; //hi speed
    BRG16 = 0;
    SPBRG = 0x19;//Page 165 / BaudRate 9600
    TXREG = 0x00;//Clear Transmit register

//So Everything is set up, I hook this up to pc and get the 
//50x times of "Hello World \r\n"

    for(i=0;i<50;i++)
        transmitMsg(greet_msg);

//Then this segment

        while(!RCIF)
        {
            CLEAR_USART_ERRORS;
        }

//Just SKIPS over the first time, even though nothing is being sent
//For some reason RCIF is set as the RCREG register is full / buffered.

//This just makes the invalid value and all 4 LEDs light up
//On the next iteration if the while(1) loop [2nd iter to be exact]

        while(!RCIF) ...

//Waits, and waits, and waits ....
I've tried the code in Stimulus, and it works as expected .. Or so I think since
MPLABX Stimulus is a bit different from the older one in mplab 8.
The thing is in Stimulus here I can set the register RCREG, which apparently sets the RCIF interrupt bit as well. So it executes nicely.
However, a thing to notice is that it doesn't skip the while(!RCIF) at startup.
Somehow I think this is "cheating" and the actual transmission on the RX pin is skipped and the only way this works on Stimulus is because im directly setting the RCREG register.

For reference complete code :
Rich (BB code):
#include <stdio.h>
#include <stdlib.h>
#include <pic16f689.h>

#define ITER_DELAY  25000

#define LED1 0x10 //RC5
#define LED2 0x01 //RC0
#define LED3 0x02 //RC1
#define LED4 0x08 //RC4

#define CLEAR_USART_ERRORS \
if (OERR)\
{\
    TXEN = 0;\
    TXEN = 1;\
    CREN = 0;\
    CREN = 1;\
}\
if (FERR)\
{\
    TXEN = 0; \
    TXEN = 1; \
}

void initSystem()
{
    //Oscilator Control
    //Page 50 - IRCF -- Default 110 -> 4MHz

    //Port C - Outputs for PNP Transisedr Control (LEDs atm)
    TRISC = 0x00;
    PORTC = 0x00;

    /**********
     * EUSART *
     **********/
    //AsyncMode (pg 150)
    //Transmit Status And Control Register (pg 160)
    //Receive Status And Control Register (pg 161)

    CREN = 1; //Enable Receiver
    TXEN = 1; //Enable Transmitter
    SPEN = 1;

    //Disable interrupts
    TXIE = 0;
    RCIE = 0;

    //Transmission Type -  8 bit (Disable 9bit comm)
    TX9 = 0;
    RX9 = 0;

    SYNC = 0;
    BRGH = 1;
    BRG16 = 0;

    //Baud Rate
    SPBRG = 0x19;//Page 165 / BaudRate 9600

    //RCREG - EUART Receive Register
    //TXREG - EUART Transmit Register
    TXREG = 0x00;//Clear Transmit register
}

void transmitMsg(register const char *msg)
{
    char c;
    while(c = *msg++)
    {
        while(!TXIF)
        {
            CLEAR_USART_ERRORS;
        }
        TXREG = c;
        delay();
    }
}

/*
 * Main Program Entry
 */
int main(int argc, char** argv)
{
    initSystem();

    char greet_msg[] = "Hello World \r\n";
    unsigned char tmp_rcreg = 'A';
    unsigned int i = 0;

    PORTC = LED4;

    for(i=0;i<50;i++)
        transmitMsg(greet_msg);

    //Bit Mask - 0001 1011
    //Valid input mask (0x1B)
    while(1)
    {
        transmitMsg("Waiting for Input : \r\n");
        //Wait for some data
        while(!RCIF)
        {
            CLEAR_USART_ERRORS;
        }
        tmp_rcreg = RCREG;

        if(tmp_rcreg == 'A')
            PORTC = LED1;
        else if(tmp_rcreg == 'B')
            PORTC = LED2;
        else if(tmp_rcreg == 'C')
            PORTC = LED3;
        else if(tmp_rcreg == 'D')
            PORTC = LED4;
        else
            PORTC = LED1 | LED2 | LED3 | LED4;


        //TXREG=tmp_rcreg;//tmp_rcreg;
        // while(TRMT==0);//Wait till all bytes transmitted
        transmitMsg(tmp_rcreg);
        for(i=0;i<ITER_DELAY;i++);
    }

    return (EXIT_SUCCESS);
}
*The page # is the page from the datasheet for memo

You can probably by now notice that I am beginner at this.
I always liked electronics and decided it was about time to learn it.

After reading datasheets, comparing other C code from PIC site samples, comparing assembler code from other sources and reading datasheets from those MCUs used in the examples, tried finding the similarities .. blah blah ..
No success .. And I'm stuck :(

So please, any advice / tips / pointers / hints / solutions are more than welcome.
 

JohnInTX

Joined Jun 26, 2012
4,787
Since you get the hello message, the baud rate etc. is good and transmit is working.

Have you turned all handshaking OFF on the terminal? If not, you'll have to strap RTS/CTS and DTR/DSR on the UART pins of the USB adapter to get it to send anything to the PIC. Check it by putting a DVM (or scope) on the RX pin of the PIC. It should be Vdd. Send a BREAK and the voltage should drop to 0 for the duration. You can approximate this by sending a 'U' (55h) and seeing if the pin wiggles.

I haven't scoured your code but here are some things that jump out:

TXREG = 0x00; does not clear TXREG, it sends 00h. Probably not your problem but..

Its better to enable the UART TX/RX after setting up its mode, not before.

You don't have to continually inspect FERR/OERR and neither is related to transmission. On each RXIF char received, first examine these error bits and THEN take corrective action only if set. Otherwise, just read RXBUF and continue.

RCIF clears when RCREG is read. To avoid OERR, you must read it before the next char has been fully received. On OERR, the UART will stop receiving until the error is cleared.

When transmitting, TXIF == 1 when you can put another char onto TXBUF to send it. You don't have to monitor TRMT unless you want to know when the last char has been shifted out (the transmitter is double buffered). The last few lines can monitor TXIF as well.

Consider using a switch statement instead of lots of if/else.

If you are going to add stuff after the RX gets working, consider a buffered, interrupt driven RX section to avoid missing chars while you are busy doing other things in the code.

Have fun!
 
Last edited:

t06afre

Joined May 11, 2009
5,934
Start very simple by using a terminal program. Send one character from the terminal program each time. And type it in slow . Then let the PIC return it. As said before you have received a hello word message. So that mean your UART setup are OK.
 

MMcLaren

Joined Feb 14, 2010
861
... You have to clear RCIF manually after reading RXBUF to avoid OERR. On OERR, the UART will stop receiving until the error is cleared.
Just wondering if this was a typo', John? I'm not aware of any way to clear the RCIF flag manually. I thought it could only be cleared by reading RCREG...
 

MMcLaren

Joined Feb 14, 2010
861
Learning how to use the library functions isn't really the same as learning how the actual hardware works, is it? That said, I think you're on the right track. Please note John's suggestions.

If it helps, I've attached a plain vanilla Serial Demo for the little 8-pin 12F1822 written using the free/lite BoostC compiler (which I run within MPLAB).

Cheerful regards, Mike

Rich (BB code):
/********************************************************************
 *                                                                  *
 *  Project: 12F1822 Serial Demo                                    *
 *   Source: 12F1822_Serial_Demo.c                                  *
 *   Author: Mike McLaren, K8LH                                     *
 *  (C)2012: Micro Application Consultants, All Rights Reserved     *
 *     Date: 08-Jul-12                                              *
 *  Revised: 08-Jul-12                                              *
 *                                                                  *
 *  12F1822 Serial Demo                                             *
 *                                                                  *
 *                                                                  *
 *      IDE: MPLAB 8.84 (tabs = 4)                                  *
 *     Lang: SourceBoost BoostC v7.05, Lite/Free version            *
 *                                                                  *
 ********************************************************************/

   #include <system.h>

   #pragma DATA _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _MCLRE_OFF
   #pragma DATA _CONFIG2, _LVP_OFF & _PLLEN_OFF

   #pragma CLOCK_FREQ 8000000   // 8-MHz INTOSC

  /******************************************************************
   *  function prototypes                                           *
   ******************************************************************/
  /******************************************************************
   *  type definitions                                              *
   ******************************************************************/

   typedef unsigned char u08;
   typedef unsigned int u16;

   #define r08 rom unsigned char

  /******************************************************************
   *  variables and constants                                       *
   ******************************************************************/
  /******************************************************************
   *  low level drivers                                             *
   ******************************************************************/
  /******************************************************************
   *  functions                                                     *
   ******************************************************************/

   char get232()                //
   { while(pir1.RCIF == 0);     // wait for RX char available
     return rcreg;              //
   }                            //
   
   void put232(char txdata)     //
   { while(pir1.TXIF == 0);     // wait for TX buffer empty
     txreg = txdata;            //
   }                            //
   
   void put232(rom char *data)  //
   { char ndx = 0;              //
     while(wreg = data[ndx++])  //
       put232(wreg);            //
   }                            //

  /******************************************************************
   *  main init                                                     *
   ******************************************************************/

   void main()
   { ansela = 0;                // make pins digital
     trisa = 0b00000000;        // porta all outputs
     porta = 0;                 // all output latches low
     osccon = 0b01110010;       // initialize 8-MHz INTOSC
     while(!oscstat.HFIOFS);    // wait until OSC stable

     ansela = 0;                // adc off, digital I/O
     trisa.1 = 1;               // set RX pin as input

    /*                                                              *
     *  setup alternate pin functions for TX on RA0 and RX on RA1   *
     *                                                              */
     apfcon.RXDTSEL = 0;        // RX/DT function on RA1
     apfcon.TXCKSEL = 0;        // TX/CK function on RA0

    /*                                                              *
     *  setup serial port for 9600 baud (8 MHz clock)               *
     *                                                              */
     spbrg = 51;                // 9600 baud
     baudcon.BRG16 = 0;         // use 8-bit brg clock
     txsta = 1<<TXEN|1<<BRGH;   // txen=1, brgh=1, sync=0
     rcsta = 1<<SPEN|1<<CREN;   // spen=1, cren=1

     delay_ms(200);             //

     put232("\n\r\n\rK8LH 12F1822 Serial Demo\n\r\n\r");


  /******************************************************************
   *  main loop                                                     *
   ******************************************************************/

     while(1)                   //
     { char work;               //
       work = get232();         //
       put232(work);            //
       put232(0x0A);            //
       put232(0x0D);            //
     }                          //
   }
 

spinnaker

Joined Oct 29, 2009
7,830
Learning how to use the library functions isn't really the same as learning how the actual hardware works, is it? That said, I think you're on the right track. Please note John's suggestions.
Maybe. But then there is having it not work with working with the hardware or having it work with the libraries. Or at the very least spending days of sleepless nights as the OP has done trying to get it to work or moving on to get the rest of your project working

When you code for the PC, do you write to the graphics display hardware or do you use higher level calls like MS .Net?
 

takao21203

Joined Apr 28, 2012
3,702
Serial port isn't so difficult, even on the hardware level, and writing a terminal program yourself.

-Use Visual Studio. There is a serial port class: Toolbox -> Components -> Serial port.

-Set correct handshaking on the PC side, as well take care of all serial port settings (see datasheet). You need to go through it a few times.

-Use LEDs and piezo buzzers on all signal lines (9600 baud etc.), to verify they are actually activated.

It should not take long until communication works.

I have done this some years ago in assembler, but now, I use USB directly.
 

Thread Starter

matrix7181

Joined Nov 8, 2012
9
First of all I want to thank you all for your replies.

Pattern : Quote -> Reply

Have you turned all handshaking OFF on the terminal?
I believe so, yes.

Rich (BB code):
Client Settings
---------------
Baud : 9600
Data bits : 8
Stop bits : 1
Parity : None
Flow Control : None
If not, you'll have to strap RTS/CTS and DTR/DSR on the UART pins of the USB adapter to get it to send anything to the PIC. Check it by putting a DVM (or scope) on the RX pin of the PIC. It should be Vdd. Send a BREAK and the voltage should drop to 0 for the duration. You can approximate this by sending a 'U' (55h) and seeing if the pin wiggles.
It doesn't wiggle. The voltage goes from 3.32 to 3.31 and, if I hold the button to 3.25

I did the same thing when PIC was transmitting and the voltage wiggles between : 3.20 - 3.19

I wish I had a scope to see the signal, but can't afford it.
My voltmeter is manual, so my assumption this was a "faster" voltage test since no need to auto range the value. Or so I think, probably I'm wrong, or it doesn't have a difference.

If you are going to add stuff after the RX gets working, consider a buffered, interrupt driven RX section to avoid missing chars while you are busy doing other things in the code.
I need to figure out how interrupts work and how to write them in C.

Have fun!
Thank you, and thanks for the suggestions, also I want to have fun .. but but .. I'm failing at these basic stuff. It's bringing my morale down.

Why reinvent the wheel when the Microchip UART libraries work perfectly fine?

... get it to work or moving on to get the rest of your project working ...
My project is to learn, and so making will be a success.

I'd like to be able to write the library before using it. I have this motto of not using anything I can't program / write myself. (As in knowing how it works and being able to make it, makes it okay for me to use it)

I downloaded Microchip Source Serial as well some other projects but, different compilers, different MCU .. :\

That was my reference for code / logic.

Learning how to use the library functions isn't really the same as learning how the actual hardware works, is it?
I completely agree with this for my particular case.
Thank you for the attachment. I used it side by side comparison with my code.

Thanks, I've already seen it. Did google quite a bit before posting here.

So reading all replies I think maybe I know what the problem is however, I'm not sure, and if that is the case .. I will be embarrassed.

This is the stripped down / Redone from scratch code that I'm using :

Rich (BB code):
#include <stdio.h>
#include <stdlib.h>
#include <pic16f689.h>

// CONFIG
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#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)

void init()
{
    //Port C
    TRISC = 0x00;
    PORTC = 0x00;

    IRCF0 = 0;//OSCCON Register - IRCF Bits for 4MHz
    IRCF1 = 1;
    IRCF2 = 1;

    /**************
     *    USART   *
     **************/

    SPBRG = 0x19;//Page 165 / BaudRate 9600 @ 4MHz
    BRGH = 1;
    BRG16 = 0;
    
    SYNC = 0;   //Asynchronous
    SPEN = 1;   //enable serial port pins
    CREN=1;     //enable reception
    SREN = 0;   //No effect
    TXIE=0;	//disable tx interrupts
    RCIE=0;	//disable rx interrupts
    TX9 = 0;    //8-bit transmission
    RX9 = 0;    //8-bit reception
    TXEN=0;     //reset transmitter
    TXEN=1;     //enable the transmitter
}

void delay(int x)
{
    int c = x*1000;
    while(c--);
}

void transmitMsg(register const char *msg)
{
    char c;
    while(c = *msg++)
    {
        while(!TXIF);
        TXREG = c;
        delay(2);
    }
}

/*
 * 
 */
int main(int argc, char** argv)
{
    init();
    char tmp_rcreg;
    
    transmitMsg("Echo service started ...\r\n");

    while(1)
    {
        PORTC = 0x00;
        transmitMsg("Input : \r\n");

        while(!RCIF);
        tmp_rcreg = RCREG;

        while(!TXIF);
        TXREG = tmp_rcreg;

        PORTC = 0x01;
        delay(9);
    }
    
    return (EXIT_SUCCESS);
}
So ... Comparing all the codes I've read so far they are quite similar. And I think the problem is not in the code but in my circuit.

Do I need to use MAX232 ?

I've seen the MAX232 being used everywhere, and wiki tells me it translates the signal logic voltage :

RS-232 TTL Logic
-----------------------------------------------
-15V ... -3V <-> +2V ... +5V <-> 1
+3V ... +15V <-> 0V ... +0.8V <-> 0

I assume I couldn't be able to receive the data from the PIC if I needed to use it ?

When the USB to UART brigde is connected and I try to program the PIC MPLAB gives me this error :

Too much current has been drawn on VDD. Please disconnect your circuit, check
the CLK and DATA lines for shorts and then reconnect.


I "solved" it by adding
10k ohm resistor to TX pin on PIC
1k ohm resistor on RX pin on PIC

The decision was based on my basic knowledge of .. "Strap a resistor the current will go down".

Was this ok to do ?

Do you have any more ideas / suggestions ?
 

spinnaker

Joined Oct 29, 2009
7,830
My project is to learn, and so making will be a success.

I'd like to be able to write the library before using it. I have this motto of not using anything I can't program / write myself. (As in knowing how it works and being able to make it, makes it okay for me to use it)
But your coding in C. Why not assembler? Heck don't stop there, enter the program in binary.

We use tons of tools every day that we have no idea on the details of the way they work. If we did try to understand everything, to detail, we would never get anywhere.

You can get the project working with the library. There is nothing that says you can't take a look a how they did it then try to repeat the library on your own later.
 

Thread Starter

matrix7181

Joined Nov 8, 2012
9
But your coding in C. Why not assembler? Heck don't stop there, enter the program in binary.

We use tons of tools every day that we have no idea on the details of the way they work. If we did try to understand everything, to detail, we would never get anywhere.

You can get the project working with the library. There is nothing that says you can't take a look a how they did it then try to repeat the library on your own later.
I did program in assembler before going to C.
And of course I would gladly use the library as I said, but I can't find any for this pic, and this XC8 compiler.

All my search got is : UART Library (mC PRO for PIC)
Also, I don't think it's the code.
 

Thread Starter

matrix7181

Joined Nov 8, 2012
9
10K is a bit high. You can also use a switch for the connection.
1K should be enough.

Does this PIC have a SCKP bit?
Yes.

In BAUDCTL: BAUD RATE CONTROL REGISTER

SCKP: Synchronous Clock Polarity Select bit

Asynchronous mode:
1 = Transmit inverted data to the RB7/TX/CK pin
0 = Transmit non-inverted data to the RB7/TX/CK pin
Synchronous mode:
1 = Data is clocked on rising edge of the clock
0 = Data is clocked on falling edge of the clock

But the transmit works as intended. It's the getting data to the pic is what doesn't work.

Also thanks for the tip, will replace it with lower value.
 

MMcLaren

Joined Feb 14, 2010
861
But your coding in C. Why not assembler? Heck don't stop there, enter the program in binary.

We use tons of tools every day that we have no idea on the details of the way they work. If we did try to understand everything, to detail, we would never get anywhere.

You can get the project working with the library. There is nothing that says you can't take a look a how they did it then try to repeat the library on your own later.
Not exactly sure what you're ranting about but, considering the amount of energy you're using, why not throw together a working XC8 example program that we can all admire?
 

takao21203

Joined Apr 28, 2012
3,702
It might be easier to write a terminal software yourself.
So you can have more control.

I have developed I2C, LED multiplex, LCD, and serial port circuits only with the help of LEDs, and a piezo buzzer.*

This is very helpful to see how far signals will actually make it.

You need to know if something arrives on the PIC RX.
If not, there is a problem with the terminal.
If so, your PIC software is wrong.

*they are kind of high-Z, most clocked signals still work even with a piezo attached.
 

spinnaker

Joined Oct 29, 2009
7,830
I did program in assembler before going to C.
And of course I would gladly use the library as I said, but I can't find any for this pic, and this XC8 compiler.

All my search got is : UART Library (mC PRO for PIC)
Also, I don't think it's the code.
The microchip lib does work for most pics but I am guessing the complier could be an issue. Are you certain they don't have their own lib?


You should try to at least afford a logic analyzer. I have a Zeroplus Logic Cube. While not the most serious LA on the market, it suits my needs and it is very affordable. It was worth it's weight in gold when I had similar serial port issues.
 
Top