Shop OBEX P1 Docs P2 Docs Learn Events
i2c Nothing Works!!! - Page 3 — Parallax Forums

i2c Nothing Works!!!

13

Comments

  • Cluso99Cluso99 Posts: 18,069
    edited 2014-02-12 01:05
    r.daneel wrote: »
    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.
  • r.daneelr.daneel Posts: 96
    edited 2014-02-12 01:27
    Cluso99 wrote: »
    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...?
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-12 01:37
    r.daneel wrote: »
    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).
  • r.daneelr.daneel Posts: 96
    edited 2014-02-12 01:43
    > 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...
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-12 01:54
    r.daneel wrote: »
    > 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.
  • SRLMSRLM Posts: 5,045
    edited 2014-02-12 06:34
    r.daneel wrote: »
    Maybe I need to pick up a book...

    I like The Circuit Designer's Companion and Surface Mount Technology for PC Boards.
  • T ChapT Chap Posts: 4,223
    edited 2014-02-12 07:06
    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.
  • Mike GreenMike Green Posts: 23,101
    edited 2014-02-12 07:38
    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.
  • zlantzzlantz Posts: 136
    edited 2014-02-12 10:12
    T Chap wrote: »
    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. wink.png

    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).
  • T ChapT Chap Posts: 4,223
    edited 2014-02-12 11:10
    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.
    VAR
    
      word   testword
    
      testword :=   %0000_1111 
      pst.bin(testword, 8) 
      pst.LineFeed
      pst.LineFeed
      pst.bin(testword << 8, 16)  
      pst.LineFeed
      pst.LineFeed
    
      testword  := %1000_0000_0000_0000  
      testword   ~>= 8   
      pst.bin(testword , 16) 
    
    
    
    


    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.
    con
    
       i2cack    = 0                                        
       i2cnak    = 1                                           
       i2cxmit   = 0                                               
       i2crecv   = 1                                              
       i2cboot   = 28                                               
       i2ceeprom = $a0                                           
    
    pub i2cstart(i2cscl) | i2csda                                       
    
       i2csda := i2cscl + 1
       outa[i2cscl]~~                                         
       dira[i2cscl]~~
       outa[i2csda]~~                                         
       dira[i2csda]~~
       outa[i2csda]~                                   
       outa[i2cscl] ~                              
    
    pub i2cstop(i2cscl) | i2csda                                        
    
       i2csda := i2cscl + 1
       outa[i2cscl] ~~                              
       outa[i2csda] ~~                              
       dira[i2cscl] ~                                   
       dira[i2csda] ~                                                      
    
    pub i2cwrite(i2cscl, i2cdata) : ackbit | i2csda
    
                                                                            
                                                                          
                                                                        
       i2csda := i2cscl + 1
       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] ~~
    
    pub i2cread(i2cscl, ackbit): i2cdata | i2csda                              
    
                                                                       
                                                                            
       i2csda := i2cscl + 1
       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] ~                                      
    
    pub i2creadpage(i2cscl, i2caddr, addrreg, dataptr, count) : ackbit
    
                                                                               
                                                                                 
                                                                                  
       i2caddr |= addrreg >> 15 & %1110
       i2cstart(i2cscl)
       ackbit := i2cwrite(i2cscl, i2caddr | i2cxmit)
       ackbit := (ackbit << 1) | i2cwrite(i2cscl, addrreg >> 8 & $ff)
       ackbit := (ackbit << 1) | i2cwrite(i2cscl, addrreg & $ff)          
       i2cstart(i2cscl)
       ackbit := (ackbit << 1) | i2cwrite(i2cscl, i2caddr | i2crecv)
       repeat count - 1
          byte[dataptr++] := i2cread(i2cscl, i2cack)
       byte[dataptr++] := i2cread(i2cscl, i2cnak)
       i2cstop(i2cscl)
       return ackbit
    
    pub i2cwritepage(i2cscl, i2caddr, addrreg, dataptr, count) : ackbit
    
                                                                                 
                                                                                 
                                                                                  
                                                                               
       i2caddr |= addrreg >> 15 & %1110
       i2cstart(i2cscl)
       ackbit := i2cwrite(i2cscl, i2caddr | i2cxmit)
       ackbit := (ackbit << 1) | i2cwrite(i2cscl, addrreg >> 8 & $ff)
       ackbit := (ackbit << 1) | i2cwrite(i2cscl, addrreg & $ff)          
       repeat count
          ackbit := ackbit << 1 | ackbit & $80000000                             
          ackbit |= i2cwrite(i2cscl, byte[dataptr++])
       i2cstop(i2cscl)
       return ackbit
    
  • D.PD.P Posts: 790
    edited 2014-02-12 11:18
    zlantz wrote: »
    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. wink.png

    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.

    https://docs.google.com/document/d/1bEH0DfGmu99M1SqCbmlzl991Ssv2J5m6XWmkJX0XSl8/pub#h.5101fz2nlt5o

    It reads a byte from the eeprom, just like that!
    I2CSTART  $A0 I2C!  $7F I2C!  $E0 I2C!  I2CSTART   $A1 I2C!   1 I2C@       
    
    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.
  • zlantzzlantz Posts: 136
    edited 2014-02-12 15:08
    T Chap wrote: »
    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
    
    
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2014-02-12 15:13
    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]
    
  • T ChapT Chap Posts: 4,223
    edited 2014-02-12 16:23
    
    
    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
    


    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
  • zlantzzlantz Posts: 136
    edited 2014-02-12 17:13
    T Chap wrote: »
    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.
  • T ChapT Chap Posts: 4,223
    edited 2014-02-12 17:18
    Post the updated code...

    Notice what they are doing in PASM
    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  
    
    
    
  • zlantzzlantz Posts: 136
    edited 2014-02-12 17:46
    T Chap wrote: »
    Post the updated code...
    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
      
      word 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 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
       
    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
        
        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_Register2(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_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
       
    

    I can read the Who Am I Register at any time, the chip does not have to be "awake" to get its value 0x68.
  • T ChapT Chap Posts: 4,223
    edited 2014-02-12 19:02
        ' // 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
    
    
    
    619 x 179 - 73K
    sc.jpg 73.4K
  • zlantzzlantz Posts: 136
    edited 2014-02-12 22:02
    T Chap wrote: »
    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
    
    }
    
    
    
  • zlantzzlantz Posts: 136
    edited 2014-02-12 23:39
    Your last code appears to work!

    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.
  • zlantzzlantz Posts: 136
    edited 2014-02-12 23:52
    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!
  • zlantzzlantz Posts: 136
    edited 2014-02-13 01:06
    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
    
    
  • zlantzzlantz Posts: 136
    edited 2014-02-13 01:49
    WEW!!! I guess I spoke too soon!

    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!
    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, mID, mInfo, mStatus  
    
      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.bin(mID, 8)         ' 01001000  =  Correct!
        ser.str(string(", "))
        ser.bin(mInfo, 8)       ' 00001001
        ser.str(string(", "))
        ser.bin(mStatus, 8)     ' 00000000
        ser.str(string(", "))
        ser.dec(mX)             ' 0
        ser.str(string(", "))
        ser.dec(mY)             ' 0
        ser.str(string(", "))
        ser.dec(mZ)             ' 0                                                                                        
        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 | iaXn, iaYn, iaZn, iTn, irXn, irYn, irZn 
    
      ser.start(31, 30, 0, 115200)
         
      ser.str(string("Starting..."))
    
      i2csda := I2C_SDA
      i2cscl := I2C_SCL
      
      SetConfig                   
      
      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
        ' // Start at Register $3B  (Accel, Temp, Gyro) 
        i2cStart                   ' start the transaction  
        i2cWrite(AddressW)         ' 0 for write
        i2cWrite(Accel_XOut_H)     ' send the register to start the read at
        i2cStart
        i2cWrite(AddressR)         ' start the read 
    
        aX  := i2cRead(0)   << 8      
        aX  |= i2cRead(0)     
        aY  := i2cRead(0)   << 8      
        aY  |= i2cRead(0)   
        aZ  := i2cRead(0)   << 8    
        aZ  |= i2cRead(0)   
        Temp := i2cRead(0)  << 8  
        Temp |= i2cRead(0)    
        gX  := i2cRead(0)   << 8      
        gX  |= i2cRead(0)   
        gY  := i2cRead(0)   << 8      
        gY  |= i2cRead(0)    
        gZ  := i2cRead(0)   << 8       
        gZ  |= i2cRead(1)
    
        ' // Change Register to $75  (Chip ID)
        i2cStart                     
        i2cWrite(AddressW)
        i2cWrite(WHO_AM_I)
        i2cStop
        i2cStart
        i2cWrite(AddressR)          
        cID := i2cRead(1)          
    
        ' // *** Enable Passthrough Mode if not enabled (Default = Enabled)
        
        ' // Change Register to $03  (Magnetometer)
    
        ' // Set CTRL ($0A) = %00000001  = Single Mesurement Mode
        i2cStart            
        i2cWrite(%00011000)    ' AK8975 Write Address
        i2cWrite($0A)          ' Address to start reading from
        i2cWrite(%00000001)    ' Address to start reading from    
        i2cStop    
        
        i2cStart            
        i2cWrite(%00011000)    ' AK8975 Write Address
        i2cWrite($00)          ' Address to start reading from
        i2cStop
        i2cStart
        i2cWrite(%00011001)    ' AK8975 Read Address
        mID     := i2cRead(1)
    
        i2cStart            
        i2cWrite(%00011000)    ' AK8975 Write Address
        i2cWrite($01)          ' Address to start reading from
        i2cStop
        i2cStart
        i2cWrite(%00011001)    ' AK8975 Read Address    
        mInfo   := i2cRead(1)
    
        i2cStart            
        i2cWrite(%00011000)    ' AK8975 Write Address
        i2cWrite($02)          ' Address to start reading from
        i2cStop
        i2cStart
        i2cWrite(%00011001)    ' AK8975 Read Address    
        mStatus := i2cRead(1)
    
        i2cStart            
        i2cWrite(%00011000)    ' AK8975 Write Address
        i2cWrite($03)          ' Address to start reading from
        i2cStop
        i2cStart
        i2cWrite(%00011001)    ' AK8975 Read Address
            
        mX := i2cRead(0) 
        mX |= i2cRead(0) << 8  
        mY := i2cRead(0) 
        mY |= i2cRead(0) << 8 
        mZ := i2cRead(0) 
        mZ |= i2cRead(1) << 8         
    
    
        i2cStop 
    
        
    Pri MPUReadValues2 | 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
    
    
    
    
    
    {
    
    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 %11010000  ' mask = %10000000 ??? What does this do???
        i2cWrite($3B)         '    Write Register
        i2cWrite(AddressW)    '    Write Address %11010000  ' mask = %10000000 ??? 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, %00000000)    ' // *** Disable Master Mode       
        'Write_Register($37, %00000010)    ' // *** 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, %00100000)    ' // *** Re-Enable Master Mode
    
    }
    
    
    
    {
         
      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("   "))       
    
        ' // Accelerometer                   
        'ser.hex(StartRead_Register(Accel_XOut_H), 2)    ' // Tried this, Dont work
        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,   %00000000)    ' // *** Disable Master Mode       
        'Write_Register(INT_Pin_Cfg, %00000010)    ' // *** Enable Pass-Through Mode   
    
        ' // Switch Address
        '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.str(string("   "))
        
    {
        ' // Verify Registers Wrote
        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)
    }    
    }
    
    
    {
    
        
        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 )
        
        cID := Read_Register($75)         ' 117 - Chip_ID = 0x68  (Correct)
    
    }
    
  • zlantzzlantz Posts: 136
    edited 2014-02-13 03:37
    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:
        mX := i2cRead(0)       ' Mag_X_L
        mX |= i2cRead(0) << 8  ' Mag_X_H
        mY := i2cRead(0)       ' Mag_Y_L
        mY |= i2cRead(0) << 8  ' Mag_Y_H
        mZ := i2cRead(0)       ' Mag_Z_L
        mZ |= i2cRead(1) << 8  ' Mag_Z_H   
    

    a mpu working.jpg
    932 x 668 - 96K
  • T ChapT Chap Posts: 4,223
    edited 2014-02-13 04:59
    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.
  • JonnyMacJonnyMac Posts: 9,107
    edited 2014-02-13 07:43
    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).
  • zlantzzlantz Posts: 136
    edited 2014-02-13 11:03
    T Chap wrote: »
    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.

    Yes, that is the method I ended up using in my main loop
    Pri MPUReadValues
        [B]' // Start at Register $3B  (Accel, Temp, Gyro) 
        i2cStart                   ' start the transaction  
        i2cWrite(AddressW)         ' 0 for write
        i2cWrite(Accel_XOut_H)     ' send the register to start the read at
        i2cStart
        i2cWrite(AddressR)         ' start the read 
    
        aX  := i2cRead(0)   << 8      
        aX  |= i2cRead(0)     
        aY  := i2cRead(0)   << 8      
        aY  |= i2cRead(0)   
        aZ  := i2cRead(0)   << 8    
        aZ  |= i2cRead(0)   
        Temp := i2cRead(0)  << 8  
        Temp |= i2cRead(0)    
        gX  := i2cRead(0)   << 8      
        gX  |= i2cRead(0)   
        gY  := i2cRead(0)   << 8      
        gY  |= i2cRead(0)    
        gZ  := i2cRead(0)   << 8       
        gZ  |= i2cRead(1)[/B]
    
        ' // Change Register to $75  (Chip ID)
        i2cStart                     
        i2cWrite(AddressW)
        i2cWrite(WHO_AM_I)
        i2cStop
        i2cStart
        i2cWrite(AddressR)          
        cID := i2cRead(1)          
    
        ' // *** Enable Passthrough Mode if not Enabled (Default = Enabled)
        if PT_En == 0
          Write_Register(User_Ctrl,    %00000000)   ' 106 - Master Mode Disabled
          Write_Register(INT_Pin_Cfg,  %00110010)   ' 55  - Bypass Enabled   
    
        ' // Change Address to MagAddr    (Magnetometer)
    
        ' // Set CTRL ($0A) = %00000001  = Single Measurement Mode
        i2cStart            
        i2cWrite(MagAddrW)     ' AK8975 Write Address
        i2cWrite($0A)          ' CTRL Register
        i2cWrite(%00000001)    ' Set config $0A to %00000001 to turn on the device.    
        i2cStop    
        
        i2cStart            
        i2cWrite(MagAddrW)     ' AK8975 Write Address
        i2cWrite($00)          ' Address to start reading from
        i2cStop
        i2cStart
        i2cWrite(MagAddrR)     ' AK8975 Read Address
        mID     := i2cRead(1)
    
        i2cStart            
        i2cWrite(MagAddrW)     ' AK8975 Write Address
        i2cWrite($01)          ' Address to start reading from
        i2cStop
        i2cStart
        i2cWrite(MagAddrR)     ' AK8975 Read Address    
        mInfo   := i2cRead(1)
    
        i2cStart            
        i2cWrite(MagAddrW)     ' AK8975 Write Address
        i2cWrite($02)          ' Address to start reading from
        i2cStop
        i2cStart
        i2cWrite(MagAddrR)     ' AK8975 Read Address    
        mStatus := i2cRead(1)
    
        i2cStart            
        i2cWrite(MagAddrW)     ' AK8975 Write Address
        i2cWrite($03)          ' Address to start reading from
        i2cStop
        i2cStart
        i2cWrite(MagAddrR)     ' AK8975 Read Address
            
        mX := i2cRead(0)       ' Mag_X_L
        mX |= i2cRead(0) << 8  ' Mag_X_H
        mY := i2cRead(0)       ' Mag_Y_L
        mY |= i2cRead(0) << 8  ' Mag_Y_H
        mZ := i2cRead(0)       ' Mag_Z_L
        mZ |= i2cRead(1) << 8  ' Mag_Z_H       
    
        ' // TODO: Read BMP-180 Via Master Mode or Pass-Through Mode next
    
        i2cStop 
    

    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.
  • zlantzzlantz Posts: 136
    edited 2014-02-13 11:14
    JonnyMac wrote: »
    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)


    Different Readouts.jpg
    1024 x 640 - 50K
  • T ChapT Chap Posts: 4,223
    edited 2014-02-13 12:38
    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.
  • zlantzzlantz Posts: 136
    edited 2014-02-13 12:51
    T Chap wrote: »
    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 (+/-).
Sign In or Register to comment.