Treiber für I2C-Bus

'SCL und SDA sind noch anzugeben.

'Mit I2CDelay kann die Busgeschwindigkeit eingestellt werden.
'Beachte das hier die vom langsamsten Busteilnehmer hinein muss.

CON
   SCL      = ?                        ' pin ?  I2C SCL
   SDA      = ?                        ' pin ?  I2C SDA
   ACK      = 0                        ' I2C Acknowledge
   NAK      = 1                        ' I2C No Acknowledge
   Xmit     = 0                        ' I2C Direction Transmit
   Recv     = 1                        ' I2C Direction Receive
   I2CDelay  = 100_000                 ' delay to lower speed to 100KHz
   I2CDelayS = 10_000                  ' clock stretch delay

PRI Initialize                         ' An I2C device may be left in an
   outa[SCL] := 1                      '   reinitialized.  Drive SCL high.
   dira[SCL] := 1
   dira[SDA] := 0                      ' Set SDA as input
   repeat 9
      outa[SCL] := 0                   ' Put out up to 9 clock pulses
      outa[SCL] := 1
   repeat 9
      outa[SCL] := 0                   ' Put out up to 9 clock pulses
      outa[SCL] := 1
      if ina[SDA]                      ' Repeat if SDA not driven high
         quit                          '  by the EEPROM
   dira[SCL]~                          ' Now let them float
   dira[SDA]~                          ' If pullups present, they'll stay HIGH

PRI Start                              ' SDA goes HIGH to LOW with SCL HIGH
   outa[SCL]~~                         ' Initially drive SCL HIGH
   dira[SCL]~~
   outa[SDA]~~                         ' Initially drive SDA HIGH
   dira[SDA]~~
   outa[SDA]~                          ' Now drive SDA LOW
   outa[SCL]~                          ' Leave SCL LOW
 
PRI ReStart                              ' SDA goes HIGH to LOW with SCL HIGH
   outa[SDA]~~                         ' Initially drive SDA HIGH
   dira[SDA]~~
   outa[SCL]~~                         ' Initially drive SCL HIGH
   outa[SDA]~                          ' Now drive SDA LOW
   outa[SCL]~                          ' Leave SCL LOW
 
PRI Stop                               ' SDA goes LOW to HIGH with SCL High
   outa[SCL]~~                         ' Drive SCL HIGH
   outa[SDA]~~                         '  then SDA HIGH
   dira[SCL]~                          ' Now let them float
   dira[SDA]~                          ' If pullups present, they'll stay HIGH

PRI WriteNS(data) : ackbit
'' Write i2c data.  Data byte is output MSB first, SDA data line is valid
'' only while the SCL line is HIGH.  Data is always 8 bits (+ ACK/NAK).
'' SDA is assumed LOW and SCL and SDA are both left in the LOW state.
'' Doesn't do clock stretching so would work without pull-up on SCL
   ackbit := 0
   data <<= 24
   repeat 8                            ' Output data to SDA
      outa[SDA] := (data <-= 1) & 1
      outa[SCL]~~                      ' Toggle SCL from LOW to HIGH to LOW
      outa[SCL]~
   dira[SDA]~                          ' Set SDA to input for ACK/NAK
   outa[SCL]~~
   ackbit := ina[SDA]                  ' Sample SDA when SCL is HIGH
   outa[SCL]~
   dira[SCL]~~
   outa[SDA]~                          ' Leave SDA driven LOW
   dira[SDA]~~
 
PRI ReadNS(ackbit): data | b
'' Read in i2c data, Data byte is output MSB first, SDA data line is
'' valid only while the SCL line is HIGH.  SCL and SDA left in LOW state.
'' Doesn't do clock stretching so would work without pull-up on SCL
   data := 0
   dira[SDA]~                          ' Make SDA an input
   repeat 8                            ' Receive data from SDA
      outa[SCL]~~                      ' Sample SDA when SCL is HIGH
      b := ina[SDA]
      outa[SCL]~
      data := (data << 1) | b
   outa[SDA] := ackbit                 ' Output ACK/NAK to SDA
   dira[SDA]~~
   dira[SCL]~                          ' Toggle SCL from LOW to HIGH to LOW
   dira[SCL]~~
   outa[SDA]~                          ' Leave SDA driven LOW

PRI Write(data) : ackbit | wait
'' Write i2c data.  Data byte is output MSB first, SDA data line is valid
'' only while the SCL line is HIGH.  Data is always 8 bits (+ ACK/NAK).
'' SDA is assumed LOW and SCL and SDA are both left in the LOW state.
'' Requires pull-up on SCL
   ackbit := 0
   data <<= 24
   repeat 8                            ' Output data to SDA
     outa[SDA] := (data <-= 1) & 1
     dira[SCL]~                        ' Toggle SCL from LOW to HIGH to LOW
     dira[SCL]~~
   dira[SDA]~                          ' Set SDA to input for ACK/NAK
   dira[SCL]~
   wait := cnt
   repeat while 0 == ina[SCL]
     if (cnt-wait) > clkfreq/I2CDelayS
       quit
   ackbit := ina[SDA]                  ' Sample SDA when SCL is HIGH
   dira[SCL]~~
   outa[SDA]~                          ' Leave SDA driven LOW
   dira[SDA]~~

