-
-
Notifications
You must be signed in to change notification settings - Fork 311
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
434 additions
and
509 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,309 +1,139 @@ | ||
/* | ||
* Copyright (c) 2018 Alexander Wachter | ||
* Copyright (c) 2021-2022 Henrik Brix Andersen <[email protected]> | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <stdio.h> | ||
|
||
#include <zephyr/kernel.h> | ||
#include <zephyr/sys/printk.h> | ||
#include <zephyr/device.h> | ||
#include <zephyr/devicetree.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; | ||
#include <zephyr/kernel.h> | ||
|
||
CAN_MSGQ_DEFINE(change_led_msgq, 2); | ||
CAN_MSGQ_DEFINE(counter_msgq, 2); | ||
/* Devicetree */ | ||
#define CANBUS_NODE DT_CHOSEN(zephyr_canbus) | ||
#define BUTTON_NODE DT_ALIAS(sw0) | ||
#define BUTTON_NAME DT_PROP_OR(BUTTON_NODE, label, "sw0") | ||
|
||
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) | ||
#if DT_NODE_EXISTS(BUTTON_NODE) | ||
struct button_callback_context { | ||
struct gpio_callback callback; | ||
struct k_sem sem; | ||
}; | ||
|
||
void tx_irq_callback(const struct device *dev, int error, void *arg) | ||
static void button_callback(const struct device *port, struct gpio_callback *cb, | ||
gpio_port_pins_t pins) | ||
{ | ||
char *sender = (char *)arg; | ||
|
||
ARG_UNUSED(dev); | ||
struct button_callback_context *ctx = | ||
CONTAINER_OF(cb, struct button_callback_context, callback); | ||
|
||
if (error != 0) { | ||
printf("Callback! error-code: %d\nSender: %s\n", | ||
error, sender); | ||
} | ||
k_sem_give(&ctx->sem); | ||
} | ||
#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ | ||
|
||
void rx_thread(void *arg1, void *arg2, void *arg3) | ||
static void can_tx_callback(const struct device *dev, int error, void *user_data) | ||
{ | ||
ARG_UNUSED(arg1); | ||
ARG_UNUSED(arg2); | ||
ARG_UNUSED(arg3); | ||
const struct can_filter filter = { | ||
.flags = 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 (IS_ENABLED(CONFIG_CAN_ACCEPT_RTR) && (frame.flags & CAN_FRAME_RTR) != 0U) { | ||
continue; | ||
} | ||
|
||
if (frame.dlc != 2U) { | ||
printf("Wrong data length: %u\n", frame.dlc); | ||
continue; | ||
} | ||
struct k_sem *tx_queue_sem = user_data; | ||
|
||
printf("Counter received: %u\n", | ||
sys_be16_to_cpu(UNALIGNED_GET((uint16_t *)&frame.data))); | ||
} | ||
k_sem_give(tx_queue_sem); | ||
} | ||
|
||
void change_led_work_handler(struct k_work *work) | ||
int main(void) | ||
{ | ||
struct can_frame frame; | ||
int ret; | ||
|
||
while (k_msgq_get(&change_led_msgq, &frame, K_NO_WAIT) == 0) { | ||
if (IS_ENABLED(CONFIG_CAN_ACCEPT_RTR) && (frame.flags & CAN_FRAME_RTR) != 0U) { | ||
continue; | ||
} | ||
#if DT_NODE_EXISTS(BUTTON_NODE) | ||
const struct gpio_dt_spec btn = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios); | ||
struct button_callback_context btn_cb_ctx; | ||
#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ | ||
const struct device *dev = DEVICE_DT_GET(CANBUS_NODE); | ||
struct k_sem tx_queue_sem; | ||
struct can_frame frame = {0}; | ||
int err; | ||
|
||
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); | ||
} | ||
} | ||
k_sem_init(&tx_queue_sem, CONFIG_SAMPLE_CAN_BABBLING_TX_QUEUE_SIZE, | ||
CONFIG_SAMPLE_CAN_BABBLING_TX_QUEUE_SIZE); | ||
|
||
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"; | ||
if (!device_is_ready(dev)) { | ||
printk("CAN device not ready"); | ||
return 0; | ||
} | ||
} | ||
|
||
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 (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_FD_MODE)) { | ||
err = can_set_mode(dev, CAN_MODE_FD); | ||
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)); | ||
printk("Error setting CAN FD mode (err %d)", err); | ||
return 0; | ||
} | ||
} | ||
} | ||
|
||
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"); | ||
} | ||
err = can_start(dev); | ||
if (err != 0) { | ||
printk("Error starting CAN controller (err %d)", err); | ||
return 0; | ||
} | ||
#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); | ||
} | ||
#if DT_NODE_EXISTS(BUTTON_NODE) | ||
k_sem_init(&btn_cb_ctx.sem, 0, 1); | ||
|
||
int main(void) | ||
{ | ||
const struct can_filter change_led_filter = { | ||
.flags = 0U, | ||
.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); | ||
if (!gpio_is_ready_dt(&btn)) { | ||
printk("button device not ready\n"); | ||
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); | ||
err = gpio_pin_configure_dt(&btn, GPIO_INPUT); | ||
if (err != 0) { | ||
printk("failed to configure button GPIO (err %d)\n", err); | ||
return 0; | ||
} | ||
|
||
if (led.port != NULL) { | ||
if (!gpio_is_ready_dt(&led)) { | ||
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"); | ||
err = gpio_pin_interrupt_configure_dt(&btn, GPIO_INT_EDGE_TO_ACTIVE); | ||
if (err != 0) { | ||
printk("failed to configure button interrupt (err %d)\n", err); | ||
return 0; | ||
} | ||
|
||
printf("Change LED filter ID: %d\n", ret); | ||
gpio_init_callback(&btn_cb_ctx.callback, button_callback, BIT(btn.pin)); | ||
gpio_add_callback(btn.port, &btn_cb_ctx.callback); | ||
#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ | ||
|
||
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; | ||
if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_EXT_ID)) { | ||
frame.flags |= CAN_FRAME_IDE; | ||
} | ||
|
||
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"); | ||
if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_RTR)) { | ||
frame.flags |= CAN_FRAME_RTR; | ||
} | ||
|
||
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"); | ||
if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_FD_MODE)) { | ||
frame.flags |= CAN_FRAME_FDF; | ||
} | ||
|
||
can_set_state_change_callback(can_dev, state_change_callback, &state_change_work); | ||
frame.id = CONFIG_SAMPLE_CAN_BABBLING_CAN_ID; | ||
|
||
printf("Finished init.\n"); | ||
printk("babbling on %s with %s (%d-bit) CAN ID 0x%0*x, RTR %d, CAN FD %d\n", | ||
dev->name, | ||
(frame.flags & CAN_FRAME_IDE) != 0 ? "extended" : "standard", | ||
(frame.flags & CAN_FRAME_IDE) != 0 ? 29 : 11, | ||
(frame.flags & CAN_FRAME_IDE) != 0 ? 8 : 3, frame.id, | ||
(frame.flags & CAN_FRAME_RTR) != 0 ? 1 : 0, | ||
(frame.flags & CAN_FRAME_FDF) != 0 ? 1 : 0); | ||
|
||
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); | ||
#if DT_NODE_EXISTS(BUTTON_NODE) | ||
printk("abort by pressing %s button\n", BUTTON_NAME); | ||
#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ | ||
|
||
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); | ||
while (true) { | ||
if (k_sem_take(&tx_queue_sem, K_MSEC(100)) == 0) { | ||
err = can_send(dev, &frame, K_NO_WAIT, can_tx_callback, &tx_queue_sem); | ||
if (err != 0) { | ||
printk("failed to enqueue CAN frame (err %d)\n", err); | ||
} | ||
} | ||
|
||
#if DT_NODE_EXISTS(BUTTON_NODE) | ||
if (k_sem_take(&btn_cb_ctx.sem, K_NO_WAIT) == 0) { | ||
printk("button press detected, babbling stopped\n"); | ||
return 0; | ||
} | ||
#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ | ||
} | ||
} |
Oops, something went wrong.