From 2de9f2f8fd17385f545effd007bdaf3da8e97dbe Mon Sep 17 00:00:00 2001 From: Alexander Wachter Date: Sat, 28 Apr 2018 00:59:14 +0200 Subject: [PATCH] samples: can: Add example code for CAN driver This adds example and testing code for CAN driver. Tested on stm32f072b disco. Examples are given for: - can_configure - can_attach_isr - can_attach_msgq - can_send Signed-off-by: Alexander Wachter --- include/can.h | 2 +- samples/can/CMakeLists.txt | 7 + samples/can/Kconfig | 52 +++++++ samples/can/README.rst | 43 ++++++ samples/can/prj.conf | 8 ++ samples/can/src/main.c | 269 +++++++++++++++++++++++++++++++++++++ samples/samples.rst | 1 + 7 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 samples/can/CMakeLists.txt create mode 100644 samples/can/Kconfig create mode 100644 samples/can/README.rst create mode 100644 samples/can/prj.conf create mode 100644 samples/can/src/main.c diff --git a/include/can.h b/include/can.h index bf017ec4503..d19acd4d470 100644 --- a/include/can.h +++ b/include/can.h @@ -329,7 +329,7 @@ __syscall int can_attach_msgq(struct device *dev, struct k_msgq *msg_q, static inline int _impl_can_attach_msgq(struct device *dev, struct k_msgq *msg_q, - struct can_filter *filter) + const struct can_filter *filter) { const struct can_driver_api *api = dev->driver_api; diff --git a/samples/can/CMakeLists.txt b/samples/can/CMakeLists.txt new file mode 100644 index 00000000000..faef446edfd --- /dev/null +++ b/samples/can/CMakeLists.txt @@ -0,0 +1,7 @@ + +set(KCONFIG_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/Kconfig) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/can/Kconfig b/samples/can/Kconfig new file mode 100644 index 00000000000..33b8877c3e6 --- /dev/null +++ b/samples/can/Kconfig @@ -0,0 +1,52 @@ +# Kconfig - Private config options for can sample app + +# +# Copyright (c) 2018 Alexander Wachter +# +# SPDX-License-Identifier: Apache-2.0 +# + +mainmenu "Controller Area Network sample application" + +config ZEPHYR_BASE + string + option env="ZEPHYR_BASE" + +source "$ZEPHYR_BASE/Kconfig.zephyr" + +config CAN_DEV + string "Name of the CAN device" + default "CAN_1" + help + Name of the can device used for send an receive. + +config GPIO_LED_DEV + string "Name of the LED GPIO port" + default "GPIOC" + help + Name of the LED port for signaling message reception. + +config GPIO_BUTTON_DEV + string "Name of the button GPIO port" + default "GPIOA" + help + Name of the button port for triggering messages. + +config PIN_USER_BUTTON + int "Pin User Button" + default 0 + help + Pin number of the user Button. + +config PIN_LED_1 + int "Pin LED 1" + default 6 + help + Pin number of the first LED. + +config LOOPBACK_MODE + bool "Loopback LOOPBACK_MODE" + default y + help + Set the controller to loopback mode. + This allows testing without a second board. diff --git a/samples/can/README.rst b/samples/can/README.rst new file mode 100644 index 00000000000..641d9fcf182 --- /dev/null +++ b/samples/can/README.rst @@ -0,0 +1,43 @@ +.. _can-sample: + +Controller Area Network +####################### + +Overview +******** + +This sample demonstrates how to use the Controller Area Network (CAN) API. +Messages with standard and extended identifiers are sent over the bus, triggered +by a button event. +Messages are received using message queues and ISRs. +Reception is indicated by blink LEDs and output to the console. + +Building and Running +******************** + +In loopback mode, the board receives its own messages. This could be used for +standalone testing. + +The sample can be built and executed on boards supporting CAN. +The output ports and pins of the LEDs can be configured by Kconfig. + +Sample output +============= + +.. code-block:: console + + Finished init. waiting for Interrupts + TX thread is running. + filter id: 1 + Button pressed! Send message 1 + Button pressed 1 times + Button pressed! Send message 0 + Button pressed 2 times + String sent over CAN + Button pressed! Send message 1 + Button pressed 3 times + Button pressed! Send message 0 + Button pressed 4 times + String sent over CAN + +.. note:: The values shown above might differ. diff --git a/samples/can/prj.conf b/samples/can/prj.conf new file mode 100644 index 00000000000..cfb4197ec0f --- /dev/null +++ b/samples/can/prj.conf @@ -0,0 +1,8 @@ +CONFIG_CAN=y +CONFIG_CAN_INIT_PRIORITY=80 +CONFIG_CAN_PHASE_SEG1_PROP_SEG=5 +CONFIG_CAN_PHASE_SEG2=6 +CONFIG_CAN_SJW=1 +CONFIG_CAN_1=y +CONFIG_CAN_STM32=y +CONFIG_CAN_MAX_FILTER=5 diff --git a/samples/can/src/main.c b/samples/can/src/main.c new file mode 100644 index 00000000000..9a0e1dbf5d1 --- /dev/null +++ b/samples/can/src/main.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2018 Alexander Wachter + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#define TX_THREAD_STACK_SIZE 512 +#define LED_THREAD_STACK_SIZE 512 +#define RX_STR_THREAD_STACK_SIZE 512 +#define TX_THREAD_PRIORITY 2 +#define LED_MSG_ID (0x10) +#define BUTTON_MSG_ID (0x01) +#define STR_MSG_ID (0x12345) + +#define SET_LED 0 +#define RESET_LED 1 + + +#define NUM_LEDS_STR STRINGIFY(NUM_LEDS) + +K_THREAD_STACK_DEFINE(tx_thread_stack, TX_THREAD_STACK_SIZE); +K_THREAD_STACK_DEFINE(led_thread_stack, LED_THREAD_STACK_SIZE); +K_THREAD_STACK_DEFINE(rx_str_thread_stack, RX_STR_THREAD_STACK_SIZE); +struct k_thread tx_thread_data; +struct k_thread led_thread_data; +struct k_thread rx_str_thread_data; +struct k_sem tx_sem; +static struct gpio_callback gpio_cb; +CAN_DEFINE_MSGQ(led_msgq, 2); +CAN_DEFINE_MSGQ(str_msgq, 5); + +void tx_irq_callback(u32_t error_flags) +{ + if (error_flags) { + printk("Callback! error-code: %d\n", error_flags); + } +} + +void button_callback(struct device *port, + struct gpio_callback *cb, u32_t pins) +{ + k_sem_give(&tx_sem); +} + +void send_string(char *string, struct device *can_dev) +{ + struct can_msg msg; + int str_len; + + msg.ext_id = STR_MSG_ID; + msg.id_type = CAN_EXTENDED_IDENTIFIER; + msg.dlc = 0; + msg.rtr = CAN_DATAFRAME; + + for (str_len = strlen(string); str_len; ) { + msg.dlc = str_len >= 8 ? 8 : str_len; + str_len -= msg.dlc; + memcpy(msg.data, string, msg.dlc); + string += msg.dlc; + can_send(can_dev, &msg, 10, tx_irq_callback); + } +} + +void tx_thread(void *can_dev_param, void *unused2, void *unused3) +{ + u8_t toggle = SET_LED; + u16_t button_press_cnt = 0; + struct can_msg msg; + struct can_msg msg_button_cnt; + struct device *can_dev = can_dev_param; + + msg.std_id = LED_MSG_ID; + msg.id_type = CAN_STANDARD_IDENTIFIER; + msg.dlc = 1; + msg.rtr = CAN_DATAFRAME; + msg.data[0] = 0; + + msg_button_cnt.std_id = BUTTON_MSG_ID; + msg_button_cnt.id_type = CAN_STANDARD_IDENTIFIER; + msg_button_cnt.dlc = 2; + msg_button_cnt.rtr = CAN_DATAFRAME; + msg_button_cnt.data[0] = 0; + msg_button_cnt.data[1] = 0; + + printk("TX thread is running.\n"); + while (1) { + k_sem_take(&tx_sem, K_FOREVER); + button_press_cnt++; + toggle = (toggle == SET_LED) ? RESET_LED : SET_LED; + printk("Button pressed! Send message %u\n", toggle); + msg.data[0] = toggle; + msg_button_cnt.data[0] = button_press_cnt & 0xFF; + msg_button_cnt.data[1] = (button_press_cnt >> 8) & 0xFF; + can_send(can_dev, &msg, 10, tx_irq_callback); + can_send(can_dev, &msg_button_cnt, 10, NULL); + if (toggle == SET_LED) { + send_string("String sent over CAN\n", can_dev); + } + } +} + +void rx_str_thread(void *msgq, void *can_dev_param, void *unused) +{ + struct can_msg msg; + const struct can_filter filter = { + .id_type = CAN_EXTENDED_IDENTIFIER, + .rtr = CAN_DATAFRAME, + .ext_id = STR_MSG_ID, + .rtr_mask = 1, + .ext_id_mask = CAN_EXT_ID_MASK + }; + struct device *can_dev = can_dev_param; + + can_attach_msgq(can_dev, msgq, &filter); + + while (1) { + k_msgq_get((struct k_msgq *)msgq, &msg, K_FOREVER); + for (int i = 0; i < msg.dlc; i++) + printk("%c", msg.data[i]); + } +} + +void led_thread(void *msgq, void *can_dev_param, void *gpio_dev_param) +{ + const struct can_filter filter = { + .id_type = CAN_STANDARD_IDENTIFIER, + .rtr = CAN_DATAFRAME, + .std_id = LED_MSG_ID, + .rtr_mask = 1, + .std_id_mask = CAN_STD_ID_MASK + }; + struct device *can_dev = can_dev_param; + struct device *gpio_dev = gpio_dev_param; + struct can_msg msg; + int ret; + int filter_id; + + ret = gpio_pin_configure(gpio_dev, CONFIG_PIN_LED_1, GPIO_DIR_OUT); + gpio_pin_write(gpio_dev, CONFIG_PIN_LED_1, 0); + + if (ret) { + printk("ERROR configure pins\n"); + return; + } + + filter_id = can_attach_msgq(can_dev, msgq, &filter); + printk("filter id: %d\n", filter_id); + + while (1) { + k_msgq_get((struct k_msgq *)msgq, &msg, K_FOREVER); + + if (msg.dlc != 1) { + continue; + } + + switch (msg.data[0]) { + case SET_LED: + gpio_pin_write(gpio_dev, CONFIG_PIN_LED_1, 1); + + break; + case RESET_LED: + gpio_pin_write(gpio_dev, CONFIG_PIN_LED_1, 0); + break; + } + } +} + +void rx_button_isr(struct can_msg *msg) +{ + u16_t cnt = msg->data[0] | (msg->data[1] << 8); + + printk("Button pressed %d times\n", cnt); +} + +void main(void) +{ + const struct can_filter filter = { + .id_type = CAN_STANDARD_IDENTIFIER, + .rtr = CAN_DATAFRAME, + .std_id = BUTTON_MSG_ID, + .rtr_mask = 1, + .std_id_mask = CAN_STD_ID_MASK + }; + struct device *can_dev, *led_gpio_dev, *button_gpio_dev; + int ret; + + can_dev = device_get_binding(CONFIG_CAN_DEV); + if (!can_dev) { + printk("CAN: Device driver not found.\n"); + return; + } + +#ifdef CONFIG_LOOPBACK_MODE + can_configure(can_dev, CAN_LOOPBACK_MODE, 250000); +#endif + + led_gpio_dev = device_get_binding(CONFIG_GPIO_LED_DEV); + if (!led_gpio_dev) { + printk("LED: Device driver not found.\n"); + return; + } + + k_sem_init(&tx_sem, 0, INT_MAX); + + button_gpio_dev = device_get_binding(CONFIG_GPIO_BUTTON_DEV); + if (!button_gpio_dev) { + printk("Button: Device driver not found.\n"); + return; + } + + ret = gpio_pin_configure(button_gpio_dev, CONFIG_PIN_USER_BUTTON, + (GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | + GPIO_INT_ACTIVE_HIGH | GPIO_INT_DEBOUNCE)); + if (ret) { + printk("Error configuring button pin\n"); + } + + gpio_init_callback(&gpio_cb, button_callback, + BIT(CONFIG_PIN_USER_BUTTON)); + + ret = gpio_add_callback(button_gpio_dev, &gpio_cb); + if (ret) { + printk("Cannot setup callback!\n"); + } + + ret = gpio_pin_enable_callback(button_gpio_dev, CONFIG_PIN_USER_BUTTON); + if (ret) { + printk("Error enabling callback!\n"); + } + + can_attach_isr(can_dev, rx_button_isr, &filter); + + k_tid_t tx_tid = k_thread_create(&tx_thread_data, tx_thread_stack, + K_THREAD_STACK_SIZEOF(tx_thread_stack), + tx_thread, + can_dev, NULL, NULL, + TX_THREAD_PRIORITY, 0, K_NO_WAIT); + if (!tx_tid) { + printk("ERROR spawning tx_thread\n"); + } + + k_tid_t led_tid = k_thread_create(&led_thread_data, led_thread_stack, + K_THREAD_STACK_SIZEOF(led_thread_stack), + led_thread, + &led_msgq, can_dev, led_gpio_dev, + TX_THREAD_PRIORITY, 0, K_NO_WAIT); + if (!led_tid) { + printk("ERROR spawning led_thread\n"); + } + + k_tid_t str_tid = k_thread_create(&rx_str_thread_data, + rx_str_thread_stack, + K_THREAD_STACK_SIZEOF(rx_str_thread_stack), + rx_str_thread, + &str_msgq, can_dev, NULL, + TX_THREAD_PRIORITY, 0, K_NO_WAIT); + if (!str_tid) { + printk("ERROR spawning str_thread\n"); + } + + printk("Finished init. waiting for Interrupts\n"); +} diff --git a/samples/samples.rst b/samples/samples.rst index bd2cc5b93b6..3677a6c6f67 100644 --- a/samples/samples.rst +++ b/samples/samples.rst @@ -20,6 +20,7 @@ Samples and Demos drivers/drivers.rst application_development/* display/* + can/README.rst To add a new sample document, please use the template available under