|
1 | 1 | /* |
2 | | - * Copyright (c) 2018 Alexander Wachter |
| 2 | + * Copyright (c) 2021-2022 Henrik Brix Andersen <[email protected]> |
3 | 3 | * |
4 | 4 | * SPDX-License-Identifier: Apache-2.0 |
5 | 5 | */ |
6 | 6 |
|
7 | | -#include <stdio.h> |
8 | | - |
9 | | -#include <zephyr/kernel.h> |
10 | | -#include <zephyr/sys/printk.h> |
11 | 7 | #include <zephyr/device.h> |
| 8 | +#include <zephyr/devicetree.h> |
12 | 9 | #include <zephyr/drivers/can.h> |
13 | 10 | #include <zephyr/drivers/gpio.h> |
14 | | -#include <zephyr/sys/byteorder.h> |
15 | | - |
16 | | -#define RX_THREAD_STACK_SIZE 512 |
17 | | -#define RX_THREAD_PRIORITY 2 |
18 | | -#define STATE_POLL_THREAD_STACK_SIZE 512 |
19 | | -#define STATE_POLL_THREAD_PRIORITY 2 |
20 | | -#define LED_MSG_ID 0x10 |
21 | | -#define COUNTER_MSG_ID 0x12345 |
22 | | -#define SET_LED 1 |
23 | | -#define RESET_LED 0 |
24 | | -#define SLEEP_TIME K_MSEC(250) |
25 | | - |
26 | | -K_THREAD_STACK_DEFINE(rx_thread_stack, RX_THREAD_STACK_SIZE); |
27 | | -K_THREAD_STACK_DEFINE(poll_state_stack, STATE_POLL_THREAD_STACK_SIZE); |
28 | | - |
29 | | -const struct device *const can_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus)); |
30 | | -struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios, {0}); |
31 | | - |
32 | | -struct k_thread rx_thread_data; |
33 | | -struct k_thread poll_state_thread_data; |
34 | | -struct k_work_poll change_led_work; |
35 | | -struct k_work state_change_work; |
36 | | -enum can_state current_state; |
37 | | -struct can_bus_err_cnt current_err_cnt; |
| 11 | +#include <zephyr/kernel.h> |
38 | 12 |
|
39 | | -CAN_MSGQ_DEFINE(change_led_msgq, 2); |
40 | | -CAN_MSGQ_DEFINE(counter_msgq, 2); |
| 13 | +/* Devicetree */ |
| 14 | +#define CANBUS_NODE DT_CHOSEN(zephyr_canbus) |
| 15 | +#define BUTTON_NODE DT_ALIAS(sw0) |
| 16 | +#define BUTTON_NAME DT_PROP_OR(BUTTON_NODE, label, "sw0") |
41 | 17 |
|
42 | | -static struct k_poll_event change_led_events[1] = { |
43 | | - K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_MSGQ_DATA_AVAILABLE, |
44 | | - K_POLL_MODE_NOTIFY_ONLY, |
45 | | - &change_led_msgq, 0) |
| 18 | +#if DT_NODE_EXISTS(BUTTON_NODE) |
| 19 | +struct button_callback_context { |
| 20 | + struct gpio_callback callback; |
| 21 | + struct k_sem sem; |
46 | 22 | }; |
47 | 23 |
|
48 | | -void tx_irq_callback(const struct device *dev, int error, void *arg) |
| 24 | +static void button_callback(const struct device *port, struct gpio_callback *cb, |
| 25 | + gpio_port_pins_t pins) |
49 | 26 | { |
50 | | - char *sender = (char *)arg; |
51 | | - |
52 | | - ARG_UNUSED(dev); |
| 27 | + struct button_callback_context *ctx = |
| 28 | + CONTAINER_OF(cb, struct button_callback_context, callback); |
53 | 29 |
|
54 | | - if (error != 0) { |
55 | | - printf("Callback! error-code: %d\nSender: %s\n", |
56 | | - error, sender); |
57 | | - } |
| 30 | + k_sem_give(&ctx->sem); |
58 | 31 | } |
| 32 | +#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ |
59 | 33 |
|
60 | | -void rx_thread(void *arg1, void *arg2, void *arg3) |
| 34 | +static void can_tx_callback(const struct device *dev, int error, void *user_data) |
61 | 35 | { |
62 | | - ARG_UNUSED(arg1); |
63 | | - ARG_UNUSED(arg2); |
64 | | - ARG_UNUSED(arg3); |
65 | | - const struct can_filter filter = { |
66 | | - .flags = CAN_FILTER_IDE, |
67 | | - .id = COUNTER_MSG_ID, |
68 | | - .mask = CAN_EXT_ID_MASK |
69 | | - }; |
70 | | - struct can_frame frame; |
71 | | - int filter_id; |
72 | | - |
73 | | - filter_id = can_add_rx_filter_msgq(can_dev, &counter_msgq, &filter); |
74 | | - printf("Counter filter id: %d\n", filter_id); |
75 | | - |
76 | | - while (1) { |
77 | | - k_msgq_get(&counter_msgq, &frame, K_FOREVER); |
78 | | - |
79 | | - if (IS_ENABLED(CONFIG_CAN_ACCEPT_RTR) && (frame.flags & CAN_FRAME_RTR) != 0U) { |
80 | | - continue; |
81 | | - } |
82 | | - |
83 | | - if (frame.dlc != 2U) { |
84 | | - printf("Wrong data length: %u\n", frame.dlc); |
85 | | - continue; |
86 | | - } |
| 36 | + struct k_sem *tx_queue_sem = user_data; |
87 | 37 |
|
88 | | - printf("Counter received: %u\n", |
89 | | - sys_be16_to_cpu(UNALIGNED_GET((uint16_t *)&frame.data))); |
90 | | - } |
| 38 | + k_sem_give(tx_queue_sem); |
91 | 39 | } |
92 | 40 |
|
93 | | -void change_led_work_handler(struct k_work *work) |
| 41 | +int main(void) |
94 | 42 | { |
95 | | - struct can_frame frame; |
96 | | - int ret; |
97 | | - |
98 | | - while (k_msgq_get(&change_led_msgq, &frame, K_NO_WAIT) == 0) { |
99 | | - if (IS_ENABLED(CONFIG_CAN_ACCEPT_RTR) && (frame.flags & CAN_FRAME_RTR) != 0U) { |
100 | | - continue; |
101 | | - } |
| 43 | +#if DT_NODE_EXISTS(BUTTON_NODE) |
| 44 | + const struct gpio_dt_spec btn = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios); |
| 45 | + struct button_callback_context btn_cb_ctx; |
| 46 | +#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ |
| 47 | + const struct device *dev = DEVICE_DT_GET(CANBUS_NODE); |
| 48 | + struct k_sem tx_queue_sem; |
| 49 | + struct can_frame frame = {0}; |
| 50 | + int err; |
102 | 51 |
|
103 | | - if (led.port == NULL) { |
104 | | - printf("LED %s\n", frame.data[0] == SET_LED ? "ON" : "OFF"); |
105 | | - } else { |
106 | | - gpio_pin_set(led.port, led.pin, frame.data[0] == SET_LED ? 1 : 0); |
107 | | - } |
108 | | - } |
| 52 | + k_sem_init(&tx_queue_sem, CONFIG_SAMPLE_CAN_BABBLING_TX_QUEUE_SIZE, |
| 53 | + CONFIG_SAMPLE_CAN_BABBLING_TX_QUEUE_SIZE); |
109 | 54 |
|
110 | | - ret = k_work_poll_submit(&change_led_work, change_led_events, |
111 | | - ARRAY_SIZE(change_led_events), K_FOREVER); |
112 | | - if (ret != 0) { |
113 | | - printf("Failed to resubmit msgq polling: %d", ret); |
114 | | - } |
115 | | -} |
116 | | - |
117 | | -char *state_to_str(enum can_state state) |
118 | | -{ |
119 | | - switch (state) { |
120 | | - case CAN_STATE_ERROR_ACTIVE: |
121 | | - return "error-active"; |
122 | | - case CAN_STATE_ERROR_WARNING: |
123 | | - return "error-warning"; |
124 | | - case CAN_STATE_ERROR_PASSIVE: |
125 | | - return "error-passive"; |
126 | | - case CAN_STATE_BUS_OFF: |
127 | | - return "bus-off"; |
128 | | - case CAN_STATE_STOPPED: |
129 | | - return "stopped"; |
130 | | - default: |
131 | | - return "unknown"; |
| 55 | + if (!device_is_ready(dev)) { |
| 56 | + printk("CAN device not ready"); |
| 57 | + return 0; |
132 | 58 | } |
133 | | -} |
134 | | - |
135 | | -void poll_state_thread(void *unused1, void *unused2, void *unused3) |
136 | | -{ |
137 | | - struct can_bus_err_cnt err_cnt = {0, 0}; |
138 | | - struct can_bus_err_cnt err_cnt_prev = {0, 0}; |
139 | | - enum can_state state_prev = CAN_STATE_ERROR_ACTIVE; |
140 | | - enum can_state state; |
141 | | - int err; |
142 | 59 |
|
143 | | - while (1) { |
144 | | - err = can_get_state(can_dev, &state, &err_cnt); |
| 60 | + if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_FD_MODE)) { |
| 61 | + err = can_set_mode(dev, CAN_MODE_FD); |
145 | 62 | if (err != 0) { |
146 | | - printf("Failed to get CAN controller state: %d", err); |
147 | | - k_sleep(K_MSEC(100)); |
148 | | - continue; |
149 | | - } |
150 | | - |
151 | | - if (err_cnt.tx_err_cnt != err_cnt_prev.tx_err_cnt || |
152 | | - err_cnt.rx_err_cnt != err_cnt_prev.rx_err_cnt || |
153 | | - state_prev != state) { |
154 | | - |
155 | | - err_cnt_prev.tx_err_cnt = err_cnt.tx_err_cnt; |
156 | | - err_cnt_prev.rx_err_cnt = err_cnt.rx_err_cnt; |
157 | | - state_prev = state; |
158 | | - printf("state: %s\n" |
159 | | - "rx error count: %d\n" |
160 | | - "tx error count: %d\n", |
161 | | - state_to_str(state), |
162 | | - err_cnt.rx_err_cnt, err_cnt.tx_err_cnt); |
163 | | - } else { |
164 | | - k_sleep(K_MSEC(100)); |
| 63 | + printk("Error setting CAN FD mode (err %d)", err); |
| 64 | + return 0; |
165 | 65 | } |
166 | 66 | } |
167 | | -} |
168 | | - |
169 | | -void state_change_work_handler(struct k_work *work) |
170 | | -{ |
171 | | - printf("State Change ISR\nstate: %s\n" |
172 | | - "rx error count: %d\n" |
173 | | - "tx error count: %d\n", |
174 | | - state_to_str(current_state), |
175 | | - current_err_cnt.rx_err_cnt, current_err_cnt.tx_err_cnt); |
176 | | - |
177 | | -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY |
178 | | - if (current_state == CAN_STATE_BUS_OFF) { |
179 | | - printf("Recover from bus-off\n"); |
180 | 67 |
|
181 | | - if (can_recover(can_dev, K_MSEC(100)) != 0) { |
182 | | - printf("Recovery timed out\n"); |
183 | | - } |
| 68 | + err = can_start(dev); |
| 69 | + if (err != 0) { |
| 70 | + printk("Error starting CAN controller (err %d)", err); |
| 71 | + return 0; |
184 | 72 | } |
185 | | -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ |
186 | | -} |
187 | | - |
188 | | -void state_change_callback(const struct device *dev, enum can_state state, |
189 | | - struct can_bus_err_cnt err_cnt, void *user_data) |
190 | | -{ |
191 | | - struct k_work *work = (struct k_work *)user_data; |
192 | | - |
193 | | - ARG_UNUSED(dev); |
194 | 73 |
|
195 | | - current_state = state; |
196 | | - current_err_cnt = err_cnt; |
197 | | - k_work_submit(work); |
198 | | -} |
| 74 | +#if DT_NODE_EXISTS(BUTTON_NODE) |
| 75 | + k_sem_init(&btn_cb_ctx.sem, 0, 1); |
199 | 76 |
|
200 | | -int main(void) |
201 | | -{ |
202 | | - const struct can_filter change_led_filter = { |
203 | | - .flags = 0U, |
204 | | - .id = LED_MSG_ID, |
205 | | - .mask = CAN_STD_ID_MASK |
206 | | - }; |
207 | | - struct can_frame change_led_frame = { |
208 | | - .flags = 0, |
209 | | - .id = LED_MSG_ID, |
210 | | - .dlc = 1 |
211 | | - }; |
212 | | - struct can_frame counter_frame = { |
213 | | - .flags = CAN_FRAME_IDE, |
214 | | - .id = COUNTER_MSG_ID, |
215 | | - .dlc = 2 |
216 | | - }; |
217 | | - uint8_t toggle = 1; |
218 | | - uint16_t counter = 0; |
219 | | - k_tid_t rx_tid, get_state_tid; |
220 | | - int ret; |
221 | | - |
222 | | - if (!device_is_ready(can_dev)) { |
223 | | - printf("CAN: Device %s not ready.\n", can_dev->name); |
| 77 | + if (!gpio_is_ready_dt(&btn)) { |
| 78 | + printk("button device not ready\n"); |
224 | 79 | return 0; |
225 | 80 | } |
226 | 81 |
|
227 | | -#ifdef CONFIG_LOOPBACK_MODE |
228 | | - ret = can_set_mode(can_dev, CAN_MODE_LOOPBACK); |
229 | | - if (ret != 0) { |
230 | | - printf("Error setting CAN mode [%d]", ret); |
231 | | - return 0; |
232 | | - } |
233 | | -#endif |
234 | | - ret = can_start(can_dev); |
235 | | - if (ret != 0) { |
236 | | - printf("Error starting CAN controller [%d]", ret); |
| 82 | + err = gpio_pin_configure_dt(&btn, GPIO_INPUT); |
| 83 | + if (err != 0) { |
| 84 | + printk("failed to configure button GPIO (err %d)\n", err); |
237 | 85 | return 0; |
238 | 86 | } |
239 | 87 |
|
240 | | - if (led.port != NULL) { |
241 | | - if (!gpio_is_ready_dt(&led)) { |
242 | | - printf("LED: Device %s not ready.\n", |
243 | | - led.port->name); |
244 | | - return 0; |
245 | | - } |
246 | | - ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_HIGH); |
247 | | - if (ret < 0) { |
248 | | - printf("Error setting LED pin to output mode [%d]", |
249 | | - ret); |
250 | | - led.port = NULL; |
251 | | - } |
252 | | - } |
253 | | - |
254 | | - k_work_init(&state_change_work, state_change_work_handler); |
255 | | - k_work_poll_init(&change_led_work, change_led_work_handler); |
256 | | - |
257 | | - ret = can_add_rx_filter_msgq(can_dev, &change_led_msgq, &change_led_filter); |
258 | | - if (ret == -ENOSPC) { |
259 | | - printf("Error, no filter available!\n"); |
| 88 | + err = gpio_pin_interrupt_configure_dt(&btn, GPIO_INT_EDGE_TO_ACTIVE); |
| 89 | + if (err != 0) { |
| 90 | + printk("failed to configure button interrupt (err %d)\n", err); |
260 | 91 | return 0; |
261 | 92 | } |
262 | 93 |
|
263 | | - printf("Change LED filter ID: %d\n", ret); |
| 94 | + gpio_init_callback(&btn_cb_ctx.callback, button_callback, BIT(btn.pin)); |
| 95 | + gpio_add_callback(btn.port, &btn_cb_ctx.callback); |
| 96 | +#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ |
264 | 97 |
|
265 | | - ret = k_work_poll_submit(&change_led_work, change_led_events, |
266 | | - ARRAY_SIZE(change_led_events), K_FOREVER); |
267 | | - if (ret != 0) { |
268 | | - printf("Failed to submit msgq polling: %d", ret); |
269 | | - return 0; |
| 98 | + if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_EXT_ID)) { |
| 99 | + frame.flags |= CAN_FRAME_IDE; |
270 | 100 | } |
271 | 101 |
|
272 | | - rx_tid = k_thread_create(&rx_thread_data, rx_thread_stack, |
273 | | - K_THREAD_STACK_SIZEOF(rx_thread_stack), |
274 | | - rx_thread, NULL, NULL, NULL, |
275 | | - RX_THREAD_PRIORITY, 0, K_NO_WAIT); |
276 | | - if (!rx_tid) { |
277 | | - printf("ERROR spawning rx thread\n"); |
| 102 | + if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_RTR)) { |
| 103 | + frame.flags |= CAN_FRAME_RTR; |
278 | 104 | } |
279 | 105 |
|
280 | | - get_state_tid = k_thread_create(&poll_state_thread_data, |
281 | | - poll_state_stack, |
282 | | - K_THREAD_STACK_SIZEOF(poll_state_stack), |
283 | | - poll_state_thread, NULL, NULL, NULL, |
284 | | - STATE_POLL_THREAD_PRIORITY, 0, |
285 | | - K_NO_WAIT); |
286 | | - if (!get_state_tid) { |
287 | | - printf("ERROR spawning poll_state_thread\n"); |
| 106 | + if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_FD_MODE)) { |
| 107 | + frame.flags |= CAN_FRAME_FDF; |
288 | 108 | } |
289 | 109 |
|
290 | | - can_set_state_change_callback(can_dev, state_change_callback, &state_change_work); |
| 110 | + frame.id = CONFIG_SAMPLE_CAN_BABBLING_CAN_ID; |
291 | 111 |
|
292 | | - printf("Finished init.\n"); |
| 112 | + printk("babbling on %s with %s (%d-bit) CAN ID 0x%0*x, RTR %d, CAN FD %d\n", |
| 113 | + dev->name, |
| 114 | + (frame.flags & CAN_FRAME_IDE) != 0 ? "extended" : "standard", |
| 115 | + (frame.flags & CAN_FRAME_IDE) != 0 ? 29 : 11, |
| 116 | + (frame.flags & CAN_FRAME_IDE) != 0 ? 8 : 3, frame.id, |
| 117 | + (frame.flags & CAN_FRAME_RTR) != 0 ? 1 : 0, |
| 118 | + (frame.flags & CAN_FRAME_FDF) != 0 ? 1 : 0); |
293 | 119 |
|
294 | | - while (1) { |
295 | | - change_led_frame.data[0] = toggle++ & 0x01 ? SET_LED : RESET_LED; |
296 | | - /* This sending call is none blocking. */ |
297 | | - can_send(can_dev, &change_led_frame, K_FOREVER, |
298 | | - tx_irq_callback, |
299 | | - "LED change"); |
300 | | - k_sleep(SLEEP_TIME); |
| 120 | +#if DT_NODE_EXISTS(BUTTON_NODE) |
| 121 | + printk("abort by pressing %s button\n", BUTTON_NAME); |
| 122 | +#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ |
301 | 123 |
|
302 | | - UNALIGNED_PUT(sys_cpu_to_be16(counter), |
303 | | - (uint16_t *)&counter_frame.data[0]); |
304 | | - counter++; |
305 | | - /* This sending call is blocking until the message is sent. */ |
306 | | - can_send(can_dev, &counter_frame, K_MSEC(100), NULL, NULL); |
307 | | - k_sleep(SLEEP_TIME); |
| 124 | + while (true) { |
| 125 | + if (k_sem_take(&tx_queue_sem, K_MSEC(100)) == 0) { |
| 126 | + err = can_send(dev, &frame, K_NO_WAIT, can_tx_callback, &tx_queue_sem); |
| 127 | + if (err != 0) { |
| 128 | + printk("failed to enqueue CAN frame (err %d)\n", err); |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | +#if DT_NODE_EXISTS(BUTTON_NODE) |
| 133 | + if (k_sem_take(&btn_cb_ctx.sem, K_NO_WAIT) == 0) { |
| 134 | + printk("button press detected, babbling stopped\n"); |
| 135 | + return 0; |
| 136 | + } |
| 137 | +#endif /* DT_NODE_EXISTS(BUTTON_NODE) */ |
308 | 138 | } |
309 | 139 | } |
0 commit comments