Browse Source
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>pull/7600/merge
7 changed files with 381 additions and 1 deletions
@ -0,0 +1,7 @@
@@ -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) |
@ -0,0 +1,52 @@
@@ -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. |
@ -0,0 +1,43 @@
@@ -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. |
@ -0,0 +1,8 @@
@@ -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 |
@ -0,0 +1,269 @@
@@ -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"); |
||||
} |
Loading…
Reference in new issue