LED Accelerometer

2nd year project to Design and develop a simple instrument to demonstrate the functionality of a modern accelerometer device. The provided components included :
Pic 18F14K22 i/p (20pin DIL),
MCP23017 I2C Port Expander,
74HC164 Serial In Parallel Out Shift Register,
LM317LZ Voltage Regulator,
MMA845Z 3-axis Accelerometer & 8pin dip adapter,
24 (8xRed, 8xYellow, 8xGreen) Low current LED,

To help illustrate exactly how the device works I have created a series of flow charts that show how the device works, these were used when writing the code for the device.

this project was written in assembly and i have included code below

;******************************************************************************;
; prject name: Main Program.asm ;
; Objective: Read data from 3 axis accelerometer ;
; and display as a bargraph on 3 sets of 8 LEDs ;
; Author: Queron Williams ;
;******************************************************************************;
; #include "P18F14K22.INC" ;
;******************************************************************************;
#include "P18F14K22.INC"
;******************************************************************************;
; CONFIG BITS ;
;******************************************************************************;
CONFIG LVP = OFF
CONFIG FOSC = IRCCLKOUT
CONFIG PWRTEN = OFF
CONFIG WDTEN = OFF
CONFIG DEBUG = ON
CONFIG CP0 = OFF
CONFIG CP1 = OFF
CONFIG CPB = OFF
CONFIG CPD = OFF
CONFIG EBTR0 = OFF
CONFIG EBTR1 = OFF
CONFIG EBTRB = OFF

check equ 18 ;
org 0x0000 ;

;******************************************************************************;
; MACROS ;
;******************************************************************************;
movlf macro x, y ;setup macro to move a literal value to a file register.
movlw x ;starts by moving literal register to working register,
movwf y ;then moves working register to file register
endm
;******************************************************************************;
; VARIABLES ;
;******************************************************************************;
; ----varables used in i2c---- ;
sipval equ 0x008 ;variable for data to be sent to SIPO
i2cadd equ 0x009 ;componant address for I2C
regadd equ 0x00a ;register address for I2C
i2cdat equ 0x00b ;data for I2C
output equ 0x00c ;value to be transmitted to LEDs
; ----data storage---- ;
mode equ 0x00d ;mode data. bit0:0=dot,1=line. bit4:1=2g,
;bit5:1=4g, bit6:1=8g, bit7:used for debuging
lastshift equ 0x010 ;varable to hold last shifted out value
xraw equ 0x011 ;raw data from x axis of accelerometer
yraw equ 0x012 ;raw data from y axis of accelerometer
zraw equ 0x013 ;raw data from z axis of accelerometer
xout equ 0x014 ;converted x data to be outputed
yout equ 0x015 ;converted y data to be outputed
zout equ 0x016 ;converted z data to be outputed
convert equ 0x017 ;holds data ready to be converted

;******************************************************************************;
; DEFINITIONS ;
;******************************************************************************;
PORTEX equ 0x040 ;I2C address for port expander inc write bit
TXREG equ 0xFAD ;register for sending data to SIPO
PEDIRA equ 0x00 ;registers for setting PORTA & PORTB
PEDIRB equ 0x01 ;direction on port expander
PEDATA equ 0x14 ;data registers for PORTA & PORTB
PEDATB equ 0x15 ;on port expander
ACCELR equ 0x39 ;I2C address for reading from accelerometer
ACCELW equ 0x38 ;I2C address for writing to accelerometer
ACCON1 equ 0x2A ;address on acceleromemter for control register 1
ACCON2 equ 0x0E ;address on acceleromemter for control register 2
ACXDAT equ 0x01 ;accelerometer X axis data register
ACYDAT equ 0x03 ;Y axis data register
ACZDAT equ 0x05 ;Z axis data register

SSPCON1 equ 0xFC6 ;
SSPCON2 equ 0xFC5 ;
SSPSTAT equ 0xFC7 ;
SSPBUF equ 0xFC9 ;

