I2C scanner

Technoman

Senior Member
Hi,

Not sure I am not reinventing the wheel...

In order to code an I2C scanner, using an 28X2, I need your help to check if the following principle is right, as I am not yet easy with that bus.

1) set-up with : hi2csetup i2cmaster,scan_adress,i2cslow,i2cbyte

Within a loop the scan_address is ranging 0 to 127 using bit1-bit7, skipping some addresses like 0, %1111xxx or %0000xxxx

A question is : will every chip respond with i2cslow? Need to switch to i2cfast for some?

2) then reading with : hi2cin scan_adress,(b1)
If a chip is not responding, would I systematically get an $FF in b1, otherwise a different value? Is there a better way to see if a chip is present?
 
Last edited:

nick12ab

Senior Member
A question is : will every chip respond with i2cslow? Need to switch to i2cfast for some?
All devices will respond to i2cslow.

2) then reading with : hi2cin scan_adress,(b1)
If a chip is not responding, would I systematically get an $FF in b1, otherwise a different value? Is there a better way to see if a chip is present?
You should get $FF if there is no device, but a blank EEPROM will also give you $FF.

A better way is to check if a device ACKed the I2C address write, but this can't be done on PICAXE without bit-banging the I2C interface.
 

westaust55

Moderator
If you are trying to build an i2c scanner to monitor and record/display all i2c bus activity then the only way will be to use bit-bang code.
A scanner would be independent of a specific slave address or speed and needs to monitor the clock and data waveforms to determine what slave address is being accessed and what data is being transferred.

The example you have found by hippy is a good starting point.
 

hippy

Technical Support
Staff member
I would say bit-banging was the only reliable way. The firmware commands for I2C presume the I2C device will be connected and working as expected so behaviour is not defined when one isn't.

The example code you found is for an older 18X which has fixed input and output pins. M2 and X2 devices can do it all with just two signal lines and just pull-ups. There should be examples on the forum.
 

PhilHornby

Senior Member
I2C Scanner code

As a result of my recent investigations into I²C issues here, I noticed an effect that can be used to implement an I²C Bus Scanner / Address Finder.

In master mode, on the 20X2, the SSPBUF register is used to contain the address or data to be placed onto the I²C bus. When the operation completes, it still contains whatever the last byte transmitted was.

The Hi2cout command first places the target I²C address into this register and waits for the other end to ACK it, or a timeout (NAK). If there is an ACK, it then places the (next) data byte into SSPBUF and continues until all data is successfully sent and ACK'd.

However, if there no ACK in response to the address Hi2cout aborts the operation and proceeds no further. This means that if the target address doesn't exist, its value will still be in the SSPBUF register. By sending a single data byte and choosing a value which cannot be an address, we can determine what happened - if SSPBUF contains that data byte, the address must have been valid.

Rich (BB code):
; I2C Address Scanner for Picaxe 20X2

