arduino / ArduinoCore-mbed

330 stars 195 forks source link

Raspberry Pico SPI and I2C pins #194

Open sstaub opened 3 years ago

sstaub commented 3 years ago

Raspberry Pico How to determine which pins are used for SPI, and how to do this for SPI1. Are there any standard pins? Same for I2C. Is there any API (with examples)?

Grumpy-Mike commented 3 years ago

Pico-R3-A4-Pinout.pdf You can use any of these pins for I2C, note there are two I2C buses as well. You have to tell the Pico what pins you are going to use when you initialise it.

sstaub commented 3 years ago

I know about the pins I can use. But how to configure?

Grumpy-Mike commented 3 years ago

This is an example of using the I2C to drive an LCD

lcd_1602_i2c.c.zip

sstaub commented 3 years ago

@Grumpy-Mike What have the code todo with ArduinoCore-Core?

Grumpy-Mike commented 3 years ago

Don't understand the question sorry.

stuartdd commented 3 years ago

This is my understanding | miss-understanding.

I have been using the code from earlephilhower/arduino-pico with some success. My SSD1306 plays Space Invaders, nice!

I wanted to use the official Arduino release and followed the link to this repo.

Initial Process (Not using README.md): 1) I downloaded a fresh Arduino IDE, Removed all Arduino related libs from my user and started the IDE. 2) I went to the Board Manager an installed 'Arduino OS RP2040 Boards'. 3) I selected the 'Raspberry PI Pico' board. 4) I uploaded the Blink sketch pressed upload (which failed) 5) I Ran the post-install.sh (from your repo) as root. This fixed the above issue! 6) I uploaded the sketch and the LED started to blink. YAHAOO! Does this use the code from ArduinoCore-mbed? If NOT what is the difference between ArduinoCore-mbed and the IDE's Board installation? 7) I loaded up a sketch that uses the I2C interface to drive a OLED display using 'Adafruit_SSD1306.h' library. 8) I cannot find a way of defining the SDA and SCL pins used by the Wire api. 9) I looked at lcd_1602_i2c.c.zip and it says: GPIO 4 (pin 6)-> SDA on LCD bridge board GPIO 5 (pin 7)-> SCL on LCD bridge board This does NOT work and is not an Arduino sketch (very confusing).

There is no data being sent to the display. I tried GP0/GP1, GP4/GP5, GP12/GP13, GP17/GP16, GP21/GP20 no luck on any.

How do I define (within a Sketch) the Pico pins used by the Wire API for SCL and SDA?

Second Process: 1) I started from scratch (Removed all Arduino related libs from my user and started the IDE) as above. 2) I followed the README.md on this repo. This installs mbed and ArduinoCore-API libraries. It assumed I knew what $sketchbook was, I did not. There was no 'symlink to api'. I created one. The IDE failed to load as it could not find boards.txt. I moved some directories around and finally got the IDE running.

 I could NOT see the Pico boards. I only had the original boards as selected in my initial process!

I gave up and went back to using earlephilhower/arduino-pico

So Three questions I have! 1) What is the difference between ArduinoCore-mbed and the IDE's existing Board Manager Pico installation 2) How do I define (within a Sketch) the Pico pins used by the Wire API for SCL and SDA? 3) Which should I use? ArduinoCore-API libraries (Arduino-core) or the IDE board manager?

I have to say that I am VERY excited about using Arduino code for my Pico. I do not wish to seem critical but at the moment this is not usable without help from the developers.

That earlephilhower/arduino-pico API adds setSCL(n) and setSDA(n) to the Wire API and it just works.

Please help me understand the above issues.

Regards

Stuart

sstaub commented 3 years ago

The code of earlephilhower/arduino-pico is based on c++ sdk of Arduino. This core is based on Mbed. Meanwhile I found out that the SPI / I2C pins doesn't follow the official pinup of raspberry pi pico. I'm missing an API for changing the pins like pinSwap on other cores. I need to try the underlined Mbed core the next days. In pins_arduino.h there are some basic pins for SPI and I2C but not for SPI1 and WRE1 also for UART, so I think some rework is needed.

TobiasVanDyk commented 3 years ago

