There is an excellent tutorial here that I HIGHLY recommend going through. It will teach you how to do serial io with bit banging, which will give you a good background of how RS-232 (the serial protocol) works. At least read it. The code itself doesn't work straight out of the box on the PIC 18F452. There's one instruction I had to change, if I recall correctly, change every occurence of rrf to rrcf. It's just a change in PIC instruction names between generations.
Briefly, RS-232 sends bits with one start bit and one stop bit at +12V and -12V. It uses inverted logic, meaning the voltage goes low for a 1 and high for a 0. (a better explanation of RS-232 here) This is a problem if you're trying to go straight between the computer and the PIC (I'll get to the alternative later). The PIC is built to output using TTL, which is similar, but uses normal logic (not inverted) and goes between 0V and +5V. Luckily, the computer can read TTL. Unluckily, the computer will try to talk back in RS-232, and the 12V will fry your PIC if it's not insulated by a resistor. See Physical Computing or do a google to get the rating for the resistor you need between the computer's TX (shorthand for transfer) and the PIC's RX (receive). I can't remember it off the top of my head.
So, to get the PIC to talk to the computer directly using the scheme in the tutorial above, all you need to do is insulate the PIC's RX with a resistor and invert all the logic. To invert the logic change all the bsf's to bcf's and vice versa in the XMIT and RCV subroutines.
This is the easy way to do serial communication with the PIC. It requires a MAX232, which is an integrated circuit that is available everywhere worthwhile for less than a dollar. It was very difficult for me to figure out how to wire it, so here's a bunch of pictures:
Overview
A bunch from the sides
The code I will show you here just does output from the PIC. I haven't needed input yet, so I haven't done it. It shouldn't be hard to add to this. This code will send the alphabet to the computer from the PIC.
Let's start with our LED Chaser and build on that skeleton. I did this so that the LED will keep blinking while we're working. This is helpful at this stage to debug and be sure the PIC is getting through its loop. It's also useful because you want to pause between sending each letter, otherwise they flood in at several hundred a second. The LED delays work nicely for that.
; Define Constants COUNT1 equ 0x00 COUNT2 equ 0x01 ;;Subroutines Main bsf PORTD,RD1 ; turn off the LED call Delay ; wait bcf PORTD,RD1 ; turn off the LED call Delay ; wait goto Main Delay setf COUNT1 setf COUNT2 Loop1 decfsz COUNT1, F goto Loop1 setf COUNT1 decfsz COUNT2, F goto Loop1 return
I think that's basically the LED Chaser, with the headers removed of course. See below for that.
We need to add one variable that will hold the letter we are sending to the computer:
char equ 0x04
As for port setup, we're just blinking the LED, so setting port D all out will do:
clrf TRISD
Most of the rest of what we're going to do is by use of subroutines, so start adding subroutines below the Delay sub.
The first sub is very simple, it just sets char
to the ASCII leter 'A'. We'll do this a lot, so we'll make it a sub to save memory.
Reset_char movlw 'A' movwf char return
Before we go on, let's just add a line before the Main loop, and after the port setup that will initialize char
to 'A'. Just call the Reset_char
sub for this.
Next we need two more subroutines, one to initialize the Universal Asynchronous Receiver Transmitter, or UART. The UART controls the RX and TX pins to send the RS-232 signal.
Initializing the UART has 5 steps. There are more advanced features too, but these work. For more information look at the datasheet at p. 174
The Transmit Status and Control Register (TXSTA) lets you setup how the TX works, and also allows you to later check the status of the transmission. The TXSTA has 8 bits (see datasheet p. 168 for more info):
movlw B'00100110' ; See datasheet p 168 movwf TXSTA
I'll gloss over the setting for the same register on the receive side. Suffice to say you need to set the first bit to enable the serial port.
movlw B'10000000' ; Turn serial port on. movwf RCSTA
Now we set the baudrate. To do this we use the Baudrate Generator. You feed the baudrate generator a decimal number (gotten from the tables in the datasheet on pp. 171-173) and it sets the baudrate for you. What the baudrate is is determined by whether you set the high speed baudrate bit of TXSTA, which we did to gain accuracy. If you look on the tables on p. 171 of the DS you'll notice that there's a % error column. We want to get a 9600 baud, with the smallest margin of error. This means we set the high speed bit and give the baudrate generator (SPBRG) the decimal number 25
movlw D'25' ; Set baud rate to 9600 w/ 4 MHz clock. +.16% error movwf SPBRG
Now we set the TX pin to output, just like we set the pins to output previously
bcf TRISC, TX ; Set TX to output
The last step of UART initialization is to clear the transmit interrupt. This would make it so when the PIC receives something it can branch from the current routine to deal with the transmission. We're not using this, but it's good to clear it nonetheless
bcf PIE1, TXIE ; clear transmit interrupt
Put all this together into a subroutine call UART_Init, and it should look like this:
UART_Init movlw B'00100110' ; See datasheet p 168 movwf TXSTA movlw B'10000000' ; Turn serial port on. movwf RCSTA movlw D'25' ; Set baud rate to 9600 w/ 4 MHz clock. +.16% error movwf SPBRG bcf TRISC, TX ; Set TX to output bcf PIE1, TXIE ; clear transmit interrupt return
Actually transmitting over the UART is easy. Basically you just move your number to the TXREG and the PIC sends it for you while the PIC can go on and do other things. The only catch is that you want to make sure the PIC has sent the previous byte, so you put a loop before the transmission to wait until the last one is done (if it isn't already). Note that this implementation assumes the byte to be transferred is in the the WREG.
UART_Put btfss PIR1, TXIF ; Wait for TXREG to be empty goto UART_Put movwf TXREG ; Transmit byte in W return
Now you've got all the subs you need. Put them after your main loop (or the PIC will run through them before it gets to Main) and call the UART_Init just before the Main loop, and after you set char
.
Now we need to actually do the transfer. To go through the alphabet all we do is add one to our letter 'A'. If it's larger than 'z' (z is represented by a higher number than A in ASCII, 122 and 65 respectively) we reset back to 'A'.
Main bsf PORTD,RD1 ; turn off the LED call Delay ; wait ;; This is the transmission movf char, W call UART_Put incf char, F ; increment char movlw 'z' ; reset char if greater than z cpfslt char call Reset_char bcf PORTD,RD1 ; turn off the LED call Delay ; wait goto Main
Schlack the whole thing together and you get the following. If you don't get it or I'm moving too fast, email me.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; rs232_test-1.asm ; blinks and LED while sending the alphabet to a com port using RS-232 ; by Ian Smith-Heisters, v.1 complete on October 14th, 2004 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; title "RS232 with built in TX" List p=18f452,f=inhx32 #include <p18f452.inc> ;The config aliases are already defined in the 18f452 header file. __CONFIG _CONFIG1H, _OSCS_ON_1H & _XT_OSC_1H ; External Clock on OSC1 & OSC2 __CONFIG _CONFIG2L, _BOR_ON_2L & _BORV_20_2L & _PWRT_OFF_2L ; Brown out reset on at 2.0V, no power-up timer __CONFIG _CONFIG2H, _WDT_OFF_2H & _WDTPS_128_2H ; watchdog off, postscaler count to 128 __CONFIG _CONFIG3H, _CCP2MX_ON_3H ; CCP2 pin Mux enabled. What is this? __CONFIG _CONFIG4L, _STVR_ON_4L & _LVP_ON_4L & _DEBUG_OFF_4L ; Stack under/overflow reset on, LVP on, debug off __CONFIG _CONFIG5L, _CP0_OFF_5L & _CP1_OFF_5L & _CP2_OFF_5L & _CP3_OFF_5L ; all protection off __CONFIG _CONFIG5H, _CPB_OFF_5H & _CPD_OFF_5H __CONFIG _CONFIG6L, _WRT0_OFF_6L & _WRT1_OFF_6L & _WRT2_OFF_6L & _WRT3_OFF_6L __CONFIG _CONFIG6H, _WRTC_OFF_6H & _WRTB_OFF_6H & _WRTD_OFF_6H __CONFIG _CONFIG7L, _EBTR0_OFF_7L & _EBTR1_OFF_7L & _EBTR2_OFF_7L & _EBTR3_OFF_7L __CONFIG _CONFIG7H, _EBTRB_OFF_7H org 0x0000 ; Define Constants COUNT1 equ 0x00 COUNT2 equ 0x01 ; Define variables char equ 0x04 ; Set port D to all output clrf TRISD ; initialize variables call Reset_char ; sets char to 'A' call UART_Init Main bsf PORTD,RD1 ; turn off the LED call Delay ; wait movf char, W call UART_Put incf char, F ; increment char movlw 'z' ; reset char if greater than z cpfslt char call Reset_char bcf PORTD,RD1 ; turn off the LED call Delay ; wait goto Main ;; Subroutines Reset_char movlw 'A' movwf char return Delay setf COUNT1 setf COUNT2 Loop1 decfsz COUNT1, F goto Loop1 setf COUNT1 decfsz COUNT2, F goto Loop1 return UART_Init movlw B'00100110' ; See datasheet p 168 movwf TXSTA movlw B'10000000' ; Turn serial port on. movwf RCSTA movlw D'25' ; Set baud rate to 9600 w/ 4 MHz clock. +.16% error movwf SPBRG bcf TRISC, TX ; Set TX to output bcf PIE1, TXIE ; clear transmit interrupt return UART_Put btfss PIR1, TXIF ; Wait for TXREG to be empty goto UART_Put movwf TXREG ; Transmit byte in W return end