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>
This commit is contained in:
parent
e73637af24
commit
2de9f2f8fd
@ -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;
|
||||
|
||||
|
||||
7
samples/can/CMakeLists.txt
Normal file
7
samples/can/CMakeLists.txt
Normal file
@ -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)
|
||||
52
samples/can/Kconfig
Normal file
52
samples/can/Kconfig
Normal file
@ -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.
|
||||
43
samples/can/README.rst
Normal file
43
samples/can/README.rst
Normal file
@ -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.
|
||||
8
samples/can/prj.conf
Normal file
8
samples/can/prj.conf
Normal file
@ -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
|
||||
269
samples/can/src/main.c
Normal file
269
samples/can/src/main.c
Normal file
@ -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");
|
||||
}
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user