Page 1 of 1

PIC16F690 USART, TX okay - unable to RX

PostPosted: Thu Nov 23, 2017 12:04 am
by MarkEHansen
I previously built a project using the PIC18F4520 microcontroller and a Maxim MAX232CPE serial driver chip to talk, serially to a host PC. This works fine. I'm now building a similar project using a PIC16F690 controller and the same serial driver chip. The software uses the Hi-tech C compiler (for the PIC16 family).

I've configured the chip and am able to program and run it. I can see this due to a couple LEDs I'm using on PortC. I've altered the program and see the result in the blink rate/pattern of the LEDs.

I then connected up the serial driver chip with to the controller and the host PC and gave it a try. I'm able to transmit from the controller to the host PC and the text I send shows up on the terminal window. However, I'm not receiving any characters on the microcontroller from the host PC.

I've gone through the following several times, but can't find my error:
- The schematic for the circuit, which you can see here: http://www.mehconsulting.com/PIC/PIC16F690.pdf
- The component layout on the solderless breadboard (I don't know how to show you this...)
- The data sheet sections on configuring the chip for the oscillator, baud rate generator, etc.
- The data sheet sections on the use of the USART

The code includes an interrupt service routine "void interrupt isrRoutine(void) { ...}" which doesn't appear to be getting called when I send characters to the controller from the host PC. I've added a check for the RCIF (Receive Interrupt Flag) to the main loop, but never see it get set.

I was wondering if the TXSTA and RCSTA registers suffer from the read-modify-write issue and if perhaps my configuration bits are not getting set as I've requested, but all the examples I can find which show how to set up the configuration does it the way I'm doing it.

I've tried to trim the code down as much as possible. Can someone please let me know what they think I'm missing?

First: main.h
Code: Select all
#ifndef MAIN_H
#define MAIN_H

#include <htc.h>

typedef unsigned char uint8_t;
#define true 1
#define false 0
#define EQU ==

// This is required to use the __delay_us(...) or __delay_ms(...) in pic.h:
#define _XTAL_FREQ 8000000

// General
#define _CONCAT(a,b) a##b
#define PORT(x) _CONCAT(PORT,x)
#define TRIS(x) _CONCAT(TRIS,x)

// Indicator LED
#define ALARM_STATUS_RED  0
#define ALARM_STATUS_GREEN  1
#define ACTIVITY_BLIP_TIME 1500

#define ALARM_GREEN_PORT      C
#define ALARM_RED_PORT         C
#define ACTIVITY_PORT         C

#define ALARM_GREEN_POS         0
#define ALARM_RED_POS         1
#define ACTIVITY_POS         2


#define ALARM_GREEN_PORT_TRIS   TRIS(ALARM_GREEN_PORT)
#define ALARM_RED_PORT_TRIS   TRIS(ALARM_RED_PORT)
#define ACTIVITY_PORT_TRIS   TRIS(ACTIVITY_PORT)

#define ALARM_GREEN_PORT_DATA   PORT(ALARM_GREEN_PORT)
#define ALARM_RED_PORT_DATA   PORT(ALARM_RED_PORT)
#define ACTIVITY_PORT_DATA   PORT(ACTIVITY_PORT)

#define SET_ALARM_GREEN_PORT_OUTPUT() ALARM_GREEN_PORT_TRIS &= ( ~ ( 1 << ALARM_GREEN_POS ) ); // Set the pin as an output
#define SET_ALARM_RED_PORT_OUTPUT() ALARM_RED_PORT_TRIS &= ( ~ ( 1 << ALARM_RED_POS ) ); // Set the pin as an output
#define SET_ACTIVITY_PORT_OUTPUT() ACTIVITY_PORT_TRIS &= ( ~ ( 1 << ACTIVITY_POS ) ); // Set the pin as an output

#define ALARM_GREEN_PORT_HIGH()  (ALARM_GREEN_PORT_DATA |= (1 << ALARM_GREEN_POS)) // Set the pin
#define ALARM_GREEN_PORT_LOW()   (ALARM_GREEN_PORT_DATA &= ( ~ (1 << ALARM_GREEN_POS))) // Clear the pin

#define ALARM_RED_PORT_HIGH()  (ALARM_RED_PORT_DATA |= (1 << ALARM_RED_POS)) // Set the pin
#define ALARM_RED_PORT_LOW()   (ALARM_RED_PORT_DATA &= ( ~ (1 << ALARM_RED_POS))) // Clear the pin

#define ACTIVITY_PORT_HIGH()  (ACTIVITY_PORT_DATA |= (1 << ACTIVITY_POS)) // Set the pin
#define ACTIVITY_PORT_LOW()   (ACTIVITY_PORT_DATA &= ( ~ (1 << ACTIVITY_POS))) // Clear the pin


#endif


Next, is main.c:
Code: Select all
/*
 * This is a simple project for use with the PIC18F4520 chip, internal oscillator
 * set to run at 8MHz.
 *
 * The purpose of this project is to talk to the PC via the ESUART utilizing
 * interrupts for the receiver and to talk to a 2x16 LCD.
 */
asm("LIST c=128");

#include "main.h"
#include <stdio.h>

__CONFIG( FCMEN_OFF & IESO_OFF & BOREN_OFF & CPD_OFF & MCLRE_ON & PWRTE_OFF & WDTE_OFF & FOSC_INTRCIO );

void USARTInit ( )
    {
    // Configure the EUSART:
    // 4MHz Fosc at 9600 baud = SPRG value of 207 (0xCF))
    // SPBRGH = 0x00;
    // SPBRG  = 0xCF;
    SPBRGH = 0x00;
    SPBRG = 0xCF;

    BRGH = 1;   // TXSTA: Enable high-speed baud rate
    BRG16 = 1;    // Baudcon: 16-bit Baud Rate Register Enable bit

    SYNC = 0;    // TXSTA: Select Asynchronous mode
    SPEN = 1;    // RCSTA: Serial Port Enable bit
    TX9 = 0;    // TXSTA: Select 8-bit data
    TXEN = 1;    // TXSTA: Transmitter Enable
    RX9 = 0;    // RCSTA: Select 8-bit data
    CREN = 1;    // RCSTA: Continuous receive enable bit
    }

void USARTWriteByte ( char ch )
    {
    if ( OERR == 0x1 || FERR == 0x1 )
        {
        SPEN = 0x0;
        TXEN = 0x0;
        SPEN = 0x1;
        TXEN = 0x1;
        }

    // Wait for TXREG Buffer to become available
    while ( !TXIF )
        ;

    // Write data
    TXREG = ch;
    }

void USARTWriteString ( const char *str )
    {
    while ( ( *str ) != '\0' )
        {
        //Wait for TXREG Buffer to become available
        while ( !TXIF )
            ;

        //Write data
        TXREG = ( *str );

        //Next goto char
        str++;
        }
    }

void USARTWriteLine ( const char *ln )
    {
    USARTWriteString( ln );
    USARTWriteString( "\r\n" );
    }


// Eclipse shows this as a syntax error because it doesn't know about
// the "interrupt" tag:
void interrupt
isrRoutine(void) {
   // This is the interrupt service routine

   // First, check the EUSART Receive Interrupt Flag
   if (RCIF) {
      USARTWriteLine("Found RCIF");
      // This is an EUSART Receiver interrupt
      if (OERR EQU 0x1 || FERR EQU 0x1) {
         // If we had an error, reset the USART and bail.
         SPEN = 0x0; // Serial Port Enable OFF
         CREN = 0x0; // Continuous Receive Enable OFF
         SPEN = 0x1; // Serial Port Enable ON
         CREN = 0x1; // Continuous Receive Enable ON
      } else {
         // Receive the character(s)
         while (RCIF) {
            uint8_t currentChar = RCREG;
            if ( currentChar EQU 0x0A ) {
               USARTWriteString( "\r\n" );
            } else if ( currentChar EQU 0x0D ) {
               USARTWriteString( "\r\n" );
            } else {
               USARTWriteByte( currentChar );
            }
         }
      }
   }
   // ... continue to check for other interrupts. There may be more than one source.
} // void interrupt isrRoutine( void )

void main() {
   unsigned char latC = 0b00000000;
   unsigned char activity = true;
   unsigned long activityCount = 0;
   unsigned char activityBlip = 0;
   unsigned long cycleCount = 0;
   unsigned char alarmStatus = ALARM_STATUS_RED;

   // IRFC<2:0> = 111      - 8MHz internal oscillator
   // OSTS = 0             - Device is running from the internal oscillator status bit (Read only)
   // HTS = 0              - HFINTOSC status bit (read only)
   // LTS = 0              - LFINTOSC stable bit (read only)
   // SCS = 1              - Internal oscillator used for system clock

   OSCCON = 0b1110001;
   while ( OSTS EQU 1 ) {
      ; // Wait until the processor is running from the internal oscillator.
   }
   while ( HTS EQU 0 ) {
      ; // Wait for the HFINTOSC to become stable at the new speed.
   }

   __delay_ms(50); // Delay the startup so we don't stumble during programming.

   //Initialize the USART
   USARTInit();
   __delay_ms(50);

   // Enable interrupts, used by the USART Receiver
   PEIE = 1;   // Enable peripheral interrupts
   RCIE = 1;   // Enable USART Receiver interrupts
   RCIF = 0;   // Clear the USART Receiver interrupt flag
   GIE = 1;   // Enable global interrupts

   __delay_ms(50);

   USARTWriteLine("\r\nReady");

   TRISC = 0b00000000;

   while (true) {
      if ( ++cycleCount > 15000 ) {
         cycleCount = 0;
         if ( alarmStatus EQU ALARM_STATUS_RED ) {
            latC |= (1 << ALARM_GREEN_POS); // Drive ALARM_GREEN high
            latC &= ( ~ ( 1 << ALARM_RED_POS) ); // Drive ALARM_RED low
            alarmStatus = ALARM_STATUS_GREEN;
         } else {
            latC |= (1 << ALARM_RED_POS); // Drive ALARM_RED high
            latC &= ( ~ ( 1 << ALARM_GREEN_POS) ); // Drive ALARM_GREEN low
            alarmStatus = ALARM_STATUS_RED;
            activityBlip = true;
            // USARTWriteLine("Blip");
         }
      }

      if ( activityBlip EQU true ) {
         activityBlip = false;
         latC |= (1 << ACTIVITY_POS); // Drive ACTIVITY high
         activityCount = 0;
         activity = true;
      }

      if ( activity EQU true ) {
         if ( ++activityCount EQU ACTIVITY_BLIP_TIME ) {
            latC &= ( ~ ( 1 << ACTIVITY_POS) ); // Drive ACTIVITY low
            activity = false;
            activityCount = 0;
         }
      }
      PORTC = latC;

      // This is just a check to see if we've received a character from the serial port
      if ( RCIF ) {
         USARTWriteLine( "Main loop: found RCIF set.");
      }
   }
} // main()



Thanks,

Re: PIC16F690 USART, TX okay - unable to RX

PostPosted: Fri Nov 24, 2017 7:13 am
by ric
RX/RB5 is an analog capable pin (AN11), so you need to switch it to digital mode.
Add a "ANSELHbits.ANS11 = 0" near where you initialise TRISC.

Also note, you shouldn't be doing the OERR and FERR tests in the USARTWriteByte() function, only in the receive interrupt.

Re: PIC16F690 USART, TX okay - unable to RX

PostPosted: Fri Nov 24, 2017 2:09 pm
by MarkEHansen
Thanks, Ric. I set the analog configuration as you suggested, but this made no difference. I'm going to hit the data sheet to see if there's anything I'm doing wrong in this regard (I assumed the analog features would be disabled by default).

I'm going to create a simpler program, recheck the electronics and data sheet and then post back once done.

Re: PIC16F690 USART, TX okay - unable to RX

PostPosted: Fri Nov 24, 2017 3:22 pm
by MarkEHansen
I've looked at the data sheet regarding the ANSEL registers for both port B and C. I've set appropriate ANSEL bits to zero to disable the analog function for these pins. I've also trimmed most of the unnecessary stuff out of the code and have attached the single source file.

I'm building the project with a make file. Here are the commands being run:
Code: Select all
picc.exe --pass1 main.c -q --chip=16F690 -P --opt=default -g --asmlist
picc.exe -Opic16f690v1.cof -Mpic16f690v1.map main.p1 -q --chip=16F690 --opt=default -g --asmlist


I then write the object to the PIC using a PICkit2, using the following command:
Code: Select all
pk2cmd.exe -Fpic16f690v1.hex -M -P -R -T


I've changed the code so it turns on one of the project's LEDs when the program starts, and lights the second LED when the receive interrupt is detected. The program runs, but the receive interrupt is still not getting called.

Re: PIC16F690 USART, TX okay - unable to RX

PostPosted: Fri Nov 24, 2017 3:40 pm
by MarkEHansen
Thank you all for your help and patience. I found the problem. I didn't have the power or ground lines connected on the MAX232 chip! I missed them because they are a separate block on the schematic (not a good excuse, but it's the one I'm going with :) ).