Button callback and i2c_transfer

Dev tools:
HW: nRF52DK_52832 (& LTC DC1697A)
SDK: v2.2.0

The problem:
The I2C transfer function doesn't work as expected within a callback.


Steps to reproduce:
1. Starting a new project with the zephyr button example
2. Adding RTT support to the config and setting it as console
3. Adding I2C to the config and including the lib
4. Adding my personal i2c_transfer snippets towards the end of the main function and in the button callback function
5. The slave device on the I2C 0 bus is a devkit for a power monitoring by LTC model DC1697A

prj.conf

CONFIG_GPIO=y
CONFIG_I2C=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n

main.c

/*
 * Copyright (c) 2016 Open-RnD Sp. z o.o.
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <inttypes.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>

#define SLEEP_TIME_MS 1

/*
 * Get button configuration from the devicetree sw0 alias. This is mandatory.
 */
#define SW0_NODE DT_ALIAS(sw0)
#if !DT_NODE_HAS_STATUS(SW0_NODE, okay)
#error "Unsupported board: sw0 devicetree alias is not defined"
#endif
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
static struct gpio_callback button_cb_data;

/*
 * The led0 devicetree alias is optional. If present, we'll use it
 * to turn on the LED whenever the button is pressed.
 */
static struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios, {0});

void button_press(const struct device *dev, struct gpio_callback *cb, uint32_t pins) {
    printk("Button pressed at %" PRIu32 "\n", k_cycle_get_32());

    uint8_t register_data[3] = {0x1E, 0x00, 0x00};
    struct i2c_msg msgs[3];
    msgs[0].buf = &register_data[0];
    msgs[0].len = 1U;
    msgs[0].flags = I2C_MSG_WRITE;
    msgs[1].buf = &register_data[1];
    msgs[1].len = 1U;
    msgs[1].flags = I2C_MSG_RESTART | I2C_MSG_READ;
    msgs[2].buf = &register_data[2];
    msgs[2].len = 1U;
    msgs[2].flags = I2C_MSG_READ | I2C_MSG_STOP;
    printk("i2c transfer return: %d\r\n", i2c_transfer(DEVICE_DT_GET(DT_NODELABEL(i2c0)), &msgs[0], 3, 0x6F));
}

void main(void) {
    int ret;

    if (!device_is_ready(button.port)) {
        printk("Error: button device %s is not ready\n", button.port->name);
        return;
    }

    ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
    if (ret != 0) {
        printk("Error %d: failed to configure %s pin %d\n", ret, button.port->name, button.pin);
        return;
    }

    ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
    if (ret != 0) {
        printk("Error %d: failed to configure interrupt on %s pin %d\n", ret, button.port->name, button.pin);
        return;
    }

    gpio_init_callback(&button_cb_data, button_press, BIT(button.pin));
    gpio_add_callback(button.port, &button_cb_data);
    printk("Set up button at %s pin %d\n", button.port->name, button.pin);

    if (led.port && !device_is_ready(led.port)) {
        printk("Error %d: LED device %s is not ready; ignoring it\n", ret, led.port->name);
        led.port = NULL;
    }

    if (led.port) {
        ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT);
        if (ret != 0) {
            printk("Error %d: failed to configure LED device %s pin %d\n", ret, led.port->name, led.pin);
            led.port = NULL;
        } else {
            printk("Set up LED at %s pin %d\n", led.port->name, led.pin);
        }
    }

    uint8_t register_data[3] = {0x1E, 0x00, 0x00};
    struct i2c_msg msgs[3];
    msgs[0].buf = &register_data[0];
    msgs[0].len = 1U;
    msgs[0].flags = I2C_MSG_WRITE;
    msgs[1].buf = &register_data[1];
    msgs[1].len = 1U;
    msgs[1].flags = I2C_MSG_RESTART | I2C_MSG_READ;
    msgs[2].buf = &register_data[2];
    msgs[2].len = 1U;
    msgs[2].flags = I2C_MSG_READ | I2C_MSG_STOP;
    printk("i2c transfer return: %d\r\n", i2c_transfer(DEVICE_DT_GET(DT_NODELABEL(i2c0)), &msgs[0], 3, 0x6F));

    printk("Press the button\n");
    if (led.port) {
        while (1) {
            /* If we have an LED, match its state to the button's. */
            int val = gpio_pin_get_dt(&button);
            if (val >= 0) {
                gpio_pin_set_dt(&led, val);
            }
            k_msleep(SLEEP_TIME_MS);
        }
    }
}

RTT:

The good transfer from the snippet at the end of the main function:

The bad transfer from the snippet at the end of the callback function:


I would like technical advice on why the callback implementation doesn't work and how to solve it.

project zip:

button_i2c.zip

Parents Reply Children
No Data
Related