Using I2C to scan for devices

Coding and general discussion relating to the compiler

Moderators: David Barker, Jerry Messina

Post Reply
bitfogav
Registered User
Registered User
Posts: 169
Joined: Sat Oct 09, 2010 1:39 pm
Location: United Kingdom

Using I2C to scan for devices

Post by bitfogav » Mon Jun 15, 2020 12:01 pm

Hey guys,

I have been trying to make a simple sub to read all the connected addresses on a I2C bus, the following routine works, but sometimes I get strange results on a serial monitor (as in the bus thinks there's 127 addresses on it?, when there's not), now I don't know if the sub is fully correct with how the I2C works or whether its a device on the bus that spaz-out the bus and gives me the wrong output data to a serial monitor.. If there's any suggestions or recommendations then it would be much appreciated..

Code: Select all

dim addrs, error, nDevices as byte
  
sub ScanForDevices()
   
   for addrs = 1 to 127
      i2c.start()
      i2c.writebyte(addrs)
      i2c.writebyte($20)         '<--- the sub doesn't work unless I add this, the value isn't important.
      i2c.stop()
            
      If (I2C.NotAcknowledged = true) Then
      	error = 1    ' no device
      Else
      	error = 0    ' device detected
      EndIf 
      
      
      If error = 0 Then   ' no error we have found a device.
         USART.Write("$", HexToStr(addrs), 13, 10)
         Inc(nDevices)    ' found a device so add number of device.
      End If
   Next
   
   If nDevices = 0 Then
      USART.Write("No I2C devices found ", DecToStr(nDevices), 13, 10)
   Else
      USART.Write("Complete found ", DecToStr(nDevices), 13, 10)
   End If
   nDevices = 0 ' reset 
End Sub

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Using I2C to scan for devices

Post by Jerry Messina » Mon Jun 15, 2020 2:19 pm

Here's how I do it:

Bit 0 of the address is the RD_WRN bit, so I only use the even numbered "addresses".
(I can't recall ever seeing a "read-only" device).

If the slave is present, it will ACK its address byte, so no other data needs to be sent. Besides, you never know what you might be doing there.

I also add a little delay between addr probes in case the slave is really slow and can't process fast transactions.
Here I used 100us but you can usually get away with much less.

Code: Select all

dim nDevices as byte
  
sub ScanForDevices()
   dim addrs, error as byte
   
   nDevices = 0 ' reset 

   for addrs = 0 to 126 step 2
      i2c.start()
      i2c.writebyte(addrs)
      i2c.stop()
            
      If (I2C.NotAcknowledged = true) Then
      	error = 1    ' no device
      Else
      	error = 0    ' device detected
      EndIf 
      
      If error = 0 Then   ' no error we have found a device.
         USART.Write("$", HexToStr(addrs), 13, 10)
         Inc(nDevices)    ' found a device so add number of device.
      End If

      ' add a little delay betweens transactions for some slow slave devices
      delayus(100)
   Next
   
   USART.Write(DecToStr(nDevices), " I2C device(s) found", 13, 10)
End Sub

bitfogav
Registered User
Registered User
Posts: 169
Joined: Sat Oct 09, 2010 1:39 pm
Location: United Kingdom

Re: Using I2C to scan for devices

Post by bitfogav » Mon Jun 15, 2020 3:19 pm

Bit 0 of the address is the RD_WRN bit, so I only use the even numbered "addresses".
(I can't recall ever seeing a "read-only" device).

If the slave is present, it will ACK its address byte, so no other data needs to be sent. Besides, you never know what you might be doing there.

I also add a little delay between addr probes in case the slave is really slow and can't process fast transactions.
Here I used 100us but you can usually get away with much less.

Yes I was sure that I didn't need to send the second byte of data because I only wanted to probe the bus for the connected devices.

So a small delay in the loop basically all it really needed for slow devices :lol:
Thank you so much for always helping Jerry.

I have attached the code of my program that might also be useful for anyone else that might need to find the address of a device..

Full Code Below

Code: Select all

Device = 18F2550
Clock = 20        
   
// import libraries...   
Include "usart.bas"
Include "convert.bas"
Include "I2C.bas"

dim nDevices as byte
  
sub ScanForDevices()
   dim addrs, error as byte
   
   nDevices = 0 ' reset 

   for addrs = 0 to 126 step 2
      i2c.start()
      i2c.writebyte(addrs)
      i2c.stop()
            
      If (I2C.NotAcknowledged = true) Then
      	error = 1    ' no device
      Else
      	error = 0    ' device detected
      EndIf 
      
      If error = 0 Then   ' no error we have found a device.
         USART.Write("$", HexToStr(addrs), 13, 10)
         Inc(nDevices)    ' found a device so add number of device.
      End If

      ' add a little delay betweens transactions for some slow slave devices
      delayus(100)
   Next
   
   USART.Write(DecToStr(nDevices), " I2C device(s) found", 13, 10)
End Sub

// init I2C... 
   I2C.Initialize(I2C_100_KHZ)
 
// start serial...
   USART.SetBaudrate(br19200)
   USART.Write("I2C Scanner", 13, 10)
   USART.Write("By Gavin Wiggett (Bitfogav)", 13, 10)

// main prog loop...
While true 
   USART.Write("Scanning for Devices..", 13, 10)
   ScanForDevices()
   DelayMS(5000)  ' 5sec delay between bus reads. 
Wend
serialouti2c.png
serialouti2c.png (21.94 KiB) Viewed 2258 times

Post Reply