Why is that? Aren't they connected to ground anyway? Why do they need to be connected to the fill directly? This is a genuine question - I'm a software person and only have a superficial understanding of electronics. Isn't ground ground? Or is there a problem with long(ish) traces (or wires) to ground?
Yes, you should minimise the length of the ground traces. Preferably they go straight to a ground plane. However, analog have their own requirements too.
Yes, you should minimise the length of the ground traces. Preferably they go straight to a ground plane. However, analog have their own requirements too.
How is a non-hardware person supposed to cope?! (With the help of these forums...). So... how big is a trace before it's a plane...?
How is a non-hardware person supposed to cope?! (With the help of these forums...). So... how big is a trace before it's a plane...?
The idea is to minimize the impedance both to DC and to higher frequency. Connecting to a "ground plane" through vias is fine although a direct connection on the same side that's also shorter (and better) is much preferred. That is why I wondered why this large center "pad" was not utilized in this manner. In fact if it were then there would be less reliance on the bypass caps and the crystal could, even a large one, move closer to the Prop. The crystal input pin is a very sensitive amplifier which can easily pick up electrical noise, even from other signals on the PCB and the clock signal that is generated from the crystal is vital to the reliable operation of the Prop, everything derives from this one clock.
A trace is never a plane. A plane is normally a large mostly unbroken area that runs under multiple pads and devices and is also characterized by it's low impedance and shielding effect (and multiple via connections).
> A trace is never a plane. A plane is normally a large mostly unbroken area that runs under multiple pads and devices.
Right, but... is a trace (say) 2mm wide running the full length of a 200mm board worse than a pad 20mm square? Each is 400mm^2. What if the trace is 6mm wide? Or 10mm? (In other words, is it then a plane and not a trace?) Is the surface area important, or the length of the run? And then the next question would be if it's the length of the run then what about a pad which is the entire under-side of the (200mm) board? Same length as the trace? So is it a combination of surface area and length?
> A trace is never a plane. A plane is normally a large mostly unbroken area that runs under multiple pads and devices.
Right, but... is a trace (say) 2mm wide running the full length of a 200mm board worse than a pad 20mm square? Each is 400mm^2. What if the trace is 6mm wide? Or 10mm? (In other words, is it then a plane and not a trace?) Is the surface area important, or the length of the run? And then the next question would be if it's the length of the run then what about a pad which is the entire under-side of the (200mm) board? Same length as the trace? So is it a combination of surface area and length?
Maybe I need to pick up a book...
Some reading material wouldn't hurt, there is quite a bit of science to it but it is called artwork after all, there is an art to it. A long trace will have more inductance than a square pad of the same area.
Zlantz, a suggestion for you. Your thread has become rather confusing to follow.
I am pretty confident that it is in the software. My MPU-6050 works with a PASM i2c Driver 100%, but it dont work with the spin i2c driver.
.
If a device works perfectly with PASM, then you should not be spending time trying to chase down PCB design issues. Since most people will not invest a lot of time in decrypting all the conclusions you have made, you may want to restate exactly where you are at with this. For example, if you have a driver working in PASM, why are you still trying to find a SPIN driver? Also, you are mentioning multiple devices over several threads, and I have no idea which device is the problem at this point. Maybe you should focus on a single device, target just one register on that one device. You may want to contact the module's tech support to find out what the results should mean, and get some suggestions on how to know what you should be seeing. Maybe they have a forum for the module? You really need to have a clear idea of what you are looking for in the data, and some method to know if you are close or far away with the data you are getting. It has been mentioned to you that in the screenshot you posted, the example code shows that part of the replies are signed values, some are not. If it is true that you need to be working with signed values, then you must get an understanding of how to convert the unsigned 16 bit response ( AA MSB and AB LSB) into an integer format that is signed. This was discussed using the bitshift left, shift arithmetic right etc. You should study the Prop manual on bitshift left and SAR shift arithmetic right to understand what the examples mean and how to apply them yourself. It is very easy to understand frustration with this. With the Propeller, you have four choices as a business owner; learn the code, hire a consultant, hire a programmer, post on the forum for help, or some combination. Hiring a Propeller programmer is not easy. Hiring a consultant is not easy, even if you have the money, a real firm may not care about a small one off gadget. That means, you are stuck with the forum as your resource. Working in the forum requires some skill and patience. It also requires focus on small bite size issues, not giant multi device, multi problem discussions at one go.
Do you have a hardware problem, or software problem, or BOTH? In your case, it is hard to tell because the info you have state seems contradictory. As a friendly suggestion, as stated earlier as well, you are trying to tackle too much at one time. Take one device, learn about one register and how to access that register, convert it to what it needs to be in the final output, find out what you should be seeing and what it means so that you know if the data is even close. If you are 100% confident in the code, then you can address hardware as a problem. But, don't chase hardware when you haven't ruled out the software, especially since ( according do you) you have a PASM driver working. As discussed, you have a digital and analog aspect to the modules. Any I2C driver should get an ack or device ID returned. The first thing is to know that you are talking to the chip on the digital side, which is far more forgiving as far as PCB concerns.
The Propeller I runs at 80MHz internally, faster if overclocked. The I/O pins switch on and off in maybe 1-2ns which has significant components at 1GHz. At these frequencies, wires don't behave like wires anymore. Most of the energy travels on the surface and sharp bends can create very significant impedance. The time it takes for a signal to travel a few inches can represent a major delay. A lot of the behavior of these fast signals is not intuitive, so reading up on the subject is very helpful.
Part of the issue with the multiple power and ground connections on the Propeller and other large chips is that the wiring inside the chip is very small ... thin layers of aluminum deposited in thin traces on the surface of the chip and connected sometimes via polysilicon traces and vias in lower layers in the chip. These have much more resistance than wires and copper traces on the PCB, but, since the distances are very short and the amount of current small usually, this is all very tolerable. The problem you get into leaving out power connections is that the fraction of the current expected to be supplied for the portion of the chip near the pins now has to travel from other power pins across the surface of the chip. There can be significant voltage drops across the chip at peak currents and, in some circumstances, these can be enough to damage small areas of the chip when an input signal "pegged" to one supply source comes into a chip area powered by another supply source and the input signal is much higher than the supply voltage.
Zlantz, a suggestion for you. Your thread has become rather confusing to follow.
Thank you for being understanding, I am not a master at the forums.
I would like to have a Spin driver that works because I understand Spin.
I have a PASM driver that works, but Only for the MPU-6050 (the i2c driver is built into the mpu-6050 driver), and I dont understand PASM.
I have multiple i2c devices that I have tried with each i2c driver, each one at a time.
I tried multiple because of mixed results (none / random values / "looks" correct values / correct values) with various drivers (spin/pasm).
There are 2 important sensors I need working. The BMP-180 and the MPU-9150.
The BMP-180 gives results that "appear" correct, but are not. And associated math doesn't work. I did contact tech support for this device & they sent me some c++ source code for the device.
The MPU-9150's Gyro, Accel & Temperature values can be obtained with the MPU-6050 Driver (PASM), but the Magnetometer cannot be read.
The MPU-6050 Only works with its PASM driver.
Each sensor has a register to read to "verify" that your communications are working properly, typically a Device ID Register that is hard coded to the device. The BMP's ID is 0x55. Which I am obtaining, but the raw pressure & temperature readings dont correlate to what the sensor is detecting.
How does spin handle signed & unsigned ints & longs? All it has is byte, word, and long for Var's.
I have made an attempt at modding the PASM 6050 driver to get the Mag Value, and to read the BMP via Aux Bus. But like I said, I really dont know nor understand PASM one bit.
I also have a HMC5883, that potentially I could use via the MPU-6050's Aux Bus. I have tested it with its spin driver, which gives good results, but the Z axis dont work. And that driver is totally different than all the rest. Making this insanely confusing.
As advertised in datasheets, an external Compass (6050 datasheet) or Perssure Sensor (9150 datasheet) can be connected via the Aux Bus and either read via the MPU's Master i2c controler, or be put into Pass-through mode and read directly by the mcu via its scl/sda lines. You just set a few Config Registers to make the switch.
These are the reasons I feel it is a Software issue.
If I had a 100% Spin Driver that worked 100%, then I could easily write drivers for these sensors.
When I first started out with the prop I would only connect One +3.3v & One Gnd and it worked 100% fine, with all of my non i2c systems. I have since been trying to connect all of them, but with layout concerns, getting that last one in there has been a bit of an issue. I will fix this from now on. My apologies, I am more of a software guy than hardware, so I know I will overlook & entirely miss stuff along the way.
I think I am starting to understand bit shifting, but then I am confused because the Examples show it just shifts bits around: 10001101 << 3 becomes 00110110. But I have come to understand that in propeller, it shifts them over: 10001101 << 8 becomes 10001101_00000000. So which one is it? I am really confused about this one & when I search I find both are correct at the same time.
Studying a BMP.c & MPU.c files, and in them I have seen in to take 3 readings & combine in one value would be like this:
A = ReadVal << 8
A += ReadVal << 16
A += ReadVal
But I have been suggested to do it this way (for the BMP180):
A = i2c.readregister << 8
A |= i2c.readregister
Which gives the apparently correct results. Except when i compare it against i2c.readregister16 the results are very close, but totally different.
Bit Shifting has got me a bit shifted.
I know it seems as tho I am jumping all over the place, which I kinda am. I'll take a break from one device to go work on another. I have created test files for each of them, so it is easy to jump around. Most of my "jumping" time was from spin to pasm. I spent quite a few hours last night trying to get the pasm 6050 driver to read the magnetometer threw the aux bus, with no luck. I changed a large chunk of it around & the Accel/Gyro/Temp still worked right so I would assume I am getting values but cannot retrieve them from the pasm section to the spin section, or I am retreiving right but getting no values. Grasping the conscept of how to pass data is a bit difficult. I identically added to the original & everything I added did nothing, with the exception of making it possible to user set config settings on startup (Accel Range, Gyro Range, DLPF, etc).
Bitshifting is moving bits left or right by some number. If you move a set of bits, what is replacing the space is a zero. Any time you want to learn a new instruction, it is very easy to do so using PST. I would suggest you create a testing.spin file for nothing but compiling a list of instructions and the various tests you do to understand them, and include comments for future reference.
pst.bin(%0000_1111 << 8, 16) 'this will show the effect of moving the value %00001111 to the left 8 bits, 16 represents how many binary positions to display.
Experiment with bytes, words, long values, bitshifting left, right, etc, to see what you get. At some point there is no more ambiguity. You KNOW what it does and never think about it again. Put every misunderstood instruction to the test. You cannot begin to write a driver without the basic fundamentals. So, as the need arises to manipulate the data, take that as an opportunity to learn the fundamentals each step of the way. With the I2C devices I am familiar with, they all shift out data in chucks of 8 bits * a byte *. Even if the data is to represent a word or long value, it still shifts out the data in bytes. So you have seen that you must shift out the AA value and the AB value one at a time. Then you reconstruct the original 16 bi value(word) by bitshifting the MSB( most significant byte) to the left by 8 to put it where it belongs in the word. Then you either add the LSB or OR it to the value, so now you have the original value that started in the I2C device. If there is a requirement to use a signed value, then you have to understand are you dealing with a signed 16 bit value, or long value. The prop is a 32 bit device, so the sign must live in the most significant bit position( the most far left bit position) bit 31. Your device outputs a 16 bit signed value. So you must read the MSB byte, then bit shift it left by 24 bits to put the SIGN bit in the correct position in the WORD. Then, you must put the other math back where it belongs by shifting the arithmetic to the right by 16. Then you OR the LSB from the device. Now, you have the sign bit in tact in the bit 31 position in the long. Now, I do not know your device, only going by the example you posted. It may well be that you do NOT need to worry about maintaining the sign for your uses, that is something you must determine. But, the bottom line is, these are basic fundamentals and building blocks of drivers and must be understood, just like adding, subtracting, multiplying etc, there are a few basics you must understand for bit manipulation. If you spin driver is not working, but a preconfigured PASM object does work, then you have something wrong in the SPIN code related to the register addressing or device address, including the correct use of W/R in the LSB of the address. It is unlikely that the device has a minimum I2C clock speed, and even it if does, you should find a minimum I2C clock speed in the datasheet. A minimali2cdriver code would look something like this;
PUB ReadKP
i2c2.i2cStart(i2cSCL2) 'start the transaction
i2c2.i2cWrite(i2cSCL2, KPAaddrW) '
i2c2.i2cWrite(i2cSCL2, 1) '1 = 0-7 bits start read address
i2c2.i2cstop(i2cSCL2)
i2c2.i2cStart(i2cSCL2)
i2c2.i2cWrite(i2cSCL2, KPAaddrR) 'start the read
readkeyvar := i2c2.i2cRead(i2cSCL2, 0) 'read first bit field 0 - 7
readkeyvar |= i2c2.i2cRead(i2cSCL2, 0) << 8 'read second bit field 8 - 15
readkeyvar[1] := i2c2.i2cRead(i2cSCL2, 1) 'read last unused bits 9 - 24 24 bit total read out
i2c2.i2cStop(i2cSCL2)
key := >| readkeyvar 'encode >| bitfield tonumber
'beep(19, 2000,30) 'method to debug using piezo
'go(0,0) 'lcd debug
'ser.bin(3, readkeyvar , 16)
'go(0,0)
'ser.decf(3, readkeyvar, 2)
if readkeyvar == $FFFF
{use this to insure that if the KP fails or is unplugged
that the program does not lock since I2C will be showing $FFFF}
return 0
return readkeyvar
Using PST, you can start to SEE what is happening versus guessing what is going on.
I use this on most all i2c devices. Once you get a driver working, unless you need more speed or special features, most new devices require just a little copy and paste from other devices.
Thank you for being understanding, I am not a master at the forums.
I would like to have a Spin driver that works because I understand Spin.
I have a PASM driver that works, but Only for the MPU-6050 (the i2c driver is built into the mpu-6050 driver), and I dont understand PASM.
I have multiple i2c devices that I have tried with each i2c driver, each one at a time.
I tried multiple because of mixed results (none / random values / "looks" correct values / correct values) with various drivers (spin/pasm).
There are 2 important sensors I need working. The BMP-180 and the MPU-9150.
The BMP-180 gives results that "appear" correct, but are not. And associated math doesn't work. I did contact tech support for this device & they sent me some c++ source code for the device.
The MPU-9150's Gyro, Accel & Temperature values can be obtained with the MPU-6050 Driver (PASM), but the Magnetometer cannot be read.
The MPU-6050 Only works with its PASM driver.
Each sensor has a register to read to "verify" that your communications are working properly, typically a Device ID Register that is hard coded to the device. The BMP's ID is 0x55. Which I am obtaining, but the raw pressure & temperature readings dont correlate to what the sensor is detecting.
How does spin handle signed & unsigned ints & longs? All it has is byte, word, and long for Var's.
I have made an attempt at modding the PASM 6050 driver to get the Mag Value, and to read the BMP via Aux Bus. But like I said, I really dont know nor understand PASM one bit.
I also have a HMC5883, that potentially I could use via the MPU-6050's Aux Bus. I have tested it with its spin driver, which gives good results, but the Z axis dont work. And that driver is totally different than all the rest. Making this insanely confusing.
As advertised in datasheets, an external Compass (6050 datasheet) or Perssure Sensor (9150 datasheet) can be connected via the Aux Bus and either read via the MPU's Master i2c controler, or be put into Pass-through mode and read directly by the mcu via its scl/sda lines. You just set a few Config Registers to make the switch.
These are the reasons I feel it is a Software issue.
If I had a 100% Spin Driver that worked 100%, then I could easily write drivers for these sensors.
When I first started out with the prop I would only connect One +3.3v & One Gnd and it worked 100% fine, with all of my non i2c systems. I have since been trying to connect all of them, but with layout concerns, getting that last one in there has been a bit of an issue. I will fix this from now on. My apologies, I am more of a software guy than hardware, so I know I will overlook & entirely miss stuff along the way.
I think I am starting to understand bit shifting, but then I am confused because the Examples show it just shifts bits around: 10001101 << 3 becomes 00110110. But I have come to understand that in propeller, it shifts them over: 10001101 << 8 becomes 10001101_00000000. So which one is it? I am really confused about this one & when I search I find both are correct at the same time.
Studying a BMP.c & MPU.c files, and in them I have seen in to take 3 readings & combine in one value would be like this:
A = ReadVal << 8
A += ReadVal << 16
A += ReadVal
But I have been suggested to do it this way (for the BMP180):
A = i2c.readregister << 8
A |= i2c.readregister
Which gives the apparently correct results. Except when i compare it against i2c.readregister16 the results are very close, but totally different.
Bit Shifting has got me a bit shifted.
I know it seems as tho I am jumping all over the place, which I kinda am. I'll take a break from one device to go work on another. I have created test files for each of them, so it is easy to jump around. Most of my "jumping" time was from spin to pasm. I spent a few hours last night trying to get the pasm 6050 driver to read the magnetometer threw the aux bus, with no luck. I changed a large chunk of it around & the Accel/Gyro/Temp still worked right so I would assume I am getting values but cannot retrieve them from the pasm section to the spin section, or I am retreiving right but getting no values. Grasping the conscept of how to pass data is a bit difficult. I identically added to the original & everything I added did nothing, with the exception of making it possible to user set config settings on startup (Accel Range, Gyro Range, DLPF, etc).
While Peter has suggested this I will try too. If you load Tachyon on the your board you will have a command line FORTH enviroment to FROB the chips with. You can try many different approaches and not have to compile->link->download->bring up the serial terminal between each attempt which can be really tiresome after say the 20th attempt. You don't have to create your "final" code in Tachyon / FORTH but for debugging I have found this environment to be absolutely required for figuring out chips that just don't play well by the "standard".
Here is a quick one liner of what is possible with Tachyon from the command line from the Intro Doc.
This one line in Tachyon issues an I2C start command, sends the device's WR address, sends the upper and lower byte of the address to be read from in this case,
sends another start comand, sends the devices RD address and finally reads 1 byte from the device.
Just imagine how fast you can make many permutations of this basic syntax from the terminal to try to "find" and "send" / "recieve" data from your I2C chips.
Not to mention just issue: .I2CBUS to scan the bus
Again I'm not saying to do your project in Tachyon / FORTH but to debug new hardware this tool is invaluable, hat tip once again to Peter.
I use this on most all i2c devices. Once you get a driver working, unless you need more speed or special features, most new devices require just a little copy and paste from other devices.
Thank you for this code, I wrote a simple i2c test to read the Chip's ID with great success. But when I went to add in the other values, I get numbers, but only 7 of 21 change (reading each individual register & displaying them), the Chip ID is still correct, and it does not matter if is run SetConfig (it should as setting PWR_MGMT to 1 brings it out of sleep mode).
Still for the life of me cannot get this working like it should be.
This is my port of MPU-6050.spin using your code in Spin. You can easily follow it compared to mpu-6050.spin.
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq ' system freq as a constant
MS_001 = CLK_FREQ / 1_000 ' ticks in 1ms
US_001 = CLK_FREQ / 1_000_000 ' ticks in 1us
I2C_SDA = 10
I2C_SCL = 11
AddressW = 01_0000 ' MPU-6050/9150 Write Address
AddressR = 01_0001 ' MPU-6050/9150 Read Address
Pause = clk_freq / 100000 ' Pause between checks for operations
Obj
ser : "FullDuplexSerial"
VAR
long Cog, Stack[32], Ready
long i2csda, i2cscl
long cID, Temp, aX, aY, aZ, gX, gY, gZ, mX, mY, mZ, drift
Pub TestSelect | vSel
computetimes
vSel := 2
if vSel == 1
TestMPU1 ' Reads 2 Registers at a Time via MPU9150_Loop
elseif vSel == 2
TestMPU2 ' Reads 1 Register at a Time
PUB TestMPU1 | MPUcog
ser.start(31, 30, 0, 115200)
ser.str(string("Starting..."))
i2csda := I2C_SDA
i2cscl := I2C_SCL
MPUcog := Start( I2C_SCL, I2C_SDA )
repeat
ser.tx(13)
ser.str(string("Chip ID = 0x68: "))
ser.hex(cID, 2) ' = 0x68 (Correct)
ser.str(string(" "))
ser.hex(Temp, 4)
ser.str(string(", "))
ser.dec(drift)
ser.str(string(" "))
ser.hex(aX, 4)
ser.str(string(", "))
ser.hex(aY, 4)
ser.str(string(", "))
ser.hex(aZ, 4)
ser.str(string(" "))
ser.hex(gX, 4)
ser.str(string(", "))
ser.hex(gY, 4)
ser.str(string(", "))
ser.hex(gZ, 4)
ser.str(string(" "))
ser.hex(mX, 4)
ser.str(string(", "))
ser.hex(mY, 4)
ser.str(string(", "))
ser.hex(mZ, 4)
ser.tx(13)
PUB TestMPU2 | MPUcog
ser.start(31, 30, 0, 115200)
ser.str(string("Starting..."))
i2csda := I2C_SDA
i2cscl := I2C_SCL
' // *** MPUcog cannot be Started if reading Registers this way
'MPUcog := Start( I2C_SCL, I2C_SDA )
SetConfig
repeat
' // *** Only 4 Values Change
' // Who Am I
ser.tx(13)
ser.str(string("Who Am I: "))
ser.hex(Read_Register($75), 2) ' = 0x68 (Correct)
ser.str(string(" "))
' // Accelerometer
ser.hex(Read_Register($3B), 2)
ser.str(string(", "))
ser.hex(Read_Register($3C), 2)
ser.str(string(", "))
ser.hex(Read_Register($3D), 2)
ser.str(string(" "))
ser.hex(Read_Register($3E), 2)
ser.str(string(", "))
ser.hex(Read_Register($3F), 2)
ser.str(string(", "))
ser.str(string(" "))
ser.hex(Read_Register($40), 2)
' // Temperature
ser.hex(Read_Register($41), 2)
ser.str(string(", "))
ser.hex(Read_Register($42), 2)
ser.str(string(" "))
' // Gyroscope
ser.hex(Read_Register($43), 2)
ser.str(string(", "))
ser.hex(Read_Register($44), 2)
ser.str(string(", "))
ser.hex(Read_Register($45), 2)
ser.str(string(" "))
ser.hex(Read_Register($46), 2)
ser.str(string(", "))
ser.hex(Read_Register($47), 2)
ser.str(string(", "))
ser.hex(Read_Register($48), 2)
ser.str(string(" "))
' // Magnetometer
ser.hex(Read_Register($03), 2)
ser.str(string(", "))
ser.hex(Read_Register($04), 2)
ser.str(string(", "))
ser.hex(Read_Register($05), 2)
ser.str(string(" "))
ser.hex(Read_Register($06), 2)
ser.str(string(", "))
ser.hex(Read_Register($07), 2)
ser.str(string(", "))
ser.hex(Read_Register($08), 2)
ser.str(string(" "))
PUB Start(MPUscl, MPUsda)
i2csda := MPUsda
i2cscl := MPUscl
Ready := 0
cog := cognew(MPU9150_Loop, @stack)
PUB Stop
''Stops the Cog and the PID controller
cogstop(cog)
' // Main Loop
Pri MPU9150_Loop
SetConfig
waitcnt(Pause)
repeat
MPUReadValues
'MPUComputeDrift
'ComputeAngles
' // MPU-6050 / MPU-9150 Config
Pri SetConfig
{
WriteRegisterByte($6C, 000001) ' 107 - PWR_MGMT
WriteRegisterByte($19, 000001) ' 25 - SMPLRT_DIV = 1 => 1khz/(1+1) = 500hz sample rate
WriteRegisterByte($1A, 000100) ' 26 - Set DLPF_CONFIG to 4 for 20Hz bandwidth
WriteRegisterByte($1B, 011000) ' 27 - Gyro_Config
WriteRegisterByte($1C, 010000) ' 28 - Accel_Config
' // Extra Config Settings
'WriteRegisterByte($FF, 000000)
}
Write_Register($6C, 000001) ' 107 - PWR_MGMT
Write_Register($19, 000001) ' 25 - SMPLRT_DIV = 1 => 1khz/(1+1) = 500hz sample rate
Write_Register($1A, 000100) ' 26 - Set DLPF_CONFIG to 4 for 20Hz bandwidth
Write_Register($1B, 011000) ' 27 - Gyro_Config
Write_Register($1C, 010000) ' 28 - Accel_Config
' // Extra Config Settings
'Write_Register($FF, 000000)
' // MPU-6050 / MPU-9150 Get Values
Pri MPUReadValues | tTemp
cID := Read_Register($75) ' 117 - Chip_ID = 0x68 (Correct)
aX := Read_Register($3B) << 8 ' 59 - Accel_XOut_H
aX |= Read_Register($3C) ' 60 - Accel_XOut_L
aY := Read_Register($3D) << 8 ' 61 - Accel_XOut_H
aY |= Read_Register($3E) ' 62 - Accel_XOut_L
aZ := Read_Register($3F) << 8 ' 63 - Accel_XOut_H
aZ |= Read_Register($40) ' 64 - Accel_XOut_L
Temp := Read_Register($41) << 8 ' 65 - Temp_Out_H
Temp |= Read_Register($42) ' 66 - Temp_Out_L
tTemp := Temp
~~tTemp
drift := (tTemp + 15000) / 100
'waitcnt(Pause)
'waitcnt( constant(80_000_000 / 200) + cnt )
gX := Read_Register($43) << 8 ' 67 - Gyro_XOut_H
gX |= Read_Register($44) ' 68 - Gyro_XOut_L
gY := Read_Register($45) << 8 ' 69 - Gyro_XOut_H
gY |= Read_Register($46) ' 70 - Gyro_XOut_L
gZ := Read_Register($47) << 8 ' 71 - Gyro_XOut_H
gZ |= Read_Register($48) ' 72 - Gyro_XOut_L
'waitcnt(Pause)
'waitcnt( constant(80_000_000 / 200) + cnt )
mX := Read_Register($03) << 8 ' 59 - Mag_XOut_H
mX |= Read_Register($04) ' 60 - Mag_XOut_L
mY := Read_Register($05) << 8 ' 61 - Mag_XOut_H
mY |= Read_Register($06) ' 62 - Mag_XOut_L
mZ := Read_Register($07) << 8 ' 63 - Mag_XOut_H
mZ |= Read_Register($08) ' 64 - Mag_XOut_L
'waitcnt(Pause)
'waitcnt( constant(80_000_000 / 200) + cnt )
' // MPU Read / Write Functions
Pri Read_Register(rReg) : rVal | key
i2cStart ' start the transaction
i2cWrite(AddressW)
i2cWrite(rReg)
i2cStop
i2cStart
i2cWrite(AddressR) ' start the read
rVal := i2cRead(0) ' read first bit field 0 - 7
rVal |= i2cRead(0) << 8 ' read second bit field 8 - 15
rVal[1] := i2cRead(1) ' read last unused bits 9 - 24 24 bit total read out
i2cStop
key := >| rVal 'encode >| bitfield tonumber
' // Method to debug with piezo (from example code)
if rVal == $FFFF
{use this to insure that if the Address fails or is unplugged
that the program does not lock since I2C will be showing $FFFF}
return 0
return rVal
Pri Write_Register(wReg, wVal)
i2cStart
i2cWrite(AddressW)
i2cWrite(wReg)
i2cWrite(wVal)
i2cStop
Pri WriteRegisterByte(wReg, wVal)
i2cStart
i2cWrite(AddressW)
waitcnt(i2cPause)
i2cWrite(wReg)
waitcnt(i2cPause)
i2cWrite(wVal)
i2cStop
PRI computeTimes '' Set up timing constants in assembly
' (Done this way to avoid overflow)
i2cDataSet := ((clkfreq / 10000) * 350) / 100000 ' Data setup time - 350ns (400KHz)
i2cClkLow := ((clkfreq / 10000) * 1300) / 100000 ' Clock low time - 1300ns (400KHz)
i2cClkHigh := ((clkfreq / 10000) * 600) / 100000 ' Clock high time - 600ns (400KHz)
i2cPause := clkfreq / 100000 ' Pause between checks for operations
' // Minimal I2C Driver:
con
i2cack = 0
i2cnak = 1
i2cxmit = 0
i2crecv = 1
i2cboot = 28
i2ceeprom = $a0
'Var
' long i2csda, i2cscl
Pri i2cstart
outa[i2cscl]~~
dira[i2cscl]~~
outa[i2csda]~~
dira[i2csda]~~
outa[i2csda]~
outa[i2cscl] ~
Pri i2cstop
outa[i2cscl] ~~
outa[i2csda] ~~
dira[i2cscl] ~
dira[i2csda] ~
Pri i2cwrite(i2cdata) : ackbit
ackbit := 0
i2cdata <<= 24
repeat 8
outa[i2csda] := (i2cdata <-= 1) & 1
outa[i2cscl] ~~
outa[i2cscl] ~
dira[i2csda] ~
outa[i2cscl] ~~
ackbit := ina[i2csda]
outa[i2cscl] ~
outa[i2csda] ~
dira[i2csda] ~~
Pri i2cread(ackbit): i2cdata
i2cdata := 0
dira[i2csda]~
repeat 8
outa[i2cscl] ~~
i2cdata := (i2cdata << 1) | ina[i2csda]
outa[i2cscl] ~
outa[i2csda] := ackbit
dira[i2csda] ~~
outa[i2cscl] ~~
outa[i2cscl] ~
outa[i2csda] ~
Pri i2creadpage(i2caddr, addrreg, dataptr, count) : ackbit
i2caddr |= addrreg >> 15 & 10
i2cstart
ackbit := i2cwrite(i2caddr | i2cxmit)
ackbit := (ackbit << 1) | i2cwrite(addrreg >> 8 & $ff)
ackbit := (ackbit << 1) | i2cwrite(addrreg & $ff)
i2cstart
ackbit := (ackbit << 1) | i2cwrite(i2caddr | i2crecv)
repeat count - 1
byte[dataptr++] := i2cread(i2cack)
byte[dataptr++] := i2cread(i2cnak)
i2cstop
return ackbit
Pri i2cwritepage(i2caddr, addrreg, dataptr, count) : ackbit
i2caddr |= addrreg >> 15 & 10
i2cstart
ackbit := i2cwrite(i2caddr | i2cxmit)
ackbit := (ackbit << 1) | i2cwrite(addrreg >> 8 & $ff)
ackbit := (ackbit << 1) | i2cwrite(addrreg & $ff)
repeat count
ackbit := ackbit << 1 | ackbit & $80000000
ackbit |= i2cwrite(byte[dataptr++])
i2cstop
return ackbit
Dat
i2cDataSet long 0 ' Minumum data setup time (ticks)
i2cClkLow long 0 ' Minimum clock low time (ticks)
i2cClkHigh long 0 ' Minimum clock high time (ticks)
i2cPause long 0 ' Pause before re-fetching next operation
And the Working PASM Driver (MPU-6050.spin):
'' MPU-60X0-PASM.spin
'' Reads gyro and accelerometer data from the MPU-60X0 chips
'' Read loop is in Propeller Assembler
''
'' Based on Jason Dorie's code for the ITG-3200 and ADCL345 chips
''
'' Note that this code assumes an 80 MHz clock
''
'' The TestMPU routine can be used to verify correct setup of, and
'' communication with, the MPU-60X0. Load the object into RAM, then
'' use f12 to bring up the terminal emulator to see the output.
''
{
*******************************************
* User Init Select *
* Updates by: Zack Lantz *
*******************************************
Place in Main .Spin:
Con
' // Accelerometer Settings
mAFS0 = 0
mAFS1 = 1
mAFS2 = 2
mAFS3 = 3
' // Gyroscope Settings
mFS0 = 0
mFS1 = 1
mFS2 = 2
mFS3 = 3
' // Digital Low Pass Filter Settings
DLP0 = 0 ' Bandwidth = 260 Hz
DLP1 = 1 ' Bandwidth = 184 Hz
DLP2 = 2 ' Bandwidth = 94 Hz
DLP3 = 3 ' Bandwidth = 44 Hz
DLP4 = 4 ' Bandwidth = 21 Hz
DLP5 = 5 ' Bandwidth = 10 Hz
DLP6 = 6 ' Bandwidth = 5 Hz
DLP7 = 7 ' Reserved
' // Current Settings (Passed to MPU-6050.spin)
AccelFS = AFS2
GyroFS = FS3
mAFS = mAFS2
mFS = mFS3
mDLP = DLP3
Then start MPU Driver with:
MPU.Start(MPUscl, MPUsda, mAFS, mFS, mDLP) ' MPU-6050 Gyro & Accel Sensor Data w/ User Init AFS, FS, & DLP
Everything else works the same.
New Functions:
Start (SCL, SDA, aFS, gFS, fDLP)
StartA(SCL, SDA, aFS, gFS, fDLP, PM) ', SF)
StartX(SCL, SDA)
*** Note: I didn't add in SampleRate feature as it is 0 to 255 & Calculated as follows:
Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
Where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or 7), and 1kHz when the DLPF is enabled (see Register 26)
}
{{
The slave address of the MPU-60X0 is b110100X which is 7 bits long. The LSB bit of the 7 bit address is
determined by the logic level on pin AD0. This allows two MPU-60X0s to be connected to the same I2C bus.
When used in this configuration, the address of the one of the devices should be b1101000 (pin AD0
is logic low) and the address of the other should be b1101001 (pin AD0 is logic high).
}}
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
CON ' CONs for TestMPU test routine
SDA_PIN = 8
SCL_PIN = 9
SERIAL_TX_PIN = 30
SERIAL_RX_PIN = 31
VAR
long x0, y0, z0, a0, b0, c0, t
long Cog
long rx, ry, rz, temp, ax, ay, az, arx, ary 'PASM code assumes these to be contiguous
OBJ
debug : "FullDuplexSerial"
PUB TestMPU | MPUcog
'-----------------------------------------------
' Start serial i/o cog
' Start cog to pull gyro/accel data from chip
' Print data to serial out every few seconds
'------------------------------------------------
debug.start(SERIAL_RX_PIN, SERIAL_TX_PIN, 0, 115200) 'Start cog to allow IO with serial terminal
repeat 4
waitcnt(clkfreq + cnt)
debug.str(string("Starting..."))
debug.tx(13)
debug.str(string("GX GY GZ AX AY AZ"))
debug.tx(13)
debug.str(string("-------------------------"))
debug.tx(13)
MPUcog := StartX( SCL_PIN, SDA_PIN )
'Output gyro data, then accel data, once per second
repeat
debug.dec(GetRX)
debug.str(string(", "))
debug.dec(GetRY)
debug.str(string(", "))
debug.dec(GetRZ)
debug.str(string(" "))
debug.dec(GetAX)
debug.str(string(", "))
debug.dec(GetAY)
debug.str(string(", "))
debug.dec(GetAZ)
debug.tx(13)
waitcnt((clkfreq / 10) + cnt)
' // Basic Start with User Accel AFS & Gyro FS & DLP
PUB Start( SCL, SDA, aFS, gFS, fDLP ) : Status
if aFS == 0
AccelFS := %00000000 ' ± 2 g
elseif aFS == 1
AccelFS := %00001000 ' ± 4 g
elseif aFS == 2
AccelFS := %00010000 ' ± 8 g
elseif aFS == 3
AccelFS := %00011000 ' ± 16 g
if gFS == 0
GyroFS := %00000000 ' ± 250 ° /s
elseif gFS == 1
GyroFS := %00001000 ' ± 500 ° /s
elseif gFS == 2
GyroFS := %00010000 ' ± 1000 ° /s
elseif gFS == 3
GyroFS := %00011000 ' ± 2000 ° /s
'| DLPF_CFG | Accelerometer | Gyroscope |
if fDLP == 0 ' Bw (Hz) Delay (ms) Bw (Hz) Delay (ms) FS (Khz)
DLP := %00000000 ' 0 260 0 256 0.98 8
elseif fDLP == 1
DLP := %00000001 ' 1 184 2.0 188 1.9 1
elseif fDLP == 2
DLP := %00000010 ' 2 94 3.0 98 2.8 1
elseif fDLP == 3
DLP := %00000011 ' 3 44 4.9 42 4.8 1
elseif fDLP == 4
DLP := %00000100 ' 4 21 8.5 20 8.3 1
elseif fDLP == 5
DLP := %00000101 ' 5 10 13.8 10 13.4 1
elseif fDLP == 6
DLP := %00000110 ' 6 5 19.0 5 18.6 1
elseif fDLP == 7
DLP := %00000111 ' 7 RESERVED RESERVED 8
PowerMgmt := %00000001 ' X gyro as clock source
SampleRate := %00000001 ' 500 Hz
ComputeTimes
gyroSCL := 1 << SCL 'save I2C pins
gyroSDA := 1 << SDA
Status := Cog := cognew(@Start_Sensors, @rx) + 1
CalibrateAccel
' // Start w/ Full User Init Settings
PUB StartA( SCL, SDA, aFS, gFS, fDLP, PM ) : Status
if aFS == 0
AccelFS := %00000000
elseif aFS == 1
AccelFS := %00001000
elseif aFS == 2
AccelFS := %00010000
elseif aFS == 3
AccelFS := %00011000
if gFS == 0
GyroFS := %00000000
elseif gFS == 1
GyroFS := %00001000
elseif gFS == 2
GyroFS := %00010000
elseif gFS == 3
GyroFS := %00011000
'| DLPF_CFG | Accelerometer | Gyroscope |
if fDLP == 0 ' Bw (Hz) Delay (ms) Bw (Hz) Delay (ms) FS (Khz)
DLP := %00000000 ' 0 260 0 256 0.98 8
elseif fDLP == 1
DLP := %00000001 ' 1 184 2.0 188 1.9 1
elseif fDLP == 2
DLP := %00000010 ' 2 94 3.0 98 2.8 1
elseif fDLP == 3
DLP := %00000011 ' 3 44 4.9 42 4.8 1
elseif fDLP == 4
DLP := %00000100 ' 4 21 8.5 20 8.3 1
elseif fDLP == 5
DLP := %00000101 ' 5 10 13.8 10 13.4 1
elseif fDLP == 6
DLP := %00000110 ' 6 5 19.0 5 18.6 1
elseif fDLP == 7
DLP := %00000111 ' 7 RESERVED RESERVED 8
if PM == 0
PowerMgmt := %00000000 ' Internal 8MHz oscillator
elseif PM == 1
PowerMgmt := %00000001 ' PLL with X axis gyroscope reference
elseif PM == 2
PowerMgmt := %00000010 ' PLL with Y axis gyroscope reference
elseif PM == 3
PowerMgmt := %00000011 ' PLL with Z axis gyroscope reference
elseif PM == 4
PowerMgmt := %00000100 ' PLL with external 32.768kHz reference
elseif PM == 5
PowerMgmt := %00000101 ' PLL with external 19.2MHz referenc
elseif PM == 6
PowerMgmt := %00000110 ' Reserved
elseif PM == 7
PowerMgmt := %00000111 ' Stops the clock and keeps the timing generator in reset
' *** Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
' // 0 threw 255
SampleRate := %00000001 ' 500 Hz Sample Rate, %00000000 = 1000 Hz Sample rate
ComputeTimes
gyroSCL := 1 << SCL 'save I2C pins
gyroSDA := 1 << SDA
Status := Cog := cognew(@Start_Sensors, @rx) + 1
CalibrateAccel
' // Start Basic, No User Init
PUB StartX( SCL, SDA ) : Status
AccelFS := %00010000 ' AFS2
GyroFS := %00011000 ' FS3
DLP := %00000011 ' 40 Hz
PowerMgmt := %00000001 ' X gyro as clock source
SampleRate := %00000001 ' 500 Hz
ComputeTimes
gyroSCL := 1 << SCL 'save I2C pins
gyroSDA := 1 << SDA
Status := Cog := cognew(@Start_Sensors, @rx) + 1
CalibrateAccel
CalibrateGyro
PUB Stop
if Cog
cogstop(Cog~ - 1)
'**********************
' Accessors
'**********************
PUB GetTemp ' Temperature
return temp
Pub GetTempF ' Temp Deg F
Pub GetTempC ' Temp Deg C
PUB GetRX ' Accel X - Zero Offset
return rx - x0
PUB GetRY ' Accel Y - Zero Offset
return ry - y0
PUB GetRZ ' Accel Z - Zero Offset
return rz - z0
PUB GetAX ' Gyro X - Zero Offset
return ax - a0
PUB GetAY ' Gyro Y - Zero Offset
return ay - b0
PUB GetAZ ' Gyrp Z - Zero Offset
return az - c0
PUB GetARX ' Pitch Angle
return arx
PUB GetARY ' Roll Angle
return ary
Pub GetAccelOffsetX ' Accelerometer Zero Offset X
return x0
Pub GetAccelOffsetY ' Accelerometer Zero Offset Y
return y0
Pub GetAccelOffsetZ ' Accelerometer Zero Offset Z
return z0
Pub GetGyroOffsetX ' Gyroscope Zero Offset X
return a0
Pub GetGyroOffsetY ' Gyroscope Zero Offset Y
return b0
Pub GetGyroOffsetZ ' Gyroscope Zero Offset Z
return c0
Pub CalAccel ' Calibrate Accelerometer
CalibrateAccel
Pub CalGyro ' Calibrate Gyroscope
CalibrateGyro
PRI computeTimes '' Set up timing constants in assembly
' (Done this way to avoid overflow)
i2cDataSet := ((clkfreq / 10000) * 350) / 100000 ' Data setup time - 350ns (400KHz)
i2cClkLow := ((clkfreq / 10000) * 1300) / 100000 ' Clock low time - 1300ns (400KHz)
i2cClkHigh := ((clkfreq / 10000) * 600) / 100000 ' Clock high time - 600ns (400KHz)
i2cPause := clkfreq / 100000 ' Pause between checks for operations
PRI CalibrateAccel | tc, xc, yc, zc, dr
x0 := 0 ' Initialize offsets
y0 := 0
z0 := 0
'wait 1/2 second for the body to stop moving
waitcnt( constant(80_000_000 / 2) + cnt )
'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
xc := 0
yc := 0
zc := 0
repeat 256
xc += rx
yc += ry
zc += rz
waitcnt( constant(80_000_000/192) + cnt )
'Perform rounding
if( xc > 0 )
xc += 128
elseif( xc < 0 )
xc -= 128
if( yc > 0 )
yc += 128
elseif( yc < 0 )
yc -= 128
if( zc > 0 )
zc += 128
elseif( zc < 0 )
zc -= 128
x0 := xc / 256
y0 := yc / 256
z0 := zc / 256
PRI CalibrateGyro | tc, xc, yc, zc, dr
a0 := 0 ' Initialize offsets
b0 := 0
c0 := 0
'wait 1/2 second for the body to stop moving
waitcnt( constant(80_000_000 / 2) + cnt )
'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
xc := 0
yc := 0
zc := 0
repeat 256
xc += ax
yc += ay
zc += az
waitcnt( constant(80_000_000/192) + cnt )
'Perform rounding
if( xc > 0 )
xc += 128
elseif( xc < 0 )
xc -= 128
if( yc > 0 )
yc += 128
elseif( yc < 0 )
yc -= 128
if( zc > 0 )
zc += 128
elseif( zc < 0 )
zc -= 128
a0 := xc / 256
b0 := yc / 256
c0 := zc / 256
DAT
org 0
Start_Sensors
' --------- Debugger Kernel add this at Entry (Addr 0) ---------
' long $34FC1202,$6CE81201,$83C120B,$8BC0E0A,$E87C0E03,$8BC0E0A
' long $EC7C0E05,$A0BC1207,$5C7C0003,$5C7C0003,$7FFC,$7FF8
' --------------------------------------------------------------
mov p1, par ' Get data pointer
mov prX, p1 ' Store the pointer to the rx var in HUB RAM
add p1, #4
mov prY, p1 ' Store the pointer to the ry var in HUB RAM
add p1, #4
mov prZ, p1 ' Store the pointer to the rz var in HUB RAM
add p1, #4
mov pT, p1 ' Store the pointer to the temp var in HUB RAM
add p1, #4
mov paX, p1 ' Store the pointer to the ax var in HUB RAM
add p1, #4
mov paY, p1 ' Store the pointer to the ay var in HUB RAM
add p1, #4
mov paZ, p1 ' Store the pointer to the az var in HUB RAM
add p1, #4
mov paRX, p1 ' Store the pointer to the arx var in HUB RAM
add p1, #4
mov paRY, p1 ' Store the pointer to the ary var in HUB RAM
mov i2cTemp,i2cPause
add i2cTemp,CNT ' Wait 10us before starting
waitcnt i2cTemp,#0
call #SetConfig
mov loopCount, CNT
add loopCount, loopDelay
'------------------------------------------------------------------------------
' Main loop
' loopDelay defined in data section
' Nominally set to CLK_FREQ/200 give 200hz update rate, but the update takes less than
' 500us, so the delay could potentially be set to give an update rate as high as 2000hz
'
:loop
call #MPUReadValues
call #MPUComputeDrift
call #ComputeAngles
wrlong iT, pT
subs irX, drift
wrlong irX, prX
subs irY, drift
wrlong irY, prY
subs irZ, drift
wrlong irZ, prZ
wrlong iaX, paX
wrlong iaY, paY
wrlong iaZ, paZ
wrlong iaRX, paRX
wrlong iaRY, paRY
waitcnt loopCount, loopDelay
jmp #:loop
'------------------------------------------------------------------------------
' MPUReadValues
'
' Starting at the ACCEL_X data register, read in the 3 accel values,
' the temperature, and the 3 gyro values, as these are held in
' sequential register locations.
'
MPUReadValues
mov i2cSDA, gyroSDA 'Use gyro SDA,SCL
mov i2cSCL, gyroSCL
mov i2cAddr, #59 ' Address of ACCEL_XOUT_H
mov i2cDevID, #%11010000 ' Device ID of the MPU
call #StartRead ' Tell the I2C device we're starting
mov i2cMask, i2cWordReadMask
test i2cTestCarry, #0 wc ' Clear the carry flag to make reads auto-increment
call #i2cRead
call #i2cRead
'Sign extend the 15th bit
test i2cData, i2cWordReadMask wc
muxc i2cData, i2cWordMask
mov iaX, i2cData
mov i2cMask, i2cWordReadMask
test i2cTestCarry, #0 wc ' Clear the carry flag to make reads auto-increment
call #i2cRead
call #i2cRead
'Sign extend the 15th bit
test i2cData, i2cWordReadMask wc
muxc i2cData, i2cWordMask
mov iaY, i2cData
mov i2cMask, i2cWordReadMask
test i2cTestCarry, #0 wc ' Clear the carry flag to make reads auto-increment
call #i2cRead
call #i2cRead
'Sign extend the 15th bit
test i2cData, i2cWordReadMask wc
muxc i2cData, i2cWordMask
mov iaZ, i2cData
mov i2cMask, i2cWordReadMask
test i2cTestCarry, #0 wc ' Clear the carry flag to make reads auto-increment
call #i2cRead
'test i2cTestCarry, #1 wc ' Set the carry flag to tell it we're done
call #i2cRead
test i2cData, i2cWordReadMask wc
muxc i2cData, i2cWordMask
mov iT, i2cData
mov i2cMask, i2cWordReadMask
test i2cTestCarry, #0 wc ' Clear the carry flag to make reads auto-increment
call #i2cRead
call #i2cRead
'Sign extend the 15th bit
test i2cData, i2cWordReadMask wc
muxc i2cData, i2cWordMask
mov irX, i2cData
mov i2cMask, i2cWordReadMask
test i2cTestCarry, #0 wc ' Clear the carry flag to make reads auto-increment
call #i2cRead
call #i2cRead
'Sign extend the 15th bit
test i2cData, i2cWordReadMask wc
muxc i2cData, i2cWordMask
mov irY, i2cData
mov i2cMask, i2cWordReadMask
test i2cTestCarry, #0 wc ' Clear the carry flag to make reads auto-increment
call #i2cRead
test i2cTestCarry, #1 wc ' Set the carry flag to tell it we're done
call #i2cRead
'Sign extend the 15th bit
test i2cData, i2cWordReadMask wc
muxc i2cData, i2cWordMask
mov irZ, i2cData
call #i2cStop
MPUReadValues_Ret ret
'------------------------------------------------------------------------------
' Compute drift - for my gyro (Jason's ITG-3200)
'(Temp + 15000) / 100 = drift
'------------------------------------------------------------------------------
MPUComputeDrift
mov drift, iT ' Start with the temperature reading
add drift, tempOffset ' Offset it by 15,000
' divide drift by 100
mov divisor, #100
mov dividend, drift
test dividend, i2cWordReadMask wc
muxc signbit, #1 ' record the sign of the original value
abs dividend, dividend
mov divCounter, #10
shl divisor, divCounter
mov resultShifted, #1
shl resultShifted, divCounter
add divCounter, #1
mov drift, #0
:divLoop
cmp dividend, divisor wc
if_nc add drift, resultShifted
if_nc sub dividend, divisor
shr resultShifted, #1
shr divisor, #1
djnz divCounter, #:divLoop
test signbit, #1 wc
negc drift, drift
MPUComputeDrift_Ret ret
'------------------------------------------------------------------------------
ComputeAngles
mov cx, iaZ
mov cy, iaX
call #cordic
mov iaRX, ca
mov cx, iaZ
mov cy, iaY
call #cordic
mov iaRY, ca
ComputeAngles_ret
ret
'------------------------------------------------------------------------------
' SetConfig
'
' See MPU-6000/6050 Register Map document for register addresses and
' valid settings
'
SetConfig
mov i2cSDA, gyroSDA 'Use gyro SDA,SCL
mov i2cSCL, gyroSCL
call #i2cReset 'Reset i2c
:MPUSetConfig mov i2cDevID, #%11010000 'Device ID for the MPU-6000/6050
' // Power Management
mov i2cAddr, #107 'Set PWR_MGMT_1 register bit 0 to choose
mov i2cValue, PowerMgmt ' X gyro as clock source '
call #i2cWriteRegisterByte
{
mov i2cAddr, #107 'Set PWR_MGMT_1 register bit 0 to choose
mov i2cValue, #%00000001 ' X gyro as clock source '
call #i2cWriteRegisterByte
}
' // Digital Low Pass Filter _ Config
{
mov i2cAddr, #26
mov i2cValue, #%00000100 'Set DLPF_CONFIG to 4 for 20Hz bandwidth
call #i2cWriteRegisterByte
}
mov i2cAddr, #26
mov i2cValue, DLP 'Set DLPF_CONFIG to 3 for 40Hz bandwidth
call #i2cWriteRegisterByte
' // Sample Rate Divider
{
mov i2cAddr, #25 'SMPLRT_DIV = 1 => 1khz/(1+1) = 500hz sample rate
mov i2cValue, #%00000001
call #i2cWriteRegisterByte
}
mov i2cAddr, #25 'SMPLRT_DIV = 1 => 1khz/(1+0) = 1000hz sample rate
mov i2cValue, SampleRate
call #i2cWriteRegisterByte
' // Gyro _ Config
mov i2cAddr, #27 'GYRO_CONFIG register, set FS_SEL bits to 3 gives a
mov i2cValue, GyroFS ' full scale range of +-2000 deg/sec
call #i2cWriteRegisterByte
{
' // Gyro _ Config
mov i2cAddr, #27 'GYRO_CONFIG register, set FS_SEL bits to 3 gives a
mov i2cValue, #%00011000 ' full scale range of +-2000 deg/sec
call #i2cWriteRegisterByte
}
' // Accel _ Config
{
mov i2cAddr, #28 'Set ACCEL_CONFIG register AFS_SEL bits to 1,
'mov i2cValue, #%00001000 ' sets +-4g full scale range
mov i2cValue, #%00010000 ' sets +-8g full scale range
call #i2cWriteRegisterByte 'ACCEL_HPF is zero which turns off high-pass filtering
}
mov i2cAddr, #28 'Set ACCEL_CONFIG register AFS_SEL bits to 2
mov i2cValue, AccelFS ' sets +-8g full scale range
call #i2cWriteRegisterByte 'ACCEL_HPF is zero which turns off high-pass filtering
{
' // Extra Config Settings:
mov i2cAddr, #Address
mov i2cValue, #%BIN_Value
call #i2cWriteRegisterByte
}
SetConfig_Ret
ret
'------------------------------------------------------------------------------
StartRead
call #i2cStart
mov i2cData, i2cDevID
mov i2cMask, #%10000000
call #i2cWrite
mov i2cData, i2cAddr
mov i2cMask,#%10000000
call #i2cWrite
call #i2cStart
mov i2cData, i2cDevID
or i2cData, #1
mov i2cMask, #%10000000
call #i2cWrite
StartRead_Ret ret
'------------------------------------------------------------------------------
i2cWriteRegisterByte
call #i2cStart
mov i2cData, i2cDevID
mov i2cMask,#%10000000
call #i2cWrite
mov i2cTime,i2cClkLow
add i2cTime,cnt ' Allow for minimum SCL low
waitcnt i2cTime, #0
mov i2cData, i2cAddr
mov i2cMask,#%10000000
call #i2cWrite
mov i2cTime,i2cClkLow
add i2cTime,cnt ' Allow for minimum SCL low
waitcnt i2cTime, #0
mov i2cData, i2cValue
mov i2cMask,#%10000000
call #i2cWrite
call #i2cStop
i2cWriteRegisterByte_Ret
ret
'------------------------------------------------------------------------------
'' Low level I2C routines. These are designed to work either with a standard I2C bus
'' (with pullups on both SCL and SDA) or the Propellor Demo Board (with a pullup only
'' on SDA). Timing can be set by the caller to 100KHz or 400KHz.
'------------------------------------------------------------------------------
'' Do I2C Reset Sequence. Clock up to 9 cycles. Look for SDA high while SCL
'' is high. Device should respond to next Start Sequence. Leave SCL high.
i2cReset andn dira,i2cSDA ' Pullup drive SDA high
mov i2cBitCnt,#9 ' Number of clock cycles
mov i2cTime,i2cClkLow
add i2cTime,cnt ' Allow for minimum SCL low
:i2cResetClk andn outa,i2cSCL ' Active drive SCL low
or dira,i2cSCL
waitcnt i2cTime,i2cClkHigh
or outa,i2cSCL ' Active drive SCL high
or dira,i2cSCL
andn dira,i2cSCL ' Pullup drive SCL high
waitcnt i2cTime,i2cClkLow ' Allow minimum SCL high
test i2cSDA,ina wz ' Stop if SDA is high
if_z djnz i2cBitCnt,#:i2cResetClk ' Stop after 9 cycles
i2cReset_ret ret ' Should be ready for Start
'------------------------------------------------------------------------------
'' Do I2C Start Sequence. This assumes that SDA is a floating input and
'' SCL is also floating, but may have to be actively driven high and low.
'' The start sequence is where SDA goes from HIGH to LOW while SCL is HIGH.
i2cStart
or outa,i2cSCL ' Active drive SCL high
or dira,i2cSCL
or outa,i2cSDA ' Active drive SDA high
or dira,i2cSDA
mov i2cTime,i2cClkHigh
add i2cTime,cnt ' Allow for bus free time
waitcnt i2cTime,i2cClkLow
andn outa,i2cSDA ' Active drive SDA low
waitcnt i2cTime,#0
andn outa,i2cSCL ' Active drive SCL low
i2cStart_ret ret
'------------------------------------------------------------------------------
'' Do I2C Stop Sequence. This assumes that SCL is low and SDA is indeterminant.
'' The stop sequence is where SDA goes from LOW to HIGH while SCL is HIGH.
'' i2cStart must have been called prior to calling this routine for initialization.
'' The state of the (c) flag is maintained so a write error can be reported.
i2cStop
or outa,i2cSCL ' Active drive SCL high
mov i2cTime,i2cClkHigh
add i2cTime,cnt ' Wait for minimum clock low
waitcnt i2cTime,i2cClkLow
or outa,i2cSDA ' Active drive SDA high
waitcnt i2cTime,i2cClkLow
andn dira,i2cSCL ' Pullup drive SCL high
waitcnt i2cTime,i2cClkLow ' Wait for minimum setup time
andn dira,i2cSDA ' Pullup drive SDA high
waitcnt i2cTime,#0 ' Allow for bus free time
i2cStop_ret ret
'------------------------------------------------------------------------------
'' Write I2C data. This assumes that i2cStart has been called and that SCL is low,
'' SDA is indeterminant. The (c) flag will be set on exit from ACK/NAK with ACK == false
'' and NAK == true. Bytes are handled in "little-endian" order so these routines can be
'' used with words or longs although the bits are in msb..lsb order.
i2cWrite mov i2cBitCnt,#8
mov i2cTime,i2cClkLow
add i2cTime,cnt ' Wait for minimum SCL low
:i2cWriteBit waitcnt i2cTime,i2cDataSet
test i2cData,i2cMask wz
if_z or dira,i2cSDA ' Copy data bit to SDA
if_nz andn dira,i2cSDA
waitcnt i2cTime,i2cClkHigh ' Wait for minimum setup time
or outa,i2cSCL ' Active drive SCL high
waitcnt i2cTime,i2cClkLow
andn outa,i2cSCL ' Active drive SCL low
ror i2cMask,#1 ' Go do next bit if not done
djnz i2cBitCnt,#:i2cWriteBit
andn dira,i2cSDA ' Switch SDA to input and
waitcnt i2cTime,i2cClkHigh ' wait for minimum SCL low
or outa,i2cSCL ' Active drive SCL high
waitcnt i2cTime,i2cClkLow ' Wait for minimum high time
test i2cSDA,ina wc ' Sample SDA (ACK/NAK) then
andn outa,i2cSCL ' active drive SCL low
andn outa,i2cSDA ' active drive SDA low
or dira,i2cSDA ' Leave SDA low
rol i2cMask,#16 ' Prepare for multibyte write
waitcnt i2cTime,#0 ' Wait for minimum low time
i2cWrite_ret ret
'------------------------------------------------------------------------------
'' Read I2C data. This assumes that i2cStart has been called and that SCL is low,
'' SDA is indeterminant. ACK/NAK will be copied from the (c) flag on entry with
'' ACK == low and NAK == high. Bytes are handled in "little-endian" order so these
'' routines can be used with words or longs although the bits are in msb..lsb order.
i2cRead mov i2cBitCnt,#8
andn dira,i2cSDA ' Make sure SDA is set to input
mov i2cTime,i2cClkLow
add i2cTime,cnt ' Wait for minimum SCL low
:i2cReadBit waitcnt i2cTime,i2cClkHigh
or outa,i2cSCL ' Active drive SCL high
waitcnt i2cTime,i2cClkLow ' Wait for minimum clock high
test i2cSDA,ina wz ' Sample SDA for data bits
andn outa,i2cSCL ' Active drive SCL low
if_nz or i2cData,i2cMask ' Accumulate data bits
if_z andn i2cData,i2cMask
ror i2cMask,#1 ' Shift the bit mask and
djnz i2cBitCnt,#:i2cReadBit ' continue until done
waitcnt i2cTime,i2cDataSet ' Wait for end of SCL low
if_c or outa,i2cSDA ' Copy the ACK/NAK bit to SDA
if_nc andn outa,i2cSDA
or dira,i2cSDA ' Make sure SDA is set to output
waitcnt i2cTime,i2cClkHigh ' Wait for minimum setup time
or outa,i2cSCL ' Active drive SCL high
waitcnt i2cTime,i2cClkLow ' Wait for minimum clock high
andn outa,i2cSCL ' Active drive SCL low
andn outa,i2cSDA ' Leave SDA low
waitcnt i2cTime,#0 ' Wait for minimum low time
i2cRead_ret ret
'------------------------------------------------------------------------------
'' Perform CORDIC cartesian-to-polar conversion
''Input = cx(x) and cy(x)
''Output = cx(ro) and ca(theta)
cordic abs cx,cx wc
if_c neg cy,cy
mov ca,#0
rcr ca,#1
movs :lookup,#cordicTable
mov t1,#0
mov t2,#20
:loop mov dx,cy wc
sar dx,t1
mov dy,cx
sar dy,t1
sumc cx,dx
sumnc cy,dy
:lookup sumc ca,cordicTable
add :lookup,#1
add t1,#1
djnz t2,#:loop
shr ca, #16
cordic_ret ret
cordicTable long $20000000
long $12E4051E
long $09FB385B
long $051111D4
long $028B0D43
long $0145D7E1
long $00A2F61E
long $00517C55
long $0028BE53
long $00145F2F
long $000A2F98
long $000517CC
long $00028BE6
long $000145F3
long $0000A2FA
long $0000517D
long $000028BE
long $0000145F
long $00000A30
long $00000518
dx long 0
dy long 0
cx long 0
cy long 0
ca long 0
t1 long 0
t2 long 0
'' Variables for the gyro routines
p1 long 0
pT long 0 ' Pointer to Temperature in hub ram
prX long 0 ' Pointer to X rotation in hub ram
prY long 0 ' Pointer to Y rotation in hub ram
prZ long 0 ' Pointer to Z rotation in hub ram
paX long 0 ' Pointer to X accel in hub ram
paY long 0 ' Pointer to Y accel in hub ram
paZ long 0 ' Pointer to Z accel in hub ram
paRX long 0 ' Pointer to X accel angle in hub ram
paRY long 0 ' Pointer to Y accel angle in hub ram
iT long 0 ' Interim temperature value
irX long 0 ' Interim rX value
irY long 0 ' Interim rY value - These values are temp storage before drift compensation
irZ long 0 ' Interim rZ value
iaX long 0 ' Interim aX value
iaY long 0 ' Interim aY value
iaZ long 0 ' Interim aZ value
iaRX long 0 ' Interim aX value
iaRY long 0 ' Interim aY value
i2cWordReadMask long %10000000_00000000
i2cWordMask long $ffff0000
loopDelay long 80_000_000 / 200
loopCount long 0
'' Variables for dealing with drift / division
tempOffset long 15000
drift long 0
divisor long 0
dividend long 0
resultShifted long 0
signbit long 0
divCounter long 0
'' Variables for i2c routines
i2cTemp long 0
i2cCount long 0
i2cValue long 0
i2cDevID long 0
i2cAddr long 0
i2cDataSet long 0 ' Minumum data setup time (ticks)
i2cClkLow long 0 ' Minimum clock low time (ticks)
i2cClkHigh long 0 ' Minimum clock high time (ticks)
i2cPause long 0 ' Pause before re-fetching next operation
i2cTestCarry long 1 ' Used for setting the carry flag
'' Local variables for low level I2C routines
' // MPU-6050 User Set Init Settings
GyroFS long %00000000
AccelFS long %00000000
PowerMgmt long %00000000
SampleRate long %00000000
DLP long %00000000
gyroSCL long 0 ' Bit mask for SCL
gyroSDA long 0 ' Bit mask for SDA
i2cSCL long 0 ' Bit mask for SCL
i2cSDA long 0 ' Bit mask for SDA
i2cTime long 0 ' Used for timekeeping
i2cData long 0 ' Data to be transmitted / received
i2cMask long 0 ' Bit mask for bit to be tx / rx
i2cBitCnt long 0 ' Number of bits to tx / rx
FIT 496
Just to give you an idea what I might do in Forth to debug the BMP180 for instance. After defining these few words BMP180, BMP!, BMP@, BMPDUMP, BMPRST you can debug to your heart's content.
i.e. 0 $100 BMPDUMP will dump out every register of the BMP180
[FONT=courier new]$EE == bmpAddr
\ Select BMP180 device pins
pub BMP180
8 9 I2CPINS
;
\ Write to a BMP180 register
pub BMP! ( dat reg -- )
BMP180
I2CSTART bmpAddr I2C! I2C! I2C! I2CSTOP
;
\ Read from any BMP180 register
pub BMP@ ( reg -- dat )
BMP180
I2CSTART bmpAddr I2C! I2C! \ Select device and register
I2CSTART bmpAddr 1+ I2C! 1 I2C@ \ restart and read the register
;
\ Dump specified contents of BMP180
pub BMPDUMP ( start cnt -- )
ADO
I 0F AND 0= IF .INDEX THEN \ Start a new line and address
I BMP@ .BYTE SPACE \ read the register and display
LOOP
;
\ Reset the BMP180
pub BMPRST
$B6 $E0 BMP! \ write $B6 to reg $E0 to soft reset
;
[/FONT]
VAR
long Cog, Stack[32], testlong, Ready 'add this extra long testlong
Pri Read_Register(rReg) : rVal | key
i2cStart ' start the transaction
i2cWrite(AddressW)
i2cWrite(rReg)
i2cStop
i2cStart
i2cWrite(AddressR) ' start the read
rVal := i2cRead(0) ' read first bit field 0 - 7
rVal |= i2cRead(1) << 8 ' you only need to read two bytes msb and lsb. the last read should include the 1 to tell it we are done now
i2cStop
repeat
if testlong <> 0 'add this to the main loop
ser.str(string("stack overrun"))
ser.tx(13)
ser.str(string("Chip ID = 0x68: "))
ser.hex(cID, 2) ' = 0x68 (Correct)
ser.str(string(" "))
Note; my example was for a chip ( Qprox 60160) that allows multiple reads within a single interaction, it expects you to read out 3 bytes. In your case, you only need to read out 2 bytes then quit that interaction. Now, that being said, I have not studied whether your chip will allow to read both msb and lsb in one interaction. I made the change so you can at least test it. It may be that you have to read for AA, then do a new read for AB. Your attempt with my example would not work because you were attempting to read 3 bytes in a row, when the device is only holding 2 bytes for that register or register pair.\
Note2: Oops, my comments above were as if you were still working with the other module trying to read out the eeprom calibration data in pairs of aa ab, etc. For any other module, you need to determine how many bytes are to be read from the register, and modify my example to suit that number. SO, instead of the example above that reads two bytes at once, if you are only wanting ONE byte read, you might try
Pri Read_Register(rReg) : rVal | key
i2cStart ' start the transaction
i2cWrite(AddressW)
i2cWrite(rReg)
i2cStop
i2cStart
i2cWrite(AddressR) ' start the read
rVal := i2cRead(1) ' read first bit field 0 - 7
i2cStop
return rVal
The datasheet shows that some info is a single byte, and some has a high and a low byte like the above. At a glance, I do not see that you can read out two bytes in a single transaction, but I didn't look that far. In this case, you will have to read each register at a time, even if there is a low byte location and a high byte location, then bitshift the high byte value << 8, then OR the low byte read.
datavalue := Read_Register($3B) << 8 'get the high byte first in cases where there are high and low
datavalue |= Read_Register($3B + 1) 'get the low byte and OR it
Note; my example was for a chip ( Qprox 60160) that allows multiple reads within a single interaction, it expects you to read out 3 bytes. In your case, you only need to read out 2 bytes then quit that interaction. Now, that being said, I have not studied whether your chip will allow to read both msb and lsb in one interaction. I made the change so you can at least test it. It may be that you have to read for AA, then do a new read for AB. Your attempt with my example would not work because you were attempting to read 3 bytes in a row, when the device is only holding 2 bytes for that register or register pair.\
Note2: Oops, my comments above were as if you were still working with the other module trying to read out the eeprom calibration data in pairs of aa ab, etc. For any other module, you need to determine how many bytes are to be read from the register, and modify my example to suit that number. SO, instead of the example above that reads two bytes at once, if you are only wanting ONE byte read, you might try
I see what you mean by 3 byte read with your chip. I have updated the code to a single byte read for testing, still yet only the 7 of 21 valuse change.
By the looks of the PASM driver, it just starts at address #59 and reads consecutively the Accel X, Y, Z, Temp, Gyro X, Y, Z High & Low Bytes, passing each one to an "i" pointer (iaX, iaY, iaZ, etc) as it reads each register (2 bytes at a time).
I have been trying to get a Spin verson of the MPU-6050 driver working since I can compare my code & results with the PASM verson that works fine.
StartRead
call #i2cStart ' start
mov i2cData, i2cDevID ' send the address %11010000
mov i2cMask, #%10000000
call #i2cWrite ' do the write with the address using the Write value in the address lsb = 0
mov i2cData, i2cAddr 'get the decimal 59 read to send for the register address
mov i2cMask,#%10000000
call #i2cWrite ' do a write with the register address
call #i2cStart 'start
mov i2cData, i2cDevID ' get the device ID loaded and ready to send %11010000
or i2cData, #1 ' now make this write include a 1 to say we are ready to read something back
mov i2cMask, #%10000000
call #i2cWrite 'send the write with the read instruction so that the device will reply with the data in register location 59
StartRead_Ret ret
' // StartRead
i2cStart
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
i2cWrite($3B) ' Write Register
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
Not sure why you have this in here above? Try removing that.
There are a lot of configuration bits to set on this device, I haven't looked in great detail, but you need to be sure that what is being configured in PASM is also being set in your spin code. There is info that states that your I2C read must take place no slower than the sample rate, else your read gets destroyed half way though the read. There is a way to reduce the sample rate so that you can insure that the spin I2C read occurs no slower than the sample rate, but you will have to research this yourself. This is the most complicated I2C device I have ever looked at, certainly not ideal for a first time I2C learning experience. The PASM code is checking to see if the value was negative, and if so, it sign extends the sign bit 15 to bit 31. I did not do this, so if this works the values will at some point need to be bitshifted left 16, then SAR 16.
write: 'this is the PASM version of the i2c read initialize
call #i2cStart
call #i2cWrite i2cDevID 'with a 0 to indicate a write
call #i2cWrite i2cAddr 'send the register
call #i2cStart
call #i2cWrite i2cDevID with a 1 to indicate a read
Try this and see what happens
Pri Read_Register2(rReg) : rval | key
i2cStart ' start the transaction
i2cWrite(AddressW) '0 for write
i2cWrite(59) 'send the register to start the read at
i2cStart
i2cWrite(AddressR) ' start the read
iaXn := i2cRead(0) << 8
iaXn |= i2cRead(0)
iaYn := i2cRead(0) << 8
iaYn |= i2cRead(0)
iaZn := i2cRead(0) << 8
iaZn |= i2cRead(0)
iTn := i2cRead(0) << 8
iTn |= i2cRead(0)
irXn := i2cRead(0) << 8
irXn |= i2cRead(0)
irYn := i2cRead(0) << 8
irYn |= i2cRead(0)
irZn := i2cRead(0) << 8
irZn |= i2cRead(1)
i2cStop
'declare the variables as longs
'display the values in pst
Not sure why you have this in here above? Try removing that.
That was added because of your suggestion to "Notice what they are doing in PASM" from previous post.
That is the equivalent to adding a #StartRead just before it began reading registers.
StartRead
call #i2cStart ' start
mov i2cData, i2cDevID ' send the address 010000
mov i2cMask, #000000
call #i2cWrite ' do the write with the address using the Write value in the address lsb = 0
mov i2cData, i2cAddr 'get the decimal 59 read to send for the register address
mov i2cMask,#000000
call #i2cWrite ' do a write with the register address
call #i2cStart 'start
mov i2cData, i2cDevID ' get the device ID loaded and ready to send 010000
or i2cData, #1 ' now make this write include a 1 to say we are ready to read something back
mov i2cMask, #000000
call #i2cWrite 'send the write with the read instruction so that the device will reply with the data in register location 59
StartRead_Ret ret
' // StartRead
i2cStart
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
i2cWrite($3B) ' Write Register
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
Try this and see what happens
It gives results that appear correct, however, Gyro Z is completely missing (both High & Low = 0).
One thing to note, this works with & without bringing the chip out of "sleep" mode (PWR_MGMT_1).
I have done some more coding and defined the entire Register Map under Con, so there should be less confusion on what registers I am calling. Only using this on TestMPU2.
Running your last code in TestMPU3:
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq ' system freq as a constant
MS_001 = CLK_FREQ / 1_000 ' ticks in 1ms
US_001 = CLK_FREQ / 1_000_000 ' ticks in 1us
' // MPU-6050 Register Map
Con
Self_Test_X = $0D ' 13
Self_Test_Y = $0E ' 14
Self_Test_Z = $0F ' 15
Self_Test_A = $10 ' 15
SMPLRT_Div = $19 ' 25
Config = $1A ' 26
Gyro_Config = $1B ' 27
Accel_Config = $1C ' 28
Mot_Thr = $1F ' 31
FIFO_En = $23 ' 35
I2C_Mst_Ctrl = $24 ' 36
I2C_Slv0_Addr = $25 ' 37
I2C_Slv0_Reg = $26 ' 38
I2C_Slv0_Ctrl = $27 ' 39
I2C_Slv1_Addr = $28 ' 40
I2C_Slv1_Reg = $29 ' 41
I2C_Slv1_Ctrl = $2A ' 42
I2C_Slv2_Addr = $2B ' 43
I2C_Slv2_Reg = $2C ' 44
I2C_Slv2_Ctrl = $2D ' 45
I2C_Slv3_Addr = $2E ' 46
I2C_Slv3_Reg = $2F ' 47
I2C_Slv3_Ctrl = $30 ' 48
I2C_Slv4_Addr = $31 ' 49
I2C_Slv4_Reg = $32 ' 50
I2C_Slv4_Do = $33 ' 51
I2C_Slv4_Ctrl = $34 ' 52
I2C_Slv4_Di = $35 ' 53
I2C_Mst_Status = $36 ' 54
INT_Pin_Cfg = $37 ' 55
INT_Enable = $38 ' 56
INT_Status = $3A ' 58
Accel_XOut_H = $3B ' 59
Accel_XOut_L = $3C ' 60
Accel_YOut_H = $3D ' 61
Accel_YOut_L = $3E ' 62
Accel_ZOut_H = $3F ' 63
Accel_ZOut_L = $40 ' 64
Temp_Out_H = $41 ' 65
Temp_Out_L = $42 ' 66
Gyro_XOut_H = $43 ' 67
Gyro_XOut_L = $44 ' 68
Gyro_YOut_H = $45 ' 69
Gyro_YOut_L = $46 ' 70
Gyro_ZOut_H = $47 ' 71
Gyro_ZOut_L = $48 ' 72
Ext_Sens_Data_00 = $49 ' 73
Ext_Sens_Data_01 = $4A ' 74
Ext_Sens_Data_02 = $4B ' 75
Ext_Sens_Data_03 = $4C ' 76
Ext_Sens_Data_04 = $4D ' 77
Ext_Sens_Data_05 = $4E ' 78
Ext_Sens_Data_06 = $4F ' 79
Ext_Sens_Data_07 = $50 ' 80
Ext_Sens_Data_08 = $51 ' 81
Ext_Sens_Data_09 = $52 ' 82
Ext_Sens_Data_10 = $53 ' 83
Ext_Sens_Data_11 = $54 ' 84
Ext_Sens_Data_12 = $55 ' 85
Ext_Sens_Data_13 = $56 ' 86
Ext_Sens_Data_14 = $57 ' 87
Ext_Sens_Data_15 = $58 ' 88
Ext_Sens_Data_16 = $59 ' 89
Ext_Sens_Data_17 = $5A ' 90
Ext_Sens_Data_18 = $5B ' 91
Ext_Sens_Data_19 = $5C ' 92
Ext_Sens_Data_20 = $5D ' 93
Ext_Sens_Data_21 = $5E ' 94
Ext_Sens_Data_22 = $5F ' 95
Ext_Sens_Data_23 = $60 ' 96
I2C_Slv0_Do = $63 ' 99
I2C_Slv1_Do = $64 ' 100
I2C_Slv2_Do = $65 ' 101
I2C_Slv3_Do = $66 ' 102
I2C_Mst_Delay_Ctrl = $67 ' 103
Signal_Path_Reset = $68 ' 104
Mot_Detect_Ctrl = $69 ' 105
User_Ctrl = $6A ' 106
PWR_MGMT_1 = $6B ' 107
PWR_MGMT_2 = $6C ' 108
FIFO_CountH = $72 ' 114
FIFO_CountL = $73 ' 115
FIFO_R_W = $74 ' 116
WHO_AM_I = $75 ' 117
' // *** Reset Value is 0x00 for all registers other than:
' // Register 107: 0x40 (PWR_MGMT_1)
' // Register 117: 0x68 (WHO_AM_I)
' // This _Spin_ Con's
Con
I2C_SDA = 10
I2C_SCL = 11
AddressW = 01_0000 ' MPU-6050/9150 Write Address
AddressR = 01_0001 ' MPU-6050/9150 Read Address
MagAddr = 00_1100 ' AK8975 Slave Address $0C, $0D, $0E
Pause = clk_freq / 100000 ' Pause between checks for operations
Obj
ser : "FullDuplexSerial"
VAR
long Cog, Stack[32], Ready
long i2csda, i2cscl
long cID, Temp, aX, aY, aZ, gX, gY, gZ, mX, mY, mZ, drift
long iaXn, iaYn, iaZn, iTn, irXn, irYn, irZn
Pub TestSelect | vSel
computetimes
vSel := 3
if vSel == 1
TestMPU1 ' Reads 2 Registers at a Time via MPU9150_Loop
elseif vSel == 2
TestMPU2 ' Reads 1 Register at a Time
elseif vSel == 3
TestMPU3
PUB TestMPU1 | MPUcog
ser.start(31, 30, 0, 115200)
ser.str(string("Starting..."))
i2csda := I2C_SDA
i2cscl := I2C_SCL
MPUcog := Start( I2C_SCL, I2C_SDA )
repeat
ser.tx(13)
ser.str(string("Chip ID = 0x68: "))
ser.hex(cID, 2) ' = 0x68 (Correct)
ser.str(string(" "))
ser.hex(Temp, 4)
ser.str(string(", "))
ser.dec(drift)
ser.str(string(" "))
ser.hex(aX, 4)
ser.str(string(", "))
ser.hex(aY, 4)
ser.str(string(", "))
ser.hex(aZ, 4)
ser.str(string(" "))
ser.hex(gX, 4)
ser.str(string(", "))
ser.hex(gY, 4)
ser.str(string(", "))
ser.hex(gZ, 4)
ser.str(string(" "))
ser.hex(mX, 4)
ser.str(string(", "))
ser.hex(mY, 4)
ser.str(string(", "))
ser.hex(mZ, 4)
ser.tx(13)
PUB TestMPU2 | MPUcog
ser.start(31, 30, 0, 115200)
ser.str(string("Starting..."))
i2csda := I2C_SDA
i2cscl := I2C_SCL
' // *** MPUcog cannot be Started if reading Registers this way
'MPUcog := Start( I2C_SCL, I2C_SDA )
SetConfig
repeat
' // *** Only 7 Values Change
' // Who Am I
ser.tx(13)
ser.str(string("Who Am I: "))
ser.hex(Read_Register(WHO_AM_I), 2) ' = 0x68 (Correct)
ser.str(string(" "))
' // StartRead
i2cStart
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
i2cWrite(Accel_XOut_H) ' Write Register
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
' // Accelerometer
ser.hex(Read_Register(Accel_XOut_H), 2) ' High G
ser.str(string(", "))
ser.hex(Read_Register(Accel_XOut_L), 2) ' Low G
ser.str(string(", "))
ser.hex(Read_Register(Accel_YOut_H), 2) ' High G
ser.str(string(", "))
ser.hex(Read_Register(Accel_YOut_L), 2) ' Low G
ser.str(string(", "))
ser.hex(Read_Register(Accel_ZOut_H), 2) ' High G
ser.str(string(", "))
ser.hex(Read_Register(Accel_ZOut_L), 2) ' Low G
ser.str(string(" "))
' // Temperature
ser.hex(Read_Register(Temp_Out_H), 2) ' Fairly Stationary
ser.str(string(", "))
ser.hex(Read_Register(Temp_Out_L), 2) ' All Over
ser.str(string(" "))
' // Gyroscope
ser.hex(Read_Register(Gyro_XOut_H), 2) ' Moves With Rotation
ser.str(string(", "))
ser.hex(Read_Register(Gyro_XOut_L), 2) ' Moves With Rotation
ser.str(string(", "))
ser.hex(Read_Register(Gyro_YOut_H), 2) ' Moves With Rotation
ser.str(string(", "))
ser.hex(Read_Register(Gyro_YOut_L), 2) ' Moves With Rotation
ser.str(string(", "))
ser.hex(Read_Register(Gyro_ZOut_H), 2) ' 00
ser.str(string(", "))
ser.hex(Read_Register(Gyro_ZOut_L), 2) ' 00
ser.str(string(" "))
{
*** i2c Pass-Through Enabled:
#55 00000010 i2c_bypass_en = 1
#106 00000000 i2c_mst_en = 0
*** i2c Master Enabled:
#55 00000000 i2c_bypass_en = 0
#106 00100000 i2c_mst_en = 1
}
' // Should have to Enable Pass-Through Mode for this to work
'Write_Register(User_Ctrl, 000000) ' // *** Disable Master Mode
'Write_Register(INT_Pin_Cfg, 000010) ' // *** Enable Pass-Through Mode
Write_Address(MagAddr)
' // Magnetometer
ser.hex(Read_Register($03), 2) ' FC
ser.str(string(", "))
ser.hex(Read_Register($04), 2) ' 0F
ser.str(string(", "))
ser.hex(Read_Register($05), 2) ' A1
ser.str(string(", "))
ser.hex(Read_Register($06), 2) ' 84
ser.str(string(", "))
ser.hex(Read_Register($07), 2) ' A1
ser.str(string(", "))
ser.hex(Read_Register($08), 2) ' A1
ser.tx(13)
Pub TestMPU3
ser.start(31, 30, 0, 115200)
ser.str(string("Starting..."))
i2csda := I2C_SDA
i2cscl := I2C_SCL
'SetConfig ' Bring module out of "sleep" mode by setting PWR_MGMT_1 to 000001 (X gyro as clock source)
repeat
i2cStart ' start the transaction
i2cWrite(AddressW) ' 0 for write
i2cWrite(59) ' send the register to start the read at
i2cStart
i2cWrite(AddressR) ' start the read
iaXn := i2cRead(0) << 8
iaXn |= i2cRead(0)
iaYn := i2cRead(0) << 8
iaYn |= i2cRead(0)
iaZn := i2cRead(0) << 8
iaZn |= i2cRead(0)
iTn := i2cRead(0) << 8
iTn |= i2cRead(0)
irXn := i2cRead(0) << 8
irXn |= i2cRead(0)
irYn := i2cRead(0) << 8
irYn |= i2cRead(0)
irZn := i2cRead(0) << 8
irZn |= i2cRead(1)
i2cStop
'declare the variables as longs
'display the values in pst
ser.tx(13)
ser.hex(iaXn, 2)
ser.str(string(", "))
ser.hex(iaYn, 2)
ser.str(string(", "))
ser.hex(iaZn, 2)
ser.str(string(", "))
ser.hex(iTn, 2)
ser.str(string(", "))
ser.hex(irXn, 2)
ser.str(string(", "))
ser.hex(irYn, 2)
ser.str(string(", "))
ser.hex(irZn, 2)
ser.tx(13)
PUB Start(MPUscl, MPUsda)
i2csda := MPUsda
i2cscl := MPUscl
Ready := 0
cog := cognew(MPU9150_Loop, @stack)
PUB Stop
''Stops the Cog and the PID controller
cogstop(cog)
' // Main Loop
Pri MPU9150_Loop
SetConfig
waitcnt(Pause)
repeat
MPUReadValues
'MPUComputeDrift
'ComputeAngles
' // MPU-6050 / MPU-9150 Config
Pri SetConfig
i2cstart ' #i2cReset
Write_Register($6C, 000001) ' 107 - PWR_MGMT_1
Write_Register($19, 000001) ' 25 - SMPLRT_DIV = 1 => 1khz/(1+1) = 500hz sample rate
Write_Register($1A, 000100) ' 26 - Set DLPF_CONFIG to 4 for 20Hz bandwidth
Write_Register($1B, 011000) ' 27 - Gyro_Config
Write_Register($1C, 010000) ' 28 - Accel_Config
' // Pass-Through Mode Enabled
Write_Register($6A, 000000) ' // *** Disable Master Mode
Write_Register($37, 000010) ' // *** Enable Pass-Through Mode
' // Extra Config Settings
'Write_Register($FF, 000000)
' // MPU-6050 / MPU-9150 Get Values
Pri MPUReadValues | tTemp
cID := Read_Register($75) ' 117 - Chip_ID = 0x68 (Correct)
' // StartRead
i2cStart
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
i2cWrite($3B) ' Write Register
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
aX := Read_Register($3B) << 8 ' 59 - Accel_XOut_H
aX |= Read_Register($3C) ' 60 - Accel_XOut_L
aY := Read_Register($3D) << 8 ' 61 - Accel_XOut_H
aY |= Read_Register($3E) ' 62 - Accel_XOut_L
aZ := Read_Register($3F) << 8 ' 63 - Accel_XOut_H
aZ |= Read_Register($40) ' 64 - Accel_XOut_L
Temp := Read_Register($41) << 8 ' 65 - Temp_Out_H
Temp |= Read_Register($42) ' 66 - Temp_Out_L
tTemp := Temp
~~tTemp
drift := (tTemp + 15000) / 100
'waitcnt(Pause)
'waitcnt( constant(80_000_000 / 200) + cnt )
gX := Read_Register($43) << 8 ' 67 - Gyro_XOut_H
gX |= Read_Register($44) ' 68 - Gyro_XOut_L
gY := Read_Register($45) << 8 ' 69 - Gyro_XOut_H
gY |= Read_Register($46) ' 70 - Gyro_XOut_L
gZ := Read_Register($47) << 8 ' 71 - Gyro_XOut_H
gZ |= Read_Register($48) ' 72 - Gyro_XOut_L
'waitcnt(Pause)
'waitcnt( constant(80_000_000 / 200) + cnt )
mX := Read_Register($03) << 8 ' 59 - Mag_XOut_H
mX |= Read_Register($04) ' 60 - Mag_XOut_L
mY := Read_Register($05) << 8 ' 61 - Mag_XOut_H
mY |= Read_Register($06) ' 62 - Mag_XOut_L
mZ := Read_Register($07) << 8 ' 63 - Mag_XOut_H
mZ |= Read_Register($08) ' 64 - Mag_XOut_L
'waitcnt(Pause)
'waitcnt( constant(80_000_000 / 200) + cnt )
Pri Read_Register(rReg) : rVal | key
i2cStart ' start the transaction
i2cWrite(AddressW)
i2cWrite(rReg)
i2cStop
i2cStart
i2cWrite(AddressR) ' start the read
rVal := i2cRead(1) ' read first bit field 0 - 7
i2cStop
key := >| rVal 'encode >| bitfield tonumber
' // Method to debug with piezo (from example code)
if rVal == $FFFF
{use this to insure that if the Address fails or is unplugged
that the program does not lock since I2C will be showing $FFFF}
return 0
return rVal
Pri Read_Register2b(rReg) : rval | key
i2cStart ' start the transaction
i2cWrite(AddressW)
i2cWrite(rReg)
i2cStop
i2cStart
i2cWrite(AddressR) ' start the read
rVal := i2cRead(0) ' read first bit field 0 - 7
rVal |= i2cRead(1) << 8 ' you only need to read two bytes msb and lsb. the last read should include the 1 to tell it we are done now
i2cStop
key := >| rVal 'encode >| bitfield tonumber
' // Method to debug with piezo (from example code)
if rVal == $FFFF
{use this to insure that if the Address fails or is unplugged
that the program does not lock since I2C will be showing $FFFF}
return 0
return rVal
' // MPU Read / Write Functions
Pri Read_Register3(rReg) : rVal | key
i2cStart ' start the transaction
i2cWrite(AddressW)
i2cWrite(rReg)
i2cStop
i2cStart
i2cWrite(AddressR) ' start the read
rVal := i2cRead(0) '<< 8 ' read first bit field 0 - 7
rVal |= i2cRead(0) << 8 ' read second bit field 8 - 15
rVal[1] := i2cRead(1) ' read last unused bits 9 - 24 24 bit total read out
i2cStop
key := >| rVal 'encode >| bitfield tonumber
' // Method to debug with piezo (from example code)
if rVal == $FFFF
{use this to insure that if the Address fails or is unplugged
that the program does not lock since I2C will be showing $FFFF}
return 0
return rVal
Pri Write_Address(wAddr)
i2cStart
i2cWrite(wAddr)
i2cStop
Pri Write_Register(wReg, wVal)
i2cStart
i2cWrite(AddressW)
i2cWrite(wReg)
i2cWrite(wVal)
i2cStop
PRI computeTimes '' Set up timing constants in assembly
' (Done this way to avoid overflow)
i2cDataSet := ((clkfreq / 10000) * 350) / 100000 ' Data setup time - 350ns (400KHz)
i2cClkLow := ((clkfreq / 10000) * 1300) / 100000 ' Clock low time - 1300ns (400KHz)
i2cClkHigh := ((clkfreq / 10000) * 600) / 100000 ' Clock high time - 600ns (400KHz)
i2cPause := clkfreq / 100000 ' Pause between checks for operations
' // Minimal I2C Driver:
con
i2cack = 0
i2cnak = 1
i2cxmit = 0
i2crecv = 1
i2cboot = 28
i2ceeprom = $a0
'Var
' long i2csda, i2cscl
Pri i2cstart
outa[i2cscl]~~
dira[i2cscl]~~
outa[i2csda]~~
dira[i2csda]~~
outa[i2csda]~
outa[i2cscl] ~
Pri i2cstop
outa[i2cscl] ~~
outa[i2csda] ~~
dira[i2cscl] ~
dira[i2csda] ~
Pri i2cwrite(i2cdata) : ackbit
ackbit := 0
i2cdata <<= 24
repeat 8
outa[i2csda] := (i2cdata <-= 1) & 1
outa[i2cscl] ~~
outa[i2cscl] ~
dira[i2csda] ~
outa[i2cscl] ~~
ackbit := ina[i2csda]
outa[i2cscl] ~
outa[i2csda] ~
dira[i2csda] ~~
Pri i2cread(ackbit): i2cdata
i2cdata := 0
dira[i2csda]~
repeat 8
outa[i2cscl] ~~
i2cdata := (i2cdata << 1) | ina[i2csda]
outa[i2cscl] ~
outa[i2csda] := ackbit
dira[i2csda] ~~
outa[i2cscl] ~~
outa[i2cscl] ~
outa[i2csda] ~
Pri i2creadpage(i2caddr, addrreg, dataptr, count) : ackbit
i2caddr |= addrreg >> 15 & 10
i2cstart
ackbit := i2cwrite(i2caddr | i2cxmit)
ackbit := (ackbit << 1) | i2cwrite(addrreg >> 8 & $ff)
ackbit := (ackbit << 1) | i2cwrite(addrreg & $ff)
i2cstart
ackbit := (ackbit << 1) | i2cwrite(i2caddr | i2crecv)
repeat count - 1
byte[dataptr++] := i2cread(i2cack)
byte[dataptr++] := i2cread(i2cnak)
i2cstop
return ackbit
Pri i2cwritepage(i2caddr, addrreg, dataptr, count) : ackbit
i2caddr |= addrreg >> 15 & 10
i2cstart
ackbit := i2cwrite(i2caddr | i2cxmit)
ackbit := (ackbit << 1) | i2cwrite(addrreg >> 8 & $ff)
ackbit := (ackbit << 1) | i2cwrite(addrreg & $ff)
repeat count
ackbit := ackbit << 1 | ackbit & $80000000
ackbit |= i2cwrite(byte[dataptr++])
i2cstop
return ackbit
Dat
i2cDataSet long 0 ' Minumum data setup time (ticks)
i2cClkLow long 0 ' Minimum clock low time (ticks)
i2cClkHigh long 0 ' Minimum clock high time (ticks)
i2cPause long 0 ' Pause before re-fetching next operation
{
PUB TestMPU2 | MPUcog
ser.start(31, 30, 0, 115200)
ser.str(string("Starting..."))
i2csda := I2C_SDA
i2cscl := I2C_SCL
' // *** MPUcog cannot be Started if reading Registers this way
'MPUcog := Start( I2C_SCL, I2C_SDA )
SetConfig
repeat
' // *** Only 7 Values Change
' // Who Am I
ser.tx(13)
ser.str(string("Who Am I: "))
ser.hex(Read_Register($75), 2) ' = 0x68 (Correct)
ser.str(string(" "))
' // StartRead
i2cStart
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
i2cWrite($3B) ' Write Register
i2cWrite(AddressW) ' Write Address 010000 ' mask = 000000 ??? What does this do???
' // Accelerometer
ser.hex(Read_Register($3B), 2) ' 80
ser.str(string(", "))
ser.hex(Read_Register($3C), 2) ' 00
ser.str(string(", "))
ser.hex(Read_Register($3D), 2) ' High G
ser.str(string(", "))
ser.hex(Read_Register($3E), 2) ' Mid G
ser.str(string(", "))
ser.hex(Read_Register($3F), 2) ' Low G
ser.str(string(", "))
ser.hex(Read_Register($40), 2) ' Low G
ser.str(string(" "))
' // Temperature
ser.hex(Read_Register($41), 2) ' Fairly Stationary
ser.str(string(", "))
ser.hex(Read_Register($42), 2) ' All Over
ser.str(string(" "))
' // Gyroscope
ser.hex(Read_Register($43), 2) ' Moves With Rotation
ser.str(string(", "))
ser.hex(Read_Register($44), 2) ' Moves With Rotation
ser.str(string(", "))
ser.hex(Read_Register($45), 2) ' Moves With Rotation
ser.str(string(", "))
ser.hex(Read_Register($46), 2) ' Moves With Rotation
ser.str(string(", "))
ser.hex(Read_Register($47), 2) ' 00
ser.str(string(", "))
ser.hex(Read_Register($48), 2) ' 00
ser.str(string(" "))
{
*** i2c Pass-Through Enabled:
#55 00000010 i2c_bypass_en = 1
#106 00000000 i2c_mst_en = 0
*** i2c Master Enabled:
#55 00000000 i2c_bypass_en = 0
#106 00100000 i2c_mst_en = 1
}
' // Should have to Enable Pass-Through Mode for this to work
'Write_Register($6A, 000000) ' // *** Disable Master Mode
'Write_Register($37, 000010) ' // *** Enable Pass-Through Mode
' // Magnetometer
ser.hex(Read_Register($03), 2) ' FC
ser.str(string(", "))
ser.hex(Read_Register($04), 2) ' 0F
ser.str(string(", "))
ser.hex(Read_Register($05), 2) ' A1
ser.str(string(", "))
ser.hex(Read_Register($06), 2) ' 84
ser.str(string(", "))
ser.hex(Read_Register($07), 2) ' A1
ser.str(string(", "))
ser.hex(Read_Register($08), 2) ' A1
ser.str(string(" "))
'Write_Register($6A, 100000) ' // *** Re-Enable Master Mode
}
I am thinking that it is working to the equivalent of the mpu6050 pasm driver (to some extent). Pass-through mode wont enable (which I have read others had same issue) & I cannot read the aux bus. And by the looks of it, my Gyro Z axis is dead, as it is misreading with the pasm driver as well.
It would appear that my Gyro Z axis is Not dead, instead, looking back over my code, I had set PWR_MGM_2 = 1 and it should = 0 (=1 is gyro z standby).
I wrote a test to check the registers that I have wrote to to verify that it is receiving my input, which it was.
Now for getting the Magnetometer data from the sensor, which is supposedly possible by setting it to pass-through mode, which I have, and still no data back? Not too sure about this one.
Many Many Thanks to everyone that got me this far!
Here is my latest code for the MPU-9150 Driver. The only problem I belive I am facing now is getting the Aux bus to work either in Master mode or Pass-Through mode. If I have to use the FIFO to read ALL the data so be it.
I now understand bit shifting & i2c a whole hell of a lot better, thanks to you guys! Getting these next few sensors on lock will be no problem at this point.
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
CLK_FREQ = ((_clkmode - xtal1) >> 6) * _xinfreq ' system freq as a constant
MS_001 = CLK_FREQ / 1_000 ' ticks in 1ms
US_001 = CLK_FREQ / 1_000_000 ' ticks in 1us
' // MPU-6050 Register Map
Con
Self_Test_X = $0D ' 13
Self_Test_Y = $0E ' 14
Self_Test_Z = $0F ' 15
Self_Test_A = $10 ' 15
SMPLRT_Div = $19 ' 25
Config = $1A ' 26
Gyro_Config = $1B ' 27
Accel_Config = $1C ' 28
Mot_Thr = $1F ' 31
FIFO_En = $23 ' 35
I2C_Mst_Ctrl = $24 ' 36
I2C_Slv0_Addr = $25 ' 37
I2C_Slv0_Reg = $26 ' 38
I2C_Slv0_Ctrl = $27 ' 39
I2C_Slv1_Addr = $28 ' 40
I2C_Slv1_Reg = $29 ' 41
I2C_Slv1_Ctrl = $2A ' 42
I2C_Slv2_Addr = $2B ' 43
I2C_Slv2_Reg = $2C ' 44
I2C_Slv2_Ctrl = $2D ' 45
I2C_Slv3_Addr = $2E ' 46
I2C_Slv3_Reg = $2F ' 47
I2C_Slv3_Ctrl = $30 ' 48
I2C_Slv4_Addr = $31 ' 49
I2C_Slv4_Reg = $32 ' 50
I2C_Slv4_Do = $33 ' 51
I2C_Slv4_Ctrl = $34 ' 52
I2C_Slv4_Di = $35 ' 53
I2C_Mst_Status = $36 ' 54
INT_Pin_Cfg = $37 ' 55
INT_Enable = $38 ' 56
INT_Status = $3A ' 58
Accel_XOut_H = $3B ' 59
Accel_XOut_L = $3C ' 60
Accel_YOut_H = $3D ' 61
Accel_YOut_L = $3E ' 62
Accel_ZOut_H = $3F ' 63
Accel_ZOut_L = $40 ' 64
Temp_Out_H = $41 ' 65
Temp_Out_L = $42 ' 66
Gyro_XOut_H = $43 ' 67
Gyro_XOut_L = $44 ' 68
Gyro_YOut_H = $45 ' 69
Gyro_YOut_L = $46 ' 70
Gyro_ZOut_H = $47 ' 71
Gyro_ZOut_L = $48 ' 72
Ext_Sens_Data_00 = $49 ' 73
Ext_Sens_Data_01 = $4A ' 74
Ext_Sens_Data_02 = $4B ' 75
Ext_Sens_Data_03 = $4C ' 76
Ext_Sens_Data_04 = $4D ' 77
Ext_Sens_Data_05 = $4E ' 78
Ext_Sens_Data_06 = $4F ' 79
Ext_Sens_Data_07 = $50 ' 80
Ext_Sens_Data_08 = $51 ' 81
Ext_Sens_Data_09 = $52 ' 82
Ext_Sens_Data_10 = $53 ' 83
Ext_Sens_Data_11 = $54 ' 84
Ext_Sens_Data_12 = $55 ' 85
Ext_Sens_Data_13 = $56 ' 86
Ext_Sens_Data_14 = $57 ' 87
Ext_Sens_Data_15 = $58 ' 88
Ext_Sens_Data_16 = $59 ' 89
Ext_Sens_Data_17 = $5A ' 90
Ext_Sens_Data_18 = $5B ' 91
Ext_Sens_Data_19 = $5C ' 92
Ext_Sens_Data_20 = $5D ' 93
Ext_Sens_Data_21 = $5E ' 94
Ext_Sens_Data_22 = $5F ' 95
Ext_Sens_Data_23 = $60 ' 96
I2C_Slv0_Do = $63 ' 99
I2C_Slv1_Do = $64 ' 100
I2C_Slv2_Do = $65 ' 101
I2C_Slv3_Do = $66 ' 102
I2C_Mst_Delay_Ctrl = $67 ' 103
Signal_Path_Reset = $68 ' 104
Mot_Detect_Ctrl = $69 ' 105
User_Ctrl = $6A ' 106
PWR_MGMT_1 = $6B ' 107
PWR_MGMT_2 = $6C ' 108
FIFO_CountH = $72 ' 114
FIFO_CountL = $73 ' 115
FIFO_R_W = $74 ' 116
WHO_AM_I = $75 ' 117
' // *** Reset Value is 0x00 for all registers other than:
' // Register 107: 0x40 (PWR_MGMT_1)
' // Register 117: 0x68 (WHO_AM_I)
' // This _Spin_ Con's
Con
I2C_SDA = 10
I2C_SCL = 11
AddressW = %1101_0000 ' MPU-6050/9150 Write Address
AddressR = %1101_0001 ' MPU-6050/9150 Read Address
MagAddr = %0000_1100 ' AK8975 Slave Address $0C, $0D, $0E
Pause = clk_freq / 100000 ' Pause between checks for operations
Obj
ser : "FullDuplexSerial"
VAR
long Cog, Stack[32], Ready, drift
long i2csda, i2cscl
long cID, Temp, aX, aY, aZ, gX, gY, gZ, mX, mY, mZ, E1, E2, E3, FIFOcnt, FIFOrw
long iaXn, iaYn, iaZn, iTn, irXn, irYn, irZn
long vPWR, vSMP, vDLPF, vGyro, vAccel, vMM, vPT
Pub TestSel | tSel
tSel := 1
if tSel == 1
TestMPU
elseif tSel == 2
TestMPU2
Pub TestMPU | MPUcog
ser.start(31, 30, 0, 115200)
ser.str(string("Starting..."))
i2csda := I2C_SDA
i2cscl := I2C_SCL
MPUcog := Start( I2C_SCL, I2C_SDA )
repeat
ser.tx(13)
ser.str(string("Who Am I: "))
ser.hex(cID, 2)
ser.str(string(" "))
' // Accelerometer
ser.dec(aX)
ser.str(string(", "))
ser.dec(aY)
ser.str(string(", "))
ser.dec(aZ)
ser.str(string(" "))
' // Temperature
ser.dec(Temp)
ser.str(string(" "))
' // Gyroscope
ser.dec(gX)
ser.str(string(", "))
ser.dec(gY)
ser.str(string(", "))
ser.dec(gZ)
ser.str(string(" "))
' // Magnetometer
ser.dec(mX)
ser.str(string(", "))
ser.dec(mY)
ser.str(string(", "))
ser.dec(mZ)
ser.str(string(" "))
{
' // *** Master Mode External Sensor:
' // Set Slave 0 Address
' // Set Slave 0 Register
' // Set Slave 0 Control Bytes Read = 2
' // Slave0_Do = Data To Slave
' // Slave0_Di = Data From Slave
' // External Sensor Data
ser.dec(E1)
ser.str(string(", "))
ser.dec(E2)
ser.str(string(", "))
ser.dec(E3)
ser.str(string(" "))
}
{
' // Set Use FIFO
' // R/W FIFOcount
' // Read To / Write From FIFO
ser.str(string("FIFO: "))
ser.dec(FIFOcnt)
ser.str(string(", "))
ser.dec(FIFOrw)
}
ser.tx(13)
{
' // Verify Registers Wrote Correctly
vPWR := Read_Register($6B)
vSMP := Read_Register($19)
vDLPF := Read_Register($1A)
vGyro := Read_Register($1B)
vAccel := Read_Register($1C)
vMM := Read_Register($6A)
vPT := Read_Register($37)
' // Verified Wrote Params
ser.bin(vPWR, 8) ' %00000001
ser.str(string(", "))
ser.bin(vSMP, 8) ' %00000001
ser.str(string(", "))
ser.bin(vDLPF, 8) ' %00000100
ser.str(string(", "))
ser.bin(vGyro, 8) ' %00011000
ser.str(string(", "))
ser.bin(vAccel, 8) ' %00010000
ser.str(string(", "))
ser.bin(vMM, 8) ' %00000000
ser.str(string(", "))
ser.bin(vPT ,8) ' %00000010
ser.tx(13)
}
Pub TestMPU2
ser.start(31, 30, 0, 115200)
ser.str(string("Starting..."))
i2csda := I2C_SDA
i2cscl := I2C_SCL
SetConfig ' Bring module out of "sleep" mode by setting PWR_MGMT_1 to %00000001 (X gyro as clock source)
repeat
i2cStart ' start the transaction
i2cWrite(AddressW) ' 0 for write
i2cWrite(59) ' send the register to start the read at
i2cStart
i2cWrite(AddressR) ' start the read
iaXn := i2cRead(0) << 8
iaXn |= i2cRead(0)
iaYn := i2cRead(0) << 8
iaYn |= i2cRead(0)
iaZn := i2cRead(0) << 8
iaZn |= i2cRead(0)
iTn := i2cRead(0) << 8
iTn |= i2cRead(0)
irXn := i2cRead(0) << 8
irXn |= i2cRead(0)
irYn := i2cRead(0) << 8
irYn |= i2cRead(0)
irZn := i2cRead(0) << 8
irZn |= i2cRead(1)
i2cStop
'declare the variables as longs
'display the values in pst
ser.tx(13)
ser.hex(iaXn, 4)
ser.str(string(", "))
ser.hex(iaYn, 4)
ser.str(string(", "))
ser.hex(iaZn, 4)
ser.str(string(", "))
ser.hex(iTn, 4)
ser.str(string(", "))
ser.hex(irXn, 4)
ser.str(string(", "))
ser.hex(irYn, 4)
ser.str(string(", "))
ser.hex(irZn, 4)
ser.tx(13)
PUB Start(MPUscl, MPUsda)
i2csda := MPUsda
i2cscl := MPUscl
Ready := 0
cog := cognew(MPU9150_Loop, @stack)
PUB Stop
''Stops the Cog and the PID controller
cogstop(cog)
' // Main Loop
Pri MPU9150_Loop
SetConfig
repeat
MPUReadValues
'MPUComputeDrift
'ComputeAngles
' // MPU-6050 / MPU-9150 Config
Pri SetConfig
i2cstart ' #i2cReset
Write_Register(PWR_MGMT_1, %00000001) ' 107 - PWR_MGMT_1
Write_Register(SMPLRT_DIV, %00000001) ' 25 - SMPLRT_DIV = 1 => 1khz/(1+1) = 500hz sample rate
Write_Register(CONFIG, %00000100) ' 26 - Set DLPF_CONFIG to 4 for 20Hz bandwidth
Write_Register(User_Ctrl, %00000000) ' 106 - Disable Master Mode
Write_Register(Gyro_Config, %00011000) ' 27 - Gyro_Config
Write_Register(Accel_Config, %00010000) ' 28 - Accel_Config
Write_Register(INT_Pin_Cfg, %00110010) ' 55 - INT_Pin_Cfg
Write_Register(INT_Enable, %00000001) ' 56 - INT_Enable
{
Write_Register($6B, %00000001) ' 107 - PWR_MGMT_1
Write_Register($19, %00000001) ' 25 - SMPLRT_DIV = 1 => 1khz/(1+1) = 500hz sample rate
Write_Register($1A, %00000100) ' 26 - Set DLPF_CONFIG to 4 for 20Hz bandwidth
Write_Register($6A, %00000000) ' 106 - Disable Master Mode
Write_Register($1B, %00011000) ' 27 - Gyro_Config
Write_Register($1C, %00010000) ' 28 - Accel_Config
Write_Register($37, %00110010) ' 55 - INT_Pin_Cfg
Write_Register($38, %00000001) ' 56 - INT_Enable
}
{
0x6B = 0x01; // Clock source is PLLGYROZ
0x19 = 0x04; // Set SMPLRT_DIV to 0x04; this gives a 200 Hz sample rate when using the DLPF
0x1A = 0x03; // 42 Hz LPF
0x6A = 0x01; // Disable master mode and clear all signal paths
0x1B = 0x18; // Full gyro range
0x1C = 0x08; // 4G accel range
0x37 = 0x32; // Interrupts pin stays high until cleared, cleared on any read, I2C bypass
0x38 = 0x01; // Data ready interrupt enabled
}
{
You could try putting it in FUSEROM mode (write 0x0F to register 0xA) and then reading the sensitivity
adjustment values from registers 0x10 to 0x12; you should get the same value across multiple reads.
I'm not sure if there's a "proper" initialization sequence for the AK8975, but I've had the most
success with: Put in FUSEROM mode -> read ASAX/Y/Z -> single sample mode, and then waiting on the
DRDY interrupt (which, despite what the MPU9150 data sheet claims, is available on pin 14). I
definitely had a large amount of trouble actually getting it to work correctly though.
}
' // MPU-6050 / MPU-9150 Get Values
Pri MPUReadValues | tTemp
' // Chip ID
cID := Read_Register(WHO_AM_I)
' // Accelerometer
aX := Read_Register(Accel_XOut_H) << 8
aX |= Read_Register(Accel_XOut_L)
aY := Read_Register(Accel_YOut_H) << 8
aY |= Read_Register(Accel_YOut_L)
aZ := Read_Register(Accel_ZOut_H) << 8
aZ |= Read_Register(Accel_ZOut_L)
' // Temperature
Temp := Read_Register(Temp_Out_H) << 8
Temp |= Read_Register(Temp_Out_L)
' // Gyroscope
gX := Read_Register(Gyro_XOut_H) << 8
gX |= Read_Register(Gyro_XOut_L)
gY := Read_Register(Gyro_YOut_H) << 8
gY |= Read_Register(Gyro_YOut_L)
gZ := Read_Register(Gyro_ZOut_H) << 8
gZ |= Read_Register(Gyro_ZOut_L)
' // External Sensor Data
' // *** Master Mode = ON, Slave#_Enabled = True, Slave# Configured (Address & Register)
E1 := Read_Register(Ext_Sens_Data_00) << 8
E1 |= Read_Register(Ext_Sens_Data_01)
E2 := Read_Register(Ext_Sens_Data_02) << 8
E2 |= Read_Register(Ext_Sens_Data_03)
E3 := Read_Register(Ext_Sens_Data_04) << 8
E3 |= Read_Register(Ext_Sens_Data_05)
' // Get FIFO Count
FIFOcnt := Read_Register(FIFO_CountH) << 8
FIFOcnt |= Read_Register(FIFO_CountL)
'repeat FIFOcnt
FIFOrw := Read_Register(FIFO_R_W) << 8
FIFOrw |= Read_Register(FIFO_R_W)
' // *** Pass-Through Enabled in SetConfig
' // Should have to Enable Pass-Through Mode for this to work
'Write_Register(User_Ctrl, %00000000) ' // *** Disable Master Mode
'Write_Register(INT_Pin_Cfg, %00000010) ' // *** Enable Pass-Through Mode
' // Switch Address
'Write_Address(MagAddr)
' // Magnetometer
mX := Read_Register($03) << 8
mX |= Read_Register($04)
mY := Read_Register($05) << 8
mY |= Read_Register($06)
mZ := Read_Register($07) << 8
mZ |= Read_Register($08)
' // Read Mag Register
Pri Read_Reg(rReg) : rVal | key
i2cStart ' start the transaction
i2cWrite(%0000_1100) ' AK8975 Write Address
i2cWrite(rReg)
i2cStop
i2cStart ' start the read
i2cWrite(%0000_1101) ' AK8975 Read Address
rVal := i2cRead(1) ' read first bit field 0 - 7
i2cStop
key := >| rVal 'encode >| bitfield tonumber
' // Method to debug with piezo (from example code)
if rVal == $FFFF
{use this to insure that if the Address fails or is unplugged
that the program does not lock since I2C will be showing $FFFF}
return 0
return rVal
' // Read MPU Register
Pri Read_Register(rReg) : rVal | key
i2cStart ' start the transaction
i2cWrite(AddressW)
i2cWrite(rReg)
i2cStop
i2cStart
i2cWrite(AddressR) ' start the read
rVal := i2cRead(1) ' read first bit field 0 - 7
i2cStop
key := >| rVal 'encode >| bitfield tonumber
' // Method to debug with piezo (from example code)
if rVal == $FFFF
{use this to insure that if the Address fails or is unplugged
that the program does not lock since I2C will be showing $FFFF}
return 0
return rVal
Pri Write_Address(wAddr)
i2cStart
i2cWrite(wAddr)
i2cStop
Pri Write_Register(wReg, wVal)
i2cStart
i2cWrite(AddressW)
i2cWrite(wReg)
i2cWrite(wVal)
i2cStop
PRI computeTimes '' Set up timing constants in assembly
' (Done this way to avoid overflow)
i2cDataSet := ((clkfreq / 10000) * 350) / 100000 ' Data setup time - 350ns (400KHz)
i2cClkLow := ((clkfreq / 10000) * 1300) / 100000 ' Clock low time - 1300ns (400KHz)
i2cClkHigh := ((clkfreq / 10000) * 600) / 100000 ' Clock high time - 600ns (400KHz)
i2cPause := clkfreq / 100000 ' Pause between checks for operations
' // Minimal I2C Driver:
con
i2cack = 0
i2cnak = 1
i2cxmit = 0
i2crecv = 1
i2cboot = 28
i2ceeprom = $a0
'Var
' long i2csda, i2cscl
Pri i2cstart
outa[i2cscl]~~
dira[i2cscl]~~
outa[i2csda]~~
dira[i2csda]~~
outa[i2csda]~
outa[i2cscl] ~
Pri i2cstop
outa[i2cscl] ~~
outa[i2csda] ~~
dira[i2cscl] ~
dira[i2csda] ~
Pri i2cwrite(i2cdata) : ackbit
ackbit := 0
i2cdata <<= 24
repeat 8
outa[i2csda] := (i2cdata <-= 1) & 1
outa[i2cscl] ~~
outa[i2cscl] ~
dira[i2csda] ~
outa[i2cscl] ~~
ackbit := ina[i2csda]
outa[i2cscl] ~
outa[i2csda] ~
dira[i2csda] ~~
Pri i2cread(ackbit): i2cdata
i2cdata := 0
dira[i2csda]~
repeat 8
outa[i2cscl] ~~
i2cdata := (i2cdata << 1) | ina[i2csda]
outa[i2cscl] ~
outa[i2csda] := ackbit
dira[i2csda] ~~
outa[i2cscl] ~~
outa[i2cscl] ~
outa[i2csda] ~
Pri i2creadpage(i2caddr, addrreg, dataptr, count) : ackbit
i2caddr |= addrreg >> 15 & %1110
i2cstart
ackbit := i2cwrite(i2caddr | i2cxmit)
ackbit := (ackbit << 1) | i2cwrite(addrreg >> 8 & $ff)
ackbit := (ackbit << 1) | i2cwrite(addrreg & $ff)
i2cstart
ackbit := (ackbit << 1) | i2cwrite(i2caddr | i2crecv)
repeat count - 1
byte[dataptr++] := i2cread(i2cack)
byte[dataptr++] := i2cread(i2cnak)
i2cstop
return ackbit
Pri i2cwritepage(i2caddr, addrreg, dataptr, count) : ackbit
i2caddr |= addrreg >> 15 & %1110
i2cstart
ackbit := i2cwrite(i2caddr | i2cxmit)
ackbit := (ackbit << 1) | i2cwrite(addrreg >> 8 & $ff)
ackbit := (ackbit << 1) | i2cwrite(addrreg & $ff)
repeat count
ackbit := ackbit << 1 | ackbit & $80000000
ackbit |= i2cwrite(byte[dataptr++])
i2cstop
return ackbit
Dat
i2cDataSet long 0 ' Minumum data setup time (ticks)
i2cClkLow long 0 ' Minimum clock low time (ticks)
i2cClkHigh long 0 ' Minimum clock high time (ticks)
i2cPause long 0 ' Pause before re-fetching next operation
There is one more thing with this I need to understand, the values I get with the Spin driver is between 0 and 65535, but the PASM driver it is -4096 to 4096.
Do I just divide 65535 by 8192 then multiply the sum of that with the Value_IN to shift the number line?
Also, the compass reads its values in Low Bit first then High Bit, would I do the bitshifting like this:
As stated before, you MUST convert some values in the device to SIGNED values by taking the FINAL 16bit result, bitshift left 16, then Shift Arithmetic Right 16. Then display the number. You cannot skip over this info that was already mentioned several times as part of the basic fundamentals of building this engine. If you review the PASM object, you will see where they are "sign extending" the result in cases where bit 15 was tested to be a 1. If bit 15, the most significant bit, of a 16bit value is 1, then the value is negative. Since you are working in a 32 bit device, you must move the sign bit over to the position of the sign bit of a 32 bit device. You do this by bitshifting the entire value over 16 so now the high bit, bit 15, is now in the high bit position of a LONG. Then, you move the remaining MATH back where it was to retain the same value as was original found in the device The device uses a 16 bit format, with the high bit being the sign bit,
Whatever you read from a device that is declared to be the HIGH byte, assuming that it is part of a word value(16 bits total, as your device is), you must move that byte over by 8, as you have shown.
Pri Read_Register(rReg) : rVal | key
i2cStart ' start the transaction
i2cWrite(AddressW)
i2cWrite(rReg)
i2cStop
i2cStart
i2cWrite(AddressR) ' start the read
rVal := i2cRead(1) ' read first bit field 0 - 7
i2cStop
return rVal
Use this above if you are going after one read at a time, I see that you may not be using this anymore but in case you do. The other stuff I deleted was for my device and requirements. Were you not able to get the other example I posted to work that read in all the 14 bytes in one scoop? That would be a faster loop.
I'll be cleaning it up tomorrow to be posted in the obex!
If I may, please ensure that your code is fully vetted before posting in ObEx -- you're not doing anyone any favors by posting too early.
the values I get with the Spin driver is between 0 and 65535, but the PASM driver it is -4096 to 4096
Spin has a special operator to extend the sign of a 16-bit value. Assuming that you've read back the register into a long, you can do this
mX := ~~mX
You just need to ensure the sign bit is in bit15 of the value. If it's not, you can use a combination of << (to move the sign bit to bit31) and ~> (to restore the LSB position).
If I may, please ensure that your code is fully vetted before posting in ObEx -- you're not doing anyone any favors by posting too early.
Spin has a special operator to extend the sign of a 16-bit value. Assuming that you've read back the register into a long, you can do this
mX := ~~mX
You just need to ensure the sign bit is in bit15 of the value. If it's not, you can use a combination of << (to move the sign bit to bit31) and ~> (to restore the LSB position).
Absolutely, I am not interested in posting bad / unfinished code. I wouldn't be needing to write my own 9150 or BMP driver, if that was the case for everyone, as there is already one on obex for it... But the driver dont work. Has all the right stuff, just no data comes out. Same for the BMP085 / BMP108. it has multiple drivers that dont work on obex.
I tried the ~~ method, and that puts it in a +/- scale, but the range is still off.
The Top order is Accel X, Y, Z, Temp, Gyro X, Y, Z, Mag X, Y, Z. (Output from My Driver)
The Bottom order is Accel X, Y, Z, Gyro X, Y, Z. (Output from MPU-6050 PASM Driver)
Have you verified if the sampling rate is no faster than the i2c read? If the sampling rate exceeds the i2c read time, you will get garbage. There may be better ways, but one idea is to put a variable in the loop that is incrementing per repeat. Then if the count reaches some number, put a flag on the screen or turn on an led so you can approximate the i2c read frequency. Also, you could get the cnt at the start of the loop, then after some number of repeats, get the new time - old time and see how many i2c reads occurred in the elapse count time.
Pub TestSel | tSel
countold := cnt
timer~
repeat
TestMPU2
timer++
if timer == 5000
finalcount := cnt - countold
pst.dec(finalcount)
repeat 'lock up here
clockfreq / finalcount is the time in seconds that elapsed over the 5000 reads. so the i2c freq = x seconds / 5000. You better confirm the formula but you get the idea. Compare to your sample rate.
Have you verified if the sampling rate is no faster than the i2c read? If the sampling rate exceeds the i2c read time, you will get garbage.
That I havent and I am not exactly sure how I would do that.
It doesn't appear to be trash, the mag is fairly stationary & knows were it is at. The number line is trash tho. Temperature only changes with temp changes, up is up, down is down, 0-65535. The Accel & Gyro look right too, just with another jacked number line. The Gyro X axis is inverted, need to subtract gX := 65535 - gX, to bring it back to the right spot, but then the whole gyro reading has inverted sign (+/-).
Comments
How is a non-hardware person supposed to cope?! (With the help of these forums...). So... how big is a trace before it's a plane...?
The idea is to minimize the impedance both to DC and to higher frequency. Connecting to a "ground plane" through vias is fine although a direct connection on the same side that's also shorter (and better) is much preferred. That is why I wondered why this large center "pad" was not utilized in this manner. In fact if it were then there would be less reliance on the bypass caps and the crystal could, even a large one, move closer to the Prop. The crystal input pin is a very sensitive amplifier which can easily pick up electrical noise, even from other signals on the PCB and the clock signal that is generated from the crystal is vital to the reliable operation of the Prop, everything derives from this one clock.
A trace is never a plane. A plane is normally a large mostly unbroken area that runs under multiple pads and devices and is also characterized by it's low impedance and shielding effect (and multiple via connections).
Right, but... is a trace (say) 2mm wide running the full length of a 200mm board worse than a pad 20mm square? Each is 400mm^2. What if the trace is 6mm wide? Or 10mm? (In other words, is it then a plane and not a trace?) Is the surface area important, or the length of the run? And then the next question would be if it's the length of the run then what about a pad which is the entire under-side of the (200mm) board? Same length as the trace? So is it a combination of surface area and length?
Maybe I need to pick up a book...
I like The Circuit Designer's Companion and Surface Mount Technology for PC Boards.
.
If a device works perfectly with PASM, then you should not be spending time trying to chase down PCB design issues. Since most people will not invest a lot of time in decrypting all the conclusions you have made, you may want to restate exactly where you are at with this. For example, if you have a driver working in PASM, why are you still trying to find a SPIN driver? Also, you are mentioning multiple devices over several threads, and I have no idea which device is the problem at this point. Maybe you should focus on a single device, target just one register on that one device. You may want to contact the module's tech support to find out what the results should mean, and get some suggestions on how to know what you should be seeing. Maybe they have a forum for the module? You really need to have a clear idea of what you are looking for in the data, and some method to know if you are close or far away with the data you are getting. It has been mentioned to you that in the screenshot you posted, the example code shows that part of the replies are signed values, some are not. If it is true that you need to be working with signed values, then you must get an understanding of how to convert the unsigned 16 bit response ( AA MSB and AB LSB) into an integer format that is signed. This was discussed using the bitshift left, shift arithmetic right etc. You should study the Prop manual on bitshift left and SAR shift arithmetic right to understand what the examples mean and how to apply them yourself. It is very easy to understand frustration with this. With the Propeller, you have four choices as a business owner; learn the code, hire a consultant, hire a programmer, post on the forum for help, or some combination. Hiring a Propeller programmer is not easy. Hiring a consultant is not easy, even if you have the money, a real firm may not care about a small one off gadget. That means, you are stuck with the forum as your resource. Working in the forum requires some skill and patience. It also requires focus on small bite size issues, not giant multi device, multi problem discussions at one go.
Do you have a hardware problem, or software problem, or BOTH? In your case, it is hard to tell because the info you have state seems contradictory. As a friendly suggestion, as stated earlier as well, you are trying to tackle too much at one time. Take one device, learn about one register and how to access that register, convert it to what it needs to be in the final output, find out what you should be seeing and what it means so that you know if the data is even close. If you are 100% confident in the code, then you can address hardware as a problem. But, don't chase hardware when you haven't ruled out the software, especially since ( according do you) you have a PASM driver working. As discussed, you have a digital and analog aspect to the modules. Any I2C driver should get an ack or device ID returned. The first thing is to know that you are talking to the chip on the digital side, which is far more forgiving as far as PCB concerns.
Part of the issue with the multiple power and ground connections on the Propeller and other large chips is that the wiring inside the chip is very small ... thin layers of aluminum deposited in thin traces on the surface of the chip and connected sometimes via polysilicon traces and vias in lower layers in the chip. These have much more resistance than wires and copper traces on the PCB, but, since the distances are very short and the amount of current small usually, this is all very tolerable. The problem you get into leaving out power connections is that the fraction of the current expected to be supplied for the portion of the chip near the pins now has to travel from other power pins across the surface of the chip. There can be significant voltage drops across the chip at peak currents and, in some circumstances, these can be enough to damage small areas of the chip when an input signal "pegged" to one supply source comes into a chip area powered by another supply source and the input signal is much higher than the supply voltage.
Thank you for being understanding, I am not a master at the forums.
I would like to have a Spin driver that works because I understand Spin.
I have a PASM driver that works, but Only for the MPU-6050 (the i2c driver is built into the mpu-6050 driver), and I dont understand PASM.
I have multiple i2c devices that I have tried with each i2c driver, each one at a time.
I tried multiple because of mixed results (none / random values / "looks" correct values / correct values) with various drivers (spin/pasm).
There are 2 important sensors I need working. The BMP-180 and the MPU-9150.
The BMP-180 gives results that "appear" correct, but are not. And associated math doesn't work. I did contact tech support for this device & they sent me some c++ source code for the device.
The MPU-9150's Gyro, Accel & Temperature values can be obtained with the MPU-6050 Driver (PASM), but the Magnetometer cannot be read.
The MPU-6050 Only works with its PASM driver.
Each sensor has a register to read to "verify" that your communications are working properly, typically a Device ID Register that is hard coded to the device. The BMP's ID is 0x55. Which I am obtaining, but the raw pressure & temperature readings dont correlate to what the sensor is detecting.
How does spin handle signed & unsigned ints & longs? All it has is byte, word, and long for Var's.
I have made an attempt at modding the PASM 6050 driver to get the Mag Value, and to read the BMP via Aux Bus. But like I said, I really dont know nor understand PASM one bit.
I also have a HMC5883, that potentially I could use via the MPU-6050's Aux Bus. I have tested it with its spin driver, which gives good results, but the Z axis dont work. And that driver is totally different than all the rest. Making this insanely confusing.
As advertised in datasheets, an external Compass (6050 datasheet) or Perssure Sensor (9150 datasheet) can be connected via the Aux Bus and either read via the MPU's Master i2c controler, or be put into Pass-through mode and read directly by the mcu via its scl/sda lines. You just set a few Config Registers to make the switch.
These are the reasons I feel it is a Software issue.
If I had a 100% Spin Driver that worked 100%, then I could easily write drivers for these sensors.
When I first started out with the prop I would only connect One +3.3v & One Gnd and it worked 100% fine, with all of my non i2c systems. I have since been trying to connect all of them, but with layout concerns, getting that last one in there has been a bit of an issue. I will fix this from now on. My apologies, I am more of a software guy than hardware, so I know I will overlook & entirely miss stuff along the way.
I think I am starting to understand bit shifting, but then I am confused because the Examples show it just shifts bits around: 10001101 << 3 becomes 00110110. But I have come to understand that in propeller, it shifts them over: 10001101 << 8 becomes 10001101_00000000. So which one is it? I am really confused about this one & when I search I find both are correct at the same time.
Studying a BMP.c & MPU.c files, and in them I have seen in to take 3 readings & combine in one value would be like this:
A = ReadVal << 8
A += ReadVal << 16
A += ReadVal
But I have been suggested to do it this way (for the BMP180):
A = i2c.readregister << 8
A |= i2c.readregister
Which gives the apparently correct results. Except when i compare it against i2c.readregister16 the results are very close, but totally different.
Bit Shifting has got me a bit shifted.
I know it seems as tho I am jumping all over the place, which I kinda am. I'll take a break from one device to go work on another. I have created test files for each of them, so it is easy to jump around. Most of my "jumping" time was from spin to pasm. I spent quite a few hours last night trying to get the pasm 6050 driver to read the magnetometer threw the aux bus, with no luck. I changed a large chunk of it around & the Accel/Gyro/Temp still worked right so I would assume I am getting values but cannot retrieve them from the pasm section to the spin section, or I am retreiving right but getting no values. Grasping the conscept of how to pass data is a bit difficult. I identically added to the original & everything I added did nothing, with the exception of making it possible to user set config settings on startup (Accel Range, Gyro Range, DLPF, etc).
pst.bin(%0000_1111 << 8, 16) 'this will show the effect of moving the value %00001111 to the left 8 bits, 16 represents how many binary positions to display.
Experiment with bytes, words, long values, bitshifting left, right, etc, to see what you get. At some point there is no more ambiguity. You KNOW what it does and never think about it again. Put every misunderstood instruction to the test. You cannot begin to write a driver without the basic fundamentals. So, as the need arises to manipulate the data, take that as an opportunity to learn the fundamentals each step of the way. With the I2C devices I am familiar with, they all shift out data in chucks of 8 bits * a byte *. Even if the data is to represent a word or long value, it still shifts out the data in bytes. So you have seen that you must shift out the AA value and the AB value one at a time. Then you reconstruct the original 16 bi value(word) by bitshifting the MSB( most significant byte) to the left by 8 to put it where it belongs in the word. Then you either add the LSB or OR it to the value, so now you have the original value that started in the I2C device. If there is a requirement to use a signed value, then you have to understand are you dealing with a signed 16 bit value, or long value. The prop is a 32 bit device, so the sign must live in the most significant bit position( the most far left bit position) bit 31. Your device outputs a 16 bit signed value. So you must read the MSB byte, then bit shift it left by 24 bits to put the SIGN bit in the correct position in the WORD. Then, you must put the other math back where it belongs by shifting the arithmetic to the right by 16. Then you OR the LSB from the device. Now, you have the sign bit in tact in the bit 31 position in the long. Now, I do not know your device, only going by the example you posted. It may well be that you do NOT need to worry about maintaining the sign for your uses, that is something you must determine. But, the bottom line is, these are basic fundamentals and building blocks of drivers and must be understood, just like adding, subtracting, multiplying etc, there are a few basics you must understand for bit manipulation. If you spin driver is not working, but a preconfigured PASM object does work, then you have something wrong in the SPIN code related to the register addressing or device address, including the correct use of W/R in the LSB of the address. It is unlikely that the device has a minimum I2C clock speed, and even it if does, you should find a minimum I2C clock speed in the datasheet. A minimali2cdriver code would look something like this;
Using PST, you can start to SEE what is happening versus guessing what is going on.
I use this on most all i2c devices. Once you get a driver working, unless you need more speed or special features, most new devices require just a little copy and paste from other devices.
While Peter has suggested this I will try too. If you load Tachyon on the your board you will have a command line FORTH enviroment to FROB the chips with. You can try many different approaches and not have to compile->link->download->bring up the serial terminal between each attempt which can be really tiresome after say the 20th attempt. You don't have to create your "final" code in Tachyon / FORTH but for debugging I have found this environment to be absolutely required for figuring out chips that just don't play well by the "standard".
Here is a quick one liner of what is possible with Tachyon from the command line from the Intro Doc.
https://docs.google.com/document/d/1bEH0DfGmu99M1SqCbmlzl991Ssv2J5m6XWmkJX0XSl8/pub#h.5101fz2nlt5o
It reads a byte from the eeprom, just like that! This one line in Tachyon issues an I2C start command, sends the device's WR address, sends the upper and lower byte of the address to be read from in this case,
sends another start comand, sends the devices RD address and finally reads 1 byte from the device.
Just imagine how fast you can make many permutations of this basic syntax from the terminal to try to "find" and "send" / "recieve" data from your I2C chips.
Not to mention just issue: .I2CBUS to scan the bus
Again I'm not saying to do your project in Tachyon / FORTH but to debug new hardware this tool is invaluable, hat tip once again to Peter.
Thank you for this code, I wrote a simple i2c test to read the Chip's ID with great success. But when I went to add in the other values, I get numbers, but only 7 of 21 change (reading each individual register & displaying them), the Chip ID is still correct, and it does not matter if is run SetConfig (it should as setting PWR_MGMT to 1 brings it out of sleep mode).
Still for the life of me cannot get this working like it should be.
This is my port of MPU-6050.spin using your code in Spin. You can easily follow it compared to mpu-6050.spin.
And the Working PASM Driver (MPU-6050.spin):
i.e. 0 $100 BMPDUMP will dump out every register of the BMP180
Note; my example was for a chip ( Qprox 60160) that allows multiple reads within a single interaction, it expects you to read out 3 bytes. In your case, you only need to read out 2 bytes then quit that interaction. Now, that being said, I have not studied whether your chip will allow to read both msb and lsb in one interaction. I made the change so you can at least test it. It may be that you have to read for AA, then do a new read for AB. Your attempt with my example would not work because you were attempting to read 3 bytes in a row, when the device is only holding 2 bytes for that register or register pair.\
Note2: Oops, my comments above were as if you were still working with the other module trying to read out the eeprom calibration data in pairs of aa ab, etc. For any other module, you need to determine how many bytes are to be read from the register, and modify my example to suit that number. SO, instead of the example above that reads two bytes at once, if you are only wanting ONE byte read, you might try
ACCEL_XOUT_H ACCEL_XOUT[15:8]
ACCEL_XOUT_L ACCEL_XOUT[7:0]
The datasheet shows that some info is a single byte, and some has a high and a low byte like the above. At a glance, I do not see that you can read out two bytes in a single transaction, but I didn't look that far. In this case, you will have to read each register at a time, even if there is a low byte location and a high byte location, then bitshift the high byte value << 8, then OR the low byte read.
datavalue := Read_Register($3B) << 8 'get the high byte first in cases where there are high and low
datavalue |= Read_Register($3B + 1) 'get the low byte and OR it
datavalue must be at least a word length var
I see what you mean by 3 byte read with your chip. I have updated the code to a single byte read for testing, still yet only the 7 of 21 valuse change.
By the looks of the PASM driver, it just starts at address #59 and reads consecutively the Accel X, Y, Z, Temp, Gyro X, Y, Z High & Low Bytes, passing each one to an "i" pointer (iaX, iaY, iaZ, etc) as it reads each register (2 bytes at a time).
I have been trying to get a Spin verson of the MPU-6050 driver working since I can compare my code & results with the PASM verson that works fine.
Notice what they are doing in PASM
I can read the Who Am I Register at any time, the chip does not have to be "awake" to get its value 0x68.
Not sure why you have this in here above? Try removing that.
There are a lot of configuration bits to set on this device, I haven't looked in great detail, but you need to be sure that what is being configured in PASM is also being set in your spin code. There is info that states that your I2C read must take place no slower than the sample rate, else your read gets destroyed half way though the read. There is a way to reduce the sample rate so that you can insure that the spin I2C read occurs no slower than the sample rate, but you will have to research this yourself. This is the most complicated I2C device I have ever looked at, certainly not ideal for a first time I2C learning experience. The PASM code is checking to see if the value was negative, and if so, it sign extends the sign bit 15 to bit 31. I did not do this, so if this works the values will at some point need to be bitshifted left 16, then SAR 16.
Try this and see what happens
That was added because of your suggestion to "Notice what they are doing in PASM" from previous post.
That is the equivalent to adding a #StartRead just before it began reading registers.
It gives results that appear correct, however, Gyro Z is completely missing (both High & Low = 0).
One thing to note, this works with & without bringing the chip out of "sleep" mode (PWR_MGMT_1).
I have done some more coding and defined the entire Register Map under Con, so there should be less confusion on what registers I am calling. Only using this on TestMPU2.
Running your last code in TestMPU3:
I am thinking that it is working to the equivalent of the mpu6050 pasm driver (to some extent). Pass-through mode wont enable (which I have read others had same issue) & I cannot read the aux bus. And by the looks of it, my Gyro Z axis is dead, as it is misreading with the pasm driver as well.
I wrote a test to check the registers that I have wrote to to verify that it is receiving my input, which it was.
Now for getting the Magnetometer data from the sensor, which is supposedly possible by setting it to pass-through mode, which I have, and still no data back? Not too sure about this one.
Many Many Thanks to everyone that got me this far!
I now understand bit shifting & i2c a whole hell of a lot better, thanks to you guys! Getting these next few sensors on lock will be no problem at this point.
Just figured out why I wasnt getting a reading from the mag! Had to set its config $0A to %00000001 to turn on the device.
So, here is the rough draft code for the MPU-9150, I'll be cleaning it up tomorrow to be posted in the obex!
Do I just divide 65535 by 8192 then multiply the sum of that with the Value_IN to shift the number line?
Also, the compass reads its values in Low Bit first then High Bit, would I do the bitshifting like this:
Whatever you read from a device that is declared to be the HIGH byte, assuming that it is part of a word value(16 bits total, as your device is), you must move that byte over by 8, as you have shown.
Use this above if you are going after one read at a time, I see that you may not be using this anymore but in case you do. The other stuff I deleted was for my device and requirements. Were you not able to get the other example I posted to work that read in all the 14 bytes in one scoop? That would be a faster loop.
If I may, please ensure that your code is fully vetted before posting in ObEx -- you're not doing anyone any favors by posting too early.
Spin has a special operator to extend the sign of a 16-bit value. Assuming that you've read back the register into a long, you can do this
You just need to ensure the sign bit is in bit15 of the value. If it's not, you can use a combination of << (to move the sign bit to bit31) and ~> (to restore the LSB position).
Yes, that is the method I ended up using in my main loop
I am down to shifting the numbers to get the right outputs. I am new to using bit shifting instead of math to do the same job.
Absolutely, I am not interested in posting bad / unfinished code. I wouldn't be needing to write my own 9150 or BMP driver, if that was the case for everyone, as there is already one on obex for it... But the driver dont work. Has all the right stuff, just no data comes out. Same for the BMP085 / BMP108. it has multiple drivers that dont work on obex.
I tried the ~~ method, and that puts it in a +/- scale, but the range is still off.
The Top order is Accel X, Y, Z, Temp, Gyro X, Y, Z, Mag X, Y, Z. (Output from My Driver)
The Bottom order is Accel X, Y, Z, Gyro X, Y, Z. (Output from MPU-6050 PASM Driver)
clockfreq / finalcount is the time in seconds that elapsed over the 5000 reads. so the i2c freq = x seconds / 5000. You better confirm the formula but you get the idea. Compare to your sample rate.
That I havent and I am not exactly sure how I would do that.
It doesn't appear to be trash, the mag is fairly stationary & knows were it is at. The number line is trash tho. Temperature only changes with temp changes, up is up, down is down, 0-65535. The Accel & Gyro look right too, just with another jacked number line. The Gyro X axis is inverted, need to subtract gX := 65535 - gX, to bring it back to the right spot, but then the whole gyro reading has inverted sign (+/-).