Author Topic: PIC 18F2523 Software UART  (Read 1836 times)

0 Members and 1 Guest are viewing this topic.

Offline kosmos890Topic starter

  • Newbie
  • Posts: 2
  • Country: gr
PIC 18F2523 Software UART
« on: October 06, 2025, 03:10:55 pm »
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: [Select]

#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  0x9F

void Timer1_BitDelay(void)
{
    PIR1bits.TMR1IF = 0;         // Clear flag
    TMR1H = TMR1H_PRELOAD;
    TMR1L = TMR1L_PRELOAD;
    while(!PIR1bits.TMR1IF);     // Wait overflow
}

void SUART_Init(void)
{
    // Digital configuration
    ADCON1bits.PCFG = 0x0F;      // All digital
    TRISA = 0x00;
    TRISC = 0x00;

    // TX pin idle high
    UART_TX_DIR = 0;
    UART_TX = 1;

    LED_DIR = 0;
    LED_PIN = 0;

    // Timer1: Fosc/4, 1:1 prescale
    T1CONbits.TMR1CS = 0;  // Internal
    T1CONbits.T1CKPS = 0;  // 1:1
    T1CONbits.TMR1ON = 1;  // Enable

    // Internal oscillator 8 MHz, internal source
    OSCCON = 0b01110100;
}

void SUART_Transmit(uint8_t data)
{
    uint8_t i;

    // Start bit
    UART_TX = 0;
    Timer1_BitDelay();

    // Data bits
    for (i = 0; i < 8; i++) {
        UART_TX = (data >> i) & 1;
        Timer1_BitDelay();
    }

    // Stop bit
    UART_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
« Last Edit: October 06, 2025, 05:00:32 pm by kosmos890 »
 

Offline woofy

  • Frequent Contributor
  • **
  • Posts: 510
  • Country: gb
    • Woofys Place
Re: PIC 18F2523 Software UART
« Reply #1 on: October 06, 2025, 03:55:14 pm »
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: [Select]
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.
« Last Edit: October 06, 2025, 03:57:16 pm by woofy »
 
The following users thanked this post: kosmos890

Online aeg

  • Frequent Contributor
  • **
  • Posts: 475
  • Country: us
Re: PIC 18F2523 Software UART
« Reply #2 on: October 07, 2025, 12:02:02 am »
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.
 
The following users thanked this post: kosmos890

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3914
  • Country: it
Re: PIC 18F2523 Software UART
« Reply #3 on: October 07, 2025, 01:19:28 pm »
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.
 
The following users thanked this post: kosmos890

Online langwadt

  • Super Contributor
  • ***
  • Posts: 5822
  • Country: dk
Re: PIC 18F2523 Software UART
« Reply #4 on: October 07, 2025, 03:54:33 pm »
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.

if you want to do both rx and tx the timer need to run several times faster than the baudrate
 
The following users thanked this post: kosmos890

Offline spostma

  • Regular Contributor
  • *
  • Posts: 191
  • Country: nl
Re: PIC 18F2523 Software UART
« Reply #5 on: October 07, 2025, 04:20:37 pm »
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.
 
The following users thanked this post: kosmos890

Offline harerod

  • Frequent Contributor
  • **
  • Posts: 553
  • Country: de
  • ee - digital & analog
    • My services:
Re: PIC 18F2523 Software UART
« Reply #6 on: October 07, 2025, 05:52:02 pm »
Quote from: JPortici on Today at 14:19:28
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.

if you want to do both rx and tx the timer need to run several times faster than the baudrate
If 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.
 
The following users thanked this post: kosmos890

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 4814
  • Country: us
Re: PIC 18F2523 Software UART
« Reply #7 on: October 07, 2025, 08:01:08 pm »
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) [Select]
;********************************************************************************
;  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     
;********************************************************************************
clock    equ     8               ; 4, 8, 12, 16, or 20 MHz clock
usecs    equ     clock/4         ; cycles/microsecond multiplier
msecs    equ     clock/4*1000    ; cycles/millisecond multiplier
DelayCy macro   delay           ; 12..327690 cycle range
movlw high((delay-11)/5)+1
movwf delayhi
    movlw    low((delay-11)/5)
     call    uDelay-((delay-11)%5)
     endm

;********************************************************************************
; SUBROUTINES
;********************************************************************************
;    DelayCy() subsystem "uDelay"                        
;********************************************************************************
        nop                     ; entry for (delay-12)%5 == 4     
        nop                     ; entry for (delay-12)%5 == 3     
        nop                     ; entry for (delay-12)%5 == 2     
        nop                     ; entry for (delay-12)%5 == 1     
uDelay  addlw   -1              ; 5 cycle loop                   
        skpc                    ;                                 
        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 Bank2
     movlb     0              ;only necessary to call from any bank
     movwf     txbyte         ;save Tx data byte (not preserved)           
     movlw     10             ;1 start + 8 data + 1 stop bit   
     movwf     bit_ctr        ;setup bit counter               
     bcf       STATUS,0       ;STATUS,0 used as flag, C = 0 (start bit)
     DelayCy   (104*usecs-10) ;104 usecs = Baud 9600, "-10" adj for housekeeping
     goto      SendBit        ;send start bit                 
NextBit
     DelayCy   (104*usecs-10)     
     bsf       STATUS,0       ;always shift in a 'stop' bit   
rrf       txbyte,f       ;put data bit in Carry           
SendBit
     movf      LATA,w         ;read port latch                       
     iorlw     1<<RA0  ;set Serout pin to 1   
     btfss     STATUS,0       ;if data bit from rrf = 1 skip, else 
     xorlw     1<<RA0 ;XOR WREG with 1 at the bit position of Serout   
     movwf     LATA           ;precise update intervals       
     decfsz    bit_ctr,f      ;done? yes, skip, else           
     goto      NextBit        ;send next bit     
     retlw     0              ;returns in Bank0

;*******************************************************************************

   
 
The following users thanked this post: kosmos890

Offline kosmos890Topic starter

  • Newbie
  • Posts: 2
  • Country: gr
Re: PIC 18F2523 Software UART
« Reply #8 on: October 08, 2025, 01:06:38 pm »
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 me
1)A pullup resistance (4.7KOhm) is required to implement software uart? I think that pullup is used only for input

2)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
« Last Edit: October 08, 2025, 02:36:57 pm by kosmos890 »
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6926
  • Country: es
Re: PIC 18F2523 Software UART
« Reply #9 on: October 08, 2025, 02:07:02 pm »
See this:

https://www.eevblog.com/forum/microcontrollers/xc8-soft-uart-on-pic16f819-help-please/msg4893806/#msg4893806

Uses a timer in PWM mode for best accuracy, handles up to 115200 baud @ 8MHz Fcpu.
« Last Edit: October 09, 2025, 03:08:03 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Online jpanhalt

  • Super Contributor
  • ***
  • Posts: 4814
  • Country: us
Re: PIC 18F2523 Software UART
« Reply #10 on: October 08, 2025, 02:35:16 pm »
2)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?

What 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.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->