Author Topic: XC8 Soft UART on PIC16F819-Help please  (Read 1652 times)

0 Members and 1 Guest are viewing this topic.

Offline djsbTopic starter

  • Frequent Contributor
  • **
  • Posts: 891
  • Country: gb
XC8 Soft UART on PIC16F819-Help please
« on: June 03, 2023, 08:29:14 am »
Here is the main.c file of my software serial project

Code: [Select]
/*
 * File: Main.c
 * Author: David J S Briscoe
 * PIC: 16F819 with INTERNAL RC OSC @ 8MHz, 5v
 * Program: 03_RS232
 * Compiler: XC8 (v2.40, MPLABX X v6.00)
 * Program Version: 1.0
 *               
 * Program Description: This Program Allows PIC16F819 to write to/from RS232.
 * .
 *
 * Hardware Description:
 *
 * Notes:
 *
 * 
 *                       
 * Created May 30th 2023, 11:32 AM
 * Last Updated:31st May 2023
 */

/*******************************************************************************
 Change History:
 Revision                  Description
 31st May 2023             Added calls to SUART_Init()routine and SUART_Write_Text("Hello") to main().
 1st to 2nd June 2023     
 
*******************************************************************************/
/*******************************************************************************
 TO DO:
 Task                      Details
 Check header file/s       Make sure calls in main() are same as in sw_uart.h and sw_uart.c.
       
 
*******************************************************************************/



/*******************************************************************************
 *Includes and defines
 ******************************************************************************/
#include "16F819_Internal.h" // Include header which sets  config fuses and oscillator frequency (set @ 8MHz) if using a XTAL.
#include "sw_uart.h"
#include <string.h>



/*******************************************************************************
 * Function: void initMain()
 *
 * Returns: Nothing
 *
 * Description: Contains initializations for main
 *
 * Usage: initMain()
 ******************************************************************************/

void initMain()
{
   
    OSCCONbits.IRCF = 0b0111; //Set the Clock to 8MHz when using internal oscillator.
                              /*
                              111 = 8 MHz (8 MHz source drives clock directly)
                              110 = 4 MHz
                              101 = 2 MHz
                              100 = 1 MHz
                              011 = 500 kHz
                              010 = 250 kHz
                              001 = 125 kHz
                              000 = 31.25 kHz (INTRC source drives clock directly)
                              Page 38 of the PIC16F819 data sheet.
                              */
    ADCON1bits.PCFG = 0b0110; //See page 82 of data sheet..
    ///////////////////////
     // Setup SW UART
     //////////////////////
   
    PORTA = 0x00;              //Clear Port A.
    PORTB = 0x00;              //Clear Port B.
   
   
}

/*******************************************************************************
 * Function: Main
 *
 * Returns: Nothing
 *
 * Description: Program entry point
 ******************************************************************************/

int main(void)
{
char letter;   
int n,i,j;
initMain();

// Initialize SW UART
SUART_Init();
__delay_ms(1000);
// Do forever.
while(1)
     {
    for ( n = 65; n < 91; n++)
        {
        letter = n;
        __delay_ms(1000);
        SUART_Write_Text(letter);// SPACE for testing. Measure bit time on a scope.
        __delay_ms(1000);
        }
     }
    for ( i = 97; i < 123; i++)
        {
        letter = i;
        __delay_ms(1000);
        SUART_Write_Text(letter);// SPACE for testing. Measure bit time on a scope.
        __delay_ms(1000);
        }
    for ( j = 48; j < 58; j++)
        {
        letter = n;
        __delay_ms(1000);
        SUART_Write_Text(letter);// SPACE for testing. Measure bit time on a scope.
        __delay_ms(1000);
        }

}


Here is the sw_uart.c code

Code: [Select]
/*
 * File: sw_uart.c
 * Author: Armstrong Subero modified by David Briscoe
 * PIC: 16F819 w/Int OSC @ 8MHz, 5v
 * Program: Library file for Software UART
 * Compiler: XC8 (v1.41, MPLAX X v3.55)
 * Program Version: 1.0
 *
 * Program Description: This Library allows you to use a software UART with
 *                      the PIC microcontroller
 *                     
 * Created on June 1st, 2023
 */

#include "sw_uart.h"


/*******************************************************************************
 * Function: SUART_Init(void)
 *
 * Returns: Nothing
 *
 * Description: Initializes UART pins
 *
 ******************************************************************************/

void SUART_Init(void)
{
 UART_TX = 1; // TX pin is high in idle state
 
 UART_RX_DIR = 1; // Input
 UART_TX_DIR = 0; // Output
}


/*******************************************************************************
 * Function: unsigned char SUART_Receive(void)
 *
 * Returns: Nothing
 *
 * Description: recieves via soft UART
 *
 ******************************************************************************/

unsigned char SUART_Receive(void)
{
 // Initial at 0
 unsigned char DataValue = 0;

 //wait for start bit
 while(UART_RX==1);

 __delay_us(OneBitDelay);
 __delay_us(OneBitDelay/2); // Take sample value in the mid of bit duration

 for ( unsigned char i = 0; i < DataBitCount; i++ )
 {
  if ( UART_RX == 1 ) //if received bit is high
  {
   DataValue += (1<<i);
  }

  __delay_us(OneBitDelay);
 }

 // Check for stop bit
 if ( UART_RX == 1 ) //Stop bit should be high
 {
  __delay_us(OneBitDelay/2);
  return DataValue;
 }
 else //some error occurred !
 {
  __delay_us(OneBitDelay/2);
  return 0x000;
 }
}