My SSD1306 plays Space Invaders, nice!

7. I loaded up a sketch that uses the I2C interface to drive a OLED display using 'Adafruit_SSD1306.h' library.

   GPIO 4 (pin 6)-> SDA on LCD bridge board
   GPIO 5 (pin 7)-> SCL on LCD bridge board
   This does NOT work and is not an Arduino sketch (very confusing).

Stuart

Hi Stuart I also used an SD1306 i2c OLED with BOTH mbed and the C++SDK Arduino libraries For the mbed I copy this part from my github: an existing Arduino IDE 1.8.13 installation was used with the Arduino mbed-based installation and two Adafruit graphics libraries were installed as described. Connect the OLED display SDA to Pico pin 9 (GP6) and SCL to Pico pin 10 (GP7). This was based on the discussion here - the Arduino-mbed-Pico board uses a fixed i2c-1 assignment for SDA and SCL. The Adafruit example used, had its i2c address changed from 0x3D to 0x3C for this particular OLED display - the modified sketch is inside the OLEDArduino folder above as ssd1306_128x64_i2cPico.ino.

It is working with the mbed Arduino and Adafruit graphics libs

sstaub commented 3 years ago

The official hardware documentation says following. I2C GP4 SDA GP5 SCL

SPI0 GP16 SS GP17 MISO GP18 SCK GP19 MOSI

I think the board definition should follow the official docs as a reference. SDA on GP6 and SCL on GP7 is I2C 1 not 0 as expected MISO is GP4, MOSI GP3, SCK is GP2, SS is GP5 this should changed to the pins above.

There is a need a function to set alternative pins for UART, SPI, I2C On Mbed you do it when declaring a class like MbedI2C I2C(GP6, GP7); The DX core https://github.com/SpenceKonde/DxCore have this functionality

sstaub commented 3 years ago

I forgot, we need some standard pins for I2C 1, SPI 1 and UART 1

facchinm commented 3 years ago

