Reimplementing UART on the PIC16F

Thread Starter

Robin66

Joined Jan 5, 2016
275
Hi circuiteers

I have a pair of cheap 433MHz modules. I'm hoping such links will be the basis for a wireless home alarm system which I'll build up with extra peripherals around the home. I'm new to UART but have an experiment Tx/Rx pair working at 2kHz baud. However I'm not impressed by the EUSART module on the PIC16F. The Rx logic seems to do the bare minimum so seems not suited for a noisy RF link. For example, it determines a START bit but first detecting a high->low transition and then sampling 1/2 baud period later; if the input is still low the full byte capture continues ie. the module is busy for 9 more baud periods. However if the channel is just noise there's a 50:50 chance it'll be low at any time, so the Rx unit would be busy recording noise almost all the time. This would be fine but it means my Tx units have a very narrow opportunity to be heard

Bearing in mind in my Rx I'll have a PIC16F15313 with not a lot to do most of the time and a 100ksps ADC on-board I'm thinking it wouldn't be infeasible to write my own UART receiver based on measuring the time of V transitions and sampling the bit 50 times per bit (100ksps / 2kbaud) and using some majority function to reduce noise substantially eg. ON would need to have >40 high samples per bit; OFF would be <10 high samples per bit. I could also have an identifier per Tx unit, and the Rx would evaluate these on some rolling window basis.

Am I crazy to reinvent the wheel, or have others came to the same conclusion?
 

hexreader

Joined Apr 16, 2011
580
It is a long time since I messed with those modules, but I seem to remember that the cheaper modules are not suited to UART serial communication. Mine required Manchester encoding for a workable link.

What does the datasheet for your modules have to say? Does it specifically specify that UART serial communication is supported, or just it just say that "serial" communication is supported?
 

AlbertHall

Joined Jun 4, 2014
12,293
Yes, if if you have the standard 433MHz T/RX modules you will need to use manchester coding to get decent results. Then yo will also need some sort of error checking or error correction because of the noise on an empty channel.

I was trying to use them and gave up and used the HC12 modules. Much better range, even with the supplied 'springs' as aerials and the noise problem is solved.

https://www.allaboutcircuits.com/pr...enting-the-hc-12-wireless-transceiver-module/
 

MrChips

Joined Oct 2, 2009
29,849
I agree.

The 433MHz RX/TX modules I have worked with will generate noise if no RF is received or if continuous RF is received. You would be better to scrap the UART protocol and use something like Manchester encoding.
 

ebp

Joined Feb 8, 2018
2,332
The PIC UART is really no different from UART designs that have been in use for decades in terms of the sampling algorithm.

As hexreader says, encoding techniques can make a big difference in performance of RF links. Sometimes NRZ can be made to work by using a preamble that gives the AGC of the receiver some time to get its act together so weak noise signals are rejected.
 

Thread Starter

Robin66

Joined Jan 5, 2016
275
What does the datasheet for your modules have to say?
Not a good sign, but I couldn't find a datasheet for these. From reading forums I've established I can drive the Tx module up to 12V for max range and that the signal duty cycle should be within the range 30-70% to maximise reliability

Yes, if if you have the standard 433MHz T/RX modules you will need to use manchester coding to get decent results.
I was trying to use them and gave up and used the HC12 modules. Much better range, even with the supplied 'springs' as aerials and the noise problem is solved.
Thanks for the tip. I'll persevere with the cheap modules for a couple of nights and might resort to HC12. Power consumption will be critical for my Tx modules so I was going to keep the data packets v dense. Hence i was hoping I could get away without Manchester encoding, but I am aware of the advantages and it'll keep the signal within the 30-70% range
You would be better to scrap the UART protocol and use something like Manchester encoding.
My understanding of manchester encoding is that it would be layered over the top of the uart protocol ie. I'd manchester encode my packets (thus doubling their length) and then transmit these over uart, then decode the manchester packets at the Rx.
 
Last edited:

Thread Starter

Robin66

Joined Jan 5, 2016
275
The PIC UART is really no different from UART designs that have been in use for decades in terms of the sampling algorithm.

As hexreader says, encoding techniques can make a big difference in performance of RF links. Sometimes NRZ can be made to work by using a preamble that gives the AGC of the receiver some time to get its act together so weak noise signals are rejected.
At a minimum the preamble will be to keep the signal low (UART transmitting high) for >10 BAUD periods. This is necessary to ensure the Rx isn't in the middle of recording noise and therefore misses the start of my Tx transmission.
 

AlbertHall

Joined Jun 4, 2014
12,293

MrChips

Joined Oct 2, 2009
29,849
You can use the UART to transmit the data in an encoding scheme other than NRZ.

I would choose not to use the UART to receive the data. I would use interrupts on pin-change and the timer to receive the data in a reverse bit-bang scheme.
 