;******************************************************************************;
; SETUP MODULES ;
;******************************************************************************;
; ----setup oscillator---- ;
movlw 0x0F2 ;configure OSCCON so internal OSC
movwf OSCCON ;runs at 16MHZ
; ----setup ports---- ;
movlf 0x00, ANSEL ;all I/O pins are configured as digital
movlf 0x00, ANSELH ;pins
movlf 0xff, TRISA ;port a set to inputs
movlf 0xff, TRISB ;port b set to inputs
movlf 0xff, TRISC ;port c set to inputs
; ----setup USART baud rate generator---- ;
movlw d'417' ;places 417 in SPBRG to set the
movwf 0xFAF ;BRG to 9600 baud (ish)
; ----setup I2C baud rate generator---- ;
movlw d'39' ;configures MSSP baud rate
movwf SSPADD ;to 100KHz
movlw d'128'
movwf SSPSTAT
; ----setup MSSP module in master I2C mode---- ;
movlw 0x028 ;configures MSSP module as i2c
movwf SSPCON1 ;master
; ----setup USART modules---- ;
bsf 0xFAC, 4 ;sets SYNC
bsf 0xFAC, 7 ;sets CSRC
bcf 0xFAB, 5 ;clears SREN
bcf 0xFAB, 4 ;clears CREN
bsf 0xFAB, 7 ;sets SPEN
bsf 0xFAC, 5 ;sets TXEN
bsf 0xFB8, 4 ;sets CKTXP
; ----set mode for initial startup---- ;
movlf 0x10,mode ;sets mode variable so that initial startup
;up sets the device to dot mode,2g
;******************************************************************************;
; SETUP PERIPHERALS ;
;******************************************************************************;
; ----setup port expander---- ;
; port expander (PE) address stoed in PEADD ;
; register address of DIRA & DIRB stored in PEDIRA & PEDIRB ;
; register address of GPIOA & GPIOB stored in PEDATA & PEDATB ;
movlf PORTEX, i2cadd ;puts port expander address into i2cadd
movlf PEDIRA, regadd ;puts PE direction register for PORTA
;into regadd
movlf 0x00, i2cdat ;data to set PE PORTA as output
call i2ctx ;send this data to port expander
movlf PORTEX, i2cadd ;puts port expander address into i2cadd
movlf PEDIRB, regadd ;puts PE direction register for PORTB
;into regadd
movlf 0x00, i2cdat ;data to set PE PORTB as output
call i2ctx ;send this data to port expander

; ----setup accelerometer---- ;
;accelerometer has seperate read and write addresses ;
;write address(ACCELW) = 0x3A ;
;read address(ACCELR) = 0x3B ;
;there is a controll register and one for each axis: ;
;control register1(ACCON1) = 0x2A ;
;X(ACXDAT) = 0x01 ;
;Y(ACYDAT) = 0x03 ;
;Z(ACZDAT) = 0x05 ;
movlf ACCELW, i2cadd ;puts accelerometer address into i2cadd
movlf ACCON1, regadd ;puts controll register into regadd
movlf 0x07, i2cdat ;configures the accelerometer as
;active, with fast transfer and
;reduced noise turned on (fast transfer
;means only the 8 MSBs of data are
;transmitted not the full 12 bits)
call i2ctx ;send this data to accelerometer

;******************************************************************************;
; MAIN PROGRAM ;
;******************************************************************************;
; ----begining of program---- ;
start btfsc PORTC,RC0 ;if button is not pressed
goto run ;then run main program
btfss PORTC,RC0 ;if button is pressed
goto menu ;run the function to bring up menu

; ----normal opperation---- ;
;this is the code for displaying accelerometer inputs to the user ;
run call getx ;get x axis data from accelerometer
call gety ;get y axis data from accelerometer
call getz ;get z axis data from accelerometer
btfss mode,0 ;if in dot mode
call dot ;convert axis data into dot output
btfsc mode,0 ;if in line emode
call line ;convert axis data into line output

call sendx ;output x data to leds
call sendy ;output y data to leds
call sendz ;output z data to leds

goto start ;return to start

