Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * 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 zcan_frame msg;
	int str_len;

	msg.ext_id = STR_MSG_ID;
	msg.id_type = CAN_EXTENDED_IDENTIFIER;
	msg.dlc = 0U;
	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 = 0U;
	struct zcan_frame msg;
	struct zcan_frame msg_button_cnt;
	struct device *can_dev = can_dev_param;

	msg.std_id = LED_MSG_ID;
	msg.id_type = CAN_STANDARD_IDENTIFIER;
	msg.dlc = 1U;
	msg.rtr = CAN_DATAFRAME;
	msg.data[0] = 0U;

	msg_button_cnt.std_id = BUTTON_MSG_ID;
	msg_button_cnt.id_type = CAN_STANDARD_IDENTIFIER;
	msg_button_cnt.dlc = 2U;
	msg_button_cnt.rtr = CAN_DATAFRAME;
	msg_button_cnt.data[0] = 0U;
	msg_button_cnt.data[1] = 0U;

	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 zcan_frame msg;
	const struct zcan_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 zcan_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 zcan_frame 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 != 1U) {
			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 zcan_frame *msg)
{
	u16_t cnt = msg->data[0] | (msg->data[1] << 8);

	printk("Button pressed %d times\n", cnt);
}

void main(void)
{
	const struct zcan_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");
	}

	ret = can_attach_isr(can_dev, rx_button_isr, &filter);
	if (ret == CAN_NO_FREE_FILTER) {
		printk("Error, no filter available!\n");
		return;
	}

	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");
}