Accelerate

01/31/09 | by anthony [mail] | Categories: Code, Projects

The project:

This project is a simple but fun one. We are going to be using a tri-axis accelerometer to collect analog data from and then display it on the LCD. Our output on the LCD will look like:

|X||:|| ||Digit1||Digit2||Digit3|
|Y||:|| ||Digit1||Digit2||Digit3|
|Z||:|| ||Digit1||Digit2||Digit3|

The X:, Y:, and Z: are all static and only have to be sent once. (once you update the DDRAM address it stays there until power is cut) The digits however, have to be updated as we get new values, thus it is dynamically updated in our code and sent periodically.

This algorithm is fairly simple and works automatically. The procedure goes:

-Initialize the MCU
-Initialize the LCD
-Draw the static information
-Collect the analog information
-Convert the raw data into digits
-Display the data on the LCD.

The Code:

The Code:  #include p16F690.inc
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0              ; Begginning of code
cblock 0x20
	CHARLOCATION
	RESULT
	ONES	
	TENS
	HUNDREDS
	TEMP
	DELAY1
	DELAY2
	COUNT
endc
PORTS:
	bsf       STATUS,RP1     ; Bank 2
	movlw     0x14           ; RA2 & 4 Configured for analog AN2,3
	movwf     ANSEL          ; 
	movlw     0x08           ; RB5 configured for analog (AN11)
	movwf     ANSELH
	bcf       STATUS,RP1
	bsf       STATUS,RP0     ; select Register Page 1
	movlw     0x00           ; Move the hex value of 0 to multi purpose register W
    movwf     TRISC          ; make IO PortC all output
	movlw     0x16           ; Make Ra4,2 and 1 Input
	movwf     TRISA
	movlw     0x20           ;RB5 Input
	movwf     TRISB
	movlw     0x10           ;set A2d Clock 
    movwf     ADCON1
	bcf       STATUS,RP0     ;Exit bank 1
	;initialize 
	clrf      PORTC
	clrf      PORTA
	clrf      PORTB
	clrf      COUNT
	clrf      TEMP
	call      INITIALIZELCD
	call      DRAWLCD
MAIN:                                           ; Our MAIN routine gets the analog value off a port, and updates the LCD with the info
	movlw     0x41                             
	movwf     ADCON0     ;Channel 2
	call      A2D        ;Get A2D value
	movwf     RESULT
	movlw     0x83       ;Char position 03
	call      UPDATELCD  ;Call Update LCD, having the char position in W
	movlw     0x4D  
	movwf     ADCON0     ;Channel 3
	call      A2D
	movwf     RESULT
	movlw     0xC3       ;Char Position 43
	call      UPDATELCD	
	movlw     0x6D  
	movwf     ADCON0     ;Channel 11
	call      A2D
	movwf     RESULT
	movlw     0x97      ;Char position 17
	call      UPDATELCD
	call      DELAY
	goto 	  MAIN      ; Return to main
A2D:
	nop 
	nop
	nop
	nop
	nop                 ; wait 5 uS
	bsf       ADCON0,GO ; Start the conversion
	btfss     ADCON0,GO ; Bit will stay high untill conversion is complete
	goto      $-1       ; Untill then keep going back
    movf      ADRESH,w
	return