/*******************************************************************************
 * Function: void SUART_Transmit(const char DataValue)
 *
 * Returns: Nothing
 *
 * Description: transmits via soft UART
 *
 ******************************************************************************/

void SUART_Transmit(const char DataValue)
{
 /* Basic Logic
   
    TX pin is usually high. A high to low bit is the starting bit and
    a low to high bit is the ending bit. No parity bit. No flow control.
    BitCount is the number of bits to transmit. Data is transmitted LSB first.

 */

 // Send Start Bit
 UART_TX = 0;
 __delay_us(OneBitDelay);

 for ( unsigned char i = 0; i < DataBitCount; i++ )
 {
  //Set Data pin according to the DataValue
  if(((DataValue>>i)&0x1) == 0x1 ) //if Bit is high
  {
   UART_TX = 1;
  }
  else //if Bit is low
  {
   UART_TX = 0;
  }

     __delay_us(OneBitDelay);
 }

 //Send Stop Bit
 UART_TX = 1;
 __delay_us(OneBitDelay);
}

/*******************************************************************************
 * Function: void SUART_Write_Text(char *text)
 *
 * Returns: Nothing
 *
 * Description: writes text via soft UART
 *
 ******************************************************************************/

void SUART_Write_Text(char *text)
{
  int i;
 
  for(i=0;text[i]!='\0';i++)
      SUART_Transmit(text[i]);
}


/*******************************************************************************
 * Function: void SUART_Read_Text(char *Output, unsigned int length)
 *
 * Returns: Nothing
 *
 * Description: reads text via soft UART
 *
 ******************************************************************************/

void SUART_Read_Text(char *Output, unsigned int length)
{
int i;
for(int i=0;i<length;i++)
Output[i] = SUART_Receive();
}


/*******************************************************************************
 * Function: void SUART_Write_Char(char a)
 *
 * Returns: Nothing
 *
 * Description: writes a character to SOFT UART
 *
 ******************************************************************************/

void SUART_Write_Char(char a)
{
    SUART_Transmit(a - 128);
}

Here is the sw_uart.h header file

Code: [Select]
/*
 * File: sw_uart.h
 * Author: Armstrong Subero modified by David Briscoe
 * PIC: 16F819 w/X OSC @ 8MHz, 5v
 * Program: Header file to setup PIC16F819 software UART
 * Compiler: XC8 (v1.41, MPLAX X v3.55)
 * Program Version 1.0
 *           
 * Program Description: This program header allows PIC to use a software UART
 *
 * Created on April 15th, 2017, 12:57 PM
 * Modified 1st June 2023.
 */


#include "16F819_Internal.h"

// bits per second
#define Baudrate 9600

// delay for UART
#define OneBitDelay (1000000/Baudrate)

// no parity no flow control
#define DataBitCount 8

// SW UART RX
#define UART_RX PORTBbits.RB4 //Wherever the compiler sees SW1_PORTB4 it substitutes PORTBbits.RB4
// SW UART TX
#define UART_TX PORTBbits.RB2 //Wherever the compiler sees LED_PORTB2 it substitutes PORTBbits.RB2



// RX pin direction register
#define UART_RX_DIR TRISBbits.TRISB4   //Ensure RB4 is an input pin (RX). Make same pins as in course material.

// TX pin direction register
#define UART_TX_DIR TRISBbits.TRISB2   //Ensure RB2 is an output pin (TX).

//Function Declarations
void SUART_Init(void);
unsigned char SUART_Receive(void);
void SUART_Transmit(const char);
void SUART_Write_Text(char *text);
void SUART_Write_Char(char a);
void SUART_Read_Text(char *Output, unsigned int length);


And here is the 16F819_Internal.h file.
Code: [Select]
/*
 * File: 16F819_Internal.h
 * Author: David J S Briscoe
 * PIC: 16F819 with INTERNAL OSC @ 8MHz, 5v
 * Program: Header file to setup PIC16F819
 * Compiler: XC8 (v2.40, MPLAX X v6.00)
 * Program Version 1.0
 *               
 *                *Changed comments and layout
 *                 
 * Program Description: This program header will allows setup of configuration
 *                      bits and provides routines for setting up internal
 *                      oscillator and includes all devices and MCU modules
 *
 * Created on February 24th, 2023
 ******************************************************************************/

/*******************************************************************************
 *Includes and defines
 ******************************************************************************/
// PIC16F819 Configuration Bit Settings
/*
Fuses: NOWDT,WDT,PUT,NOPUT,LP,XT,HS,EC_IO,INTRC_IO,INTRC,RC_IO,RC
//////// Fuses: NOMCLR,MCLR,NOBROWNOUT,BROWNOUT,NOLVP,LVP,CPD,NOCPD,WRT_600
//////// Fuses: WRT_400,WRT_200,NOWRT,DEBUG,NODEBUG,CCPB3,CCPB2,PROTECT
//////// Fuses: NOPROTECT
*/

// PIC16F819 Configuration Bit Settings

// 'C' source line config statements

// CONFIG
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = ON       // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB3/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB2      // CCP1 Pin Selection bit (CCP1 function on RB2)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

//XC8 Standard Include
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>

//Other Includes
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <math.h>


//For delay routines WHEN USING A CRYSTAL OSCILLATOR ONLY
#define _XTAL_FREQ 8000000

//MCU Modules Includes