Thread Starter

Robin66

Joined Jan 5, 2016
275
I experimented last night with this and it went surprisingly well.
Transmitter:
  • Stationed at my desk driven from a PS.
  • Tx 433MHz module powered at 5V
  • Transmitted the sequence "Sam" from EUSART at 4kHz baud, every 500ms, and flash an LED when it was doing so.
Receiver:
  • The Rx was battery operated in my hand.
  • It was in a continuous loop querying the RC1IF and dequeuing chars as they were made available.
  • If the chars "S", "a" and "m" were received contiguously then an LED would light for 200ms.
Both units had a 17.2cm straight wire as an antenna. When I was at my desk with the receiver the LEDs would flash in tandem. As I moved ~6 foot away the receive rate would drop to once per second. As I moved around my house, upstairs, or at the end of the garden (about 80 ft away and thru a 4 course brick wall) the receive rate would be a minimum of once every 2 seconds. When I switched off the Tx PS the Rx LED was always off.
Further investigation:
  1. Tx was powered with 5V. Try up to 12V
  2. Turn off existing home burglar alarm (may be transmitting in similar range)
  3. Test reliability of longer packets.
  4. If I get desperate, write bespoke UART Rx algo using ADC, IoC, majority filtering etc.
1 & 2 are easy wins. 3 is necessary for a real world application. 4 is not an easy win but it could reap huge benefits over the noddy EUSART implementation. A majority function of 50 samples per bit vs a single sample at the middle of the bit must be game changing.
 

Thread Starter

Robin66

Joined Jan 5, 2016
275
I did some further trials. First I extended the transmitted message to 9 chars: "Sam hello". The Tx broadcast at a 500ms repeat-rate. At the end of my garden (4 course wall + 100ft) I received the full message contiguously once every 4 seconds, so ~10% success rate. When I ramped up the Tx voltage to 12V (I believe the max for my module) it was dropping only ~20% of packets. I'm very impressed by this and I don't need to implement (4).

Realistically I'll only need about 6 chars for my purposes. Not sure why others have so much difficultly. Either my requirements are v easy, or I have less steel in my house, or I got lucky with the modules I tested (I've heard some are better than others, even from the same supplier).
 

Thread Starter

Robin66

Joined Jan 5, 2016
275
The failure rate will likely also depend on what data you send if you are not using Manchester.
Just before bed I tried some spare modules I'd bought. Using the new Rx module gave me a range more like 2 metres. So I thought that modules are sold in matched pairs. But the new Tx module didn't work at all.

I've bought 2 HC-12 modules on amazon to experiment with tonight. If I have to use a 5-12V boost module to make the cheap modules viable and I have to sift thru a load working out which ones work together it's definitely better to use the HC-12s. Plus these are transceivers so I can have the transmitters listen for an acknowledgement and go back to sleep. Thanks for the tip
 

Thread Starter

Robin66