; ----menu mode---- ;
;this code is onley run when the button is pushed and shows the ;
;current modes to the user it then checks if it was tilted over a threshold. ;
;if it was tilted then it trigers a mode change ;
menu movlf 0x00, xout ;set x row led data to all off
; btfsc mode,7 ;this bit check was used for debugging
; goto start ;it has been comented out as it is not needed
btfsc mode,4 ;if in 2g mode
movlf 0x06, yout ;set y to 01100000 (this shows user the 2g mode)
btfsc mode,5 ;if in 4g mode
movlf 0x18, yout ;set y to 00011000 (this shows user the 4g mode)
btfsc mode,6 ;if in 8g mode
movlf 0x60, yout ;set y to 00000110 (this shows user the 8g mode)

btfsc mode,0 ;if in line mode
movlf 0x60, zout ;set z to 01100000 (this shows user "bar" mode)
btfss mode,0 ;if in dot mode
movlf 0x06, zout ;set z to 00000110 (this shows user "dot" mode)
call sendx ;update new x data onto leds
call sendy ;update new x data onto leds
call sendz ;update new x data onto leds

call gety ;get data from y axis to check against threshold
btfsc mode,4 ;depending on how many g the device is curentey
movlw 0x48 ;set to measure the threshold must be adjusted
btfsc mode,5 ;so that it is not harder to cross threshold
movlw 0x68 ;in a lower sensitivity mode.
btfsc mode,6
movlw 0x78
cpfsgt yraw ;y data is checked agains the threshold, if value is
goto ledmode ;less than threshold (tilted forwards), toggle
;led mode

call getx ;get data from x axis
btfsc mode,4 ;adjust lower threshold for diffrent
movlw 0x48 ;sensitivity modes
btfsc mode,5
movlw 0x68
btfsc mode,6
movlw 0x78
cpfsgt xraw ;check if the board has been tilted to the left
goto accdown ;if past threshold change down a mode

btfsc mode,4 ;adjust higher threshold for diffrent
movlw 0xb8 ;sensitivity modes
btfsc mode,5
movlw 0x98
btfsc mode,6
movlw 0x88
cpfslt xraw ;check if the board has been tilted to the right
goto accup ;if past threshold change down a mode

goto start ;return to start

; ----turn sensitivity up---- ;
accdown btfsc PORTC,RC0 ;check button is still pressed
goto run ;if not then run program without making changes
movlf 0x06, xout ;change data on x axis to show threshold
call sendx ;has been passed
call getx ;get new x data
movlw 0x80 ;move central reading to w
cpfsgt xraw ;check if board has returned to level
goto accdown ;if not then repeat
btfsc mode,5 ;if 4g mode set
bsf mode,4 ;set 2g mode
btfsc mode,5 ;if 4g mode set
bcf mode,5 ;clear 4g mode
btfsc mode,6 ;if 8g mode set
bsf mode,5 ;set 4g mode
bcf mode,6 ;clear 8g mode
movlf 0x00, xout ;turn off leds on x axis to show that
call sendx ;action was compleated
goto accchange ;update mode on accelerometer

; ----turn sensitivity down---- ;
accup btfsc PORTC,RC0 ;check button is still pressed
goto run ;if not then run program without making changes
movlf 0x60, xout ;change data on x axis to show threshold
call sendx ;has been passed
call getx ;get new x data
movlw 0x80 ;move central reading to w
cpfslt xraw ;check if board has returned to level
goto accup ;if not then repeat
btfsc mode,5 ;if 4g mode set
bsf mode,6 ;set 8g mode
btfsc mode,5 ;if 4g mode set
bcf mode,5 ;clear 4g mode
btfsc mode,4 ;if 2g mode set
bsf mode,5 ;set 4g mode
bcf mode,4 ;clear 2g mode
movlf 0x00, xout ;turn off leds on x axis to show that
call sendx ;action was compleated
goto accchange ;update mode on accelerometer

