Electrical – Bit Banging UART PIC32mx

interruptsmicrochipmicrocontrollerpictimer

I am trying to bit banging an UART Port using PIC32MX with timer5 (the only available at this time) but I am struggling how to configure the timer for a Baud Rate of 115200bps. I don't want a flow control not even to deal with parity bit.
If possible can someone explain me on a Step-by-step procedure?

My Definitions are:

    #define GetSystemClock()            48000000UL              // System clock frequency (Hz)
    #define GetPeripheralClock()        48000000UL              // Peripheral clock frequency
    #define GetInstructionClock()       (GetSystemClock())      // Instruction clock frequency

    #define MILLISECONDS_PER_TICK       10                  // Definition for use with a tick timer
    #define TIMER_PRESCALER             TIMER_PRESCALER_8   // Definition for use with a tick timer
    #define TIMER_PERIOD                37500               // Definition for use with a tick timer

    #define SYS_FREQ                    40000000UL

    #pragma config FPLLMUL = MUL_20    // PLL Multiplier
    #pragma config FPLLIDIV = DIV_1 // PLL Input Divider
    #pragma config FNOSC = FRC // Oscillator Selection
    #pragma config POSCMOD = EC // Primary Oscillator
    #pragma config FPLLODIV = DIV_2 // PLL Output Divider
    #pragma config FPBDIV = DIV_1 // Peripheral Clock divisor
    #pragma config FWDTEN = OFF // Watchdog Timer (controlled by software)
    #pragma config WDTPS = PS65536 // Watchdog Timer Postscale 1048s
    #pragma config FCKSM = CSECMD // Clock Switching Enable & Fail Safe Clock Monitor Disable
    #pragma config OSCIOFNC = OFF // CLKO Disable
    #pragma config IESO = ON // Internal/External Switch-over
    #pragma config FSOSCEN = ON // Secondary Oscillator (KLO was off)


    void initTIMER5( void ){ // Initialize Timer 5

    T5CON = 0x0;   
    TMR5 = 0; 

    T5CONbits.TCKPS = 0b111;//1:256 prescale value

    IFS0bits.T5IF = 0;
    IPC2bits.T2IS = 0; 
    IPC2bits.T5IP = 2;
    IEC0bits.T5IE = 1;

    T5CONSET = 0x8000;
    }

By now I think I just need to configure the T5CONbits.TCKPS and the PR5 register in order to synchronize with the baud rate, right?

enter image description here

I really need your help on this guys.

Best Answer

For both sending and receiving, you need to set up a timer interrupt.

One bit time for 115,200 baud is:

$${1,000,000 \over 115,200} = 8.68\space µs$$

If you are running with a peripheral clock of 48 MHz, then this corresponds to a period of 20.8 ns, or 0.0208 µs:

$${1,000,000 \over 48,000,000} = 0.0208\space µs$$

The number of counts for the timer is then:

$${8.68 \over 0.0208} = 417$$

So transmitting is easy, just set up a counter, and on each interrupt output the start bit, followed by 8 data bits, followed by a stop bit which lasts two timer intervals.

Receiving is a little trickier. To catch the falling edge of the RX lead, indicating the beginning of the start bit, you could use an Change Notification as you mentioned in a comment. Or, if you have an interrupt pin (INTx) unused, you could use that instead.

When the Change Notification interrupt comes in, you then want to reset your set your timer for one half the period, i.e. 4.34 µs, or 208 counts. Then, when the timer interrupt comes in, you should be in the middle of the start bit. If it has gone back high, then this was just noise. Otherwise change the timer back to one bit time (8.68 µs), and on each timer interrupt you will be sampling the middle of each data bit.

Ideally, you would like to scan each bit more than once. So if you set your initial timer after the Change Notification interrupt for 3.34 µs (160 counts) instead of 4.34 µs, you will be sampling the middle of the start bit minus one µs. Then wait 1 µs and sample again (you are now in the middle), and then once more (1 µs past the middle). You now have three samples to look at, so you can take the majority value. Wait 6.68 µs (321 counts) this time and repeat for each data bit.

Hardware UART's typically sample 16 times for each bit time.

You might want to get the simpler version working first (scanning just once in the middle).

EDIT:

I don't believe your setup is correct. The second line of your listing assumes your peripheral clock is 48 MHz, but looking at your clock setup, I think you are only running at 8 MHz.

Without an external crystal, the only way to get above frequencies of 8 MHz is to use the PLL. However you have the oscillator selection set to just FRC, which uses the internal 8 MHz oscillator directly. To get 48 MHz, you would need something like the following settings:

#pragma config FPLLMUL = MUL_24    // PLL Multiplier
#pragma config FPLLIDIV = DIV_2    // PLL Input Divider
#pragma config FNOSC = FRCPLL      // Oscillator Selection
#pragma config POSCMOD = OFF       // No Primary Oscillator
#pragma config FPLLODIV = DIV_2    // PLL Output Divider
#pragma config FPBDIV = DIV_1      // Peripheral Clock divisor
#pragma config IESO = OFF          // No Internal/External Switch-over

In Figure 8-1, page 141 of the datasheet, it says Fin must between 4 and 5 MHz. That means you must divide the internal RC clock by 2 (FPLLIDIV). Then you multiply it by 24 (FPLLMUL) and divide it by 2 (FPLLODIV) to get 48 MHz.

I don't know what the SYS_FREQ #define is being used for. Seems it should match GetSystemClock.

You didn't say which PIC32MX processor you are using. If it is one in the PIC32MX575/675/695/775/795 family, the internal FRC clock is only accurate to ±2% (Table 31-19, page 376). This may not be good enough for doing bit-banged UART at 115,200 baud -- generally baud rates need to be within 1.5%. You really should be using a 8 MHz crystal (with the clock settings updated once again) instead of the internal FRC clock.