Hi everyone, first of all, sorry for the lack of documentation, we are working hard to fix it. On the Pico, pins are not marked as per theirs hardware function, so we decided to create two default objects (SPI and Wire hooked to pins 2,3,4,5 and 6,7 https://github.com/arduino/ArduinoCore-mbed/blob/master/variants/RASPBERRY_PI_PICO/pins_arduino.h#L44-L57 ). The hardware peripheral they are connected with is not active until you call begin(), so it can be safely overridden if you need to use another set of pins. The easiest way is to create objects (as @sstaub reported) with the pins you want to use. For example, if you need an I2C on GP14/GP15 just create an object this way

MbedI2C myi2c(p14,p15);

Keep in mind that selecting the wrong pins (if the mux is not possible) will raise a runtime error (with the usual "morse-like" led blink). For additional compatibility we are going to push https://github.com/arduino/ArduinoCore-mbed/pull/190/commits/11620860f44cf9f36afbb2c7143c0311c4367e74 on the next revision, so the objects can be created either by using the pin name or their number (not so useful on the Pico since names match numbers but convenient on other boards).

sstaub commented 3 years ago

The hardware docs of pico the default pins are marked, see screenshot from the doc.

Raspberry Pico Pinout

@facchinm can you add default pins for SERIAL2 (Uart2), SPI1 and WIRE1 ? Also a '::pins()' function will helpful for SERIAL, SPI and WIRE like 'Serial1.pins(TX pin, RX pin)' This functionality avoids digging in the Mbed API.

facchinm commented 3 years ago

@sstaub as marked I meant the silkscreen on the board itself, which we always considered the "golden standard" for naming. About pins() API, it's not standard as per https://github.com/arduino/ArduinoCore-API/tree/master/api , so we are not going to add it anytime soon. I think the way to go is to properly document how to create Serial2, Wire1 or SPI1 objects in the user sketch (or in a library) with the correct constructor (as we did to add additional SERCOM to SAMD core https://www.arduino.cc/en/Tutorial/SamdSercom). @sebromero @alranel what do you think?

sstaub commented 3 years ago

The constructors for Wire1 and SPI1In Wire Wire.cpp and SPI.cpp are already done, only the pins definitions in pins_arduino.h are missing, same for Serial.

Wire SPI
Gerriko commented 3 years ago

As this post covers issues about the Pico I2C pins I'm querying a related issue, which has to do with PeripheralNames.h found in mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040 folder.

I noticed that the default pins for MBED_I2C0 is p28, p27 but p28 is GND (as per Pico pinout diagram).

sebromero commented 3 years ago

@Gerriko Yes, but you have to look at the pinout of the RP2040, not the Pico. P28 is GPIO17.

Gerriko commented 3 years ago

@sebromero appreciate the feedback. I see it now... I should've read @facchinm 's comment (above the pinout graphic), which now makes sense.

sstaub commented 3 years ago

The standard pins should follow https://github.com/raspberrypi/pico-sdk/blob/master/src/boards/include/boards/pico.h

MRTNPRSN commented 3 years ago

Hello guys, Is there an easy way to swap the I2C pin definition to match I2C0 in place of I2C1? I managed to change pins definition by using this MbedI2C myi2c(p2,p3); Changing from GP6/GP7 to GP2/GP3 for SDA1, SCL1 is working great. But I am not able to find the way to change to GP4/GP5 for SDA0, SCL0

Gerriko commented 3 years ago

I noticed that in pins_arduino.h (found in mbed_rp2040/2.0.0/variants/RASPBERRY_PI_PICO) it has

define WIRE_HOWMANY (1)

Does this mean we can only use SDA1/SCL1 for wire? What happens if this is changed to 2 to enable wire1?

I see (from above post) this is controlled within the Wire library (wire.h):

if WIRE_HOWMANY > 0

extern arduino::MbedI2C Wire;

endif

if WIRE_HOWMANY > 1

extern arduino::MbedI2C Wire1;

endif

EDIT: I tested a change of pin numbers by amending pins_arduino.h `// Wire

define PIN_WIRE_SDA (4u) // 6

define PIN_WIRE_SCL (5u) // 7

` It works!

So SDA0/SCL0 can be used but not sure how to include both.

Gerriko commented 3 years ago

Learn by doing...

So I modified the pins_arduino.h file to the following

// Wire
#define PIN_WIRE_SDA        (20u)            
#define PIN_WIRE_SCL        (21u)            
#define PIN_WIRE_SDA1       (6u)            
#define PIN_WIRE_SCL1       (7u)            

Note that I used GP20 and GP21 for SDA0 & SCL0 as GP4 & GP5 assigned for SPI

I then changed #define WIRE_HOWMANY (2) //default 1 And that then allowed me to use Wire and Wire1.

I just tested using both I2C ports with 2 Wii nunchucks. It's pretty cool to have both working with the same I2C address.

sstaub commented 3 years ago

Like mentioned some times before, The default pins for SPI and I2C should follow the official documentation and code of the Raspi org. Making an Arduino specific pin routing is ok for the Arduino products, but not for the Raspberry Pico because it will confuse the users. There is no default assignment for SPI1, I2C1 and UART1(Serial2) so default pins can assigned without restriction.

Gerriko commented 3 years ago

I can only talk from experience. Confusion is typically caused by assumption and poor and/or lack of documentation.

In my case I have not spent much time studying the Raspi org docs so I had no preconceived view as to which default pins should be used for I2C, SPI or UART. But then for Arduino the documentation is not ready yet. So in this case I can see that many would refer to Raspi docs on the assumption this would work.

So you are right, it would help interoperability if the Arduino default pins are aligned to Raspi. However there may well be others constraints that make this difficult and thus does not have to be a requirement.

Looking at the core Arduino code for pico it looks like you would need to at least assign default pins numbers for I2C1/SPI1 etc. to enable the function.

Then it's up to the user to change using the manual assignment command if the want to use.

At least from my point of view, I know that it is now possible to use the 2 x I2C ports in the same sketch once the core library is modified.

On Fri 23 Apr 2021, 08:40 Stefan Staub, @.***> wrote:

Like mentioned some times before, The default pins for SPI and I2C should follow the official documentation and code of the Raspi org. Making an Arduino specific pin routing is ok for the Arduino products, but not for the Raspberry Pico because it will confuse the users. There is no default assignment for SPI1, I2C1 and UART1(Serial2) so default pins can assigned without restriction.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/arduino/ArduinoCore-mbed/issues/194#issuecomment-825461320, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC23YAQ4OQDKXRPBZ5FNYGLTKEP5DANCNFSM425PVFZQ .

sstaub commented 3 years ago

Confusing is that WIRE is routed to I2C 1.

Gerriko commented 3 years ago

Yes that's true. It would be helpful to match Wire with I2C0 and Wire1 with I2C1.

On Fri 23 Apr 2021, 09:18 Stefan Staub, @.***> wrote:

Confusing is that WIRE is routed to I2C 1.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/arduino/ArduinoCore-mbed/issues/194#issuecomment-825485738, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC23YASWNLAK4N25HVPSNETTKEUMZANCNFSM425PVFZQ .

MRTNPRSN commented 3 years ago

So I modified the pins_arduino.h file to the following

// Wire
#define PIN_WIRE_SDA        (20u)            
#define PIN_WIRE_SCL        (21u)            
#define PIN_WIRE_SDA1       (6u)            
#define PIN_WIRE_SCL1       (7u)            

Note that I used GP20 and GP21 for SDA0 & SCL0 as GP4 & GP5 assigned for SPI

I then changed #define WIRE_HOWMANY (2) //default 1 And that then allowed me to use Wire and Wire1.

Thanks for the tips @Gerriko ;) this is working nicely