When I compile this using MPLABX and XC8 I get a few warnings but no errors. However all I get from PUTTY is garbage. I can also see the bursts of data on the R&S RTO1024 scope at work. I'm still learning how to use the scope and I havent figured out how to freeze and zoom into the waveform of the serial data and measure the timing yet. I have attached a zip file of the project below. Can anyone see any obvious mistakes (apart from in the comments which need review and updating in parts) or any ways to get the code working? How can I use a hardware timer in this code to make the timing more accurate? Thanks.


PS Ive been asked to use a 16F819 initially but I'll be writing code for a 16f1827/47 with a hardware EUART later on.
« Last Edit: June 03, 2023, 08:39:04 am by djsb »
David
Hertfordshire,UK
University Electronics Technician, London PIC,CCS C,Arduino,Kicad, Altium Designer,LPKF S103,S62 Operator, Electronics instructor. Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime. Credited Kicad French to English translator.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5903
  • Country: es
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #1 on: June 03, 2023, 08:41:23 am »
First try toggling  a led every onebitdelay *  baudrate (9600, whatever) and see if you get anything close to 1 second.
What warnings finyu receive?
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: djsb

Offline djsbTopic starter

  • Frequent Contributor
  • **
  • Posts: 891
  • Country: gb
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #2 on: June 03, 2023, 08:52:23 am »
Here are the warnings
Code: [Select]
make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory 'F:/XC8_WORK/XC8_16F819/03_RS232.X'
make  -f nbproject/Makefile-default.mk dist/default/production/03_RS232.X.production.hex
make[2]: Entering directory 'F:/XC8_WORK/XC8_16F819/03_RS232.X'
"C:\Program Files\Microchip\xc8\v2.40\bin\xc8-cc.exe"  -mcpu=16F819 -c   -mdfp="C:/Program Files/Microchip/MPLABX/v6.05/packs/Microchip/PIC16Fxxx_DFP/1.3.42/xc8"  -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -DXPRJ_default=default  -msummary=-psect,-class,+mem,-hex,-file  -ginhx32 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mdefault-config-bits   -std=c99 -gdwarf-3 -mstack=compiled:auto:auto     -o build/default/production/Main.p1 Main.c
"C:\Program Files\Microchip\xc8\v2.40\bin\xc8-cc.exe"  -mcpu=16F819 -c   -mdfp="C:/Program Files/Microchip/MPLABX/v6.05/packs/Microchip/PIC16Fxxx_DFP/1.3.42/xc8"  -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -DXPRJ_default=default  -msummary=-psect,-class,+mem,-hex,-file  -ginhx32 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mdefault-config-bits   -std=c99 -gdwarf-3 -mstack=compiled:auto:auto     -o build/default/production/sw_uart.p1 sw_uart.c
::: advisory: (2049) C99 compliant libraries are currently not available for baseline or mid-range devices, or for enhanced mid-range devices using a reentrant stack; using C90 libraries
::: advisory: (2049) C99 compliant libraries are currently not available for baseline or mid-range devices, or for enhanced mid-range devices using a reentrant stack; using C90 libraries
Main.c:106:18: warning: implicit conversion loses integer precision: 'int' to 'char' [-Wconversion]
        letter = n;
               ~ ^
Main.c:108:26: warning: incompatible integer to pointer conversion passing 'char' to parameter of type 'char *'; take the address with & [-Wint-conversion]
        SUART_Write_Text(letter);// SPACE for testing. Measure bit time on a scope.
                         ^~~~~~
                         &
./sw_uart.h:44:29: note: passing argument to parameter 'text' here
void SUART_Write_Text(char *text);
                            ^
Main.c:114:18: warning: implicit conversion loses integer precision: 'int' to 'char' [-Wconversion]
        letter = i;
               ~ ^
Main.c:116:26: warning: incompatible integer to pointer conversion passing 'char' to parameter of type 'char *'; take the address with & [-Wint-conversion]
        SUART_Write_Text(letter);// SPACE for testing. Measure bit time on a scope.
                         ^~~~~~
                         &
./sw_uart.h:44:29: note: passing argument to parameter 'text' here
void SUART_Write_Text(char *text);
                            ^
Main.c:121:18: warning: implicit conversion loses integer precision: 'int' to 'char' [-Wconversion]
        letter = n;
               ~ ^
Main.c:123:26: warning: incompatible integer to pointer conversion passing 'char' to parameter of type 'char *'; take the address with & [-Wint-conversion]
        SUART_Write_Text(letter);// SPACE for testing. Measure bit time on a scope.
                         ^~~~~~
                         &
./sw_uart.h:44:29: note: passing argument to parameter 'text' here
void SUART_Write_Text(char *text);
                            ^
6 warnings generated.
"C:\Program Files\Microchip\xc8\v2.40\bin\xc8-cc.exe"  -mcpu=16F819 -Wl,-Map=dist/default/production/03_RS232.X.production.map  -DXPRJ_default=default  -Wl,--defsym=__MPLAB_BUILD=1   -mdfp="C:/Program Files/Microchip/MPLABX/v6.05/packs/Microchip/PIC16Fxxx_DFP/1.3.42/xc8"  -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -msummary=-psect,-class,+mem,-hex,-file  -ginhx32 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mdefault-config-bits -std=c99 -gdwarf-3 -mstack=compiled:auto:auto      -Wl,--memorysummary,dist/default/production/memoryfile.xml -o dist/default/production/03_RS232.X.production.elf  build/default/production/Main.p1 build/default/production/sw_uart.p1     
::: advisory: (2049) C99 compliant libraries are currently not available for baseline or mid-range devices, or for enhanced mid-range devices using a reentrant stack; using C90 libraries
sw_uart.c:45:: warning: (520) function "_SUART_Receive" is never called
sw_uart.c:150:: warning: (520) function "_SUART_Read_Text" is never called
sw_uart.c:167:: warning: (520) function "_SUART_Write_Char" is never called
sw_uart.c:154:: warning: (1498) pointer (SUART_Read_Text@Output) in expression may have no targets