INITIALIZELCD:          ;Initialization Routine for the LCD
	bsf       PORTC,0   ;Put CMD 0 on port C
	call      SEND      ;Call Send routine, Sends command and waits for busy flag
  	movlw     0x38      ; Set Function, 8 Bit, 5X10 Dots, Two line
  	movwf     PORTC
	call      SEND
	movlw     b'00001100' ; Turn on the Display (0x0B
	movwf     PORTC
	call      SEND
        movlw     0x06       ; Set entry mode to: Increment Adress counter, no shift.
	movwf     PORTC
	call      SEND
	return
DRAWLCD:                 ; Routine which draws static display
	movlw     0x80       ; Start with the DDRAM address 0x00
	call      WRITELOC   ; Call Function to write the DDRAM address
	movlw     'X'        ;Move the ASCII value of 'X' to W
	call      WRITECHAR  ; Call function to write the Char to DDRAM address
	movlw     0x3A       
	call      WRITECHAR
	movlw     0xC0       
	call      WRITELOC
	movlw     'Y'
	call      WRITECHAR
	movlw     0x3A
	call      WRITECHAR
	movlw     0x94       
	call      WRITELOC
	movlw     'Z'
	call      WRITECHAR
	movlw     0x3A
	call      WRITECHAR  ; Continue untill all static elements drawm
	return
WRITECHAR:               ; Function to write a characture 
	movwf     TEMP       ; Move value to temp register
	call      BUSYCHK    ; Check Busy flag
	bsf       PORTA,0    ; Turn on Data 
	movfw     TEMP       ; Move from Temp to W
	movwf     PORTC      ; Then to PORTC
	call      SEND       ; Send
	bcf       PORTA,0    ; Turn off Data
	return
WRITELOC:
	movwf     TEMP       ;Move value to temp
	call      BUSYCHK    ;Check busy flag
	movfw     TEMP       ;Temp -> W
	movwf     PORTC      ;W -> Portc
	call      SEND       ;SEND
	return
UPDATELCD:               ; Routine to update the dynamic elements of the LCD
	call      WRITELOC   ; Init the LCD at the proper DDRAM location
	clrf      ONES       ; Clear Vars
	clrf      TENS
	clrf      HUNDREDS
	call      CALCULATE  ; Call calculate routine which takes raw A2D value and separates them into digits
	movfw     HUNDREDS   ; Write the Hundreds digit to the LCD
	call      TABLE1
	call      WRITECHAR
	movfw     TENS       ;Write Tens
	call      TABLE1
	call      WRITECHAR
	movfw     ONES       ;Write Ones
	call      TABLE1
	call      WRITECHAR
	return
CALCULATE:               ; Basic math routine, takes Result from A2D and separates into 1's 10's and 100's
	movlw     0x00
	bcf       STATUS,2
	xorwf     RESULT,w
	btfsc     STATUS,2
	return    
	movlw     0x0A
	bcf       STATUS,2
	subwf     ONES,w
	btfsc     STATUS,2
	goto      UPDATETENS
	decf      RESULT,f
	incf      ONES,f
	goto      CALCULATE
UPDATETENS:
	clrf      ONES
	incf      TENS,f
	movlw     0x09
	bcf       STATUS,2
	subwf     TENS,w
	btfsc     STATUS,2
	call      UPDATEHUNS
	decf      RESULT,f
	goto      CALCULATE
UPDATEHUNS:
	clrf      TENS
	incf      HUNDREDS,f
	movlw     0x09
	bcf       STATUS,2
	subwf     HUNDREDS,w
	btfsc     STATUS,2
	incf      HUNDREDS,f
	return
SEND:                         ;Use this routine to send a command to the LCD. Placement doesnt matter as it does two BSYFLG checks
	;Send
	bsf       PORTB,4         ; Set E line High
	nop       
	nop
	nop
	nop
	bcf       PORTB,4         ;Wait a bit then set it low, sending command to LCD
BUSYCHK:
	bsf       STATUS,RP0      ; Port C, all input
	movlw     0xff	
	movwf     TRISC
	bcf       STATUS,RP0
	bsf       PORTA,5         ;Set Read
	nop
	nop
	bsf       PORTB,4         ;E Line high
	nop       
	nop
	nop
	nop
	bcf       PORTB,4          ;Wait, Now low, Polling busy flag
	btfsc     PORTC,7          ; Wait untill busy flag is clear.     
	goto      $-7
	bcf       PORTA,5          ;Turn off Read
	bsf       STATUS,RP0
	movlw     0x00	
	movwf     TRISC
	bcf       STATUS,RP0       ;Port c all output
	return
TABLE1:                        ;Generic Table: Decimal -> ASCII
	addwf     PCL;	
	retlw     0x30
	retlw     0x31
	retlw     0x32
	retlw     0x33
	retlw     0x34
	retlw     0x35
	retlw     0x36
	retlw     0x37
	retlw     0x38
	retlw     0x39
	return
DELAY:                       ;Generic Delay
	decfsz    DELAY1
	goto      DELAY
	decfsz    DELAY2
	goto      DELAY
	return
 end

The Breakdown:

This code is fairly simple, there are no interrupts to worry about. We set up our variables in available registers as always. Our ports routine is much of the same, we set the appropriate ports for analog function and then set the appropriate I/O

Initialize the LCD:

INITIALIZELCD:          ;Initialization Routine for the LCD
	bsf       PORTC,0   ;Put CMD 0 on port C
	call      SEND      ;Call Send routine, Sends command and waits for busy flag
  	movlw     0x38      ; Set Function, 8 Bit, 5X10 Dots, Two line
  	movwf     PORTC
	call      SEND
	movlw     b'00001100' ; Turn on the Display (0x0B
	movwf     PORTC
	call      SEND
        movlw     0x06       ; Set entry mode to: Increment Adress counter, no shift.
	movwf     PORTC
	call      SEND
	return

Remember all those commands? We have to first tell the LCD what we want it to do before we start writing to the DDRAM. We set our LCD up for how were going to use it. This a fairly common initialization routine. We turn on the display, set 8 bit data control, two line, 5x10 font, and we have the address counter increment on write to DDRAM.

This routine we load the command we want into PORTC and then call our Send routine

The Send Routine:

SEND:                         ;Use this routine to send a command to the LCD. Placement doesnt matter as it does two BSYFLG checks
	;Send
	bsf       PORTB,4         ; Set E line High
	nop       
	nop
	nop
	nop
	bcf       PORTB,4         ;Wait a bit then set it low, sending command to LCD
BUSYCHK:
	bsf       STATUS,RP0      ; Port C, all input
	movlw     0xff	
	movwf     TRISC
	bcf       STATUS,RP0
	bsf       PORTA,5         ;Set Read
	nop
	nop
	bsf       PORTB,4         ;E Line high
	nop       
	nop
	nop
	nop
	bcf       PORTB,4          ;Wait, Now low, Polling busy flag
	btfsc     PORTC,7          ; Wait untill busy flag is clear.     
	goto      $-7
	bcf       PORTA,5          ;Turn off Read
	bsf       STATUS,RP0
	movlw     0x00	
	movwf     TRISC
	bcf       STATUS,RP0       ;Port c all output
	return

To send data the the LCD we have to toggle the E line. We set it high momentarily then we set it low. Once we do that the LCD reads the command and performs an internal operation. As this happens the BSY Flag is set on the LCD and we poll this until it is clear. After which we continue with normal operation by exiting the routine

Drawing the Static Display:

We draw all the static information by first setting the DDRAM address position and then putting the value we wish to send into w and then calling a routine which sends the value to the LCD

WRITELOC:
	movwf     TEMP       ;Move value to temp
	call      BUSYCHK    ;Check busy flag
	movfw     TEMP       ;Temp -> W
	movwf     PORTC      ;W -> Portc
	call      SEND       ;SEND
	return

Basic routine that makes it a little more mechanical to write data to the LCD. The value in W is sent to the LCD

WRITECHAR:               ; Function to write a characture 
	movwf     TEMP       ; Move value to temp register
	call      BUSYCHK    ; Check Busy flag
	bsf       PORTA,0    ; Turn on Data 
	movfw     TEMP       ; Move from Temp to W
	movwf     PORTC      ; Then to PORTC
	call      SEND       ; Send
	bcf       PORTA,0    ; Turn off Data
	return

Another routine specially designed for writing a character to a DDRAM address. This sets our PORTA,0 (Instruction/Data) high as we send Data to the LCD.

The MAIN routine:
The main routine is fairly simple, we generate an A2D result and then call the routine which does the dynamic updating of the LCD (we do this 3 times for each of the 3 axis (xyz)

Updating the LCD:

UPDATELCD:               ; Routine to update the dynamic elements of the LCD
	call      WRITELOC   ; Init the LCD at the proper DDRAM location
	clrf      ONES       ; Clear Vars
	clrf      TENS
	clrf      HUNDREDS
	call      CALCULATE  ; Call calculate routine which takes raw A2D value and separates them into digits
	movfw     HUNDREDS   ; Write the Hundreds digit to the LCD
	call      TABLE1
	call      WRITECHAR
	movfw     TENS       ;Write Tens
	call      TABLE1
	call      WRITECHAR
	movfw     ONES       ;Write Ones
	call      TABLE1
	call      WRITECHAR
	return

This is another simple routine, we take the A2D result, break it down into its individual digits and send it to the LCD. We provide the DDRAM address in w before we call the function, thus this can be used for more than 1 axis making our code much more efficient.

Conclusion:

Overall this is a fairly easy project that goes over the basics of controlling an HD44780 character LCD using a tri axis accelerometer as an analog input.

Ill leave you with a picture of the completed project as well the source and hex. Enjoy!

Whats next: Making it display proper units

Source
Hex

Intro to the HD44780 character based LCD

01/31/09 | by anthony [mail] | Categories: Informative

This post is going to go over using an HD44780 character LCD. These LCDs come in many sizes by are usually defined by how many characters they display; split by how many lines and characters per line.

The one thing about these LCDs is that they have a built in microcontroller, so we are going to be using our PIC microcontrollers to interface with the built in MCU on the HD44780 LCD.

Interfacing with the LCD is rather easy but often guides are very vague on exactly what you have to do. I will be going over how to statically and dynamically update the LCD using our PICs.

The LCDs work by first configuring how the LCD operates (usually an initializing routine you use from code to code. The way the LCD displays data is that each "character cell" corresponds to a DDRAM address (Display Data RAM) you load the ASCII Hex value into the DDRAM address and the corresponding cell displays that character. Thats it! Its pretty easy to use these LCDs.

How it works:

I would first like to post a wonderful link which i used to learn how to use these LCDs. It has all the info you need but is a little confusing if you don't know what to look for.

Link: http://home.iae.nl/users/pouweha/lcd/lcd.shtml

Any HD44780 character LCD with <=80 characters (less than or equal to) will have 16 pins. For MOST LCDs (ALWAYS consult your data sheet first)

Pin 1 is VSS(Ground)
Pin 2 is VDD(Pos voltage)
Pin 3 is Vee, or voltage for contrast adjustment you hook this up in a voltage divider configuration with a potentiometer or trimmer. You use this to adjust contrast.
Pin 4 is the RS line which controls whether your sending data or an instruction. 0 is for an instruction, 1 for data.
Pin 5 is Read/Write from/to the LCD, 0 for Write, 1 for Read.
Pin 6 is the most important, it is the Enable signal, The enable line for the HD44780 is edge triggered, on a falling edge (1->0) The HD44780 reads whatever is on the data bus. This is the one thing people have most trouble with.
Pins 7-14 is the primary data bus. Its 8 bits long and you can either hook it up to a spare 8 bit port or use serial communication and a shift register.
Pin 15 is the anode to the back light. Make sure you put correct voltage across this and the cathode
Pin 16 is the cathode to the back light.

Now that you know the pin out lets look at some instructions:

Commands:

Instructions control the operation of the LCD, They clear the DDRAM addresses, reset the address counter, set how the address counter functions, how the text displays and various other functions. We do all this before we send any data to the LCD, this initializes the LCD and gets it ready for accepting data and displaying it in a meaningful fashion.

To send an instruction we set the RS and R/W lines both low. When this happens we put the instruction we want onto our data bus and send it by setting the enable line from high to low.

The Instruction set:

Command: Clear Home
RS:0
RW:0
DB:0x01

This command clears the DDRAM and sets the address counter to 0x00

Command: Return Home
RS:0
RW:0
DB:0x02

Moves the address counter to 0x00, leaves the DDRAM untouched.

Command: Entry Mode

RS:0
RW:0
DB:0x04-0x07 (000001MS)

This controls what the cursor does when it gets a command. Bit "M" controls weather its to the left or the right. 0 will decrement the cursor position, (move it to the left) a 1 will increment the cursor position (move it to the right). S Controls if the display shifts (Where the text moves but not the cursor position) 0 is off and 1 is on.

Command: Display Control

RS:0
RW:0
DB:0x08-0x0F (00001DCB)

Sets weather the display is on(D) (useful), if the cursor is on(C) and if the cursor blinks(B). A 1 correlates to enabled.

Command: Cursor / Display shift.
RS:0
RW:0
DB:0x10-0x1F (0001SDxx)

This allows you to shift(S) the display(1) or the Cursor(0) and what direction(D) left(0), right(1)

Command: Function Set

RS:0
RW:0
DB:0x20-0x3F (001LNFxx)

Sets basic control information for the LCD

L - Data Length: 0 - 4-Bit communication (We will be using 8-bit) 1 - 8-bit communication
N - 1 line or 2 line: 0 - 1 line 1 - 2 line, (just set to 1)
F - Font: 0 - 5X7 Dots 1 - 5X10 dots. Set to 1

Command: Set CGRAM address
RS:0
RW:0
DB:0x40-0x7F (01(ADDRESS))

HD44780 LCDs support custom characters, CGRAM is where you set them, i will not be covering custom characters in this post.

Command: Set DDRAM address

RS:0
RW:0
DB:0x80-0xFF (1(ADDRESS))

This sets the DDRAM address which you will start sending data to.

Sending Command to the LCD:

To send commands to the LCD we have to do several things.
-Set the RS and RW line
-Put the data/instruction we want to send on our data port (any 8 bit port)
-Send it
To send data to the HD44780 we set the E line high, do a few NOPs and then set it low. The HD44780 reads the command and operates accordingly.

While the HD44780 is performing an operation we cannot send data to it. There are two ways we can make sure we don't send information while the LCD is busy. One is to just wait long enough for the LCD to finish. The other is to check the built in BUSY signal of the HD44780, I prefer this method as it is much quicker.

The Busy Flag:
RS:0
RW:1
DB:(BF(ADDRESS))

When we set RW to 1 (read/write) we read the busy flag as well as the address counter. To read the busy flag we set RW to 1 and then the E line from 1 to 0. This will put the busy flag on DB7. A simple BTFSS loop will allow you to check the busy flag.

Writing Data to the LCD:

Writing data encompasses several functions:

-Initializing the LCD
We set the proper functions for the LCD to operate and set it up for data transfer
-Selecting the first memory location
We send the first DDRAM address we want to write at (most parts sending 0x80 will set us to the first character)
-Writing the hex value for the char we wish to display

Command: Write
RS:1
RW:0
DB:Character

To write a char we just load the HEX value that corresponds to the data we wish to display, set RS to 1 and send the data (E1->0)

We keep writing as long as we want to stay on whatever line we started at. Every time you write, the address counter automatically increments, no need to keep resending the DDRAM address.

Command: Read
RS:0
RW:1
DB:Address Contents

This command reads the current address's contents.

With this, you have all the information to control the HD44780 based character LCD. I will be posting Code and a project involving this LCD so stay tuned!

Project: UChat

12/28/08 | by anthony [mail] | Categories: Code, Projects

The basis of this project is 5 7 segment displays, One of which the user controls and uses to select a number 0-9, and 4 of which store previously received values. There are 3 buttons; Increment decrement and send, all of do their respectable functions.

This project takes advantage of the following procedures:

Asynchronous Serial Communication – We use the UART module on the PIC16F690 to transmit data from one PIC to another. We use a 9600 baud with the transmitter and receiver set asynchronously. RB5 is RX and RB7 is TX

Multiplexed 7-segment Displays
– One problem with running 5 segment displays is the sheer number of pins we’d need to run each one individually. Each display has 7 segments and a common cathode. For all of the displays that’s 40 pins we’d need to run all those displays. Multiplexing is where we only run one display at an instantaneous moment. We do this but loading the data for one display onto PORTC, then using another port to switch the CC connection using a bipolar transistor. We keep rotating the output on PORTC and the CC we are switching until we’ve gotten to the last display. This happens so fast all the displays seem like they’re on at one given time. One thing to consider when using a multiplexed display is that the outputs must be turned off when you rotate the display, otherwise you will get ghosting of the previous value.

Interrupts – We use the interrupt on change and the receive interrupt to handle user input.

The Code:

#include p16F690.inc
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0
;Declare Variables
	cblock 0x20
DISPLAY
FIRSTDISP
SECONDDISP
THIRDDISP
FOURTHDISP
CURRENTDISP
STATUS_TEMP
TEMP
DELAY1
DELAY2
GROUND
W_TEMP
	endc
	goto 	PORTS ;Skip interrupt
;;;;;;;;;;;;;;
;  INTERUPT  ;
;;;;;;;;;;;;;;
	org 0x04
INTERUPT:
	bcf       INTCON,7      ;Turn off interrupts
	movwf     W_TEMP        ; Move W to temp
	bcf       STATUS,RP0    ; Goto bank 0
	bcf       STATUS,RP1    ; Goto bank 0
	btfsc     INTCON,0      ; Check for IOCA/B int
	goto      BUTTONPRESS   ; Yes, goto button press routine
	btfss     PIR1,RCIF     ; No, Check if value received
	goto      OUT           ; No, leave interrupt
	call      RX            ; Yes , receive the value
OUT:
	movf      W_TEMP,w 		; Restore W
	bcf       PIR1,RCIF     ; Clear flag
	bcf       INTCON,0      ; Clear flags
	bsf       INTCON,7      ; Restart interrupts
	retfie                  ; Return from interrupt
BUTTONPRESS:
	btfss     PORTA,3       ;Check first button
	call      INCREASE      ;Goto said function
	btfss     PORTB,4       ;Check Decrement button pressed, 
	call      DECREASE      ;Goto Said function
	btfss     PORTB,6       ;Check if TX button pressed
	call      TX            ; Goto TX routine
	call      DELAY	     	; Button Debounce	
	goto      OUT
INCREASE:
	incf      DISPLAY       ;Increment display
	movlw     0x0A          ; Move 10d to W
	bcf       STATUS,2      ;Clear Z bit
	subwf     DISPLAY,w     ;Subtract w from display
	btfsc     STATUS,2      ;Check if z bit set
	clrf      DISPLAY       ; Yes, clear display
	return                  ; no, return
DECREASE:
	incf      DISPLAY       ;Increment display (incase 0)
	bcf       STATUS,2      ;Z bit
	decf      DISPLAY       ;Decrement display
	btfsc     STATUS,2      ; Check z bit
	goto      INITIALIZE    ;Yes, reinitialize the display
	decf      DISPLAY       ;No, Decrement display
	return                  
INITIALIZE:
	movlw     0x09          ;Move 9d to w
	movwf     DISPLAY       ; Move it to display
	return
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     ; Bank 0
 	return
RX:
	btfss     PIR1,RCIF     ; Wait for value to be recieved
	goto      $-1           ; loop
	bcf       PIR1,RCIF     ; Clear flag
	movf      RCREG,w       ; Move recieved word to w
	movwf     TEMP          ; then to var temp
	clrf      FOURTHDISP    ;Clear 4th display <- 3rd Disp <- Second Display <- First Display <- Temp
	movfw     THIRDDISP
	movwf     FOURTHDISP
	movfw     SECONDDISP
	movwf     THIRDDISP
	movfw     FIRSTDISP
	movwf     SECONDDISP
	movfw     TEMP
	movwf     FIRSTDISP
	return
DELAY:                     ; Generic Delay routine
	decfsz   DELAY1
	goto     DELAY
	decfsz   DELAY2
	goto     DELAY
	return
PORTS:
	; Analog
	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 
	; Tris
	bsf       STATUS,RP0     ; goto bank 1
	movlw     0x00           ; Move the hex value of 0 to multi purpose register W
              movwf     TRISC          ; make IO PortC all output
	movlw     b'01010000'    ; Move move b'01010000' to w
               movwf     TRISB          ; move to tris
	movlw     b'00001000'    ;Make porta.3 input rest output
	movwf     TRISA
	bcf       STATUS,RP0    ; goto bank 0
	; Interrupt
	bsf       INTCON,7      ;Set up global interrupts
	bsf       INTCON,6      ;Peripferal Interupts
	bsf       INTCON,3      ; IOCA/B interupt
	bsf       STATUS,RP0    ; Bank 1
	bsf       PIE1,5        ; Enable Interupt on Recieve
	bcf       STATUS,RP0    ; Bank 0
	bcf       PIR1,RCIF     ; Clear Flag
	bcf       INTCON,0      ; IOCA/B change flag
	; IOCB
	bsf       STATUS,RP1    ;Bank2
	movlw     b'01010000'   ; Make ports 4 and 6 IOC
	movwf     IOCB
	bcf       STATUS,RP1    
	bsf       STATUS,RP0    ;Bank1
	; IOCA
	movlw     b'00001000'   ;Make porta.3 IOC
	movwf     IOCA
	;serial comm transmit
	movlw     d'25'          ; Move the value of 25 or 0x19, to the baud rate genrator
	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
	; Initialize
	clrf      DISPLAY
	clrf      FIRSTDISP
	clrf      SECONDDISP
	clrf      THIRDDISP
	clrf	  FOURTHDISP
	movlw     0x01            ;Move 0x01 to ground
	movwf     GROUND
	movlw     0x20            ;Start pointer at DISLAY location: 0x20
	movwf     FSR
;;;;;;;;;;;;;;;;;;
;  MAIN ROUTINE  ;
;;;;;;;;;;;;;;;;;;
MAIN:
	bsf       STATUS,RP0 ;goto bank 1 and turn off display
	movlw     0xff
	movwf     TRISA
	movlw     0xff
	movwf     TRISC
	bcf       STATUS,RP0 ;bank 0
;Begin rotate
	movfw     INDF
	call      TABLE
	movwf     PORTC
	movfw     GROUND
	movwf     PORTA
;Turn on Displays
	bsf       STATUS,RP0
	movlw     0x00           
  	 movwf     TRISC          
	movlw     b'00001000'           
	movwf     TRISA
	bcf       STATUS,RP0
;Finish rotating
	rlf       GROUND
	call      RA3
	incf      FSR
	movlw     0x25
	bcf       STATUS,2
	subwf     FSR,w
	btfsc     STATUS,2
	call      CLEAR
	goto      MAIN
RA3:
	btfss     GROUND,3
	return
	rlf       GROUND
	return
CLEAR:
	movlw     0x20	
	movwf     FSR
	movlw     0x01
	movwf     GROUND
	return
TABLE:                          ;Generic lookup table
	addwf     PCL      ;            1;
	retlw     b'00111111';0      ;;;;;;;
	retlw     b'00000110';1    6 ;     ; 2
	retlw     b'01011011';2      ;  7  ;
	retlw     b'01001111';3      ;;;;;;;
	retlw     b'01100110';4    5 ;     ; 3
	retlw     b'01101101';5      ;     ;
	retlw     b'01111100';6      ;;;;;;;
	retlw     b'00000111';7         4
	retlw     b'01111111';8
	retlw     b'01100111';9
	return
end
;end

The Breakdown:

Initialization:
We initialize our variables to hold temporary values as well as the current values in each display.
Ports:
We set all digital I/O, Port C all output, Port A 0,1,2,4,5 as output 3 as input, Port B 4,6 as input, 7 and 5 as TX and RX respectively. The I/O Structure for this code is:
PORTC – Primary 7-Segment driver
PORTA - Drives the line for the ground for the common cathode as well as the send input
PORTB – Handles serial comm., as well as increment and decrement inputs.
We use the interrupt on change for Port A and B; we set up that interrupt for its respectful inputs
We set up the UART module for asynchronous communication, high speed baud and we turn on TX and RX.
We finally clear the display variables, set the ground to the first position and set the pointer to look at the first digit’s memory location (0x20)

Main:
We turn off the outputs to prevent ghosting. Since we can only change one port at one time and the display uses to ports to display data we shut off the ports, change the value and then update the ports. Thus we prevent the previous value from being displayed on top of the current one. We display the current value by using the value in that digit’s block of memory into a lookup table and then outputting to PORTC, along with the respectable ground for that digit’s display being HIGH.
After that we update the pointer reference to the next digit and rotate the ground position. We must note however, when the ground is at RA3 (input only) we rotate it once more since were using it as input.
Once we reach the end of the rotation, we reinitialize the displays.

Interrupts:
The interrupt routine is fairly simple it handles 4 conditions.
-Increment pressed
-Decrement pressed
-Send pressed
-Value Receive
The first two conditions are fairly simple, the interrupt happens we pool for a button press and then we update the first digit’s value.
The send routine, pools for a button press, if it was the first digit is loaded into the TX reg and is sent out.
When a value is received, we enter the RX routine, we store the value into a temp register and we begin updating the displays.
Temp>>First>>Second>>Third>>Fourth>>Clear
We do this by rotating the display into the next one. I.e. the third display into the fourth, etc.
If no conditions are met, we just exit the interrupt.

Conclusion:

This is a fairly simple project but it takes advantage of the PIC’s resources and on board hardware, we end up using 19 of the 20 pins of the PIC MCU. This is a great demo for serial communication between two PIC MCUs. I’ve expanded this project to include two Zigbee RF modules in which the two modules talk to each other wirelessly.

Where to go from here:
There is one major downside to this project, and that is the displays. The 7-segments are horribly inefficient and lack much flexibility. One way I’d like to expand this project is to add a LCD screen and a keypad matrix to expand the user input and possible output.

General Note:

I went through the explanation of the code very quickly in this article. As the code gets more complicated I won’t be able to keep revisiting basic techniques and explaining everything. That is what the previous posts are for, to lay the basic ground work down. If you need further explanation feel free to email me, I’m glad to break it down if you need it.

Thanks once again for taking the time to read this, Source and hex as always, is below.

Source
Hex

Ground control to Major Tom. (Serial communication)

11/05/08 | by anthony [mail] | Categories: Informative, Code

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.

Source Assembly

Assembled HEX

Pointers

11/05/08 | by anthony [mail] | Categories: Informative, General, Code

Pointers are a very fundamental part of programming, often you need to reference a memory location without directly addressing or calling it, pointers help us do that. With c/c++ pointers can be a little daunting because how they work isn't directly apparent. Luckily for us, assembly works directly with the ram of the MCU and using pointers is extremely easy and almost effortless.

The use of pointers with a PIC mcu is called indirect addressing. We use the FSR(File Select register) and INDF. Essentially we load the value we want to point to into FSR and we address it using INDF. Fairly simple, lets look at an example:

VALUE equ 0x20

movlw   0x03
movwf   VALUE
movlw   0x20
movwf   FSR
movf    INDF,w
movwf   PORTC

The value on port c will be 0x03

First, we set up our variable VALUE to the general purpose address of 0x20.
Then, we set it equal to 0x03
We move the value of 0x20 to FSR (the location of VALUE)
Then we move INDF (The value of the address we loaded into FSR in this case 0x03) to w
Then w to PORTC

Very simple, with this in place you can save memory and make your programs extremely efficient and quick. Using the FSR and INDF will be essential to any remotely complicated code such as communications and robotics and other advance topics.

Pages: << 1 2 3 4 5 >>

September 2010
Sun Mon Tue Wed Thu Fri Sat
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

Ads by Google

This blog is dedicated to working with digital circuitry and the use of microcontrollers, small compact computers on a chip. I will be encompassing many techniques to develop projects, tools to use to write and assemble code and i will be sharing any projects i am currently working on. User feedback is a must! I do not know it all, hell im not even that experienced, but without a general place to get all the info needed i find it very hard to get into the world of microcontrollers without pursing a CE degree. So come one come all and enter the world of mystery and creativity!

Search

XML Feeds

multiblog engine