sebromero commented 3 years ago

@facchinm I agree, making the pins configurable via constructor is an okay option which avoids changing the core API. I do think though that it would be a good idea to stick to the default pin assignment from https://github.com/raspberrypi/pico-sdk/blob/master/src/boards/include/boards/pico.h . Accordingly I'd match Wire with the I2C0 and Wire1 with I2C1 assignment. In regards to documentation, I'd suggest to just add an example sketch to the core. I can make a draft.

KenjutsuGH commented 3 years ago

I have a ZS-042 DS3231 RTC module connected to the Pico on pins 6 and 7. I2C Scanner shows no devices found. Using MicroPython:

i2c = I2C(1, sda=Pin(6), scl=Pin(7))
print(i2c.scan()[0])

Works perfectly and as expected. What should I change in the Arduino I2C Scanner program to get it to work?

UPDATE: I tested it with earlephilhower/arduino-pico with the module connected to pins 4 and 5, and I2C Scanner works as expected

Dresch123 commented 3 years ago

Hi, I am using the default SPI pins to read-communicate with an SD card and I have run into a couple of issues. I am trying to create an Arduino program for the Pico Pi that reads 565 RGB video frames from an SD card and displays them on an LCD screen. Not rocket science I know. I am using Bodmer's TFT_eSPI library and have gotten it running on both the Philhower core and the Mbed core... sort of. Painfully slow for the SD reads. Also I am doing my best to convert to Mbed because the same screen frame writes on the Philower core are ~3X faster (Mbed: 46.5fps, Philhower: 17.5fps.) . But adding the SD reads drops the frame rate to 0.64 fps on the Mbed core. Ugh. I don't know if this is the right place to ask these but here goes:

  1. I have noticed that the SD library for the Mbed Arduino core requires a "8.3" file naming format whereas the Philhower version did not. Took me a while to figure out why I was getting a file-not-found error for longer named files.
  2. There is some sort of problem with longer SD reads. With the Philhower core I could read an entire frame (153600 bytes) in one SD.read. In the Mbed core I need to break the SD file read into 4 SD.read calls to read in the data correctly and completely. Why?
  3. Is there anything that can be done to increase the SPI clock for the SD reads? For instance, I am running the LCD SPI clock at 62.5 MHz (on SPI_1) but I don't seem to have the option to change the SD SPI clock. Is there a way to do that? Thanks very much for your time. Any insights would be appreciated. Bill
junikor-sudo commented 2 years ago

Learn by doing...

So I modified the pins_arduino.h file to the following

// Wire
#define PIN_WIRE_SDA        (20u)            
#define PIN_WIRE_SCL        (21u)            
#define PIN_WIRE_SDA1       (6u)            
#define PIN_WIRE_SCL1       (7u)            