Memory Summary:
    Program space        used    CAh (   202) of   800h words   (  9.9%)
    Data space           used    11h (    17) of   100h bytes   (  6.6%)
    EEPROM space         used     0h (     0) of   100h bytes   (  0.0%)
    Configuration bits   used     1h (     1) of     1h word    (100.0%)
    ID Location space    used     4h (     4) of     4h bytes   (100.0%)

make[2]: Leaving directory 'F:/XC8_WORK/XC8_16F819/03_RS232.X'
make[1]: Leaving directory 'F:/XC8_WORK/XC8_16F819/03_RS232.X'

BUILD SUCCESSFUL (total time: 12s)
Loading code from F:/XC8_WORK/XC8_16F819/03_RS232.X/dist/default/production/03_RS232.X.production.hex...
Program loaded with pack,PIC16Fxxx_DFP,1.3.42,Microchip
Loading completed



The bit time (onebitdelay) should be 104.16 microsececonds. I will try this test (onebitdelay * 9600) (which should have been obvious to me) next week. Thanks.
« Last Edit: June 03, 2023, 08:54:39 am by djsb »
David
Hertfordshire,UK
University Electronics Technician, London PIC,CCS C,Arduino,Kicad, Altium Designer,LPKF S103,S62 Operator, Electronics instructor. Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime. Credited Kicad French to English translator.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5903
  • Country: es
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #3 on: June 03, 2023, 09:21:40 am »
That warning means something  ;).
SUART_Write_Text() expects a pointer to a string, you're loading "letter" to it.

First try something more simple (main.c loop):
Code: [Select]
while(1){
    SUART_Write_Text("Hello!\r\n");
    __delay_ms(1000);
}


Then fix your code by replacing SUART_Write_Text(letter) with SUART_Write_Char(letter)
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: djsb

Offline djsbTopic starter

  • Frequent Contributor
  • **
  • Posts: 891
  • Country: gb
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #4 on: June 03, 2023, 09:33:16 am »
Thanks. I will try that. Just for clarification in

Code: [Select]
void SUART_Write_Char(char a)
{
    SUART_Transmit(a - 128);
}


In the line

Code: [Select]
SUART_Transmit(a - 128);

What does (a - 128) parameter do?

My for loops already pass the correct range of values to the function. Maybe I need to change the parameter that I pass to SUART_Transmit? Thanks.
David
Hertfordshire,UK
University Electronics Technician, London PIC,CCS C,Arduino,Kicad, Altium Designer,LPKF S103,S62 Operator, Electronics instructor. Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime. Credited Kicad French to English translator.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5903
  • Country: es
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #5 on: June 03, 2023, 09:44:37 am »
Sorry that function is also wrong, no idea why the -128.
Use SUART_Transmit(letter) instead.
« Last Edit: June 03, 2023, 10:00:28 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline 8goran8

  • Contributor
  • Posts: 29
  • Country: cs
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #6 on: June 03, 2023, 12:46:08 pm »
Try a lower baud rate.
I tried with 2400 and it works in proteus simulation.

#define Baudrate 2400
 
The following users thanked this post: djsb

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5903
  • Country: es
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #7 on: June 04, 2023, 02:13:50 am »
Modified the code to use Timer 2, providing much better timing accuracy, now it works up to 38400 baud.
Only made to work with 9600 / 19200 / 38400 baudrates!
Both receiving and sending are working.

Code: [Select]

#include <xc.h>

#define _XTAL_FREQ      8000000UL
#define Baudrate        38400

#if (Baudrate!=9600) && (Baudrate!=19200) && (Baudrate!=38400)
 #error "Please set a valid baudrate: 9600/19200/38400"
#endif

#define OneBitDelay     ((_XTAL_FREQ/(4*(unsigned long)Baudrate))-1)

#define UART_RX         PORTBbits.RB4
#define UART_RX_DIR     TRISBbits.TRISB4
#define UART_TX         PORTBbits.RB2
#define UART_TX_DIR     TRISBbits.TRISB2

// Bit macros to avoid slow looping (We need it to be as fast as possible)
#define BIT_DELAY()     TMR2IF=0; while(!TMR2IF);
#define TX_BIT(v,n)     if(v & n){UART_TX=1;} else{UART_TX=0;} BIT_DELAY()
#define RX_BIT(v,n)     if(UART_RX){v|=n;} BIT_DELAY()

void SUART_Init(void)
{
 UART_TX = 1;                           // TX pin is high in idle state 
 UART_RX_DIR = 1;                       // Input
 UART_TX_DIR = 0;                       // Output 
 PR2 = OneBitDelay;                     // Load period counter
 T2CON = 0;                             // Use timer 2 as timebase
 T2CONbits.TMR2ON = 1;                  // Start timer
}

