PIC 18F2523 Software UART - Page 1
Forum section: electronics / general

author: kosmos890
author_rank: Newbie
timestamp: October 06, 2025, 03:10:55 pm
content: I want to implement a software uart on RA4 Tx for debugging with C programming language (x8c compiler).The source code with __delay_us(104) prints weirds chars on the debugging console.I have replaced the __delay_us with Timer1_BitDelay() based on Timer1.The problem is that the call of SUART_Write_Text("Test 4800\r\n") do not print an arbitray char like "est 4800" "Test4800" "Tes 4800"Code:#include <xc.h>#include <stdint.h>#define _XTAL_FREQ 8000000UL#define BAUDRATE 4800#pragma config OSC = INTIO67, WDT = OFF, LVP = OFF, PBADEN = OFF, MCLRE = ON#define UART_TX LATAbits.LATA4#define UART_TX_DIR TRISAbits.TRISA4#define LED_PIN LATCbits.LATC2#define LED_DIR TRISCbits.TRISC2// Preload for Timer1 (8 MHz internal, 4800 baud)#define TMR1H_PRELOAD 0xFE#define TMR1L_PRELOAD 0x9Fvoid Timer1_BitDelay(void){PIR1bits.TMR1IF = 0; // Clear flagTMR1H = TMR1H_PRELOAD;TMR1L = TMR1L_PRELOAD;while(!PIR1bits.TMR1IF); // Wait overflow}void SUART_Init(void){// Digital configurationADCON1bits.PCFG = 0x0F; // All digitalTRISA = 0x00;TRISC = 0x00;// TX pin idle highUART_TX_DIR = 0;UART_TX = 1;LED_DIR = 0;LED_PIN = 0;// Timer1: Fosc/4, 1:1 prescaleT1CONbits.TMR1CS = 0; // InternalT1CONbits.T1CKPS = 0; // 1:1T1CONbits.TMR1ON = 1; // Enable// Internal oscillator 8 MHz, internal sourceOSCCON = 0b01110100;}void SUART_Transmit(uint8_t data){uint8_t i;// Start bitUART_TX = 0;Timer1_BitDelay();// Data bitsfor (i = 0; i < 8; i++) {UART_TX = (data >> i) & 1;Timer1_BitDelay();}// Stop bitUART_TX = 1;Timer1_BitDelay();}void SUART_Write_Text(const char *txt){while (*txt)SUART_Transmit(*txt++);}void main(void){SUART_Init();__delay_ms(500);SUART_Write_Text("Ready\r\n");while (1){LED_PIN = 1;SUART_Write_Text("Test 4800\r\n");LED_PIN = 0;__delay_ms(1000);}}------ EDIT -----@Thanks woofy. (data >> i) is terrible.It gives you a variable delay increasing with i.Excellent debugging skill. I cant think the non-constant bitrate . I will test it and I reply soon

author: woofy
author_rank: Frequent Contributor
timestamp: October 06, 2025, 03:55:14 pm
content: Note that 104uS is the bit time at 9600 baud, not 4800.(data >> i) is terrible. It gives you a variable delay increasing with i.use data >>= 1 instead.Code:UART_TX = (data&1);data >>= 1;I suggest you transmit 0x55 continuously with a small gap between characters, then look at the stream on a 'scope and check all bit times are correct.

author: aeg
author_rank: Regular Contributor
timestamp: October 07, 2025, 12:02:02 am
content: Another suggestion is to run the timer continuously with a period of 4800Hz, rather than restarting it one-shot after each bit. That way, you get consistent bit periods even if your other code has variable timing.

author: JPortici
author_rank: Super Contributor
timestamp: October 07, 2025, 01:19:28 pm
content: I'd rather use a timer interrupt at whatever baud rate you need and run a state machine there, bonus: you can use the same to implement a receiver.

author: langwadt
author_rank: Super Contributor
timestamp: October 07, 2025, 03:54:33 pm
content: Quote from: JPortici on October 07, 2025, 01:19:28 pmif you want to do both rx and tx the timer need to run several times faster than the baudrate

author: spostma
author_rank: Regular Contributor
timestamp: October 07, 2025, 04:20:37 pm
content: years ago I implemented this on a PIC16F690 in assembly.using a pin change interrupt for start bit detection.The pin change interrupt handler disables the pin change interrupt and sets a 1,5 bit period Timer0 overflow time.The Timer0 interrupt shifts databits, and sets correct Timer0 overflow period.When receiving the stop bit, re-enables the pin change interrupt for the next start bit detection.

author: harerod
author_rank: Frequent Contributor
timestamp: October 07, 2025, 05:52:02 pm
content: Quote from: langwadt on October 07, 2025, 03:54:33 pmIf the application requires full duplex, then yes. However, if half duplex is sufficient, RX/TX can share a 1 bit time timer (spostma described). When the device is normally quiet and only replies to requests, a half duplex FART* is fine.*Firmware Asynchronous Receiver Transmitter.