Note that I used GP20 and GP21 for SDA0 & SCL0 as GP4 & GP5 assigned for SPI

I then changed #define WIRE_HOWMANY (2) //default 1 And that then allowed me to use Wire and Wire1.

I just tested using both I2C ports with 2 Wii nunchucks. It's pretty cool to have both working with the same I2C address.

I have yet to try this as I forgot to bring my pullup Rs at home. I assumed that you added also (?)

define WIRE_HOWMANY (2) //default is 1

define I2C_SDA (digitalPinToPinName(PIN_WIRE_SDA))

define I2C_SCL (digitalPinToPinName(PIN_WIRE_SCL))

define I2C_SDA1 (digitalPinToPinName(PIN_WIRE_SDA1)) //added for WIRE1

define I2C_SCL1 (digitalPinToPinName(PIN_WIRE_SCL1)) //added for WIRE1

Thanks!

ZinggJM commented 2 years ago

My question concerns which SPI pins can be used with the arduino::MbedSPI(...) constructor. The reason is an existing RP2040 shield from Good Display for e-paper panels that doesn't use the HW SPI pins as defined in pins_arduino.h.

I tried to find out with a small test program, which resulted in the blinking LED 4 short 4 long, and now I can't upload anymore.

// SpiTest.ino for RP2040 by Jean-Marc Zingg

#include <SPI.h>

//SPIClass& _spi = SPI;

//arduino::MbedSPI _spi(8, 7, 6);
arduino::MbedSPI _spi(25, 28, 29);

//_spi = SPIx;

void setup()
{
  _spi.begin();
}

void loop()
{
  _spi.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
  for (int i = 0; i < 10000; i++)
  {
    _spi.transfer(0xFF);
  }
  delay(1000);
  for (int i = 0; i < 10000; i++)
  {
    _spi.transfer(0x00);
  }
  _spi.endTransaction();
  delay(1000);
}

Help, please! Thank you.

Jean-Marc

sebromero commented 2 years ago

@ZinggJM It's because your firmware immediately crashes when the board boots. Put the board in bootloader mode and upload again:

Press the BOOTSEL button and hold it while you connect the other end of the micro USB cable to your computer.

ZinggJM commented 2 years ago

Thank you for the quick answer. I think I tried this, but I have to wait again during compilation. But I see a device RPI-RP2.

Which pins could I try for the second SPI channel?

ZinggJM commented 2 years ago

Sketch uses 14000 bytes (0%) of program storage space. Maximum is 16777216 bytes. Global variables use 42928 bytes (15%) of dynamic memory, leaving 227408 bytes for local variables. Maximum is 270336 bytes. processing.app.debug.RunnerException at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:152) at cc.arduino.UploaderUtils.upload(UploaderUtils.java:77) at processing.app.SketchController.upload(SketchController.java:732) at processing.app.SketchController.exportApplet(SketchController.java:703) at processing.app.Editor$UploadHandler.run(Editor.java:2055) at java.lang.Thread.run(Thread.java:748) Caused by: processing.app.SerialException: Error touching serial port 'COM23'. at processing.app.Serial.touchForCDCReset(Serial.java:107) at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:136) ... 5 more Caused by: jssc.SerialPortException: Port name - COM23; Method name - openPort(); Exception type - Port not found. at jssc.SerialPort.openPort(SerialPort.java:167) at processing.app.Serial.touchForCDCReset(Serial.java:101) ... 6 more

Doesn't seem to work as serial channel.

ZinggJM commented 2 years ago

Keep in mind that selecting the wrong pins (if the mux is not possible) will raise a runtime error (with the usual "morse-like" led blink). For additional compatibility we are going to push 1162086 on the next revision, so the objects can be created either by using the pin name or their number (not so useful on the Pico since names match numbers but convenient on other boards).

And you no longer can upload programs. And don't know which pins can be used for SPI.

Is it possible to use an ST-Link-V2 to clear the program, or would it cause more damage?

ZinggJM commented 2 years ago

@sebromero

Press the BOOTSEL button and hold it while you connect the other end of the micro USB cable to your computer.