; ----reconfigure accelerometer with new settings---- ;
accchange
movlf ACCELW, i2cadd ;configures the accelerometer as
movlf ACCON1, regadd ;standby, this alows acces to change settings
movlf 0x00, i2cdat
call i2ctx ;send data to accelerometer
movlf ACCELW, i2cadd ;changes accelerometer sensitivity
movlf ACCON2, regadd ;
btfsc mode,4 ;set to 2g if mode bit4=1
movlf 0x00, i2cdat ;
btfsc mode,5 ;set to 4g if mode bit5=1
movlf 0x01, i2cdat ;
btfsc mode,6 ;set to 8g if mode bit6=1
movlf 0x02, i2cdat ;
call i2ctx ;send data to accelerometer
movlf ACCELW, i2cadd ;configure the accelerometer as
movlf ACCON1, regadd ;active, fast transfer, and reduced noise
movlf 0x07, i2cdat ;
call i2ctx ;send data to accelerometer
goto start ;return to start once mode has changed

; ----toggle line/dot mode---- ;
ledmode btfsc PORTC,RC0 ;check button is still pressed
goto run ;if not then run program without making changes
movlf 0x18, xout ;change data on x axis to show threshold
call sendx ;has been passed
call gety ;get new y data
movlw 0x80 ;move central reading to w
cpfsgt yraw ;check if board has returned to level
goto ledmode ;if not then repeat
btg mode,0 ;if level then toggle line/dot mode bit
movlf 0x00, xout ;turn off leds on x axis to show that
call sendx ;action was compleated
goto start ;return to start once mode has changed

;******************************************************************************;
; INPUT/OUTPUT FUNCTIONS ;
;******************************************************************************;
; ----get x axis accelerometer data---- ;
getx movlf ACCELW, i2cadd ;load accelerometer address
movlf ACXDAT, regadd ;load x register adress
call i2crx ;request x data
movff i2cdat, xraw ;put recieved value in xraw
btg xraw, 7 ;convert from 2's complement to standard
return

; ----get y axis accelerometer data---- ;
gety movlf ACCELW, i2cadd ;load accelerometer address
movlf ACYDAT, regadd ;load y register adress
call i2crx ;request y data
movff i2cdat, yraw ;put recieved value in xraw
btg yraw, 7 ;convert from 2's complement to standard
return

; ----get z axis accelerometer data---- ;
getz movlf ACCELW, i2cadd ;load accelerometer address
movlf ACZDAT, regadd ;load z register adress
call i2crx ;request z data
movff i2cdat, zraw ;put recieved value in xraw
btg zraw, 7 ;convert from 2's complement to standard
return

; ----output data to port a on port expander---- ;
sendx movlf PORTEX, i2cadd ;load port expander address
movlf PEDATA, regadd ;load porta register adress
movff xout, i2cdat ;load x data to be sent
call i2ctx ;send to port expander
return

; ----output data to port a on port expander---- ;
sendy movlf PORTEX, i2cadd ;load port expander address
movlf PEDATB, regadd ;load portb register adress
movff yout, i2cdat ;load y data to be sent
call i2ctx ;send to port expander
return

; ----output data to shift register---- ;
sendz movf zout,0 ;move zout to w
xorwf lastshift,1 ;check if same as last time called
tstfsz lastshift ;if same skip resend
movff zout, TXREG ;Displays the converted value on
;the SIPO LEDs
movff zout, lastshift ;set current output for check on next call
return

;******************************************************************************;
; I2C FUNCTIONS ;
;******************************************************************************;
; ----wirting data to I2C---- ;
;first generate start condition, send device address with R/W bit clear ;
;send register address, send data, generate stop condition ;
i2ctx bsf SSPCON2, 0 ;generate i2c start condition
call i2c_idle ;wait for status bits to clear
movff i2cadd, SSPBUF ;transfers Device address and R/W bit
call i2c_idle ;wait for status bits to clear
movff regadd, SSPBUF ;transfers register address
call i2c_idle ;wait for status bits to clear
movff i2cdat, SSPBUF ;transmits data to device
call i2c_idle ;wait for status bits to clear
bsf SSPCON2, 2 ;generates stop condition
call i2c_idle ;wait for status bits to clear
return