author: jpanhalt
author_rank: Super Contributor
timestamp: October 07, 2025, 08:01:08 pm
content: I got this code from Mike McLaren (K8LH) for simple RS232 sending (not duplex) to an LCD. I have used it frequently since when debugging with only 1 signal wire. It's Assembly for the mid-range, but it may help. The delay routine is also from Mike McLaren. It may help. Of course, you can use any reasonably accurate delay routine. Please credit him, if you use it.Code: (asm);********************************************************************************; 1) DelayCy(xx*msecs) or (xx*usecs) subsystem macro generator; 2) Provided by Mike McLaren K8LH; 3) 03.18.15 Checked and confirmed this is original version for 14-bit cores;********************************************************************************clockequ 8 ; 4, 8, 12, 16, or 20 MHz clockusecsequ clock/4 ; cycles/microsecond multipliermsecsequ clock/4*1000 ; cycles/millisecond multiplierDelayCymacro delay ; 12..327690 cycle rangemovlwhigh((delay-11)/5)+1movwfdelayhimovlwlow((delay-11)/5)calluDelay-((delay-11)%5)endm;********************************************************************************;SUBROUTINES;********************************************************************************; DelayCy() subsystem "uDelay";********************************************************************************nop ; entry for (delay-12)%5 == 4nop ; entry for (delay-12)%5 == 3nop ; entry for (delay-12)%5 == 2nop ; entry for (delay-12)%5 == 1uDelay addlw -1 ; 5 cycle loopskpc ;decfsz delayhi,f ;goto uDelay ;return ;;******************************************************************************; PUT232; Put232 (9600 baud) -- Mike McLaren, K8LH (Jan-09); Change "RA0" to the pin used for RS232 serial out.; Although LATA is used, writes to PORTA are duplicated in LATA.; No requirement to change banks.; The "-10" adjustment to DelayCy (i.e., 100*usecs-10)is in Tcy, not time.; Added to account for the steps between bit changes. At 9600 Baud, it is; unnecessary. For example, with MCU at 8 MHz,the macro generates a bit width; of 109usec witout it and 104usec with it. At 4 MHz, the difference is; 114usec w/o it and 104usec with it.;******************************************************************************Put232 ;must be in Bank0 or Bank2movlb 0 ;only necessary to call from any bankmovwf txbyte ;save Tx data byte (not preserved)movlw 10 ;1 start + 8 data + 1 stop bitmovwf bit_ctr ;setup bit counterbcf STATUS,0 ;STATUS,0 used as flag, C = 0 (start bit)DelayCy (104*usecs-10) ;104 usecs = Baud 9600, "-10" adj for housekeepinggoto SendBit ;send start bitNextBitDelayCy (104*usecs-10)bsf STATUS,0 ;always shift in a 'stop' bitrrf txbyte,f ;put data bit in CarrySendBitmovf LATA,w ;read port latchiorlw 1<<RA0;set Serout pin to 1btfss STATUS,0 ;if data bit from rrf = 1 skip, elsexorlw 1<<RA0;XOR WREG with 1 at the bit position of Seroutmovwf LATA ;precise update intervalsdecfsz bit_ctr,f ;done? yes, skip, elsegoto NextBit ;send next bitretlw 0 ;returns in Bank0;*******************************************************************************

author: kosmos890
author_rank: Newbie
timestamp: October 08, 2025, 01:06:38 pm
content: Thanks all for your replies. I am new to PIC programming, but I have a good background in c/c++. I am trying to port a proton basic source code that controls a SIM800 gprs modem. (I cant change the circuit)The basic source code works. The proton basic has also SerOut and SerIn functions that makes easy the uart transmission. Unfortunately in C programming language you must use eusart (RC6 and RC7).I read carefully all your advices. I think that the best solution is the state machine but I cant implement it.The next improvement I am trying is a Timer. I have read that __delay_us isnt reliable and I am using the Timer to measure the delay watching the oveflow bit of the timer.I would appreciate if someone posted their code even the config of oscilator or timer. (for example proton basic has OSCCON = 0b11110000 the x8c has 0b01110000, the timer prescale confuses me)If you want answer me1)A pullup resistance (4.7KOhm) is required to implement software uart? I think that pullup is used only for input2)Assume that there is time jittering. Changing clock from 8MHz to 4MHZ will give more tolearance? The frequency of 4MHz has effect to the operation of the circuit?3)If I am using an external crystal the __delay_us is reliable?P.S I am beginner and I cant work with USBee DX logic analyzer and MPLABX 6.2 Simulator---- EDIT ----Finally I can work with MPLAB X ide logic analyzer . The line while(!OSCCONbits.IOFS) for stabilization prints a line. I have commented it and I get a waveform. Now I can measure delay

author: DavidAlfa
author_rank: Super Contributor
timestamp: October 08, 2025, 02:07:02 pm
content: See this:https://www.eevblog.com/forum/microcontrollers/xc8-soft-uart-on-pic16f819-help-please/msg4893806/#msg4893806Uses a timer in PWM mode for best accuracy, handles up to 115200 baud @ 8MHz Fcpu.

author: jpanhalt
author_rank: Super Contributor
timestamp: October 08, 2025, 02:35:16 pm
content: Quote from: kosmos890 on October 08, 2025, 01:06:38 pmWhat you are doing is pretty much over my head, but addressing just the question of jitter, timing with Assembly is predictable. My feeling about changing the system clock is that higher frequencies will have less jitter at 9600 baud as the contributing delays to jitter will be shorter. I use that routine almost exclusively for quickly seeing what's going on. For final display, I am often using hardware SPI.