The blinking stops, but the Pico is put into USB memory mode, and doesn't accept program upload. Is there any other easy means to get that Pico working again? Ok, I can go through all the processor docs later. For now I still have some spare Picos.

Thank you for any help.

MNS26 commented 2 years ago

@ZinggJM try the following and see if it works

Create a empty program (just the setup and loop and nothing more) Build the program (not build and upload) Manually reset the pico to usb storage Locate the just built uf2 firmware file and drag-n-drop it onto the pico

That should fix the pico not working

MNS26 commented 2 years ago

Ive also ran into this issue where it would crash on start

I had it happen trying to access my mpu6050 (pins 2, 3) testing to see what address it is with a example code worked but when i tried to communicate with it it crashed

ZinggJM commented 2 years ago

Thank you @MNS26. Yes, I found this solution also.

But the easy solution is to exit and restart the Arduino IDE, to make sure no serial line is selected, then put the pico to usb storage mode, and then compile and upload. Upload seems to copy to usb storage if no serial line is selected. Sorry for not having reported before.

The SPI pins can be selected according to the pinout diagram. Each optional SPI pin is indicated with one of the two possible SPI channels. But pins of the two channels can't be mixed.

MNS26 commented 2 years ago

@ZinggJM great to hear you fixed it.

Like you said mixing I2C0 and I2C1 pins will cause a mbed crash, i made sure to check the pins for I2C1 before using them.

The arduino I2C scanner code (with some tweaks) does work but the moment i modify it to write to the mpu6050 it crashes mbed

MNS26 commented 2 years ago

The modification to the example code was.

MBedI2C i2c(2,3);

And wherever Wire was used i used i2c, initially i wanted to use a example library but... i couldn't find any that allowed me to define the i2c pins.

frohro commented 2 years ago

It would also be nice if you could follow the lead of Earl Philhower. He added two functions setSDA() and setSCL() to the Wire library. Here is when the issue came up for him. Here is his documentation for these functions. My reason for suggesting this is it would be nice to have as much compatibility with both platforms as possible. The MBED board library is now the "official" one for the Pico, but the other one has a lot of users that would like the compatibility so they can port over to the MBED board library.

kingjamez commented 1 year ago

Just as a follow up for future folks searching this out. I wanted to use both I2C channels on the Pico but struggled when any reference to Wire1 was made in my code. I think the overall consensus in this thread is that since there are no default pins assigned to I2C1 SDA and I2C1 SCL then the pins_arduino.h file shouldn't include them. So if you need to use both I2C channels on the Pico as I did here is how to do it:

Find the pins_arduino.h file for your Pico board. On my M1 Mac using Arduino IDE 2.0 the file was found in Library/Arduino15/packages/arduino/hardware/mbed_rp2040/(VERSION NUMBER)/variants/RASPPBERRY_PI_PICO/pins_arduino.h

Add or modify the following lines:

// Wire
#define PIN_WIRE_SDA        (4u)
#define PIN_WIRE_SCL        (5u)
#define PIN_WIRE_SDA1       (2u)
#define PIN_WIRE_SCL1       (3u)

#define WIRE_HOWMANY    (2)
#define I2C_SDA         (digitalPinToPinName(PIN_WIRE_SDA))
#define I2C_SCL         (digitalPinToPinName(PIN_WIRE_SCL))
#define I2C_SDA1        (digitalPinToPinName(PIN_WIRE_SDA1))
#define I2C_SCL1        (digitalPinToPinName(PIN_WIRE_SCL1))

Note that 4u and 5u (pins 6 and 7 on the Pico board) are the default I2C0 pins. There is no default for the I2C1 pins, and I just choose the two pins above (pins 4 and 5) since they are listed on the graphic found above as I2C1 and are not otherwise special, they aren't default pins for any other function and they aren't analog input pins). You can change these to whatever suits your project though.

With those changes I was able to read from 2 AS5600 magnetic encoders which have a hard-wired I2C address, making them unable to co-exist on the same I2C channel.

Hope this is useful for someone in the future. It would have saved me a day of searching :-)

RobTillaart commented 1 year ago

His approach is similar to ARDUINO_NANO33BLE