| « Project: UChat | Pointers » |
One of the last modules you will come to know and love is that of the USART module. There are many communication modules that PIC MCUs offer, but USART is going to be one of the simplest and easiest to get off the ground, sending and receiving data.
Fundamentals of communication:
There are two predominate forms of communication serial and parallel. In serial you send bit after bit and parallel you send word after word. Parallel had an advantage before we got some fast transfer rates as it could transfer more data at lower speeds, but it required more infrastructure and it was more error prone as well as complicated. Serial communication is very simple and can be done with as little as two wires. Because of its simplicity we use serial communication. With the USART module there are two types of serial communication, they are synchronous and asynchronous. Either case we have a Baud Rate, or the clock speed of the transmission. We must declare the baud rate before transmission can be done so that the hardware is in sync and expecting results at the right time
Asynchronous:
Asynchronous is full duplex communication, which means you can send an receive data at the same time. We can do this because we have a dedicated wire for transmission(TX) and a dedicated wire for receiving data (RX) Data can be received and sent at the same time. This method is very simple to set up and you can have the same code on all your devices. It is primarily used for device to device communication where synchronization is not important.
Synchronous:
Synchronous is half duplex, which means you can only send data or receive it a given time. In this setup we use one wire as data and the other as clock. In this setup we have a master PIC that sets the clock rate and salve PICs that run off of the master's clock. This is very useful for multi device communication but it requires two sets of code, one for the master and one for the slaves. This however provides great control over your application where the master can control the show and the slaves collect and format data. Lets actually look at what we have to do to send data.
The Registers:
PIE1: (bank 1) Peripheral interrupt enable register 1. This is the register that controls what peripheral interrupts we have on. To use a peripheral interrupt we must first set INTCON bit 6 high. (INTCON,PEIE)
PIR1: (bank 0) Stores the flags for the peripheral interrupts.
SPBRG: (bank 1) Baud rate generator. We move a literal into here which correlates to a baud rate for transmission. The actual baud rate depends on what you send and what the settings for transmission are.
TXSTA: (bank 1) Transmitter Status and Control register. This register controls weather we use asynchronous or synchronous communication as well as other functions related to the transmitter.
RCSTA: (bank 0) receive Status and Control Register. This register controls various operation of the receiver and its associated properties.
TXREG: (bank 0) transmission register, we move values to here that we want to be transmitted.
RCREG: (bank 0) receive register, bits received from transmission will be here. When we read this register the receive flag automatically clears.
Sample code:
#include
__config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0
cblock 0x20
TEMP
DISPLAY
DELAY1
DELAY2
W_TEMP
endc
goto PORTS;
org 0x04
INTERUPT:
movwf W_TEMP ;Store w to TEMP
bcf INTCON,7 ; Turn of interrupts
btfss PIR1,RCIF ; Wait until RCREG is full
goto $-1
bcf PIR1,RCIF ;Clear flag
movf RCREG,w ; Move RCREG to w
movwf DISPLAY ; Move w to DISPLAY
call ROUTINE
movf DISPLAY,w ; Move DISPLAY to w
movwf PORTC ; Move it to PORTC
movf W_TEMP,w ; Restore w
bsf INTCON,7 ; Turn on interrupts
retfie
ROUTINE:
bcf STATUS,0 ;Clear flag
rlf DISPLAY ;Rotate the display
btfss STATUS,0 ; Check flag
return ; Exit if not set
movlw 0x01 ; If it is, reset display
movwf DISPLAY
return
PORTS:
bsf STATUS,RP1 ;Bank 2
clrf ANSEL ; All pins are digital I/O
clrf ANSELH ; All pins are digital I/O
bcf STATUS,RP1 ;Bank 0
bsf INTCON,7 ;Set up global interrupts
bsf INTCON,6 ; Peripheral Interrupts
bsf STATUS,RP0 ;Bank 1
bsf PIE1,5 ; Interrupt on RX buffer full
bcf STATUS,RP0 ; Bank 0
bcf PIR1,RCIF ; Clear the flag
bsf STATUS,RP0 ; Bank 1
movlw 0x00 ; Move the hex value of 0 to multi purpose register W
movwf TRISC ; make IO PortC all output
movlw 0xff ; 0xff to w
movwf TRISA ;PORTA input
;serial comm transmit
movlw d'25' ; Move the value of 25 or 0x19, to the baud rate generator
movwf SPBRG ; this will make the baud rate = to 9600 at a 4mhz internal clock in high speed mode
bsf TXSTA,BRGH ;baud rate high
bsf TXSTA,TXEN ;Enable Transmission
bcf TXSTA,SYNC ;asynchronous
bcf STATUS,RP0 ;Exit bank 1
;Serial comm Receive
bsf RCSTA,SPEN ;Set serial port enable high
bsf RCSTA,CREN ; Enable receiver
clrf PORTB ; Clear
clrf PORTC ; Clear display
movlw 0x01 ; Initialize Display
movwf DISPLAY
MAIN:
bsf PORTC,0 ; Portc high
btfsc PORTA,3 ; Poll the button press
goto $-1 ; Goto instruction - 1
call TX ; Call transmission
bcf PORTC,0 ; Clear port c,0
call DELAY ; Call delay
goto MAIN
TX:
btfss PIR1 , TXIF ; TXIF is set if TXREG has no character
goto $-1 ; Loop until TXIF is cleared
movf DISPLAY,w ; Data to be transmitted over serial port
movwf TXREG ; Copy data to transmit register
bsf STATUS,RP0 ; RAM PAGE 1
btfss TXSTA , TRMT ; Wait until transmission is completed
goto $-1 ; Loop back until transmission completed
bcf STATUS,RP0
return
DELAY: ; Generic Dela routine
decfsz DELAY1
goto DELAY
decfsz DELAY2
goto DELAY
return
end
This code is fairly complicated but its not horrible. This is basic code for asynchronous transmission which transmits a value, which triggers an interrupt for the receiving PIC. That receiving PIC then takes the transmitted value and performs an RLF operation on the data then out puts it to PORTC.
The Routine:
1) Startup – Declare environmental variables and goto main port routine
2) Ports – Set up the ports for digital I/O, Enable Interrupts, Enable peripheral interrupts, Enable asynchronous transmitter and receiver. Initialize Display to 1
3) Pool the button and wait for press
4) Button is pressed, Transmit DISPLAY by loading it into the TXREG register and waiting for it to shift out.
5) Delay is called to De-bounce switch.
6) Other PIC receives value, interrupt triggers when RXREG fills
7) Move RXREG into DISPLAY and rotate the bit (if carry reset to 0x01)
8) Return from interrupt
The Breakdown:
;serial comm transmit
movlw d'25' ; Move the value of 25 or 0x19, to the baud rate generator
movwf SPBRG ; this will make the baud rate = to 9600 at a 4mhz internal clock in high speed mode
bsf TXSTA,BRGH ;baud rate high
bsf TXSTA,TXEN ;Enable Transmission
bcf TXSTA,SYNC ;asynchronous
bcf STATUS,RP0 ;Exit bank 1
;Serial comm Receive
bsf RCSTA,SPEN ;Set serial port enable high
bsf RCSTA,CREN ; Enable receiver
That is the bulk of the code, This section sets up our PIC for serial communication.
The most important part of this section of code is the baud rate. We primarily derive the baud rate from the data sheet and the frequency we run our PIC at. We generally want the least amount of error for our application. For 4MHZ 9600bps in high speed mode is most efficient and least error-prone. If we look at the data sheet to set that baud rate we send 0x19 to SPBRG.
Because we want high speed mode, we get BRGH high, We want to enable the transmitter so we enable TXEN and we want asynchronous communication so we set SYNC to 0.
For RCSTA we want to enable the serial port so we set SPEN high and we want to enable the receiver so we set CREN high.
This is the basic setup for the most efficient asynchronous data transmission with a 4MHZ internal oscillator .
For synchronous and other types of setups refer to the data sheet, its all very self explanatory. One thing you have to consider, there is going to be different setups for the MASTER PIC and the SLAVE PICs.
TX: btfss PIR1 , TXIF ; TXIF is set if TXREG has no character goto $-1 ; Loop until TXIF is cleared movf DISPLAY,w ; Data to be transmitted over serial port movwf TXREG ; Copy data to transmit register bsf STATUS,RP0 ; RAM PAGE 1 btfss TXSTA , TRMT ; Wait until transmission is completed goto $-1 ; Loop back until transmission completed bcf STATUS,RP0 return
This is the primary code that does the transmission. We first poll PIR1,TXIF to make sure it is clear, then we move the value we want into TXREG starting the transmission. We wait for the transmission to complete and then we leave.
Once the other PIC receives a value it triggers an interrupt and the following code gets executed.
btfss PIR1,RCIF ; Wait until RCREG is full goto $-1 bcf PIR1,RCIF ;Clear flag movf RCREG,w ; Move RCREG to w movwf DISPLAY ; Move w to DISPLAY
We wait until RCREG is full, then we move the value to DISPLAY (or wherever we want).
There we have it. Asynchronous data transmission from one PIC to another. Source and hex are below. Feel free to take and modify it. This will not be the only article on data transmission. This will be a topic we will be visiting frequently and getting very in depth with.
This post has 1 feedback awaiting moderation...