; ----reciving data via I2C---- ;
;generate start condition, send device address with R/W bit clear, ;
;send register address, generate a repeated start condition, ;
;send device address with R/W bit set, enable recieve mode, ;
;data arrives in SSPBUF, store data in i2cdat, generate stop condition ;
i2crx bsf SSPCON2, 0 ;generates i2c start condition
call i2c_idle ;wait for status bits to clear
movff i2cadd, SSPBUF ;sends device address with R/W
;set to write
call i2c_idle ;wait for status bits to clear
movff regadd, SSPBUF ;sends register address that
;is to be written to
call i2c_idle ;wait for status bits to clear
bsf SSPCON2, 1 ;generates repeated start condition
call i2c_idle ;wait for status bits to clear
bsf i2cadd, 0 ;switches the R/W bit to 1(read)
movff i2cadd, SSPBUF ;Sends device address with R/W bit
;set to read
call i2c_idle ;wait for status bits to clear
bsf SSPCON2, 3 ;enables recieve mode
call i2c_idle ;wait for status bits to clear
movff SSPBUF, i2cdat ;transfers recieved data
;into i2cdat
bsf SSPCON2, 2 ;generates stop condition
call i2c_idle ;wait for status bits to clear
return

; ----I2C idle routine---- ;
;taken from the Microchip Aplication Note "AN735", this codes purpose is to ;
;check all relevent status bits and wait for everything to be clear before ;
;returning ;
i2c_idle
btfsc SSPSTAT,R_W ; transmit in progress?
goto i2c_idle ; module busy so wait
i2c_idle2
movf SSPCON2,w ; get copy of SSPCON2
andlw 0x1F ; mask out non-status
btfss STATUS,Z ; test for zero state
goto i2c_idle2 ; bus is busy test again
return ; return

;******************************************************************************;
; DATA MANIPULATION FUNCTIONS ;
;******************************************************************************;
; ----data flip---- ;
;this function is to reverse data in a varable and is used in line mode ;
;to make data on port expander originate from same side as other outputs ;
;this output makes more sense to user as flipping in hardware woud make dot ;
;mode output seem backwards. ;
flip movff output, convert ;move data to tempory varable
clrf output ;clear output
btfsc convert,0 ;if bit 0 of convert is set
bsf output, 7 ;set bit 7 of output
btfsc convert,1 ;if bit 1 of convert is set
bsf output, 6 ;set bit 6 of output
btfsc convert,2 ;if bit 2 of convert is set
bsf output, 5 ;set bit 5 of output
btfsc convert,3 ;if bit 3 of convert is set
bsf output, 4 ;set bit 4 of output
btfsc convert,4 ;if bit 4 of convert is set
bsf output, 3 ;set bit 3 of output
btfsc convert,5 ;if bit 5 of convert is set
bsf output, 2 ;set bit 2 of output
btfsc convert,6 ;if bit 6 of convert is set
bsf output, 1 ;set bit 1 of output
btfsc convert,7 ;if bit 7 of convert is set
bsf output, 0 ;set bit 0 of output
return ;return

; ----convert all axis data to dot mode output---- ;
;converts data from each axis into user readable "dot mode" data ;
dot movff xraw, convert ;move raw x data into convert
call dot2 ;run conversion to output data
movff output, xout ;move converted x data into x output varable
movff yraw, convert ;move raw y data into convert
call dot2 ;run conversion to output data
movff output, yout ;move converted y data into y output varable
movff zraw, convert ;move raw z data into convert
call dot2 ;run conversion to output data
movff output, zout ;move converted z data into z output varable
return ;return

; ----convertraw data into human readable output (dot mode)---- ;
;devides data range into diffrent 16bit data ranges. works out which range ;
;the raw value fits into and outputs the coresponding patern to the user. ;
;the first and last data ranges are 8bits larger leaving 15 diffrent states, ;
;the center value of the input then falls in the middle of the 8th output range;
dot2 clrf output ;Make sure output is 0