unsigned char SUART_Receive(void)
{
 unsigned char DataValue = 0;

  while(UART_RX==1);            // wait for start bit
  TMR2=OneBitDelay*2/3;         // Preload the timer with at 2/3 of a bitDelay (1/2 might sample too late with this slow mcu)
  BIT_DELAY();                  // Wait for it to expire
  BIT_DELAY();                  // Counter resetted, wait for a full bit time now
  RX_BIT(DataValue,0x01);       // Unrolled loop for fastest processing
  RX_BIT(DataValue,0x02);
  RX_BIT(DataValue,0x04);
  RX_BIT(DataValue,0x08);
  RX_BIT(DataValue,0x10);
  RX_BIT(DataValue,0x20);
  RX_BIT(DataValue,0x40);
  RX_BIT(DataValue,0x80); 
  return DataValue;
}

void SUART_Transmit(char DataValue)
{
  UART_TX = 0;                                           // Start
  TMR2=0;
  BIT_DELAY(); 
  TX_BIT(DataValue,0x01);       // Unrolled loop for fastest processing
  TX_BIT(DataValue,0x02);
  TX_BIT(DataValue,0x04);
  TX_BIT(DataValue,0x08);
  TX_BIT(DataValue,0x10);
  TX_BIT(DataValue,0x20);
  TX_BIT(DataValue,0x40);
  TX_BIT(DataValue,0x80); 
  UART_TX = 1;                                           //Stop
  BIT_DELAY();
}

void SUART_Write_Text(char *text)
{
  while(*text)
    SUART_Transmit(*text++);
}

void SUART_Read_Text(char *Output, unsigned int length)
{   
  while(length--)
    *Output++ = SUART_Receive();      // Receive char
}

void initMain()
{
  OSCCONbits.IRCF = 0b0111; //Set the Clock to 8MHz when using internal oscillator.
  ADCON1bits.PCFG = 0b0110; //See page 82 of data sheet..   
  PORTA = 0x00;              //Clear Port A.
  PORTB = 0x00;              //Clear Port B.   
}

