Shop OBEX P1 Docs P2 Docs Learn Events
Basic I2C Driver help [SOLVED] — Parallax Forums

Basic I2C Driver help [SOLVED]

Bobb FwedBobb Fwed Posts: 1,119
edited 2009-08-04 16:12 in Propeller 1
What am I doing wrong here?
I am trying to write "test" to my eeprom.
But it looks like it isn't all being written. All I get back when I read the address it "t".

CON

  _CLKMODE      = XTAL1' + PLL4X
  _XINFREQ      = 5_000_000                          ' 5MHz Crystal

  start_table = $3FFF

OBJ

  DEBUG  : "FullDuplexSerial"    
  i2c    : "Basic_I2C_Driver"                  

VAR

  BYTE val[noparse][[/noparse]32]

PUB Main | i, v

  DEBUG.start(31, 30, 0, 57600)
  waitcnt(clkfreq + cnt)  
  DEBUG.tx($0D)

  i2c.Initialize(i2c#BootPin)

  bytefill(@val, 0, 32)
  bytemove(@val, string("test"), 32)
  
  write_table

  REPEAT i FROM 0 TO 32
    v := read_table(start_table + i)
    IF (v == 0)
      quit
    DEBUG.tx(v)
  DEBUG.tx($0D)

PUB write_table | time 

  IF i2c.WritePage(i2c#BootPin, i2c#EEPROM, start_table, @val, 32)
    abort
  time := cnt
  repeat while i2c.WriteWait(i2c#BootPin, i2c#EEPROM, start_table)
    if cnt - time > clkfreq / 10
      abort  

PUB read_table (addr) : ret_val

  IF i2c.ReadPage(i2c#BootPin, i2c#EEPROM, addr, @val, 1)
    abort                     

  return val


▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
April, 2008: when I discovered the answers to all my micro-computational-botherations!

Post Edited (Bobb Fwed) : 8/3/2009 10:46:59 PM GMT

Comments

  • JonnyMacJonnyMac Posts: 9,197
    edited 2009-08-03 22:32
    As an exercise I wrote my own I2C driver last week; my driver lets me set the SCL and SDA lines and then forget about them, and my code does not drive the SCL or SDA pins high (which means pull-ups on both pins are required).

    To show you that it works with the internal EEPROM I enabled a section of my demo right a string to the EERPROM and then read it back. The mechanism I use may be helpful, i.e., you don't have to read back one character at a time.
  • TimmooreTimmoore Posts: 1,031
    edited 2009-08-03 22:35
    You are writing starting from $3fff, if I remember correctly you can't do a page write across that boundary.
  • Bobb FwedBobb Fwed Posts: 1,119
    edited 2009-08-03 22:35
    well it looks like that object does have the ability to read blocks of memory which would include string reading...but I can't get it to write...so I am just trying that part first. I do not have pull ups on both EEPROM lines, so I would need that (though I am sure I could modify the code to get it).

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    April, 2008: when I discovered the answers to all my micro-computational-botherations!
  • Bobb FwedBobb Fwed Posts: 1,119
    edited 2009-08-03 22:40
    Timmoore said...
    You are writing starting from $3fff, if I remember correctly you can't do a page write across that boundary.
    Oh looky there. Now it works great (starting at address $4000 instead.
    Thanks.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    April, 2008: when I discovered the answers to all my micro-computational-botherations!
  • Bobb FwedBobb Fwed Posts: 1,119
    edited 2009-08-03 22:52
    I have now modified the basic i2c driver to only declare the pins once. This reduces stack space required, lots of unnecessary code, and cycle time.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    April, 2008: when I discovered the answers to all my micro-computational-botherations!
  • cessnapilotcessnapilot Posts: 182
    edited 2009-08-04 08:26
    Hi Bobb Fwed,

    Obex item /464/ contains maybe the smallest and correct SPIN I2C driver for boot EEPROMS. Although, it is not the fastest! Supplied with a plethora of SPIN I2C drivers, I am still waiting for a stand alone I2C driver in PASM to appear somewhere. This delay is·a good excuse for me not to update the I2C protocol based driver·of the FPU on OBEX.


    Cheers,

    Istvan

    (P.S.: I mean, that the basic I2C driver task is maybe NOT SOLVED yet.)

    Post Edited (cessnapilot) : 8/4/2009 8:34:52 AM GMT
  • David BDavid B Posts: 592
    edited 2009-08-04 15:11
    I was also struggling to get an I2C circuit running.

    Different part, I'm using an MCP3221, but I'm a total I2C newbie and the examples in the obex all assumed that I knew more than I did - I couldn't get any of them to work, even one that was supposed to be written specifically for the MCP3xxx series parts.

    But just last night I finally got it working perfectly, thanks to Jon's example code attached to this thread. His code was just short and simple enough to make sense with the part documentation.

    Thanks, Jon!
  • Bobb FwedBobb Fwed Posts: 1,119
    edited 2009-08-04 15:17
    I considered porting one of these i2c drivers to PASM, but then I realized, unless you are using the driver constantly (I don't know if many people are using the EEPROM non-stop), that it would be a big resource wasted (a full cog -- but the option for the programmers to sacrifice a cog for speed may be worth it to many I guess). And it may be worth it for what I am trying to use the driver for, seeing as it will end up being very slow. I will put it on my long list of things to do.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    April, 2008: when I discovered the answers to all my micro-computational-botherations!
  • RinksCustomsRinksCustoms Posts: 531
    edited 2009-08-04 16:12
    lol.gif And i thought i was the only one struggling with this protocol in spin.. I'm trying to read temperature from a very stubborn TMP100 sensor from ti.com
    CON
    
      _clkmode = xtal1 + pll16x                  
      _xinfreq = 5_000_000
    
      SCL = 28
      SDA = 29
      read = 1
      write = 0
    
                                    
    
    OBJ
    
      out      :     "Parallax serial terminal"
      
      
    var
      long  ack, temperature, data, send_data
      
    pub init
       
      ack := temperature := data := 0 
      out.start(115200)             'start the PST
      waitcnt(clkfreq * 3 + cnt)
      
      out.str(string("TMP100 program v 0.1"))
    
      repeat 2
        out.newline
    main
    pub main |  fract, whole, number
    
        send_data := address | write 
        starti2c 
        ack := send_i2c(send_data)                          'send the address byte and indicate if a read/write follows. Ack = 1 when device exists on the i2c bus
        send_data := 0
        
        if ack 
          out.str(@devgood)
          out.newline
                                      'send pointer reg byte telling it to set the pointer to the temperature conv reg
          'starti2c 
          ack := send_i2c(pointer[noparse][[/noparse]0])                        'ack should be low from dev if all is good
          ack := !ack 
          if ack 
              out.str(@devgood)
              send_data := address | read
              stopi2c
              starti2c                                      'then it needs a start condition again
              ack := send_i2c(send_data)                    'then an address byte with the read bit set
              !ack
              if ack         'now read in data
                data := read_i2c(2,0)      'read_i2c(bytes to read, ack bit to send)  
                stopi2c
        temperature := data             
        out.char("$")                          
        out.hex(temperature,4)
        out.newline
        out.char("%")
        out.bin(temperature,32)
        waitcnt(clkfreq * 5 + cnt)      
      
        {fract >>= 4
        out.dec(whole)
        out.char(".")
        number := lookdown(fract : %0000, %1000, %0100, %1100, %0010, %1010, %0110, %1110, %0001, %1001, %0101, %1101, %0011, %1011, %1111)
        fract := lookup(number :    0,     5,     25,    75,    125,   625,   375,   875,   0625,  5625,  3125,  7825,  1875,  6875,  9375)
        out.dec(fract)
        out.str(@c) }
    
    pri starti2c 
      
      dira[noparse][[/noparse]scl..sda] := %11
      outa[noparse][[/noparse]scl..sda] := %11
      waitcnt(1200 + cnt)
      !outa[noparse][[/noparse]sda]
      waitcnt(1200 +cnt)
      !outa[noparse][[/noparse]scl]
      waitcnt(1200 +cnt)
      dira[noparse][[/noparse]scl..sda]~
    
    
    pri stopi2c
      
      dira[noparse][[/noparse]scl..sda] := %11
      outa[noparse][[/noparse]scl..sda] := %00
      waitcnt(1200 + cnt)
      !outa[noparse][[/noparse]scl]
      waitcnt(1200 +cnt)
      !outa[noparse][[/noparse]sda]
      waitcnt(1200 +cnt)
      dira[noparse][[/noparse]scl..sda]~
            
    pri send_i2c(_data):getack | sftw,a
     
     sftw := _data >< 8 
     
      dira[noparse][[/noparse]scl..sda]~~
      repeat a from 0 to 7
        outa[noparse][[/noparse]sda] := sftw
        outa[noparse][[/noparse]scl]~~
        sftw >>= 1
        !outa[noparse][[/noparse]scl]
        waitcnt(1200 +cnt)
      dira[noparse][[/noparse]sda]~
      outa[noparse][[/noparse]scl]~~
      getack := ina[noparse][[/noparse]sda]
      outa[noparse][[/noparse]scl]~
      dira[noparse][[/noparse]scl..sda]~~
    
    return getack
       
    pri read_i2c(bytes,_ackbit): recv | dataget, b 
    'read_i2c(bits to read, ack bit to send)
      
      dira[noparse][[/noparse]sda]~       
      repeat bytes
        repeat 8
          outa[noparse][[/noparse]scl]~~               'SCL high
          recv |= ina[noparse][[/noparse]sda]          'OR in sda to recv
          outa[noparse][[/noparse]scl]~                'lock in recv by bringing SCL low
          recv <<= 1
          waitcnt(80 +cnt)
        dataget := %10
        dataget := dataget | _ackbit'set the desired ackbit
        dira[noparse][[/noparse]scl..sda]~~            'make sda output the ack bit
        outa[noparse][[/noparse]scl..sda] := dataget
        outa[noparse][[/noparse]scl]~
        dira[noparse][[/noparse]sda]~
      dira[noparse][[/noparse]scl..sda]~~              'both outputs again
      outa[noparse][[/noparse]scl..sda]~               'leave databus low
    return recv
    
    DAT                           'sensor 0   sensor 1   sensor 2   sensor 3
    address         byte          %10010000, %10010010, %10010100, %10010110        'indicate a write opp follows
    
                                'Temp       Config     Temp LOW   Temp HIGH
    Pointer         byte          %00000000, %00000001, %00000010, %00000011        'write to the appropriate reg
                                '9bit       10bit      11bit      12bit                              
    Resolution      byte          %00000000, %00100000, %01000000, %01100000        'temperature resolution *MUST be "OR"d with config_reg
                                '1 fault    2 faults   4 faults   6 faults
    Fault           byte          %00000000, %00001000, %00010000, %00011000        'defines an alert when Temp High/Low is exceeded
    
    c               byte          " Celcius",0
                                  
    devgood         byte          "Device 0 found!, getting temp data...",0
    



    i believe the code i wrote (for the 8th time) is legit and follows the timing diagrams well (for SPIN) i would like to make a PASM version but i'm not willing to make one thats going to communicate wrong with a device.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Quicker answers in the #propeller chat channel on freenode.net. Don't know squat about IRC? Download Pigin! So easy a caveman could do it...
    http://folding.stanford.edu/ - Donating some CPU/GPU downtime just might lead to a cure for cancer! My team stats.
Sign In or Register to comment.