movlf 0x01, output ;set output patern to 00000001
movlw 0x18 ;set boundary
cpfsgt convert ;if data is within this range then return
return ;if not then contine to check next range
movlf 0x03, output ;set output patern to 00000011
movlw 0x28 ;reset a higher boundary
cpfsgt convert ;check if data is withn range again
return ;if not then contine to check next range
movlf 0x02, output ;set output patern to 00000010
movlw 0x38 ;contine process untill all data
cpfsgt convert ;ranges have been checked
return
movlf 0x06, output ;set output patern to 00000110
movlw 0x48
cpfsgt convert
return
movlf 0x04, output ;set output patern to 00000100
movlw 0x58
cpfsgt convert
return
movlf 0x0c, output ;set output patern to 00001100
movlw 0x68
cpfsgt convert
return
movlf 0x08, output ;set output patern to 00001000
movlw 0x78
cpfsgt convert
return
movlf 0x18, output ;set output patern to 00011000
movlw 0x88
cpfsgt convert
return
movlf 0x10, output ;set output patern to 00010000
movlw 0x98
cpfsgt convert
return
movlf 0x30, output ;set output patern to 00110000
movlw 0xa8
cpfsgt convert
return
movlf 0x20, output ;set output patern to 00100000
movlw 0xb8
cpfsgt convert
return
movlf 0x60, output ;set output patern to 01100000
movlw 0xc8
cpfsgt convert
return
movlf 0x40, output ;set output patern to 01000000
movlw 0xd8
cpfsgt convert
return
movlf 0xc0, output ;set output patern to 11000000
movlw 0xe8
cpfsgt convert
return
;if none of above data must be
;within last boundry
movlf 0x80, output ;set output patern to 10000000
return

; ----convert all axis data to line mode output---- ;
;converts data from each axis into user readable "line mode" data ;
line movff xraw, convert ;move raw x data into convert
call line2 ;run conversion to output data
movff output, xout ;move converted x data into x output varable
movff yraw, convert ;move raw y data into convert
call line2 ;run conversion to output data
movff output, yout ;move converted y data into y output varable
movff zraw, convert ;move raw z data into convert
call line2 ;run conversion to output data
call flip ;flip zaxis data for corect orientation
movff output, zout ;move converted z data into z output varable
return

; ----convertraw data into human readable output (line mode)---- ;
;devides data range into diffrent 16bit data ranges. works out which range ;
;the raw value fits into and outputs the coresponding patern to the user. ;
;the first and last data ranges are 8bits smaller leaving 17 diffrent states, ;
;the center value of the input then falls in the middle of the 9th output range;
;this alows for a deadzone where no leds are on with 8 states above and 8 below;
;the process works the same as the "dot2" function however has diffrent ;
;output patterns ;
line2 clrf output ;Make sure output is 0
movlf 0xff, output ;set output patern to 11111111
movlw 0x08
cpfsgt convert
return
movlf 0x7f, output ;set output patern to 11111110
movlw 0x18
cpfsgt convert
return
movlf 0x3f, output ;set output patern to 11111100
movlw 0x28
cpfsgt convert
return
movlf 0x1f, output ;set output patern to 11111000
movlw 0x38
cpfsgt convert
return
movlf 0x0f, output ;set output patern to 11110000
movlw 0x48
cpfsgt convert
return
movlf 0x07, output ;set output patern to 11100000
movlw 0x58
cpfsgt convert
return
movlf 0x03, output ;set output patern to 11000000
movlw 0x68
cpfsgt convert
return
movlf 0x01, output ;set output patern to 10000000
movlw 0x78
cpfsgt convert
return
movlf 0x00, output ;set output patern to 00000000
movlw 0x88
cpfsgt convert
return
movlf 0x01, output ;set output patern to 10000000
movlw 0x98
cpfsgt convert
return
movlf 0x03, output ;set output patern to 11000000
movlw 0xa8
cpfsgt convert
return
movlf 0x07, output ;set output patern to 11100000
movlw 0xb8
cpfsgt convert
return
movlf 0x0f, output ;set output patern to 11110000
movlw 0xc8
cpfsgt convert
return
movlf 0x1f, output ;set output patern to 11111000
movlw 0xd8
cpfsgt convert
return
movlf 0x3f, output ;set output patern to 11111100
movlw 0xe8
cpfsgt convert
return
movlf 0x7f, output ;set output patern to 11111110
movlw 0xf8
cpfsgt convert
return
movlf 0xff, output ;set output patern to 11111111
return

end

Leave a comment