int main(void)
{
  char i;
  initMain();   
  SUART_Init();
  __delay_ms(1000);
 
     
  /* Simple string tests */
  /*
  SUART_Write_Text("Hello!\r\n");
  SUART_Write_Text("Software uart, ");
  #if (Baudrate==9600)
    SUART_Write_Text("9600");
  #elif (Baudrate==19200)
    SUART_Write_Text("19200");
  #elif(Baudrate==38400)
    SUART_Write_Text("38400");
  #else
    SUART_Write_Text("unknown");
  #endif
  SUART_Write_Text(" bauds.\r\n\n");
  */
   
 
  /* TX/RX loop */
  /*
  SUART_Write_Text("Please type in the console now, you should see the input below.\r\n\n");
  while(1){
    SUART_Transmit(SUART_Receive());
  }
  */
 
  while(1)
  {
    for ( i = 'A'; i <= 'Z'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
    for ( i = 'a'; i < 'z'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
    for ( i = '0'; i <= '9'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
  }
}
« Last Edit: June 04, 2023, 02:48:06 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: djsb

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5903
  • Country: es
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #8 on: June 04, 2023, 03:22:40 pm »
This code can reach much faster speeds, tested up to 115200 bauds.
Uses Timer2 + CCP in PWM mode.
This PIC has only one CCP module so it's only able to send data!
There're two CCP1 pins, but you can't switch them on the fly, it's set in the config bits when programming.

Code: [Select]
#include <xc.h>
#define _XTAL_FREQ      8000000UL

// Tested with 9600, 19200, 38400, 57600 and 115200.
#define Baudrate        115200UL

#define BitPeriod       ((2*Baudrate+_XTAL_FREQ)/(4*Baudrate))

#define UART_TX         PORTBbits.RB2
#define UART_TX_DIR     TRISBbits.TRISB2

// Bit macros to avoid slow looping (We need it to be as fast as possible)
// CCPR1L holds the 8 MSB bits of the PWM period.
// By only setting MSB to 1 or 0, we set PWM >100% or 0%, thus sending a 1 or a 0.
#define TX_WAIT()       TMR2IF=0; while(!TMR2IF);
#define TX_START()      CCPR1L&=0x7F; TMR2ON=1; TX_WAIT();
#define TX_STOP()       CCPR1L|=0x80; TX_WAIT(); TMR2ON=0; TMR2=0;
#define TX_BIT(b)       if(b){CCPR1L|=0x80;} else{CCPR1L&=0x7F;} TX_WAIT()

void SUART_Init(void)
{                                       
 T2CON = 0;                             // Reset TIM2
 TMR2=0;                                // Clear counter
 PR2 = BitPeriod-1;                     // Load bit period
 CCPR1L = 0;                            // Clear PWM duty
 CCP1CON = 0x0C;                        // PWM mode, PWM 2LSB=0
 TMR2ON=1;                              // Enable timer 2
 TX_STOP();                             // Set TX high
 UART_TX_DIR = 0;                       // Set TX as output
}

void SUART_Transmit(char DataValue)
{
  TX_START();
  TX_BIT(DataValue&0x01);       // Unrolled loop for fastest processing
  TX_BIT(DataValue&0x02);
  TX_BIT(DataValue&0x04);
  TX_BIT(DataValue&0x08);
  TX_BIT(DataValue&0x10);
  TX_BIT(DataValue&0x20);
  TX_BIT(DataValue&0x40);
  TX_BIT(DataValue&0x80); 
  TX_STOP();
}

void SUART_Write_Text(char *text)
{
  while(*text)
    SUART_Transmit(*text++);
}

int main(void)
{
  char i;
  OSCCONbits.IRCF = 0b0111; //Set the Clock to 8MHz when using internal oscillator.
  ADCON1bits.PCFG = 0b0110; //See page 82 of data sheet..   
  PORTA = 0x00;              //Clear Port A.
  PORTB = 0x00;              //Clear Port B.   
  SUART_Init();
  __delay_ms(1000);
 

  SUART_Write_Text("Hello! This is a fast software UART using PWM!\r\n");
  SUART_Write_Text("CCP has only one pin, so I can only send data!\r\n\n"); 
  SUART_Write_Text("Entering test loop\r\n\n");
 
  while(1)
  {
    for ( i = 'A'; i <= 'Z'; i++){
      SUART_Transmit(i);
      __delay_ms(500);
    }
    SUART_Write_Text("\r\n");
   
    for ( i = 'a'; i < 'z'; i++){
      SUART_Transmit(i);
      __delay_ms(500);
    }
    SUART_Write_Text("\r\n");
   
    for ( i = '0'; i <= '9'; i++){
      SUART_Transmit(i);
          __delay_ms(500);
    }
    SUART_Write_Text("\r\n");
  }
}


The timer resolution is not great, so at 115.200bauds the actual baudrate is 117.647, but still under acceptable limits of +-3%,  should work just fine.
The pwm generates very small pico-second glitches/spikes at the start of each "0" bit, it might be a simulation bug or a real limitation of this PIC.
This won't cause any problem as the UART signal is sampled at 50% of the bit duration by standard.
« Last Edit: June 04, 2023, 03:44:04 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: djsb

Offline djsbTopic starter

  • Frequent Contributor
  • **
  • Posts: 891
  • Country: gb
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #9 on: June 04, 2023, 04:16:20 pm »
DavidAlfa,
Thank you :-+. I will give your examples a try during next week and if I have any further questions I'll post them here. By the way, what simulator are you using in the last example?

David.
David
Hertfordshire,UK
University Electronics Technician, London PIC,CCS C,Arduino,Kicad, Altium Designer,LPKF S103,S62 Operator, Electronics instructor. Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime. Credited Kicad French to English translator.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5903
  • Country: es
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #10 on: June 04, 2023, 04:17:08 pm »
Proteus
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: djsb

Offline djsbTopic starter

  • Frequent Contributor
  • **
  • Posts: 891
  • Country: gb
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #11 on: June 06, 2023, 06:56:10 pm »
I have tried the first example on silicon and it works perfectly. I have a few questions and the answers would help me to understand how some of the code works

1/What is this line calculating
Code: [Select]
#define OneBitDelay     ((_XTAL_FREQ/(4*(unsigned long)Baudrate))-1)

Is it the bit time or a value specifically for timer 2?

2/How does this code work?

Code: [Select]
void SUART_Read_Text(char *Output, unsigned int length)


  while(length--)
        {
        *Output++ = SUART_Receive();      // Receive char
        }
}

Can you give me an example of how this function is used (similar to SUART_Write_Text examples)?

3/In UART_Write_Text could a variable be passed to the function somehow?

4/ Are there any built in Printf/fprintf/sprintf functions in XC8 that can be used? Or does the programmer have to make their own printf like functions to compensate? What about format specifiers etc?

5/ How are pointers used in your first example?

Thanks for your help.
David
Hertfordshire,UK
University Electronics Technician, London PIC,CCS C,Arduino,Kicad, Altium Designer,LPKF S103,S62 Operator, Electronics instructor. Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime. Credited Kicad French to English translator.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5903
  • Country: es
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #12 on: June 06, 2023, 09:28:05 pm »
1/What is this line calculating
Code: [Select]
#define OneBitDelay     ((_XTAL_FREQ/(4*(unsigned long)Baudrate))-1)
Is it the bit time or a value specifically for timer 2?

Check where OneBitDelay is being used  ;).
Both! It's the Timer 2 period, matching the bit time.
Timer2 starts counting up, when the counter matches the period it resets and the cycle starts over.
So for 8MHz Xtal, Fcy is Xtal/4 =2MHz, for 9600baud the period is 2MHz/9600=208.
Real timer period is PR2+1, thus we subtract 1 from the final value.


2/How does this code work?
Code: [Select]
void SUART_Read_Text(char *Output, unsigned int length)


  while(length--)
        {
        *Output++ = SUART_Receive();      // Receive char
        }
}

You specify the buffer Output and the expected data length.
The function will keep receiving data from the serial port  until the data length is done.
SUART_Receive waits for the start bit, then processes the following bits and returns the final char.

But it's too simple, might cause stalling and weird things.
Better to modify to receive data until a null char / new line are received, and return the received count:
Code: [Select]
// Output: Buffer where to write data
// length: Max receiving bytes, to prevent overflow
unsigned int SUART_Read_Text(char *Output, unsigned int length)
{
  unsigned int rx = 0;
  while(length)
  {
    char a= SUART_Receive();         // Receive char
    if(!a || a=='\r' || a=='\n')        // Break on string termination, new line or line return
      break;
    Output[rx++] = a;                   // Store in output buffer, increase count   
    length--;
  }
  if(length)                            // Clear last char to terminate the string
    Output[rx] = '\0';                  // We didn't fill the output buffer, so clear next available char
  else
    Output[rx-1] = '\0';            // length fully used, clear last char of the buffer

  return rx;                            // Return total received chars
}