#picaxe 20x2
#no_data
#no_table
#terminal 9600
;
; I2C register
;
symbol sspbuf = $C9
     
      pause 5000                                ;chance to start logging.
      sertxd (cr,lf,"START PROGRAM")

      hi2csetup i2cmaster,2 ,i2cslow, i2cbyte   ;Set up I2c. Enable SDA/SCL, set BRG divisor.
     
      for b2 = %00000010 to %11111110 step 2    ;addresses are shifted left one bit, as bit 0 is R/W flag.
           
            hi2cout [b2],(0)                    ;Just send a 0 as data (this can't be confused with a valid i2c address)
            ;
            ; If the address setup sequence is NAK'd, then hi2cout will abort and not send the data byte(0)...
            ; ...SSPBUF always contains the last data byte sent. If no data was sent, SSPBUF will still contain the address.
            ;
            peeksfr sspbuf,b0                   ;so, what was that last byte?
            if b0 = 0 then                      ;if it's zero, it must be data - so the address must have been ACK'd.
                  b0 = b2 / 2                   ;convert address used to 'normal' i2c address
                 
                  sertxd (cr,lf,"** i2c response @ [",#bit7,#bit6,#bit5,#bit4,"],[",#bit3,#bit2,#bit1,#bit0,"] 0x")
                  ;
                  ; Print in hex too
                  ;
                  b1 = b0 / 16 + "0"            ;get top  nybble, convert to ASCII
                  if b1 > "9" then             
                        b1 = b1 + 7             ;convert 9-15 into A-F
                  endif
                  sertxd(b1)                    ;print it
                 
                  b1 = b0 & %00001111 + "0"     ;get bottom nybble, convert to ASCII
                  if b1 > "9" then             
                        b1 = b1 + 7             ;convert 9-15 into A-F
                  endif
                  sertxd(b1)                    ;print that too.
            else
                  if b0 <> b2 then
                        sertxd (cr,lf,"Um...something unexpected happened!",#b2) ; this never appears ;-)
                  endif
            endif
      next 

      sertxd (cr,lf,"END PROGRAM")
      end      
I tried it on a breadboard containing an LCD Display (PCF8574), and an eBay RTC module (DS1307 + EEPROM).

START PROGRAM
** i2c response @ [0010],[0111] 0x27
** i2c response @ [0101],[0000] 0x50
** i2c response @ [0110],[1000] 0x68
END PROGRAM
 
Last edited:

PhilHornby

Senior Member
14M2 Version of I²C scanner

Rich (BB code):
; I2C Address Scanner for Picaxe 14M2
#picaxe 14M2
#no_data
#terminal 4800
;
; I2C register
;
symbol ssp1buf = $91                            ;14M2
     
      pause 5000                                ;chance to start logging.
      sertxd (cr,lf,"START PROGRAM")
     
      for b2 = %00000010 to %11111110 step 2    ;addresses are shifted left one bit, as bit 0 is R/W flag.
     
            hi2csetup i2cmaster,b2,i2cslow,i2cbyte ; Set up I2c. Enable SDA/SCL, set BRG divisor.
           
            hi2cout (0)                         ;Just send a 0 as data (this can't be confused with a valid i2c address)
            ;
            ; If the address setup sequence is NAK'd, then hi2cout will abort and not send the data byte(0)...
            ; ...SSP1BUF always contains the last data byte sent. If no data was sent, SSP1BUF will still contain the address.
            ;
            peeksfr ssp1buf,b0                  ;so, what was that last byte?
            if b0 = 0 then                      ;if it's zero, it must be data - so the address must have been ACK'd.
                  b0 = b2 / 2                   ;convert address used to 'normal' i2c address
                 
                  sertxd (cr,lf,"** i2c response @ [",#bit7,#bit6,#bit5,#bit4,"],[",#bit3,#bit2,#bit1,#bit0,"] 0x")
                  ;
                  ; Print in hex too
                  ;
                  b1 = b0 / 16 + "0"            ;get top  nybble, convert to ASCII
                  if b1 > "9" then             
                        b1 = b1 + 7             ;convert 9-15 into A-F
                  endif
                  sertxd(b1)                    ;print it
                 
                  b1 = b0 & %00001111 + "0"     ;get bottom nybble, convert to ASCII
                  if b1 > "9" then             
                        b1 = b1 + 7             ;convert 9-15 into A-F
                  endif
                  sertxd(b1)                    ;print that too.
            else
                  if b0 <> b2 then
                        sertxd (cr,lf,"Um...something unexpected happened!",#b2) ; this never appears ;-)
                  endif
            endif
      next 
      sertxd (cr,lf,"END PROGRAM")
      end
Trace of the I²C bus during a write to an invalid address, followed by a write to a valid one :-

ValidvsInvalid.png
 
Last edited:

Technoman

Senior Member
Thank you for this great program. I run it on a 28X2 (same SSPBUF address as 20X2) on a lightly hardware modified Grove shield coupled with an AXE401.

It works fine in scanning the addresses of a Grove LCD RGB backlight display. Although I will have to investigate as I get one more valid address (0x03) than with an Arduino based I2c scanner.
 

PhilHornby

Senior Member
Although I will have to investigate as I get one more valid address (0x03) than with an Arduino based I2c scanner.
The Arduino I²C scanner that I have (by Nick Gammon), doesn't appear to scan the entire address space:
Code:
for (byte i = 8; i < 120; i++)
so maybe that's the difference?

AIUI, certain addresses are reserved - although they do still work, if something responds to them. Or it could be a peculiarity of the 28X2 - I don't have one of those to test. You might have to disconnect things, one at a time to find it :confused:
 

hippy

Technical Support
Staff member
I recently wrote an I2C scanner to identify Wii Nunchuck and other Wii controllers connected via I2C. That turned up some interesting oddities even when bit-banging.

The most notable was that, if one issues an I2C Stop command after sending a slave address and getting an Ack, some controllers would lock-up and never acknowledge their slave address again. Those required a full I2C read sequence for them not to lock-up.
 
Top