|
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