espresso1736
Posts: 19
Joined: Fri Jan 22, 2016 11:43 pm
Location: El Cerrito, CA USA

Use Pico as an I2C slave?

Sat Feb 06, 2021 11:55 pm

On Arduino I've used the Wire library, which allows it to be an I2C slave. Is there anything similar for the Pico?

Thanks.

DeanC123
Posts: 1
Joined: Sun Feb 07, 2021 3:30 am

Re: Use Pico as an I2C slave?

Sun Feb 07, 2021 3:35 am

+1, this is a really common pattern with a RPx managing peripheral uControllers. It really limits the Pico to a stand alone system if can't slave to a central controller.

jamesh
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 32874
Joined: Sat Jul 30, 2011 7:41 pm

Re: Use Pico as an I2C slave?

Sun Feb 07, 2021 11:21 am

The Pico can be a slave I2C.
Principal Software Engineer at Raspberry Pi Ltd.
Working in the Applications Team.

tangledweb
Posts: 3
Joined: Sun Feb 07, 2021 3:37 pm

Re: Use Pico as an I2C slave?

Sun Feb 07, 2021 4:01 pm

The examples I've found for other boards all seem to reference I2C.SLAVE
when I've tried that within I2C.init I get

AttributeError: type object 'I2C' has no attribute 'SLAVE'

is there somewhere I can find an I2C slave exmple in micropython that will work on the Pico.

martgut
Posts: 1
Joined: Sun Feb 07, 2021 4:35 pm

Re: Use Pico as an I2C slave?

Sun Feb 07, 2021 4:43 pm

I'm also trying to use the Pico as I2C slave, but from MicroPython.
I maybe wrong, but the machine module only supports the setup as master?
http://docs.micropython.org/en/latest/l ... e.I2C.html

I found some support in the pyb module, but this does not seem relevant for the Pico:
https://docs.micropython.org/en/latest/ ... b.I2C.html
i2c.init(I2C.SLAVE, addr=0x42) # init as a slave with given address

Help appreciated, Thanks!

espresso1736
Posts: 19
Joined: Fri Jan 22, 2016 11:43 pm
Location: El Cerrito, CA USA

Re: Use Pico as an I2C slave?

Sun Feb 07, 2021 6:30 pm

Perhaps you could give us guidance as to how to do that. I've reviewed the documentation of machine.I2C and all it seems to talk about is communicating with a slave device - nothing about being a slave device. What am I missing?

snugle
Posts: 1
Joined: Sun Feb 07, 2021 6:41 pm

Re: Use Pico as an I2C slave?

Sun Feb 07, 2021 6:49 pm

Hello,

glad to have found this thread.

@espresso: Have a look at the function list (Pico C SDK) page 114. There you will find infos how to activate the i2c slave mode.

But I am not further yet.

Can someone give an example in C how to activate the slave mode and how to set the receive and reply functions.

Thanks and keep it up

tangledweb
Posts: 3
Joined: Sun Feb 07, 2021 3:37 pm

Re: Use Pico as an I2C slave?

Mon Feb 08, 2021 11:53 am

Thing you are overlooking here is that this is posted in the micropython section. Just because you can do it in C doesn't mean it's implemented in the micropython libraries.

espresso1736
Posts: 19
Joined: Fri Jan 22, 2016 11:43 pm
Location: El Cerrito, CA USA

Re: Use Pico as an I2C slave?

Mon Feb 08, 2021 5:33 pm

Thanks, @snugle. I was indeed hoping for a MicroPython solution, however it remains elusive. I'll take a look at the C SDK and see if that's any help.

I've started to read up on the PIO capabilities, in hopes that a solution might be found there. Still early days...

espresso1736
Posts: 19
Joined: Fri Jan 22, 2016 11:43 pm
Location: El Cerrito, CA USA

Re: Use Pico as an I2C slave?

Mon Feb 08, 2021 9:37 pm