Can you give me an example of how this function is used (similar to SUART_Write_Text examples)?

Code: [Select]
while(1){
  char buffer[32];                             // Output buffer, 32 chars
  unsigned int recv=0;

  SUART_Write_Text("\r\n\nPlease type something and press enter, I'll answer back :)\r\n");
  recv = SUART_Read_Text(buffer, sizeof(buffer));     // Automatically computes the buffer size

  // Now it''ll wait for the data from RX. At the computer, type something like "Hello, djsb here".
  // Enter will cause a new line, stopping the receiving
  SUART_Write_Text("Got data!\r\n\n");
  SUART_Write_Text("I received: \"");
  SUART_Write_Text(buffer);
  SUART_Write_Text("\"\r\n");
  if (recv==sizeof(buffer)){
      SUART_Write_Text("The buffer was filled up, stopping receiving early.\r\n");
      SUART_Write_Text("Please type shorter strings.\r\n");
  }
  __delay_ms(1000);



3/In UART_Write_Text could a variable be passed to the function somehow?
No, it's an entirely different thing.


4/ Are there any built in Printf/fprintf/sprintf functions in XC8 that can be used? Or does the programmer have to make their own printf like functions to compensate? What about format specifiers etc?

Yes, by adding the header #include <stdlib.h> .
I recommend trying itoa() for (un)signed integers (Add #include <stdio.h>)
(s)printf might use a heck of resources, itoa will be much ligher for simple interger to string conversion.
Avoid floats when possible, they will use a lot of resources!
Code: [Select]
#include <stdlib.h>

  char buffer[32];                             // Output buffer, 32 chars
  unsigned int counter=0;

while(1){ 
  SUART_Write_Text("(itoa)    Counter: ");
  SUART_Write_Text(itoa(buffer, counter, 10)); // itoa returns the same char pointer as specified in the argument
  SUART_Write_Text("\r\n");

  sprintf(buffer, "(sprintf) Counter: %u\r\n", counter); 
  SUART_Write_Text(buffer);

  printf("(printf)  Counter: %u\r\n\r\n", counter);

  counter++;
  __delay_ms(1000);
}


5/ How are pointers used in your first example?
What "first example" exactly?
« Last Edit: June 06, 2023, 09:59:21 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: djsb

Offline djsbTopic starter

  • Frequent Contributor
  • **
  • Posts: 891
  • Country: gb
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #13 on: June 07, 2023, 06:49:51 pm »
Thanks for your reply and further help.
My comment about pointers refers to the Asterisks in this code

Code: [Select]

#include <xc.h>

#define _XTAL_FREQ      8000000UL
#define Baudrate        38400

#if (Baudrate!=9600) && (Baudrate!=19200) && (Baudrate!=38400)
 #error "Please set a valid baudrate: 9600/19200/38400"
#endif

#define OneBitDelay     ((_XTAL_FREQ/(4*(unsigned long)Baudrate))-1)

#define UART_RX         PORTBbits.RB4
#define UART_RX_DIR     TRISBbits.TRISB4
#define UART_TX         PORTBbits.RB2
#define UART_TX_DIR     TRISBbits.TRISB2

// Bit macros to avoid slow looping (We need it to be as fast as possible)
#define BIT_DELAY()     TMR2IF=0; while(!TMR2IF);
#define TX_BIT(v,n)     if(v & n){UART_TX=1;} else{UART_TX=0;} BIT_DELAY()
#define RX_BIT(v,n)     if(UART_RX){v|=n;} BIT_DELAY()

void SUART_Init(void)
{
 UART_TX = 1;                           // TX pin is high in idle state
 UART_RX_DIR = 1;                       // Input
 UART_TX_DIR = 0;                       // Output
 PR2 = OneBitDelay;                     // Load period counter
 T2CON = 0;                             // Use timer 2 as timebase
 T2CONbits.TMR2ON = 1;                  // Start timer
}

unsigned char SUART_Receive(void)
{
 unsigned char DataValue = 0;

  while(UART_RX==1);            // wait for start bit
  TMR2=OneBitDelay*2/3;         // Preload the timer with at 2/3 of a bitDelay (1/2 might sample too late with this slow mcu)
  BIT_DELAY();                  // Wait for it to expire
  BIT_DELAY();                  // Counter resetted, wait for a full bit time now
  RX_BIT(DataValue,0x01);       // Unrolled loop for fastest processing
  RX_BIT(DataValue,0x02);
  RX_BIT(DataValue,0x04);
  RX_BIT(DataValue,0x08);
  RX_BIT(DataValue,0x10);
  RX_BIT(DataValue,0x20);
  RX_BIT(DataValue,0x40);
  RX_BIT(DataValue,0x80);
  return DataValue;
}

void SUART_Transmit(char DataValue)
{
  UART_TX = 0;                                           // Start
  TMR2=0;
  BIT_DELAY();
  TX_BIT(DataValue,0x01);       // Unrolled loop for fastest processing
  TX_BIT(DataValue,0x02);
  TX_BIT(DataValue,0x04);
  TX_BIT(DataValue,0x08);
  TX_BIT(DataValue,0x10);
  TX_BIT(DataValue,0x20);
  TX_BIT(DataValue,0x40);
  TX_BIT(DataValue,0x80);
  UART_TX = 1;                                           //Stop
  BIT_DELAY();
}

void SUART_Write_Text(char *text)
{
  while(*text)
    SUART_Transmit(*text++);
}

void SUART_Read_Text(char *Output, unsigned int length)
{   
  while(length--)
    *Output++ = SUART_Receive();      // Receive char
}

void initMain()
{
  OSCCONbits.IRCF = 0b0111; //Set the Clock to 8MHz when using internal oscillator.
  ADCON1bits.PCFG = 0b0110; //See page 82 of data sheet..   
  PORTA = 0x00;              //Clear Port A.
  PORTB = 0x00;              //Clear Port B.   
}

int main(void)
{
  char i;
  initMain();   
  SUART_Init();
  __delay_ms(1000);
 
     
  /* Simple string tests */
  /*
  SUART_Write_Text("Hello!\r\n");
  SUART_Write_Text("Software uart, ");
  #if (Baudrate==9600)
    SUART_Write_Text("9600");
  #elif (Baudrate==19200)
    SUART_Write_Text("19200");
  #elif(Baudrate==38400)
    SUART_Write_Text("38400");
  #else
    SUART_Write_Text("unknown");
  #endif
  SUART_Write_Text(" bauds.\r\n\n");
  */
   
 
  /* TX/RX loop */
  /*
  SUART_Write_Text("Please type in the console now, you should see the input below.\r\n\n");
  while(1){
    SUART_Transmit(SUART_Receive());
  }
  */
 
  while(1)
  {
    for ( i = 'A'; i <= 'Z'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
    for ( i = 'a'; i < 'z'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
    for ( i = '0'; i <= '9'; i++){
      SUART_Transmit(i);
      __delay_ms(1000);
    }
  }
}


In particular, in the SUART_Write_Text(char *text) and void SUART_Read_Text(char *Output, unsigned int length) functions. I'm unfamiliar with the use of pointers in general and not clear on how they work in a small microcontroller. Thanks again.
David
Hertfordshire,UK
University Electronics Technician, London PIC,CCS C,Arduino,Kicad, Altium Designer,LPKF S103,S62 Operator, Electronics instructor. Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime. Credited Kicad French to English translator.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5903
  • Country: es
Re: XC8 Soft UART on PIC16F819-Help please
« Reply #14 on: June 07, 2023, 08:48:23 pm »
Pointers in this context are pretty easy!

Code: [Select]
char letter;        // This is a simple char. This variable is stored somewhere in the ram. A pointer would contain that address.
char * ptr;         // This is a char pointer. It can hold an address pointing to a char variable type

ptr = &letter;     // Store letter address into the pointer.    &(variable) takes the address of a variable
 
Write(letter);     // Those do exactly the same.
Write(*ptr);       // *(pointer) takes the value of the variable placed at the address hold by the pointer.

A string is actually a pointer to an aray:
Code: [Select]
char *dave_msg = "Hello, I'm Dave";         // Make a pointer to an array containing this string

This would be the same as:
Code: [Select]
char dave_msg_arr[] = "Hello, I'm Dave";  // Make an array containing this string
char *dave_msg = dave_msg_arr;              // Point to the first element, H

Remember strings in C are null-terminated.
This means that "Hello, I'm Dave" is not 15 char, but 16, there's a final char set to 0, indicating the end of the string.
A very simple error would be to send an unterminated string using a standard C function.
It would cause an uncontrolled situation where it would send all the data it finds until reaching a 0.

So SUART_Write_Text(char *text) would take the text pointer, send the data it points to, increase the address and repeat until it finds a 0.
Code: [Select]
void SUART_Write_Text(char *text)
{
  while(*text)                                      // If the memory address pointer by text is not 0
    SUART_Transmit(*text++);             // Send the data, increment the pointer address afterwards and repeat the loop
}

For SUART_Write_Text("Hello"), you would have an array in memory like this:

Address   Value
 0             'H'
 1             'e'
 2             'l'
 3             'l'
 4             'o'
 5              0      <- Null termination

text pointer would initially point to 'H'. It will increment after each pass, so it'll go through 'e', 'l', 'l', 'o' and finally 0.
When 0 is found, the while(*text) (Which is the same as while(*text != 0)) breaks the loop and the function returns.

SUART_Read_Text(char *Output, unsigned int length) does a similar thing.
You specify the input buffer:
Code: [Select]
char buffer[16];    // Store here the incoming characters
SUART_Read_Text(buffer, 16);  // Passing the array name automatically makes a pointer to its first element

Inside SUART_Read_Text(char *Output, unsigned int length), the Output pointer will be incremented after each received char.
So initially it'll point to buffer[0], If you write "Hello" it'll do:

- Receives 'H'. Store 'H' to Output pointer address, currently pointing to buffer[0]. Increase Output address.
- Receives 'e'. Store to Output pointer address, currently pointing to buffer[1]. Increase Output address.
- Receives 'l'. Store to Output pointer address, currently pointing to buffer[2]. Increase Output address.
- Receives 'l'. Store to Output pointer address, currently pointing to buffer[3]. Increase Output address.
- Receives 'o'. Store to Output pointer address, currently pointing to buffer[4]. Increase Output address.
- (You pressed enter) Receives '\n'. The last code I posted would detect this and finish the reception. Store 0 to Output pointer address, currently pointing to buffer[5] to set the ending,return.

Now buffer would contain ['H'] ['e'] ['l'] ['l'] ['o'] [ 0].
« Last Edit: June 08, 2023, 12:17:32 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: djsb


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf