Connecting nrf5340dk to MCP2515 CAN bus shield

I am trying to connect a nrf5340dk to a Jetson. For now I am testing with an Arduino. I am trying to run the CanCounter example with a device tree overlay file. Also I would like to connect this to a Jetson Orin in the future. This is my current setup and below is my code.

I am using 

2x I2C Logic Level Converter (because of the different voltage levels)
2x MCP2515 CAN Bus Shield AZDelivery

And I am powering the nrf5340dk using the J2 USB connector

Is there anything I am doing incorrectly?
I am able to build and flash to the nrf board but I get the following error message;

uart:~$ *** Booting nRF Connect SDK v3.5.99-ncs1-1 ***
Error starting CAN controller [-5]

nrf5340dk_nrf5340_cpuapp.overlay:

&spi4 {
    status = "okay";
    cs-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
    pinctrl-0 = <&spi4_default>;
    //pinctrl-names = "default";

    canbus:mcp2515@0 {
		compatible = "microchip,mcp2515";
		spi-max-frequency = <1000000>;
		int-gpios = <&gpio1 10 (GPIO_ACTIVE_LOW)>; 
		status = "okay";
		label = "CAN_1";
		reg = <0x0>;
		osc-freq = <16000000>;
		bus-speed = <500000>;
		sjw = <1>;
		prop-seg = <2>;
		phase-seg1 = <7>;
		phase-seg2 = <6>;
		#address-cells = <1>;
		#size-cells = <0>;
	};
};

/ {
    chosen {
		zephyr,canbus = &canbus;
	};
};

proj.conf:

CONFIG_POLL=y
CONFIG_CAN=y
CONFIG_CAN_INIT_PRIORITY=80
#CONFIG_CAN_MAX_FILTER=5

CONFIG_SHELL=y
CONFIG_CAN_SHELL=y
CONFIG_DEVICE_SHELL=y

CONFIG_GPIO=y
CONFIG_STATS=y
CONFIG_STATS_NAMES=y
CONFIG_STATS_SHELL=y
CONFIG_CAN_STATS=y

main.cpp:

/*
 * Copyright (c) 2018 Alexander Wachter
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/device.h>
#include <zephyr/drivers/can.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/byteorder.h>

#define RX_THREAD_STACK_SIZE 512
#define RX_THREAD_PRIORITY 2
#define STATE_POLL_THREAD_STACK_SIZE 512
#define STATE_POLL_THREAD_PRIORITY 2
#define LED_MSG_ID 0x10
#define COUNTER_MSG_ID 0x12345
#define SET_LED 1
#define RESET_LED 0
#define SLEEP_TIME K_MSEC(250)

K_THREAD_STACK_DEFINE(rx_thread_stack, RX_THREAD_STACK_SIZE);
K_THREAD_STACK_DEFINE(poll_state_stack, STATE_POLL_THREAD_STACK_SIZE);

const struct device *const can_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus));
struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios, {0});

struct k_thread rx_thread_data;
struct k_thread poll_state_thread_data;
struct k_work_poll change_led_work;
struct k_work state_change_work;
enum can_state current_state;
struct can_bus_err_cnt current_err_cnt;

CAN_MSGQ_DEFINE(change_led_msgq, 2);
CAN_MSGQ_DEFINE(counter_msgq, 2);

static struct k_poll_event change_led_events[1] = {
	K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_MSGQ_DATA_AVAILABLE,
					K_POLL_MODE_NOTIFY_ONLY,
					&change_led_msgq, 0)
};

void tx_irq_callback(const struct device *dev, int error, void *arg)
{
	char *sender = (char *)arg;

	ARG_UNUSED(dev);

	if (error != 0) {
		printf("Callback! error-code: %d\nSender: %s\n",
		       error, sender);
	}
}

void rx_thread(void *arg1, void *arg2, void *arg3)
{
	ARG_UNUSED(arg1);
	ARG_UNUSED(arg2);
	ARG_UNUSED(arg3);
	const struct can_filter filter = {
		.flags = CAN_FILTER_DATA | CAN_FILTER_IDE,
		.id = COUNTER_MSG_ID,
		.mask = CAN_EXT_ID_MASK
	};
	struct can_frame frame;
	int filter_id;

	filter_id = can_add_rx_filter_msgq(can_dev, &counter_msgq, &filter);
	printf("Counter filter id: %d\n", filter_id);

	while (1) {
		k_msgq_get(&counter_msgq, &frame, K_FOREVER);

		if (frame.dlc != 2U) {
			printf("Wrong data length: %u\n", frame.dlc);
			continue;
		}

		printf("Counter received: %u\n",
		       sys_be16_to_cpu(UNALIGNED_GET((uint16_t *)&frame.data)));
	}
}

void change_led_work_handler(struct k_work *work)
{
	struct can_frame frame;
	int ret;

	while (k_msgq_get(&change_led_msgq, &frame, K_NO_WAIT) == 0) {
		if (led.port == NULL) {
			printf("LED %s\n", frame.data[0] == SET_LED ? "ON" : "OFF");
		} else {
			gpio_pin_set(led.port, led.pin, frame.data[0] == SET_LED ? 1 : 0);
		}
	}

	ret = k_work_poll_submit(&change_led_work, change_led_events,
				 ARRAY_SIZE(change_led_events), K_FOREVER);
	if (ret != 0) {
		printf("Failed to resubmit msgq polling: %d", ret);
	}
}

char *state_to_str(enum can_state state)
{
	switch (state) {
	case CAN_STATE_ERROR_ACTIVE:
		return "error-active";
	case CAN_STATE_ERROR_WARNING:
		return "error-warning";
	case CAN_STATE_ERROR_PASSIVE:
		return "error-passive";
	case CAN_STATE_BUS_OFF:
		return "bus-off";
	case CAN_STATE_STOPPED:
		return "stopped";
	default:
		return "unknown";
	}
}

void poll_state_thread(void *unused1, void *unused2, void *unused3)
{
	struct can_bus_err_cnt err_cnt = {0, 0};
	struct can_bus_err_cnt err_cnt_prev = {0, 0};
	enum can_state state_prev = CAN_STATE_ERROR_ACTIVE;
	enum can_state state;
	int err;

	while (1) {
		err = can_get_state(can_dev, &state, &err_cnt);
		if (err != 0) {
			printf("Failed to get CAN controller state: %d", err);
			k_sleep(K_MSEC(100));
			continue;
		}

		if (err_cnt.tx_err_cnt != err_cnt_prev.tx_err_cnt ||
		    err_cnt.rx_err_cnt != err_cnt_prev.rx_err_cnt ||
		    state_prev != state) {

			err_cnt_prev.tx_err_cnt = err_cnt.tx_err_cnt;
			err_cnt_prev.rx_err_cnt = err_cnt.rx_err_cnt;
			state_prev = state;
			printf("state: %s\n"
			       "rx error count: %d\n"
			       "tx error count: %d\n",
			       state_to_str(state),
			       err_cnt.rx_err_cnt, err_cnt.tx_err_cnt);
		} else {
			k_sleep(K_MSEC(100));
		}
	}
}

void state_change_work_handler(struct k_work *work)
{
	printf("State Change ISR\nstate: %s\n"
	       "rx error count: %d\n"
	       "tx error count: %d\n",
		state_to_str(current_state),
		current_err_cnt.rx_err_cnt, current_err_cnt.tx_err_cnt);

#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
	if (current_state == CAN_STATE_BUS_OFF) {
		printf("Recover from bus-off\n");

		if (can_recover(can_dev, K_MSEC(100)) != 0) {
			printf("Recovery timed out\n");
		}
	}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
}

void state_change_callback(const struct device *dev, enum can_state state,
			   struct can_bus_err_cnt err_cnt, void *user_data)
{
	struct k_work *work = (struct k_work *)user_data;

	ARG_UNUSED(dev);

	current_state = state;
	current_err_cnt = err_cnt;
	k_work_submit(work);
}

int main(void)
{
	const struct can_filter change_led_filter = {
		.flags = CAN_FILTER_DATA,
		.id = LED_MSG_ID,
		.mask = CAN_STD_ID_MASK
	};
	struct can_frame change_led_frame = {
		.flags = 0,
		.id = LED_MSG_ID,
		.dlc = 1
	};
	struct can_frame counter_frame = {
		.flags = CAN_FRAME_IDE,
		.id = COUNTER_MSG_ID,
		.dlc = 2
	};
	uint8_t toggle = 1;
	uint16_t counter = 0;
	k_tid_t rx_tid, get_state_tid;
	int ret;

	if (!device_is_ready(can_dev)) {
		printf("CAN: Device %s not ready.\n", can_dev->name);
		return 0;
	}

#ifdef CONFIG_LOOPBACK_MODE
	ret = can_set_mode(can_dev, CAN_MODE_LOOPBACK);
	if (ret != 0) {
		printf("Error setting CAN mode [%d]", ret);
		return 0;
	}
#endif
	ret = can_start(can_dev);
	if (ret != 0) {
		printf("Error starting CAN controller [%d]", ret);
		return 0;
	}

	if (led.port != NULL) {
		if (!device_is_ready(led.port)) {
			printf("LED: Device %s not ready.\n",
			       led.port->name);
			return 0;
		}
		ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_HIGH);
		if (ret < 0) {
			printf("Error setting LED pin to output mode [%d]",
			       ret);
			led.port = NULL;
		}
	}

	k_work_init(&state_change_work, state_change_work_handler);
	k_work_poll_init(&change_led_work, change_led_work_handler);

	ret = can_add_rx_filter_msgq(can_dev, &change_led_msgq, &change_led_filter);
	if (ret == -ENOSPC) {
		printf("Error, no filter available!\n");
		return 0;
	}

	printf("Change LED filter ID: %d\n", ret);

	ret = k_work_poll_submit(&change_led_work, change_led_events,
				 ARRAY_SIZE(change_led_events), K_FOREVER);
	if (ret != 0) {
		printf("Failed to submit msgq polling: %d", ret);
		return 0;
	}

	rx_tid = k_thread_create(&rx_thread_data, rx_thread_stack,
				 K_THREAD_STACK_SIZEOF(rx_thread_stack),
				 rx_thread, NULL, NULL, NULL,
				 RX_THREAD_PRIORITY, 0, K_NO_WAIT);
	if (!rx_tid) {
		printf("ERROR spawning rx thread\n");
	}

	get_state_tid = k_thread_create(&poll_state_thread_data,
					poll_state_stack,
					K_THREAD_STACK_SIZEOF(poll_state_stack),
					poll_state_thread, NULL, NULL, NULL,
					STATE_POLL_THREAD_PRIORITY, 0,
					K_NO_WAIT);
	if (!get_state_tid) {
		printf("ERROR spawning poll_state_thread\n");
	}

	can_set_state_change_callback(can_dev, state_change_callback, &state_change_work);

	printf("Finished init.\n");

	while (1) {
		change_led_frame.data[0] = toggle++ & 0x01 ? SET_LED : RESET_LED;
		/* This sending call is none blocking. */
		can_send(can_dev, &change_led_frame, K_FOREVER,
			 tx_irq_callback,
			 "LED change");
		k_sleep(SLEEP_TIME);

		UNALIGNED_PUT(sys_cpu_to_be16(counter),
			      (uint16_t *)&counter_frame.data[0]);
		counter++;
		/* This sending call is blocking until the message is sent. */
		can_send(can_dev, &counter_frame, K_MSEC(100), NULL, NULL);
		k_sleep(SLEEP_TIME);
	}
}

arduino_code.ino:

#include <SPI.h>
#include <mcp2515_can.h>

const int spiCSPin = 10;  // Set the CS pin for MCP2515 CAN shield
const int ledPin = 13;    // Use the built-in LED on pin 13

mcp2515_can CAN(spiCSPin);

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

  // Initialize CAN bus at 500kbps
  while (CAN_OK != CAN.begin(CAN_500KBPS)) {
    Serial.println("CAN BUS init Failed");
    delay(100);
  }
  Serial.println("CAN BUS Shield Init OK!");
}

void loop() {
  unsigned char len = 0;
  unsigned char buf[8];
  
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    CAN.readMsgBuf(&len, buf);

    // Check the message ID
    unsigned long canId = CAN.getCanId();
    
    if (canId == 0x10) {
      // LED control message
      if (buf[0] == 1) {
        digitalWrite(ledPin, HIGH);
        Serial.println("LED ON");
      } else if (buf[0] == 0) {
        digitalWrite(ledPin, LOW);
        Serial.println("LED OFF");
      }
    } else if (canId == 0x12345) {
      // Counter message
      if (len == 2) {
        uint16_t counterValue = (buf[0] << 8) | buf[1];
        Serial.print("Counter received: ");
        Serial.println(counterValue);
      } else {
        Serial.println("Wrong data length for counter message");
      }
    }
  }
}

Parents
  • Hi

    The error message you see refers to an input/output error, so I assume the connections somewhere in this setup is incorrect. Do you have a higher resolution image of the setup you uploaded, as zooming in on it I'm not able to read what the pins on the sensor is. Also, what voltage are all parts running on here? Does the nRF53 DK, Arduino, MCP2515 and I2C converter all run at the same GPIO voltage?

    Also, it seems to me like you're mixing and I2C and SPI here, since the overlay file uses SPI, while should use TWI.

    Best regards,

    Simon

  • Thank you for the quick reply. I have attached a higher quality image of the setup.

    So the bidirectional logic level shifter is just used because the Arduino has a voltage level of 5V while the nrf5340DK has a lower voltage level (how is the voltage level of the GPIO pins set on the nrf5340dk? I think it is 1.8V or 3.3V). So right now the low voltage (LV) is connected to the VDD pin of the nrf5340DK and the high votlage (HV) is connected to the 5V pin of the nrf5340DK. The arduino is connected to the 5V pin.

    Shouldn't it be SPI since I am trying to interface with the MCP2515 CAN Bus Shield via SPI?

    Untitled drawing.pdf

Reply
  • Thank you for the quick reply. I have attached a higher quality image of the setup.

    So the bidirectional logic level shifter is just used because the Arduino has a voltage level of 5V while the nrf5340DK has a lower voltage level (how is the voltage level of the GPIO pins set on the nrf5340dk? I think it is 1.8V or 3.3V). So right now the low voltage (LV) is connected to the VDD pin of the nrf5340DK and the high votlage (HV) is connected to the 5V pin of the nrf5340DK. The arduino is connected to the 5V pin.

    Shouldn't it be SPI since I am trying to interface with the MCP2515 CAN Bus Shield via SPI?

    Untitled drawing.pdf

Children
No Data
Related