This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Problem initializing I2C on Arduino Nano 33 BLE

I'm running into an issue in which I cannot initialized the I2C drivers on an Arduino Nano 33 BLE (nrf52840). The output from error logging is

00> [00:00:00.500,122] <err> i2c_nrfx_twi: Error on I2C line occurred for message 0
00> [00:00:00.500,640] <err> HTS221: Failed to read chip ID.
00> [00:00:00.508,392] <err> i2c_nrfx_twi: Error 0x0BAD0001 occurred for message 0

I have this prj.conf file:

CONFIG_BOARD_ARDUINO_NANO_33_BLE=y
CONFIG_BOOTLOADER_MCUBOOT=y

CONFIG_BOARD_ARDUINO_NANO_33_BLE_INIT_SENSORS=y

CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_I2C_LOG_LEVEL_DBG=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n

CONFIG_SPI=n

CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_HTS221=y
CONFIG_HTS221_TRIGGER_NONE=y

CONFIG_LPS22HB=y
~                                                                                           
~                            

I have also modified the arduino_nano_33_ble.dts to enable mcu_boot and added the sensors into the &i2c1 device tree.

arduino_nano_33_ble.dts

Additionally, I changed zephyr/boards/arm/arduino_nano_33_ble/src/init_sensors.c such that initializing function is called at APPLICATION instead of PRE_KERNEL_1. Not sure why but initializing GPIO devices will cause the device to go fault at Pre_KERNEL_1.

/*
 * Copyright (c) 2020 Jefferson Lee.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <init.h>
#include <arduino_nano_33_ble.h>

/*
 * this method roughly follows the steps here:
 * https://github.com/arduino/ArduinoCore-nRF528x-mbedos/blob/6216632cc70271619ad43547c804dabb4afa4a00/variants/ARDUINO_NANO33BLE/variant.cpp#L136
 */

static int board_internal_sensors_init(const struct device *dev)
{
	ARG_UNUSED(dev);
	struct arduino_gpio_t gpios;

	arduino_gpio_init(&gpios);

	arduino_gpio_pinMode(&gpios, ARDUINO_LEDPWR, GPIO_OUTPUT);
	arduino_gpio_digitalWrite(&gpios, ARDUINO_LEDPWR, 1);

	CoreDebug->DEMCR = 0;
	NRF_CLOCK->TRACECONFIG = 0;

	/*
	 * Arduino uses software to disable RTC1,
	 * but I disabled it using DeviceTree
	 */
	/* nrf_rtc_event_disable(NRF_RTC1, NRF_RTC_INT_COMPARE0_MASK); */
	/* nrf_rtc_int_disable(NRF_RTC1, NRF_RTC_INT_COMPARE0_MASK); */

	NRF_PWM_Type * PWM[] = {
		NRF_PWM0, NRF_PWM1, NRF_PWM2, NRF_PWM3
	};

	for (unsigned int i = 0; i < (ARRAY_SIZE(PWM)); i++) {
		PWM[i]->ENABLE = 0;
		PWM[i]->PSEL.OUT[0] = 0xFFFFFFFFUL;
	}

	/*
	 * the PCB designers decided to use GPIO's
	 * as power pins for the internal sensors
	 */
	arduino_gpio_pinMode(&gpios, ARDUINO_INTERNAL_VDD_ENV_ENABLE, GPIO_OUTPUT);
	arduino_gpio_pinMode(&gpios, ARDUINO_INTERNAL_I2C_PULLUP, GPIO_OUTPUT);
	arduino_gpio_digitalWrite(&gpios, ARDUINO_INTERNAL_VDD_ENV_ENABLE, 1);
	arduino_gpio_digitalWrite(&gpios, ARDUINO_INTERNAL_I2C_PULLUP, 1);
	return 0;
}
SYS_INIT(board_internal_sensors_init, APPLICATION, 32);

Here's my main.c file.

#include <zephyr.h>
#include <device.h>
#include <drivers/sensor.h>
#include <sys/util.h>
#include <stdio.h>