Joined Jan 5, 2016
275
I have my HC12 modules now. I just did a straight swap out of the cheap modules and it doesn't work (I didn't expect it to). I haven't altered my code so I am in the default FU3 mode. I am receiving a bit stream shortly after the transmitted packet (first trace), but it isn't an exact match (second trace where I've shown them side by side). it's not clear to me what I need to do to preserve the transmitted format. The received sequence is shorter but it's not just the face it's squeezed into a tighter timeframe. You can see the relative spacing of bits has changed. I know FU3 auto-detects the baud rate so I'm wondering if this needs some preamble to calibrate. Or maybe I need to change the PIC16F usage of start and stop bits.

Any guidance much appreciated.

IMG_5777.JPG IMG_5776.JPG
 

AlbertHall

Joined Jun 4, 2014
12,293
I know FU3 auto-detects the baud rate so I'm wondering if this needs some preamble to calibrate.
No. You set the baud rate and the module adjusts the 'over-the-air' datarate to match, but you must send and expect to receive at the baud rate you have set. If you have not set a baud rate then the HC-12 will be expecting 9600.
 

Thread Starter

Robin66

Joined Jan 5, 2016
275
Brilliant, thanks. I'll try that. I got confused by the varying baud rate but no I've re-read the datasheet I understand what you're saying.
 

Thread Starter

Robin66

Joined Jan 5, 2016
275
No. You set the baud rate and the module adjusts the 'over-the-air' datarate to match, but you must send and expect to receive at the baud rate you have set. If you have not set a baud rate then the HC-12 will be expecting 9600.
Ok this is looking much better. I adjusted my PIC Fosc to 8MHz to allow the 9.6k baud rate with 0.16% err (according to datasheet). The received sequence is still shorter but I think this is due to missing stop bits per char. Sent and received mismatch on the10th bit in the below. Any idea what i need to do to get this to match perfectly?

PS. can't see how to disable the stop bits from the PIC.

IMG_5779.JPG
 

hexreader

Joined Apr 16, 2011
580
Do not disable stop bits; they are a vital part of UART serial communication.

You may want to change from 2 stop bits to 1 stop bit, if you have 2 set at the moment.

Without seeing your code, I can only guess at what UART settings you chose.

Bottom line is that settings should match at both ends of the link
 

Thread Starter

Robin66

Joined Jan 5, 2016
275
Do not disable stop bits; they are a vital part of UART serial communication.

You may want to change from 2 stop bits to 1 stop bit, if you have 2 set at the moment.

Without seeing your code, I can only guess at what UART settings you chose.

Bottom line is that settings should match at both ends of the link
Thanks for taking the time to respond. Here's my transmitter code. I haven't enabled 2 stop bits

Code:
#define _XTAL_FREQ 8000000

// PIC16F15313 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FEXTOSC = OFF    // External Oscillator mode selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINT1  // Power-up default value for COSC bits (HFINTOSC (1MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; i/o or oscillator function on OSC2)
#pragma config CSWEN = ON       // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable bit (FSCM timer enabled)

// CONFIG2
#pragma config MCLRE = ON       // Master Clear Enable bit (MCLR pin is Master Clear function)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config LPBOREN = OFF    // Low-Power BOR enable bit (ULPBOR disabled)
#pragma config BOREN = OFF      // Brown-out reset enable bits (Brown-out reset disabled)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (VBOR) set to 1.9V on LF, and 2.45V on F Devices)
#pragma config ZCD = OFF        // Zero-cross detect disable (Zero-cross detect circuit is disabled at POR.)
#pragma config PPS1WAY = ON     // Peripheral Pin Select one-way control (The PPSLOCK bit can be cleared and set only once in software)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a reset)

// CONFIG3
#pragma config WDTCPS = WDTCPS_31// WDT Period Select bits (Divider ratio 1:65536; software control of WDTPS)
#pragma config WDTE = ON        // WDT operating mode (WDT enabled regardless of sleep; SWDTEN ignored)
#pragma config WDTCWS = WDTCWS_7// WDT Window Select bits (window always open (100%); software control; keyed access not required)
#pragma config WDTCCS = LFINTOSC// WDT input clock selector (WDT reference clock is the 31.0kHz LFINTOSC output)

// CONFIG4
#pragma config BBSIZE = BB512   // Boot Block Size Selection bits (512 words boot block size)
#pragma config BBEN = OFF       // Boot Block Enable bit (Boot Block disabled)
#pragma config SAFEN = OFF      // SAF Enable bit (SAF disabled)
#pragma config WRTAPP = OFF     // Application Block Write Protection bit (Application Block not write protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block not write protected)
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration Register not write protected)
#pragma config WRTSAF = OFF     // Storage Area Flash Write Protection bit (SAF not write protected)
#pragma config LVP = OFF         // Low Voltage Programming Enable bit (Low Voltage programming enabled. MCLR/Vpp pin function is MCLR.)

// CONFIG5
#pragma config CP = OFF         // UserNVM Program memory code protection bit (UserNVM code protection disabled)

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

#include <xc.h>
#include <stdio.h>  // for sprintf

void main(void) {
    TRISA = 0;
    ANSELA = 0; // defaults to analog otherwise
    WPUA = 0; // weak pull up off
    OSCCON1bits.NDIV = 0; // necessary 
    OSCFRQbits.HFFRQ = 0b011; // 8MHz
    char str[10];
    sprintf(str,"Sam");
   
    PORTAbits.RA3 = 1;
    while(1)
    {
        // transmit
        RA0PPS = 0x0F; // set RA to Tx
        PIE3bits.TX1IE  = 0; // disable interrupts
        TX1STAbits.TXEN = 1; // enable transmit       
        TX1STAbits.SYNC = 0; // async
        RC1STAbits.SPEN = 1; // enable EUSART, config pin for output
        // set 96k baud rate.
        // Focs=8Mhz, SPBRG=12, error = 0.16%
        SPBRGH = 0; SPBRGL = 12;
        char i = 0;

        while(str[i]!='\0')
        {
            TX1REG = str[i];
            i++; // kill an instruction
            NOP();
            while(!TX1STAbits.TRMT) {}; // while full
        }
       
        while(!TX1STAbits.TRMT) {}; // hold off on sleep until TSR is empty
        TX1STAbits.TXEN = 0; // turn off Tx
       
        // go to sleep, setting WDT first
        VREGCONbits.VREGPM = 1; // LP sleep mode: reduces I 28uA-->11uA
        WDTCON0bits.WDTPS = 0b01001; // 500ms @ 32kHz
        CLRWDT();
        WDTCON0bits.SWDTEN = 1; // enable
        SLEEP();
        WDTCON0bits.SWDTEN = 0; // disable
    }
    return;
}
 
Top