For anyone following along - section 3.6.7 of the 2040 Datasheet shows an example program using the PIO State Machine to implement an I2C bus master. Just found this a few moments ago. So it must be possible to implement an I2C slave in a similar way ... once I wrap my head around the PIO instruction set, etc. This will be fun! I haven't written in assembly language for about 50 years.

dennisma
Posts: 6
Joined: Sun Feb 07, 2021 10:25 pm

Re: Use Pico as an I2C slave?

Mon Feb 08, 2021 11:59 pm

If you look at the MicroPython API ( https://docs.micropython.org/en/latest/ ... e.I2C.html
) there does not appear to be a way to make a slave. You would expect this to be an argument in the constructor (where you'd pass the I2C slave address).

While I like Python and Micro/CircuitPython I do tire of limitations like this. It can amazingly handle interrupts so why can't it perform as an I2C slave?

tannewt
Posts: 75
Joined: Tue Nov 17, 2020 1:14 am

Re: Use Pico as an I2C slave?

Tue Feb 09, 2021 8:18 pm

dennisma wrote:
Mon Feb 08, 2021 11:59 pm
While I like Python and Micro/CircuitPython I do tire of limitations like this. It can amazingly handle interrupts so why can't it perform as an I2C slave?
These limitations are due to the limited development resources MicroPython and CircuitPython have. It takes time and effort to support everything folks want to do.

In CircuitPython we have an `i2cperipheral` API for this but it hasn't been implemented on the RP2040 yet. We're working on audio playback and UART instead.

Slofware
Posts: 29
Joined: Tue Jul 26, 2016 5:06 pm

Re: Use Pico as an I2C slave?

Thu Feb 18, 2021 3:32 pm

jamesh wrote:
Sun Feb 07, 2021 11:21 am
The Pico can be a slave I2C.
Please tell us how.

cleverca22
Posts: 8210
Joined: Sat Aug 18, 2012 2:33 pm

Re: Use Pico as an I2C slave?

Thu Feb 18, 2021 3:35 pm

Slofware wrote:
Thu Feb 18, 2021 3:32 pm
jamesh wrote:
Sun Feb 07, 2021 11:21 am
The Pico can be a slave I2C.
Please tell us how.
https://datasheets.raspberrypi.org/rp20 ... asheet.pdf
page 464
4.3.10.1. Slave Mode Operation
This section discusses slave mode procedures.
4.3.10.1.1. Initial Configuration
To use the DW_apb_i2c as a slave, perform the following steps:
1. Disable the DW_apb_i2c by writing a ‘0’ to IC_ENABLE.ENABLE.
2. Write to the IC_SAR register (bits 9:0) to set the slave address. This is the address to which the DW_apb_i2c
responds.
3. Write to the IC_CON register to specify which type of addressing is supported (7-bit or 10-bit by setting bit 3). Enable
the DW_apb_i2c in slave-only mode by writing a ‘0’ into bit six (IC_SLAVE_DISABLE) and a ‘0’ to bit zero
(MASTER_MODE).
.....

tangledweb
Posts: 3
Joined: Sun Feb 07, 2021 3:37 pm

Re: Use Pico as an I2C slave?

Thu Feb 18, 2021 3:54 pm

So not from the available micropython libraries. :(

cleverca22
Posts: 8210
Joined: Sat Aug 18, 2012 2:33 pm

Re: Use Pico as an I2C slave?

Thu Feb 18, 2021 4:01 pm

tangledweb wrote:
Thu Feb 18, 2021 3:54 pm
So not from the available micropython libraries. :(

Code: Select all

import machine
machine.mem32[0x4001c074]=0x42
you can still access raw registers from micropython, but youll need to write the library yourself

MirekCZ
Posts: 7
Joined: Thu Feb 11, 2021 9:57 pm

Re: Use Pico as an I2C slave?

Thu Feb 18, 2021 9:31 pm

Hello, so do I understand it correctly, that there is no easy way for communication between 2 picos? Because there always must be one master and at least one slave in I2C... I will need it in the future for connecting with Fischertechnik controller. Now I am using for this Arduino, but I wanted to switch to Pico because of the python and because its Raspberry...
Last edited by MirekCZ on Thu Feb 18, 2021 9:33 pm, edited 1 time in total.

cleverca22
Posts: 8210
Joined: Sat Aug 18, 2012 2:33 pm

Re: Use Pico as an I2C slave?

Thu Feb 18, 2021 9:33 pm

MirekCZ wrote:
Thu Feb 18, 2021 9:31 pm
Hello, so do I understand it correctly, that there is no easy way for communication between 2 picos? Because there always must be one master and at least one slave...
uart serial is the simplest option there

MirekCZ
Posts: 7
Joined: Thu Feb 11, 2021 9:57 pm

Re: Use Pico as an I2C slave?

Thu Feb 18, 2021 9:37 pm

That is not a way. At my university department we use Fischertechnik to show to students basics ofIndustry 4.0 princip. And because we are not programming department, we use only theyr RoboPro for using it - programming by graphical blocks connecting. And there is only block for I2C communication. That is the reason why we use arudino in some parts - arduino is getting data from models and is sending them over the uart/usb to computer to my C# application. It would be to difficult programming models in C/C++, learn it, teach it - easily there is no time dotation for that. Doesn't matter, good to know now. In a future I believe it will be possible and that will be my time to switch :)

danjperron
Posts: 4504
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Use Pico as an I2C slave?

Sat Feb 20, 2021 12:36 pm

Oops It was to late yesterday . I did post it but not at the right place.

Proof of concept!

Ok I made a small I2C slave class that could read one byte data send to the Pico
From the Raspberry Pi, I connect the I2C to the Pico I2C.
I was able to receive data from the Pi by sending via I2c .

On The Pi.

Code: Select all

import smbus
bus = smbus.SMBus(1)
bus.write_byte(0x41,12)
bus.write_byte(0x41,15)
On the Pico I create a class i2cSlave.py

Code: Select all

from machine import mem32,Pin

class i2c_slave:
    I2C0_BASE = 0x40044000
    I2C1_BASE = 0x40048000
    IO_BANK0_BASE = 0x40014000
    
    mem_rw =  0x0000
    mem_xor = 0x1000
    mem_set = 0x2000
    mem_clr = 0x3000
    
    IC_CON = 0
    IC_TAR = 4
    IC_SAR = 8
    IC_DATA_CMD = 0x10 
    IC_RX_TL = 0x38
    IC_TX_TL = 0x3C
    IC_CLR_INTR = 0x40
    IC_ENABLE = 0x6c
    IC_STATUS = 0x70
    
    def write_reg(self, reg, data, method=0):
        mem32[ self.i2c_base | method | reg] = data
        
    def set_reg(self, reg, data):
        self.write_reg(reg, data, method=self.mem_set)
        
    def clr_reg(self, reg, data):
        self.write_reg(reg, data, method=self.mem_clr)
                
    def __init__(self, i2cID = 0, sda=0,  scl=1, slaveAddress=0x41):
        self.scl = scl
        self.sda = sda
        self.slaveAddress = slaveAddress
        self.i2c_ID = i2cID
        if self.i2c_ID == 0:
            self.i2c_base = self.I2C0_BASE
        else:
            self.i2c_base = self.I2C1_BASE
        
        # 1 Disable DW_apb_i2c
        self.clr_reg(self.IC_ENABLE, 1)
        # 2 set slave address
        # clr bit 0 to 9
        # set slave address
        self.clr_reg(self.IC_SAR, 0x1ff)
        self.set_reg(self.IC_SAR, self.slaveAddress &0x1ff)
        # 3 write IC_CON  7 bit, enable in slave-only
        self.clr_reg(self.IC_CON, 0b01001001)
        # set SDA PIN
        mem32[ self.IO_BANK0_BASE | self.mem_clr |  ( 4 + 8 * self.sda) ] = 0x1f
        mem32[ self.IO_BANK0_BASE | self.mem_set |  ( 4 + 8 * self.sda) ] = 3
        # set SLA PIN
        mem32[ self.IO_BANK0_BASE | self.mem_clr |  ( 4 + 8 * self.scl) ] = 0x1f
        mem32[ self.IO_BANK0_BASE | self.mem_set |  ( 4 + 8 * self.scl) ] = 3
        # 4 enable i2c 
        self.set_reg(self.IC_ENABLE, 1)


    def any(self):
        # get IC_STATUS
        status = mem32[ self.i2c_base | self.IC_STATUS]
        # check RFNE receive fifio not empty
        if (status &  8) :
            return True
        return False
    
    def get(self):
        while not self.any():
            pass
        return mem32[ self.i2c_base | self.IC_DATA_CMD] & 0xff
    
    if __name__ == "__main__":
        import utime
        from machine import mem32
        from i2cSlave import i2c_slave
        
        s_i2c = i2c_slave(0,sda=0,scl=1,slaveAddress=0x41)
        
        try:
            while True:
                print(s_i2c.get())
            
        except KeyboardInterrupt:
            pass
        
And I got

Code: Select all

MicroPython v1.13-290-g556ae7914 on 2021-01-21; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
12
15

So just using micropython to create the slave class is possible.

danjperron
Posts: 4504
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Use Pico as an I2C slave?

Sat Feb 20, 2021 2:23 pm

Now read and write i2c slave

the class

Code: Select all

from machine import mem32,mem8,Pin

class i2c_slave:
    I2C0_BASE = 0x40044000
    I2C1_BASE = 0x40048000
    IO_BANK0_BASE = 0x40014000
    
    mem_rw =  0x0000
    mem_xor = 0x1000
    mem_set = 0x2000
    mem_clr = 0x3000
    
    IC_CON = 0
    IC_TAR = 4
    IC_SAR = 8
    IC_DATA_CMD = 0x10
    IC_RAW_INTR_STAT = 0x34
    IC_RX_TL = 0x38
    IC_TX_TL = 0x3C
    IC_CLR_INTR = 0x40
    IC_CLR_RD_REQ = 0x50
    IC_CLR_TX_ABRT = 0x54
    IC_ENABLE = 0x6c
    IC_STATUS = 0x70
    
    def write_reg(self, reg, data, method=0):
        mem32[ self.i2c_base | method | reg] = data
        
    def set_reg(self, reg, data):
        self.write_reg(reg, data, method=self.mem_set)
        
    def clr_reg(self, reg, data):
        self.write_reg(reg, data, method=self.mem_clr)
                
    def __init__(self, i2cID = 0, sda=0,  scl=1, slaveAddress=0x41):
        self.scl = scl
        self.sda = sda
        self.slaveAddress = slaveAddress
        self.i2c_ID = i2cID
        if self.i2c_ID == 0:
            self.i2c_base = self.I2C0_BASE
        else:
            self.i2c_base = self.I2C1_BASE
        
        # 1 Disable DW_apb_i2c
        self.clr_reg(self.IC_ENABLE, 1)
        # 2 set slave address
        # clr bit 0 to 9
        # set slave address
        self.clr_reg(self.IC_SAR, 0x1ff)
        self.set_reg(self.IC_SAR, self.slaveAddress &0x1ff)
        # 3 write IC_CON  7 bit, enable in slave-only
        self.clr_reg(self.IC_CON, 0b01001001)
        # set SDA PIN
        mem32[ self.IO_BANK0_BASE | self.mem_clr |  ( 4 + 8 * self.sda) ] = 0x1f
        mem32[ self.IO_BANK0_BASE | self.mem_set |  ( 4 + 8 * self.sda) ] = 3
        # set SLA PIN
        mem32[ self.IO_BANK0_BASE | self.mem_clr |  ( 4 + 8 * self.scl) ] = 0x1f
        mem32[ self.IO_BANK0_BASE | self.mem_set |  ( 4 + 8 * self.scl) ] = 3
        # 4 enable i2c 
        self.set_reg(self.IC_ENABLE, 1)


    def anyRead(self):
        status = mem32[ self.i2c_base | self.IC_RAW_INTR_STAT] & 0x20
        if status :
            return True
        return False

    def put(self, data):
        # reset flag       
        self.clr_reg(self.IC_CLR_TX_ABRT,1)
        status = mem32[ self.i2c_base | self.IC_CLR_RD_REQ]
        mem32[ self.i2c_base | self.IC_DATA_CMD] = data  & 0xff

        
        

    def any(self):
        # get IC_STATUS
        status = mem32[ self.i2c_base | self.IC_STATUS]
        # check RFNE receive fifio not empty
        if (status &  8) :
            return True
        return False
    
    def get(self):
        while not self.any():
            pass
        return mem32[ self.i2c_base | self.IC_DATA_CMD] & 0xff
    
    if __name__ == "__main__":
        import utime
        from machine import mem32
        from i2cSlave import i2c_slave
        
        s_i2c = i2c_slave(0,sda=0,scl=1,slaveAddress=0x41)
        counter =1
        try:
            while True:
                if s_i2c.any():
                    print(s_i2c.get())
                if s_i2c.anyRead():
                    counter = counter + 1
                    s_i2c.put(counter & 0xff)
            
        except KeyboardInterrupt:
            pass

And getting data on the Pi

Code: Select all

>>> bus.read_byte(0x41)
2
>>> bus.read_byte(0x41)
3
>>> bus.read_byte(0x41)
4
>>> bus.read_byte(0x41)
5
>>> bus.read_byte(0x41)
6
>>> bus.read_byte(0x41)
7
Now it is to figure out how to make interrupt, add command and multiple transaction

hippy
Posts: 15198
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Use Pico as an I2C slave?

Sat Feb 20, 2021 3:58 pm

MirekCZ wrote:
Thu Feb 18, 2021 9:31 pm
Hello, so do I understand it correctly, that there is no easy way for ...
Unless it has been implemented then that's the way it is, and always will be, if using MicroPython, CircuitPython or other third-party languages.

For those language providers the RP2040 is a third-party product and it takes time and effort to figure out how to provide what people might like to see, implement it, test it, debug it, release it - as tannewt noted earlier.

Just because a chip supports something doesn't mean a language provider will have provided a means of using that capability with their language. Even where they would like to, want to, that takes time, effort, resources and has cost. All that has to be scheduled and balanced alongside other activities.

And it's not always easy to find the funding for additions or improvements when software is given away for free, if chip developers are not funding third-parties to support their chip or that funding is limited.

If something is lacking then one either needs to provide a solution oneself, wait for the language developers or the user community to figure out a solution, or do without, look in some other direction.

Slofware
Posts: 29
Joined: Tue Jul 26, 2016 5:06 pm

Re: Use Pico as an I2C slave?

Sun Feb 21, 2021 9:45 pm

Works! Nice

mbogelund
Posts: 1
Joined: Sun Feb 21, 2021 1:24 pm

Re: Use Pico as an I2C slave?

Mon Feb 22, 2021 6:19 am

danjperron wrote:
Sat Feb 20, 2021 12:36 pm
Proof of concept!

Ok I made a small I2C slave class that could read one byte data send to the Pico.
Would it make sense (and would it be OK with you) if someone took your proof of concept class, and put it on GitHub as a kind of starting point of a generally available MicroPython I2C slave library for the Raspberry Pi Pico?

MirekCZ
Posts: 7
Joined: Thu Feb 11, 2021 9:57 pm

Re: Use Pico as an I2C slave?

Mon Feb 22, 2021 11:44 am

Great, thank you very much for solution.

I understand, that Pico is for micropython new and for a lot of things I will have to wait and it's really not a problem for me :)

Return to “MicroPython”