void main(void)
{
        const struct device *dev = device_get_binding(DT_LABEL(DT_INST(0, st_hts221)));
        const struct device *dev2 = device_get_binding(DT_LABEL(DT_INST(0, st_lps22hb_press)));

        if (dev == NULL && dev2 == NULL) {
                printf("Could not get devices\n");
                return;
        }
        k_sleep(K_FOREVER);
}

Let me know if you want me to provide any other information about my setup. I'm currently flashing the board with a NRF52840dk with a JLink cable. I have been stuck on this problem for a few days now and I'm not sure what's the problem is.

  • Ok, I solved the issue. It turns out that the hardware designer of the Arduino Nano 33 BLE board uses GPIO pins as an enable pin for all the I2C sensors. The init_sensors.c file has to be change in order to accommodate for this.

    The original init_sensors.c file calls the initializing function board_internal_sensors_init with priority at PRE_KERNEL_1. However, this function requires the use of GPIO pins in order to work. Thus, you have to run this function after the GPIO pins are initialized. The GPIO pins are initialized during POST_KERNEL with priority number 40.

    However, the I2C driver is initialized at POST_KERNEL with priority number 60. So you have to pick a number between 40 and 60 to run the board_internal_sensors_init callback.

    I thought that would have solved everything, but it turns out the GPIO pins can't quickly pull up the voltage enough. This will still cause issue when you try to initialize your I2C sensors. I added a small delay in the callback function to give enough time for the voltage to be pull up.

    The final sensor_init.c should be as follow.

    /*
     * Copyright (c) 2020 Jefferson Lee.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <init.h>
    #include <arduino_nano_33_ble.h>
    /*
     * this method roughly follows the steps here:
     * https://github.com/arduino/ArduinoCore-nRF528x-mbedos/blob/6216632cc70271619ad43547c804dabb4afa4a00/variants/ARDUINO_NANO33BLE/variant.cpp#L136
     */
    static int board_internal_sensors_init(const struct device *dev)
    {
    	ARG_UNUSED(dev);
    	struct arduino_gpio_t gpios;
    
    	arduino_gpio_init(&gpios);
    
    	arduino_gpio_pinMode(&gpios, ARDUINO_LEDPWR, GPIO_OUTPUT);
    	arduino_gpio_digitalWrite(&gpios, ARDUINO_LEDPWR, 1);
    	
    	CoreDebug->DEMCR = 0;
    	NRF_CLOCK->TRACECONFIG = 0;
    	
    	/*
    	 * Arduino uses software to disable RTC1,
    	 * but I disabled it using DeviceTree
    	 */
    	/* nrf_rtc_event_disable(NRF_RTC1, NRF_RTC_INT_COMPARE0_MASK); */
    	/* nrf_rtc_int_disable(NRF_RTC1, NRF_RTC_INT_COMPARE0_MASK); */
    	
    	NRF_PWM_Type * PWM[] = {
    		NRF_PWM0, NRF_PWM1, NRF_PWM2, NRF_PWM3
    	};
    
    	for (unsigned int i = 0; i < (ARRAY_SIZE(PWM)); i++) {
    		PWM[i]->ENABLE = 0;
    		PWM[i]->PSEL.OUT[0] = 0xFFFFFFFFUL;
    	}
    	
    	/*
    	 * the PCB designers decided to use GPIO's
    	 * as power pins for the internal sensors
    	 */
    	arduino_gpio_pinMode(&gpios, ARDUINO_INTERNAL_VDD_ENV_ENABLE, GPIO_OUTPUT);
    	arduino_gpio_pinMode(&gpios, ARDUINO_INTERNAL_I2C_PULLUP, GPIO_OUTPUT);
    	arduino_gpio_digitalWrite(&gpios, ARDUINO_INTERNAL_VDD_ENV_ENABLE, 1);
    	arduino_gpio_digitalWrite(&gpios, ARDUINO_INTERNAL_I2C_PULLUP, 1);
    	
    	// Allow time for the GPIO pins to pullup the voltage
    	k_sleep(K_MSEC(3));
    	
    	return 0;
    }
    SYS_INIT(board_internal_sensors_init, POST_KERNEL, 50);
    
    

    This seems to fix everything so far.

Related