XC8 Soft UART on PIC16F819-Help please - Page 1
Forum section: electronics / general

author: djsb
author_rank: Super Contributor
timestamp: June 03, 2023, 08:29:14 am
content: Here is the main.c file of my software serial projectCode:/** 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 Description31st May 2023 Added calls to SUART_Init()routine and SUART_Write_Text("Hello") to main().1st to 2nd June 2023*******************************************************************************//*******************************************************************************TO DO:Task DetailsCheck 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 MHz101 = 2 MHz100 = 1 MHz011 = 500 kHz010 = 250 kHz001 = 125 kHz000 = 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 UARTSUART_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 codeCode:/** 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 stateUART_RX_DIR = 1; // InputUART_TX_DIR = 0; // Output}/******************************************************************************** Function: unsigned char SUART_Receive(void)** Returns: Nothing** Description: recieves via soft UART*******************************************************************************/unsigned char SUART_Receive(void){// Initial at 0unsigned char DataValue = 0;//wait for start bitwhile(UART_RX==1);__delay_us(OneBitDelay);__delay_us(OneBitDelay/2); // Take sample value in the mid of bit durationfor ( 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 bitif ( 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 LogicTX pin is usually high. A high to low bit is the starting bit anda 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 BitUART_TX = 0;__delay_us(OneBitDelay);for ( unsigned char i = 0; i < DataBitCount; i++ ){//Set Data pin according to the DataValueif(((DataValue>>i)&0x1) == 0x1 ) //if Bit is high{UART_TX = 1;}else //if Bit is low{UART_TX = 0;}__delay_us(OneBitDelay);}//Send Stop BitUART_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!='\0';i++)SUART_Transmit(text);}/******************************************************************************** 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 = 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 fileCode:/** 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 Declarationsvoid 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:/** 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 IncludesWhen 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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 03, 2023, 08:41:23 am
content: First try toggling a led every onebitdelay * baudrate (9600, whatever) and see if you get anything close to 1 second.What warnings did you receive?

author: djsb
author_rank: Super Contributor
timestamp: June 03, 2023, 08:52:23 am
content: Here are the warningsCode:make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-confmake: Entering directory 'F:/XC8_WORK/XC8_16F819/03_RS232.X'make -f nbproject/Makefile-default.mk dist/default/production/03_RS232.X.production.hexmake: 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 librariesMain.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' herevoid 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' herevoid 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' herevoid 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 librariessw_uart.c:45:: warning: (520) function "_SUART_Receive" is never calledsw_uart.c:150:: warning: (520) function "_SUART_Read_Text" is never calledsw_uart.c:167:: warning: (520) function "_SUART_Write_Char" is never calledsw_uart.c:154:: warning: (1498) pointer (SUART_Read_Text@Output) in expression may have no targetsMemory 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: Leaving directory 'F:/XC8_WORK/XC8_16F819/03_RS232.X'make: 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,MicrochipLoading completedThe 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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 03, 2023, 09:21:40 am
content: 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:while(1){SUART_Write_Text("Hello!\r\n");__delay_ms(1000);}Then fix your code by replacingSUART_Write_Text(letter)withSUART_Write_Char(letter)

author: djsb
author_rank: Super Contributor
timestamp: June 03, 2023, 09:33:16 am
content: Thanks. I will try that. Just for clarification inCode:void SUART_Write_Char(char a){SUART_Transmit(a - 128);}In the lineCode: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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 03, 2023, 09:44:37 am
content: Sorry that function is also wrong, no idea why the -128.Use SUART_Transmit(letter) instead.

author: 8goran8
author_rank: Contributor
timestamp: June 03, 2023, 12:46:08 pm
content: Try a lower baud rate.I tried with 2400 and it works in proteus simulation.#define Baudrate 2400

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 04, 2023, 02:13:50 am
content: 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:#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 stateUART_RX_DIR = 1; // InputUART_TX_DIR = 0; // OutputPR2 = OneBitDelay; // Load period counterT2CON = 0; // Use timer 2 as timebaseT2CONbits.TMR2ON = 1; // Start timer}unsigned char SUART_Receive(void){unsigned char DataValue = 0;while(UART_RX==1); // wait for start bitTMR2=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 expireBIT_DELAY(); // Counter resetted, wait for a full bit time nowRX_BIT(DataValue,0x01); // Unrolled loop for fastest processingRX_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; // StartTMR2=0;BIT_DELAY();TX_BIT(DataValue,0x01); // Unrolled loop for fastest processingTX_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; //StopBIT_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");#elseSUART_Write_Text("unknown");#endifSUART_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);}}}

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 04, 2023, 03:22:40 pm
content: 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'sonly 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:#include <xc.h>#define _XTAL_FREQ 8000000UL// Tested with 9600, 19200, 38400, 57600 and 115200.#define BAUDRATE 9600UL#define _FCY (_XTAL_FREQ/4)#define BIT_T ((_FCY+(BAUDRATE/2))/BAUDRATE)#define BAUD_REAL (_FCY/BIT_T)#if ( (BAUDRATE > BAUD_REAL) && ((BAUDRATE - BAUD_REAL)>(BAUDRATE/33)) || \(BAUD_REAL > BAUDRATE) && ((BAUD_REAL - BAUDRATE)>(BAUDRATE/33)) )#error Baud error higher than 3%! Please try a different baudrate!#endif#define UART_TX PORTBbits.RB2#define UART_TX_DIR TRISBbits.TRISB2void SUART_Init(void){T2CON = 0; // Reset TIM2TMR2=0; // Clear counterPR2 = BIT_T-1; // Load bit periodCCPR1L = 0x80; // PWM duty=100% (Tx Idle level)CCP1CON = 0x0C; // PWM mode, PWM 2LSB=0TMR2ON=1; // Enable timer 2TMR2IF=0; while(!TMR2IF); // Wait for the timer to resetTMR2ON=0; // Turn off timer (PWM was 100%, so TX will stay high)TMR2=0; // Clear counterUART_TX_DIR = 0; // Set TX as output}// This function is intentionally unrolled for max performance.// At 115200 baud we only have ~17 cpu clocks between bits (Fcpu=8MHz))void SUART_Transmit(char c){CCPR1L=0; // Start bitTMR2ON=1;TMR2IF=0;while(!TMR2IF);if(c & 0x01) CCPR1L |= 0x80; else CCPR1L=0; // D0. OR makes use of the BSF instruction (fastest).TMR2IF=0; while(!TMR2IF);if(c & 0x02) CCPR1L |= 0x80; else CCPR1L=0; // D1TMR2IF=0; while(!TMR2IF);if(c & 0x04) CCPR1L |= 0x80; else CCPR1L=0; // D2TMR2IF=0; while(!TMR2IF);if(c & 0x08) CCPR1L |= 0x80; else CCPR1L=0; // D3TMR2IF=0; while(!TMR2IF);if(c & 0x10) CCPR1L |= 0x80; else CCPR1L=0; // D4TMR2IF=0; while(!TMR2IF);if(c & 0x20) CCPR1L |= 0x80; else CCPR1L=0; // D5TMR2IF=0; while(!TMR2IF);if(c & 0x40) CCPR1L |= 0x80; else CCPR1L=0; // D6TMR2IF=0; while(!TMR2IF);if(c & 0x80) CCPR1L |= 0x80; else CCPR1L=0; // D7TMR2IF=0; while(!TMR2IF);CCPR1L|=0x80; // Stop bitTMR2IF=0;while(!TMR2IF);TMR2ON=0;TMR2=0;}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.

author: djsb
author_rank: Super Contributor
timestamp: June 04, 2023, 04:16:20 pm
content: 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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 04, 2023, 04:17:08 pm
content: Proteus

author: djsb
author_rank: Super Contributor
timestamp: June 06, 2023, 06:56:10 pm
content: 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 works1/What is this line calculatingCode:#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: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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 06, 2023, 09:28:05 pm
content: Quote from: djsb on June 06, 2023, 06:56:10 pmCheck 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.Quote from: djsb on June 06, 2023, 06:56:10 pmYou specify the bufferOutputand 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:// Output: Buffer where to write data// length: Max receiving bytes, to prevent overflowunsigned int SUART_Read_Text(char *Output, unsigned int length){unsigned int rx = 0;while(length){char a= SUART_Receive(); // Receive charif(!a || a=='\r' || a=='\n') // Break on string termination, new line or line returnbreak;Output[rx++] = a; // Store in output buffer, increase countlength--;}if(length) // Clear last char to terminate the stringOutput = '\0'; // We didn't fill the output buffer, so clear next available charelseOutput[rx-1] = '\0'; // length fully used, clear last char of the bufferreturn rx; // Return total received chars}Quote from: djsb on June 06, 2023, 06:56:10 pmCode:while(1){char buffer; // Output buffer, 32 charsunsigned 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 receivingSUART_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);}Quote from: djsb on June 06, 2023, 06:56:10 pmNo, it's an entirely different thing.Quote from: djsb on June 06, 2023, 06:56:10 pmYes, 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:#include <stdlib.h>char buffer; // Output buffer, 32 charsunsigned 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 argumentSUART_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);}Quote from: djsb on June 06, 2023, 06:56:10 pmWhat "first example" exactly?

author: djsb
author_rank: Super Contributor
timestamp: June 07, 2023, 06:49:51 pm
content: Thanks for your reply and further help.My comment about pointers refers to the Asterisks in this codeCode:#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 stateUART_RX_DIR = 1; // InputUART_TX_DIR = 0; // OutputPR2 = OneBitDelay; // Load period counterT2CON = 0; // Use timer 2 as timebaseT2CONbits.TMR2ON = 1; // Start timer}unsigned char SUART_Receive(void){unsigned char DataValue = 0;while(UART_RX==1); // wait for start bitTMR2=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 expireBIT_DELAY(); // Counter resetted, wait for a full bit time nowRX_BIT(DataValue,0x01); // Unrolled loop for fastest processingRX_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; // StartTMR2=0;BIT_DELAY();TX_BIT(DataValue,0x01); // Unrolled loop for fastest processingTX_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; //StopBIT_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");#elseSUART_Write_Text("unknown");#endifSUART_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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: June 07, 2023, 08:48:23 pm
content: Pointers in this context are pretty easy!Code: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 typeptr = &letter; // Store letter address into the pointer. &(variable) takes the address of a variableWrite(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:char *dave_msg = "Hello, I'm Dave"; // Make a pointer to an array containing this stringThis would be the same as:Code:char dave_msg_arr[] = "Hello, I'm Dave"; // Make an array containing this stringchar *dave_msg = dave_msg_arr; // Point to the first element, HRemember 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.SoSUART_Write_Text(char *text)would take thetextpointer, send the data it points to, increase the address and repeat until it finds a 0.Code:void SUART_Write_Text(char *text){while(*text) // If the memory address pointer by text is not 0SUART_Transmit(*text++); // Send the data, increment the pointer address afterwards and repeat the loop}ForSUART_Write_Text("Hello"), you would have an array in memory like this:Address Value0 'H'1 'e'2 'l'3 'l'4 'o'5 0 <- Null terminationtextpointer 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, thewhile(*text)(Which is the same aswhile(*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:char buffer; // Store here the incoming charactersSUART_Read_Text(buffer, 16); // Passing the array name automatically makes a pointer to its first elementInsideSUART_Read_Text(char *Output, unsigned int length), theOutputpointer will be incremented after each received char.So initially it'll point to buffer, If you write "Hello" it'll do:- Receives 'H'. Store 'H' toOutputpointer address, currently pointing to buffer. IncreaseOutputaddress.- Receives 'e'. Store toOutputpointer address, currently pointing to buffer. IncreaseOutputaddress.- Receives 'l'. Store toOutputpointer address, currently pointing to buffer. IncreaseOutputaddress.- Receives 'l'. Store toOutputpointer address, currently pointing to buffer. IncreaseOutputaddress.- Receives 'o'. Store toOutputpointer address, currently pointing to buffer. IncreaseOutputaddress.- (You pressed enter) Receives '\n'. The last code I posted would detect this and finish the reception. Store 0 toOutputpointer address, currently pointing to buffer to set the ending,return.Nowbufferwould contain ['H'] ['e'] ['l'] ['l'] ['o'] [ 0].

author: djsb
author_rank: Super Contributor
timestamp: April 30, 2024, 06:49:06 pm
content: How would I convert the output from the ADC on the PIC16F819 from an integer into ASCII format for sending to the serial port? There is no itoa function and I don't know how to use sprintf. What is the most efficient way to send an ADC reading that's been converted to a voltage reading. The resources on the PIC16F819 are very limited. Thanks.

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 30, 2024, 07:16:46 pm
content: Make your own itoa?Given it's 10 or 12bit max, you can simply it by a lot.5 minute example:https://onlinegdb.com/dNvOImm1DCode:char * _itoa (uint16_t n, uint8_t pad, char * str) {const uint16_t val = { 1000, 100, 10, 1 };uint8_t first=0;char *s = str;for(uint8_t i=0; i<4; i++){uint8_t cnt = 0;uint16_t cmp = val;while(n >= cmp){cnt++;n -= cmp;}if(cnt || first || i==3 || pad>=(4-i)){*s++ = cnt + 48;first = 1;}}*s = '\0';return str;}

author: Ground_Loop
author_rank: Frequent Contributor
timestamp: April 30, 2024, 08:13:24 pm
content: ITOA() should be in the stdlib.h library.

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 30, 2024, 08:53:05 pm
content: Indeed:http://www.gtronic.it/test/wp-content/uploads/2017/01/XC8-compiler.pdf#page=258