PRI Read(ackbit):data | wait
'' Read in i2c data, Data byte is output MSB first, SDA data line is
'' valid only while the SCL line is HIGH.  SCL and SDA left in LOW state.
'' Requires pull-up on SCL     
   data := 0
   dira[SDA]~                          ' Make SDA an input
   repeat 8                            ' Receive data from SDA
     dira[SCL]~                        ' Sample SDA when SCL is HIGH
     wait := cnt
     repeat while 0 == ina[SCL]
       if (cnt-wait) > clkfreq/I2CDelayS
         quit
     data := (data << 1) | ina[SDA]
     dira[SCL]~~
   outa[SDA] := ackbit                 ' Output ACK/NAK to SDA
   dira[SDA]~~
   dira[SCL]~                          ' Toggle SCL from LOW to HIGH to LOW
   wait := cnt
   repeat while 0 == ina[SCL]
     if (cnt-wait) > clkfreq/I2CDelayS
       quit
   dira[SCL]~~
   outa[SDA]~                          ' Leave SDA driven LOW

PRI ReadChar : data | ackbit
'' Read in i2c data, Data byte is output MSB first, SDA data line is
'' valid only while the SCL line is HIGH.  SCL and SDA left in LOW state.
   data := 0
   dira[SDA]~                          ' Make SDA an input
   repeat 8                            ' Receive data from SDA
     waitcnt(clkfreq / I2CDelay + cnt)
     outa[SCL]~~                       ' Sample SDA when SCL is HIGH
     waitcnt(clkfreq / I2CDelay + cnt)
     data := (data << 1) | ina[SDA]
     outa[SCL]~
   if data == 0
      ackbit := NAK
   else
      ackbit := ACK
   outa[SDA] := ackbit                 ' Output ACK/NAK to SDA
   dira[SDA]~~
   outa[SCL]~~                         ' Toggle SCL from LOW to HIGH to LOW
   waitcnt(clkfreq / I2CDelay + cnt)
   outa[SCL]~
   outa[SDA]~                          ' Leave SDA driven LOW

PRI ReadPage(devSel, addrReg, dataPtr, count) : ackbit
'' Read in a block of i2c data.  Device select code is devSel.  Device starting
'' address is addrReg.  Data address is at dataPtr.  Number of bytes is count.
'' The device select code is modified using the upper 3 bits of the 19 bit addrReg.
'' Return zero if no errors or the acknowledge bits if an error occurred.
   devSel |= addrReg >> 15 & %1110
   Start                          ' Select the device & send address
   ackbit := Write(devSel | Xmit)
   ackbit := (ackbit << 1) | Write(addrReg >> 8 & $FF)
   ackbit := (ackbit << 1) | Write(addrReg & $FF)
   reStart                          ' Reselect the device for reading
   ackbit := (ackbit << 1) | Write(devSel | Recv)
   repeat count - 1
      byte[dataPtr++] := Read(ACK)
   byte[dataPtr++] := Read(NAK)
   Stop
   return ackbit

PRI ReadByte(devSel, addrReg) : data
'' Read in a single byte of i2c data.  Device select code is devSel.  Device
'' starting address is addrReg.  The device select code is modified using the
'' upper 3 bits of the 19 bit addrReg.  This returns true if an error occurred.
   if ReadPage(devSel, addrReg, @data, 1)
      return -1

PRI ReadWord(devSel, addrReg) : data
'' Read in a single word of i2c data.  Device select code is devSel.  Device
'' starting address is addrReg.  The device select code is modified using the
'' upper 3 bits of the 19 bit addrReg.  This returns true if an error occurred.
   if ReadPage(devSel, addrReg, @data, 2)
      return -1

PRI ReadLong(devSel, addrReg) : data
'' Read in a single long of i2c data.  Device select code is devSel.  Device
'' starting address is addrReg.  The device select code is modified using the
'' upper 3 bits of the 19 bit addrReg.  This returns true if an error occurred.
'' Note that you can't distinguish between a return value of -1 and true error.
   if ReadPage(devSel, addrReg, @data, 4)
      return -1

