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 <alexander.wachter@student.tugraz.at>
diff --git a/include/can.h b/include/can.h
index bf017ec..d19acd4 100644
--- a/include/can.h
+++ b/include/can.h
@@ -329,7 +329,7 @@
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 0000000..faef446
--- /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 0000000..33b8877
--- /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 0000000..641d9fc
--- /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 0000000..cfb4197
--- /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 0000000..9a0e1db
--- /dev/null
+++ b/samples/can/src/main.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2018 Alexander Wachter
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr.h>
+#include <kernel.h>
+#include <misc/printk.h>
+#include <device.h>
+#include <can.h>
+#include <gpio.h>
+
+#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 bd2cc5b..3677a6c 100644
--- a/samples/samples.rst
+++ b/samples/samples.rst
@@ -20,6 +20,7 @@
drivers/drivers.rst
application_development/*
display/*
+ can/README.rst
To add a new sample document, please use the template available under