Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 7503

SDK • I²C 0 and UART0 issues (pin clash and backward pins)

$
0
0
Hi all,

So I'm building a hardware test rig for my workplace using a RPi Pico… and right now I'm running up against limitations in the SDK.

First, the set-up:
- I have two Microchip MCP23017 I²C GPIO expanders, addresses 0x20 and 0x21.
- The board also has two RS-485 transceivers, controlled over GPIO to the same UART (use case is I'll be talking via one RS-485 channel at a time, or using one in RX and the other in TX for RS-422).

Software wise: I'm using `cmake` and `gcc` as the toolchain (Linux build host). Git submodules:

Code:

stuartl@LPA075:~/vrt/projects/widesky/hub/testrig-hardware-mk2/firmware$ git submodule 9a4113fbbae65ee82d8cd6537963bc3d3b14bcca third_party/pico-sdk (2.1.1-2-g9a4113f) 29c2af7651f9075a63a036e94c7bbe3d0d00c31a third_party/tinyusb (0.18.0-514-g29c2af765)
My `main.c` is roughly based on the dual CDC-ACM UART example from the TinyUSB project.

At present, I have the following GPIO allocations:
- I2C SDA: GPIO0
- I2C SCL: GPIO1
- Expander 0x20 IntA: GPIO2
- Expander 0x20 IntB: GPIO3
- Expander 0x21 IntA: GPIO4
- Expander 0x21 IntB: GPIO5
- I²C nRESET: GPIO6
- UART RX: GPIO8
- UART TX: GPIO9

The I²C lines have external 2k2 resistors installed pulling up to 3.3V.

I haven't tried using the UART yet; but I did try loading MicroPython on the board, and verified that indeed, I could communicate with the I²C expanders (after pulsing nRESET low). So my hardware works, it's initialisation that's wrong.

When I try to do a read-modify-write though of a expander register in C, the Pico hard-locks (I don't have a SWD debug probe handy).

I think the cause may be a clash between UART0 and I2C0 on GPIOs 0 and 1. It appears the GPIO MUXing in the RP2040 is not as flexible as I'd hoped, so I may have to re-wire the RX and TX pins, and possibly use UART1 instead, but what really irritates me is that the SDK seems to have hard-coded UART0 on GPIOs 0/1. This I think is causing a clash with the I²C peripheral, and hence the hang.

Using `picotool`, the clash can be observed:

Code:

stuartl@LPA075:~/vrt/projects/widesky/hub/testrig-hardware-mk2/firmware$ build/_deps/picotool/picotool info -a output/hub_testrig.uf2 File output/hub_testrig.uf2 family ID 'rp2040':Program Information name:              hub_testrig binary start:      0x10000000 binary end:        0x1000a554Fixed Pin Information 0:                 I2C0 SDA, UART0 TX 1:                 I2C0 SCL, UART0 RX 8:                 UART1 TX 9:                 UART1 RX 25:                LED 28:                UART0 TX 29:                UART0 RXBuild Information sdk version:       2.1.1 pico_board:        pico boot2_name:        boot2_w25q080 build date:        Jul 22 2025 build attributes:  ReleaseMetadata Blocks none
In my initialisation, I have:

Code:

#define WHTR_GPIO_I2C_SDA   (0) /*!< I²C Serial Data (bidirectional) */#define WHTR_GPIO_I2C_SCL   (1) /*!< I²C Serial Clock (TTL output) */#define WHTR_GPIO_nRESET    (6) /*!< I²C bus reset (active low, out) */#define WHTR_GPIO_UART_RX   (8) /*!< UART Receive (in) */#define WHTR_GPIO_UART_TX   (9) /*!< UART Transmit (out) */#define WHTR_UART_PORT(1)/*! * I2C Bus ID in use. */#define WHTR_I2CBUS  (0)/*! * GPIO Expander 0 (Microchip MCP23017): LED sense lines. */#define WHTR_I2CADDR_EXP0 (0x20)/*! * GPIO Expander 1 (Microchip MCP23017): H-Bridge/Relay sense, system control */#define WHTR_I2CADDR_EXP1 (0x21)#include "i2cdev.h"#include "gpios.h"#include "hardware/gpio.h"#include "hardware/i2c.h"#include "hardware/uart.h"#include "pico/binary_info.h"#include "pico/time.h"#include "serial.h"#define WHTR_I2C_BAUDRATE (1000) /* lowered from 400kHz during debugging */#define WHTR_nRESET_DELAY (500)void whtr_i2c_init() {/* Initialise the I²C bus itself */i2c_inst_t* i2cu = I2C_INSTANCE(WHTR_I2CBUS);i2c_init(i2cu, WHTR_I2C_BAUDRATE);i2c_set_baudrate(i2cu, WHTR_I2C_BAUDRATE);i2c_set_slave_mode(i2cu, false, 0);gpio_set_function(WHTR_GPIO_I2C_SDA, GPIO_FUNC_I2C);gpio_set_function(WHTR_GPIO_I2C_SCL, GPIO_FUNC_I2C);gpio_pull_up(WHTR_GPIO_I2C_SDA);gpio_pull_up(WHTR_GPIO_I2C_SCL);/* Make the I2C pins available to picotool */bi_decl(bi_2pins_with_func(WHTR_GPIO_I2C_SDA, WHTR_GPIO_I2C_SCL,   GPIO_FUNC_I2C));/* Initialise nRESET GPIOs */gpio_init(WHTR_GPIO_nRESET);gpio_set_dir(WHTR_GPIO_nRESET, GPIO_OUT);gpio_put(WHTR_GPIO_nRESET, true);/* Apply nRESET */whtr_i2c_reset();}void whtr_i2c_reset() {gpio_put(WHTR_GPIO_nRESET, false);sleep_ms(WHTR_nRESET_DELAY);gpio_put(WHTR_GPIO_nRESET, true);}voidwhtr_serial_gpio_init() {   /* Assign GPIO functions to RX/TX pins */   uart_inst_t* uart = UART_INSTANCE(WHTR_UART_PORT);   gpio_set_function(WHTR_GPIO_UART_RX,     UART_FUNCSEL_NUM(uart, WHTR_GPIO_UART_RX));   gpio_set_function(WHTR_GPIO_UART_TX,     UART_FUNCSEL_NUM(uart, WHTR_GPIO_UART_TX));   /* Make the UART pins available to picotool */   bi_decl(bi_2pins_with_func(WHTR_GPIO_UART_TX, WHTR_GPIO_UART_RX, GPIO_FUNC_UART));   /* Make sure UART0 is not sitting on I²C pins! */   bi_decl(bi_2pins_with_func(28, 29, GPIO_FUNC_UART))   /* snip GPIO mode-setting for RS-485 */}int main(void) {whtr_serial_gpio_init();whtr_i2c_init();/* rest of program here */}
Note I tried to set pin functions for UART0: instead of moving the allocation, it added new ones on top of the existing (unwanted) ones. I don't want to add new ones, I want to get rid of the ones that were never asked for in the first place. It's not clear whether `bi_decl` is purely for the benefit of `picotool`, or whether it sets GPIO registers somewhere, the examples never explain why this call is present or what its significance is.

There's no cards being hidden up sleeves in the `CMakeLists.txt` either:

Code:

# WideSky Hub Mk2 Test Rig firmware# © 2025 WideSky.Cloud Pty Ltd## Based on TinyUSB USB stack (MIT license)# © 2019 Ha Thach (tinyusb.org)# We need things like cmake_path, so 3.20 is the minimumcmake_minimum_required(VERSION 3.20)# Set the source directoriesset(TINYUSB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/tinyusbCACHE PATH "TinyUSB source tree")set(PICOSDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/pico-sdkCACHE PATH "RP2040 SDK source tree")set(PICOTOOL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/picotoolCACHE PATH "`picotool` source tree")set(INC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/includeCACHE PATH "Project-wide include files")set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/srcCACHE PATH "Project source code")# point TinyUSB at our copy of the pico SDKset(PICO_SDK_PATH ${PICOSDK_DIR} CACHE PATH"path to RPi Pico SDK, as per TinyUSB project naming convention")# initialize pico-sdk from submodule# note: this must happen before project()include(third_party/pico-sdk/pico_sdk_init.cmake)set(BOARD raspberry_pi_pico)include(${TINYUSB_DIR}/hw/bsp/family_support.cmake)project(hub_testrig C CXX ASM)# Checks this example is valid for the family and initializes the projectfamily_initialize_project(hub_testrig ${CMAKE_CURRENT_LIST_DIR})# Note: we may need to re-run `cmake` when source files are added or removed!file(GLOB_RECURSEhub_testrig_sources${SRC_DIR}/*.c)add_executable(hub_testrig${hub_testrig_sources})# Add include directoriestarget_compile_options(hub_testrig PUBLIC-g-I${INC_DIR}-I${TINYUSB_DIR}/src-I${TINYUSB_DIR}/hw)# Add pico_stdlib library which aggregates commonly used featurestarget_link_libraries(hub_testrigpico_stdlibhardware_i2ctinyusb_common_basetinyusb_device_basetinyusb_bsp)# create map/bin/hex/uf2 file in addition to ELF.set_target_properties(hub_testrigPROPERTIESARCHIVE_OUTPUT_DIRECTORY ${RELEASE_DIR})pico_add_extra_outputs(hub_testrig)
So the question, where on earth do I de-configure UART0 so that I may use the pins there for their intended function (I²C)?

Statistics: Posted by Redhatter — Tue Jul 22, 2025 1:54 am — Replies 5 — Views 124



Viewing all articles
Browse latest Browse all 7503

Trending Articles