PRI WritePage(devSel, addrReg, dataPtr, count) : ackbit
'' Write out a block of i2c data.  Device select code is devSel.  Device starting
'' address is addrReg.  Data address is at dataPtr.  Number of bytes is count.
'' The device select code is modified using the upper 3 bits of the 19 bit addrReg.
'' Most devices have a page size of at least 32 bytes, some as large as 256 bytes.
'' Return zero if no errors or the acknowledge bits if an error occurred.  If
'' more than 31 bytes are transmitted, the sign bit is "sticky" and is the
'' logical "or" of the acknowledge bits of any bytes past the 31st.
   devSel |= addrReg >> 15 & %1110
   Start                          ' Select the device & send address
   ackbit := Write(devSel | Xmit)
   ackbit := (ackbit << 1) | Write(addrReg >> 8 & $FF)
   ackbit := (ackbit << 1) | Write(addrReg & $FF)
   repeat count                        ' Now send the data
      ackbit := ackbit << 1 | ackbit & $80000000 ' "Sticky" sign bit         
      ackbit |= Write(byte[dataPtr++])
   Stop
   return ackbit

PRI WriteByte(devSel, addrReg, data)
'' Write out a single byte of i2c data.  Device select code is devSel.  Device
'' starting address is addrReg.  The device select code is modified using the
'' upper 3 bits of the 19 bit addrReg.  This returns true if an error occurred.
   if WritePage(devSel, addrReg, @data, 1)
      return true
   ' james edit - wait for 5ms for page write to complete (80_000 * 5 = 400_000)
   waitcnt(400_000 + cnt)
   return false

PRI WriteWord(devSel, addrReg, data)
'' Write out a single word of i2c data.  Device select code is devSel.  Device
'' starting address is addrReg.  The device select code is modified using the
'' upper 3 bits of the 19 bit addrReg.  This returns true if an error occurred.
'' Note that the word value may not span an EEPROM page boundary.
   if WritePage(devSel, addrReg, @data, 2)
      return true
   ' james edit - wait for 5ms for page write to complete (80_000 * 5 = 400_000)
   waitcnt(400_000 + cnt)     
   return false

PRI WriteLong(devSel, addrReg, data)
'' Write out a single long of i2c data.  Device select code is devSel.  Device
'' starting address is addrReg.  The device select code is modified using the
'' upper 3 bits of the 19 bit addrReg.  This returns true if an error occurred.
'' Note that the long word value may not span an EEPROM page boundary.
   if WritePage(devSel, addrReg, @data, 4)
      return true
   ' james edit - wait for 5ms for page write to complete (80_000 * 5 = 400_000)     
   waitcnt(400_000 + cnt)     
   return false

PRI WriteWait(devSel, addrReg) : ackbit
'' Wait for a previous write to complete.  Device select code is devSel.  Device
'' starting address is addrReg.  The device will not respond if it is busy.
'' The device select code is modified using the upper 3 bits of the 18 bit addrReg.
'' This returns zero if no error occurred or one if the device didn't respond.
   devSel |= addrReg >> 15 & %1110
   Start
   ackbit := Write(devSel | Xmit)
   Stop
   return ackbit


' *************** JAMES'S Extra BITS *********************
   
PRI devicePresent(deviceAddress) : ackbit
  ' send the deviceAddress and listen for the ACK
   Start
   ackbit := Write(deviceAddress | 0)
   Stop
   if ackbit == ACK
     return true
   else
     return false

PRI writeLocation(device_address, register, value)
  start
  write(device_address)
  write(register)
  write(value)
  stop

PRI readLocation(device_address, register) : value
  start
  write(device_address | 0)
  write(register)
  restart                                          'note change to restart from start, SCP1000 doesnt work without this change and so far works with other devices
  write(device_address | 1)
  value := read(NAK)
  stop
  return value

PRI readLocation16(device_address, register) : value
  start
  write(device_address | 0)
  write(register)
  restart
  write(device_address | 1)
  value := read(ACK)
  value <<= 8
  value |= (read(NAK) & $ff)
  stop
  return value

PRI readLocation24(device_address, register) : value
  start
  write(device_address | 0)
  write(register)
  restart
  write(device_address | 1)
  value := read(ACK)
  value <<= 8
  value |= (read(ACK) & $ff)
  value <<= 8
  value |= (read(NAK) & $ff)
  stop
  return value

PRI writeValue(device_address, value)
  start
  write(device_address)
  result := write(value)
  stop

PRI readValue24(device_address) : value
  start
  write(device_address | 1)
  value := read(ACK)
  value <<= 8
  value |= (read(ACK) & $ff)
  value <<= 8
  value |= (read(NAK) & $ff)
  stop
  return value

PRI readValue16(device_address) : value
  start
  write(device_address | 1)
  value := read(ACK)
  value <<= 8
  value |= (read(NAK) & $ff)
  stop
  return value

PRI readValue8(device_address) : value
  start
  write(device_address | 1)
  value := read(NAK)
  stop
  return value
i2c/treiber.txt · Zuletzt geändert: 07.10.2012 23:41 (Externe Bearbeitung)
 
Falls nicht anders bezeichnet, ist der Inhalt dieses Wikis unter der folgenden Lizenz veröffentlicht: GNU Free Documentation License 1.3
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki