USART Serial Timing

I've had correspondence where people are unsure of the code that performs the "USART" serialisation. Constructors want to make changes to the serial data rate. They have applications monitor remotely, using simple FSK radio transmission. Dropping the speed from 19200 bps down to 2400 makes the modulation fit in to the pass band of an audio receiver.

USART

In the design I'm not using a discrete USART Universal Synchronous Asynchronous Receiver Transmitter chip. I'm not using a PIC (16F628/20) that could do this for me. I actually chose to use timing delays.

Timing delays

I specified a crystal frequency of 2.4576 MHz. This gives an instruction delay of 1.627 us. To delay one bit period at 19200 bps, 52.08 us, the PIC needs to execute 32 instructions. But where are the instructions?

Instructions

Here's the serialisation source:

Question

Why does the delay subroutine bitdly16 only have 16 NOP's? Shouldn't there be 32?

Answer

No, the rest of the delay is in the decision logic and in actually setting the port I/O to a one or zero.

I'll unravel the first part of the code, documenting the instruction set cycle count from the time the start bit begins. 

The I/O  pin is at the logic 1 from the last call to send a character. The last call would have left the pin in logic state 1, the stop bit value. We start by sending a logic 0 for the initial transition of the stop bit, then set the I/O pin back to a 1 for a contrived data bit 0.

TxChar1
    movlw .9
    movwf RS232_COUNT
    call OutZero ; start bit    
   
// this calls:
    bcf PORTA, RS232_BIT; 1
    // Pin is now at logic 0

    goto bitdly16 // 2

bitdly16 
    NOP    // 3
    NOP    // 4
    NOP    // 5
    NOP    // 6

    NOP    // 7
    NOP    // 8
    NOP    // 9
    NOP    // 10

bitdly8
    NOP    // 11
    NOP    // 12
    NOP    // 13
    NOP    // 14

    NOP    // 15
    NOP    // 16
    NOP    // 17
bitdly1
    nop    // 18
    RETURN    // 20

    nop ; trim!             // 21
    nop                     // 22
SendLp0
    decfsz RS232_COUNT, f ; // 23
    goto SendBit ; 2        // 25
SendBit
    rrf RS232_DATA, f ;     // 26
    btfss STATUS, C ;       // 28
    goto SendZero ; 
    nop                     // 29 
    call OutOne ; send 1    // 31 
   
// this calls:
OutOne
    bsf PORTA, RS232_BIT // 32
    // Pin is now at logic 1

I could carry on and pick out the delays for each data bit, and finally the 2 stop bits.

Suggestion for 2400 bps

I haven't tried this, but....

For 2400 bps bit period we need a delay of 416.66 us. That's 256 instruction cycles in place of 32. We could insert extra NOP instructions at label bitdly16. Adding 224 NOP's would do the trick. 

A more elegant solution is to set up a loop counter as below:

bitdly16

    movlw .73    // 1                 73 = 224/ 3
    movwf MY_COUNT  // 2
L1
    DECFSZ MY_COUNT,f // 3 (6, 9..... 222)
    goto L1 // 5 (8, 11.....224)
    NOP    // and carry on with existing code...
    NOP    
    NOP    
    NOP    

    NOP    
    NOP    
    NOP   
    NOP    

bitdly8
    NOP

Back to the Schematics page