bma180 accelerometer with I2C
I finally got a quickstart board and need to quickly get started on what I thought would be a simple project of connecting this breakout board I got from sparkfun.
I have dug and dug for any projects with sample code I can look at using this thing and can't find a thing. I think that quad copters since they use propellers is clouding the search engines - lol
This is such a great sensor and has been out so long, surely someone has done something with it. I started an object file with all the registers and values and have been trying to modify the c code that is on the sparkfun site, but I guess the jump to spin is too much for me.
I have been at it almost every evening for almost 2 weeks and am getting nowhere. Help?
I have dug and dug for any projects with sample code I can look at using this thing and can't find a thing. I think that quad copters since they use propellers is clouding the search engines - lol
This is such a great sensor and has been out so long, surely someone has done something with it. I started an object file with all the registers and values and have been trying to modify the c code that is on the sparkfun site, but I guess the jump to spin is too much for me.
I have been at it almost every evening for almost 2 weeks and am getting nowhere. Help?

Comments
To read from a BMA180 register if the BMA180 SCL is <SCL pin> and SDA is on the next higher I/O pin do:
Z := i2c.ReadByte(<SCL pin>, <device>, i2c#OneAddr | <register>)
To write to a BMA180 register do:
i2c.WriteByte(<SCL pin>, <device>, i2c#OneAddr | <register>, <data to write>)
The <device> code is the 8-bit byte that selects the device. It's the device address (a 7-bit number) shifted left one bit to make the byte sent to the device. The least significant bit of this byte is always zero for these routines. The BMA180 allows for two different device addresses depending on the setting of one of the device's pins. See the datasheet for details.
Similarly, you can read or write multiple bytes from / to successive BMA180 registers. See the comments in Simple_I2C_Driver for details.
I just spent $60 bucks on ink to finally print out the datasheet. I have started with the Basic_i2c_driver and created my own object file containing all the registers for the bma180.
I was confused at first thinking that I had to use the prop's SCL and SDA. I am believing now that this is bit-bang and I can use whatever pins I want. I guess my biggest confusion comes from the examples that were put together by James Burrows (I think). The i2c object is initialized and then the object/device specific routines are initialized and used. They are so device specific, it is daunting as a first propeller project.
Anyways, I am pushing on and will post some code tonight hopefully. Your examples should help a lot. I am excited about the things I see with the propeller. Last year I built a machine with an arduino mega that drove me so crazy with the interrupt game that I almost gave up. I think once this little accelerometer starts doing what I need, I may have a mega for sale on ebay.
This is where I am at for the moment. The "i2c#bma180address | BMA_CHIPID" is not making sense and is just me guessing. Maybe I am over analyzing.
Should it be i2c.ReadByte(i2cscl, bma180address, BMA_CHIPID) ? I see that the interpreter wants a constant if I do it the old way.
Thanks again for any help
A piece of my code for one axis is as follows:
VAR
long id
word bmversion, temp, temp1, temp2, temp3, temp4, temptemp
long tempX, tempY, tempZ
PUB getX
repeat while temp <> 1
temp := i2cObject.readLocation(bmaddress, ACCXLSB) & $1
temp := i2cObject.readLocation(bmaddress, ACCXMSB)
temp2 := temp << 8
temp2 |= i2cObject.readLocation(bmaddress, ACCXLSB)
tempX := temp2 >> 2
temp := 0
I guess I should convert to signed decimal? Are the <> and other bitwise methods I am calling the right ones? Oh yeah, how about degrees?
VAR long id byte temp, i word bmversion, temp1, temp2, temp3, temp4, temptemp word tempX, tempY, tempZ PUB getX repeat while temp <> 1 ''temp is a byte, temp2 and tempX are words temp := i2cObject.readLocation(bmaddress, ACCXLSB) & $1 ''check data ready bit temp := i2cObject.readLocation(bmaddress, ACCXMSB) temp2 := temp << 8 ''move MSB to position temp2 |= i2cObject.readLocation(bmaddress, ACCXLSB) tempX := temp2I decided to not right shift, leaving a couple of flag bits in the word. It is really inconsequential if I can get the negative numbers (I would rather work with 14 bits). As it is, I only get positive.
Ser.str(String("X: ")) Ser.dec(tempX)I tried setting tempX to $FF80 which should be negative 128. Still wont show negative. I printed out my words in binary and the sign bits are set.
I am playing around with numbers.spin, but not getting anywhere. Help?
Two easy ways to fix this. Use the "~~variable" 16-bit sign extension operator. (i.e. "tempX := ~~temp2" ) Or, to sign extend any word width, use the shift left and shift arithmetic right operators. (i.e. "tempX := (temp2 <<16) ~> 16" )
Lawson
OBJ i2cObject : "i2cObject" Ser : "Parallax Serial Terminal" numb : "Numbers" f32 : "F32" fstring : "FloatString" CON _clkmode = xtal1 + pll16x 'Standard clock mode * crystal frequency = 80 MHz _xinfreq = 5_000_000 CRC = $5B OFFSETZ = $3A OFFSETY = $39 OFFSETX = $38 OFFSETT = $37 O7LSB2 = $36 OLSB1 = $35 GAINZ = $34 GAINY = $33 GAINX = $32 GAINT = $31 TCOZ = $30 TCOY = $2F TCOX = $2E CD2 = $2D CD1 = $2C SLOPETH = $2B HIGHTH = $2A LOWTH = $29 TAPSENSTH = $28 HIGHDUR = $27 LOWDUR = $26 HIGHLOWINFO = $25 SLOPETAPSENSINFO = $24 HY = $23 CTRLREG4 = $22 CTRLREG3 = $21 BWTCS = $20 RESET = $10 CTRLREG2 = $0F CTRLREG1 = $0E CTRLREG0 = $0D STATUSREG4 = $0C STATUSREG3 = $0B STATUSREG2 = $0A STATUSREG1 = $09 TEMPERATURE = $08 ACCZMSB = $07 ACCZLSB = $06 ACCYMSB = $05 ACCYLSB = $04 ACCXMSB = $03 ACCXLSB = $02 VERSION = $01 CHIPID = $00 address = $80 ''Range setting RANGESHIFT = 1 RANGEMASK =$0E BWMASK = $F0 BWSHIFT = 4 i2cSCLLine = 24 i2cSDALine = 25 bmaddress = 00_0000 interrupt_pin = 0 ''for when I figure out how to do that led = 23 VAR long id, temp long stacks[20] byte tempxlsb, tempxmsb, tempylsb, tempymsb, tempzlsb, tempzmsb, temptemp, temp1 long avg_x, avg_y, avg_z, lastx, lasty, lastz word temp2, index long bmversion, pitch[numreadings], roll[numreadings], theta[numreadings] long tempY, tempZ, alarmcnt, noalarm long tempX, diff, devy, devz long readingsx[numreadings], readingsy[numreadings] long readingsz[numreadings], devx, totalx, totaly, totalz, setp, setr, sett byte cogidnum CON hyst = 180 numreadings = 20 ''stabilize = 100 lsb = 0.00019 PUB start dira[led] := 1 cognew(alarmcheck, @stacks) main PUB main 'f32.start dira[interrupt_pin]~ Numb.init ser.Start(115_200) waitcnt(clkfreq/10 + cnt) 'wait for things to settle i2cObject.init(i2cSDALine,i2cSCLLine) 'pin numbers of the sda and scl lines to i2cobject init($0, $0) waitcnt(clkfreq/10 + cnt) 'wait for things to settle startup ''get set points ''if ina[interrupt_pin] ''8192 counts at 1g 4096 at 2g ''outa[led] := 1 ''alarmcheck index := 0 repeat if index => numreadings ''debug index := 0 ReadTemp ''getXYZ ''temp := 0 repeat while temp <> 1 ''temp is a byte, temp2 and tempX are words temp := i2cObject.readLocation(bmaddress, ACCXLSB) & $1 ''check data ready bit tempxmsb := i2cObject.readLocation(bmaddress, ACCXMSB) temp2 := tempxmsb << 8 ''move MSB to position temp2 |= i2cObject.readLocation(bmaddress, ACCXLSB) temp2 := temp2 >> 2 if tempxmsb > $7F tempX := $FFFFC000 tempX |= temp2 ''knock out data ready bits temp := 0 ''getY repeat while temp <> 1 ''temp is a byte, temp2 and tempX are words temp := i2cObject.readLocation(bmaddress, ACCYLSB) & $1 ''check data ready bit tempymsb := i2cObject.readLocation(bmaddress, ACCYMSB) temp2 := tempymsb << 8 ''move MSB to position temp2 |= i2cObject.readLocation(bmaddress, ACCYLSB) temp2 := temp2 >> 2 if tempymsb > $7F tempY := $FFFFC000 tempY |= temp2 ''knock out data ready bits temp := 0 '' get z repeat while temp <> 1 ''temp is a byte, temp2 and tempX are words temp := i2cObject.readLocation(bmaddress, ACCZLSB) & $1 ''check data ready bit tempzmsb := i2cObject.readLocation(bmaddress, ACCZMSB) temp2 := tempzmsb << 8 ''move MSB to position temp2 |= i2cObject.readLocation(bmaddress, ACCZLSB) temp2 := temp2 >> 2 if tempzmsb > $7F tempZ := $FFFFC000 tempZ |= temp2 ''knock out data ready bits 'ser.clear 'ser.dec(tempz) temp := 0 ''gettotals totalx := totalx - readingsx[index] ''subtract previous reading totaly := totaly - readingsy[index] totalz := totalz - readingsz[index] readingsx[index] := tempX ''put new reading in array readingsy[index] := tempY readingsz[index] := tempZ totalx += readingsx[index] ''add newest readings to totals totaly += readingsy[index] totalz += readingsz[index] 'ser.newline 'ser.dec(tempZ) ''getAvg avg_x := totalx / numreadings - 1 avg_y := totaly / numreadings - 1 avg_z := totalz / numreadings - 1 ''get averages before wiping them and make them absolute 'lastx := avg_x 'lasty := avg_y 'lastz := avg_z f32.start tempx := f32.ffloat(avg_x) 'make true floats out of our variables using averages devx := f32.fmul(lsb,tempx) tempy := f32.ffloat(avg_y) devy := f32.fmul(lsb,tempy) tempz := f32.ffloat(avg_z) devz := f32.fmul(lsb,tempz) ''calculate pitch roll and yaw - pitch - arctan(ax/sqrt(ay^2+az^2)) tempx := f32.fmul(devy,devy) tempy := f32.fmul(devz,devz) tempx := f32.fadd(tempx, tempy) tempx := f32.fsqr(tempx) tempx := f32.fdiv(devx, tempx) pitch[index] := f32.atan(tempx) pitch[index] := f32.degrees(devx) ''end of pitch ''roll arctan(ay/sqrt(ax^2+az^2) tempy := f32.fmul(devx,devx) tempx := f32.fmul(devz,devz) tempy := f32.fadd(tempx, tempy) tempy := f32.fsqr(tempy) tempy := f32.fdiv(devy, tempy) roll[index] := f32.atan(tempy) roll[index]:= f32.degrees(devy) ''end of roll '' theta - arctan(sqrt(ax^2+ay^2)/az) tempx := f32.fmul(devx, devx) tempy := f32.fmul(devy, devy) tempz := f32.fadd(tempx, tempy) tempz := f32.fdiv(tempz, devz) theta[index] := f32.fsqr(tempz) theta[index] := f32.atan(theta[index]) theta[index] := f32.degrees(theta[index]) f32.stop 'outa[led] := 0 ''alarmcheck showall 'debug index := index + 1 tempX := 0 tempY := 0 tempZ := 0 PUB debug repeat index from 0 to numreadings - 1 Ser.str(String("X : ")) Ser.dec(readingsx[index]) Ser.str(String(" Y : ")) Ser.dec(readingsy[index]) Ser.str(String(" Z : ")) Ser.dec(readingsz[index]) Ser.str(String(" ID : ")) Ser.dec(index) Ser.newline PUB alarmcheck 'pitch roll and theta are arrays, you can compare throughout 'trying to run this in its own cog repeat outa[led] := 1 waitcnt(clkfreq + cnt) ''set bw outa[led] := 0 waitcnt(clkfreq + cnt) ''set bw PUB init(range, bw) ''init bma180 ''if(i2cObject.readLocation(bmaddress,CHIPID) <> 3) '' return -1 '' set ee_w bit temp := i2cObject.readLocation(bmaddress, CTRLREG0) temp |= $10 i2cObject.writeLocation(bmaddress, CTRLREG0, temp) waitcnt(clkfreq/10 + cnt) ''set bw temp := i2cObject.readLocation(bmaddress, BWTCS) temp1 := bw temp1 := (temp1<<4) temp &= (!BWMASK) temp |= temp1 i2cObject.writeLocation(bmaddress, BWTCS, temp) ''write new data range, leave other bits the same waitcnt(clkfreq/10 + cnt) ''set range temp := i2cObject.readLocation(bmaddress, OLSB1) temp1 := range temp1 := (temp1<<RANGESHIFT) temp &= (!RANGEMASK) temp |= temp1 i2cObject.writeLocation(bmaddress, OLSB1, temp) ''write new data range, leave other bits the same waitcnt(clkfreq/10 + cnt) '' turn on slope int temp := i2cObject.readLocation(bmaddress, CTRLREG3) temp |= $44 i2cObject.writeLocation(bmaddress, CTRLREG3, temp) ''i2cObject.writeLocation(bmaddress, SLOPETH, $FF) ''100 threshold ''i2cObject.writeLocation(bmaddress, SLOPETAPSENSINFO, $F0) ''XYZ WILL TRIGGER ''temp := i2cObject.readLocation(bmaddress, TCOX) ''temp |= $3 ''i2cObject.writeLocation(bmaddress, TCOX, temp) ''slope dur set to 7 readings waitcnt(clkfreq/10 + cnt) return 0 PUB ReadTemp temp := i2cObject.readLocation(bmaddress, TEMPERATURE) temptemp := (temp/2+15)*9/5+32 PUB showall Ser.clear Ser.str(String(" X: ")) Ser.str(fstring.floattostring(pitch[index])) Ser.str(String(" ")) Ser.str(String("Y: ")) Ser.str(fstring.floattostring(roll[index])) Ser.str(String(" ")) Ser.str(String("Z: ")) Ser.str(fstring.floattostring(theta[index])) 'Ser.str(String(" Temp:" )) 'Ser.dec(temptemp) PUB setAlarm PUB startup ''take count or do x number of loops ''wait a while to let things stabilize. put it is a variable to allow pin entry post stabilization index := 0 repeat index from 0 to numreadings - 1 Ser.str(String(" init ")) readingsx[index] := 0 readingsy[index] := 0 readingsz[index] := 0 pitch[index] := 0 roll[index] := 0 theta[index] :=0I have followed the examples from the manual and I cannot run alarmcheck and main at the same time. Sorry about all the extra comments and probably a lack of good comments.Thank you. Your profile pic leads me to think you are a tesla fan. Am I correct? I am in Victoria just south of you.
did you ever get your spin code working for the BMA180? I just ordered one and it'd be great if I could use your code to take some readings!