1 /*
2  * Copyright (c) 2019 Vestas Wind Systems A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/sys/reboot.h>
10 #include <zephyr/settings/settings.h>
11 #include <canopennode.h>
12 
13 #define LOG_LEVEL CONFIG_CANOPEN_LOG_LEVEL
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(app);
16 
17 #define CAN_INTERFACE DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus))
18 #define CAN_BITRATE (DT_PROP_OR(DT_CHOSEN(zephyr_canbus), bitrate, \
19 					  DT_PROP_OR(DT_CHOSEN(zephyr_canbus), bus_speed, \
20 						     CONFIG_CAN_DEFAULT_BITRATE)) / 1000)
21 
22 static struct gpio_dt_spec led_green_gpio = GPIO_DT_SPEC_GET_OR(
23 		DT_ALIAS(green_led), gpios, {0});
24 static struct gpio_dt_spec led_red_gpio = GPIO_DT_SPEC_GET_OR(
25 		DT_ALIAS(red_led), gpios, {0});
26 
27 static struct gpio_dt_spec button_gpio = GPIO_DT_SPEC_GET_OR(
28 		DT_ALIAS(sw0), gpios, {0});
29 static struct gpio_callback button_callback;
30 
31 struct led_indicator {
32 	const struct device *dev;
33 	gpio_pin_t pin;
34 };
35 
36 static uint32_t counter;
37 
38 /**
39  * @brief Callback for setting LED indicator state.
40  *
41  * @param value true if the LED indicator shall be turned on, false otherwise.
42  * @param arg argument that was passed when LEDs were initialized.
43  */
led_callback(bool value,void * arg)44 static void led_callback(bool value, void *arg)
45 {
46 	struct gpio_dt_spec *led_gpio = arg;
47 
48 	if (!led_gpio || !led_gpio->port) {
49 		return;
50 	}
51 
52 	gpio_pin_set_dt(led_gpio, value);
53 }
54 
55 /**
56  * @brief Configure LED indicators pins and callbacks.
57  *
58  * This routine configures the GPIOs for the red and green LEDs (if
59  * available).
60  *
61  * @param nmt CANopenNode NMT object.
62  */
config_leds(CO_NMT_t * nmt)63 static void config_leds(CO_NMT_t *nmt)
64 {
65 	int err;
66 
67 	if (!led_green_gpio.port) {
68 		LOG_INF("Green LED not available");
69 	} else if (!gpio_is_ready_dt(&led_green_gpio)) {
70 		LOG_ERR("Green LED device not ready");
71 		led_green_gpio.port = NULL;
72 	} else {
73 		err = gpio_pin_configure_dt(&led_green_gpio,
74 					    GPIO_OUTPUT_INACTIVE);
75 		if (err) {
76 			LOG_ERR("failed to configure Green LED gpio: %d", err);
77 			led_green_gpio.port = NULL;
78 		}
79 	}
80 
81 	if (!led_red_gpio.port) {
82 		LOG_INF("Red LED not available");
83 	} else if (!gpio_is_ready_dt(&led_red_gpio)) {
84 		LOG_ERR("Red LED device not ready");
85 		led_red_gpio.port = NULL;
86 	} else {
87 		err = gpio_pin_configure_dt(&led_red_gpio,
88 					    GPIO_OUTPUT_INACTIVE);
89 		if (err) {
90 			LOG_ERR("failed to configure Red LED gpio: %d", err);
91 			led_red_gpio.port = NULL;
92 		}
93 	}
94 
95 	canopen_leds_init(nmt,
96 			  led_callback, &led_green_gpio,
97 			  led_callback, &led_red_gpio);
98 }
99 
100 /**
101  * @brief Button press counter object dictionary handler function.
102  *
103  * This function is called upon SDO access to the button press counter
104  * object (index 0x2102) in the object dictionary.
105  *
106  * @param odf_arg object dictionary function argument.
107  *
108  * @return SDO abort code.
109  */
odf_2102(CO_ODF_arg_t * odf_arg)110 static CO_SDO_abortCode_t odf_2102(CO_ODF_arg_t *odf_arg)
111 {
112 	uint32_t value;
113 
114 	value = CO_getUint32(odf_arg->data);
115 
116 	if (odf_arg->reading) {
117 		return CO_SDO_AB_NONE;
118 	}
119 
120 	if (odf_arg->subIndex != 0U) {
121 		return CO_SDO_AB_NONE;
122 	}
123 
124 	if (value != 0) {
125 		/* Preserve old value */
126 		memcpy(odf_arg->data, odf_arg->ODdataStorage, sizeof(uint32_t));
127 		return CO_SDO_AB_DATA_TRANSF;
128 	}
129 
130 	LOG_INF("Resetting button press counter");
131 	counter = 0;
132 
133 	return CO_SDO_AB_NONE;
134 }
135 
136 /**
137  * @brief Button press interrupt callback.
138  *
139  * @param port GPIO device struct.
140  * @param cb GPIO callback struct.
141  * @param pins GPIO pin mask that triggered the interrupt.
142  */
button_isr_callback(const struct device * port,struct gpio_callback * cb,uint32_t pins)143 static void button_isr_callback(const struct device *port,
144 				struct gpio_callback *cb,
145 				uint32_t pins)
146 {
147 	counter++;
148 }
149 
150 /**
151  * @brief Configure button GPIO pin and callback.
152  *
153  * This routine configures the GPIO for the button (if available).
154  */
config_button(void)155 static void config_button(void)
156 {
157 	int err;
158 
159 	if (button_gpio.port == NULL) {
160 		LOG_INF("Button not available");
161 		return;
162 	}
163 
164 	if (!gpio_is_ready_dt(&button_gpio)) {
165 		LOG_ERR("Button device not ready");
166 		return;
167 	}
168 
169 	err = gpio_pin_configure_dt(&button_gpio, GPIO_INPUT);
170 	if (err) {
171 		LOG_ERR("failed to configure button gpio: %d", err);
172 		return;
173 	}
174 
175 	gpio_init_callback(&button_callback, button_isr_callback,
176 			   BIT(button_gpio.pin));
177 
178 	err = gpio_add_callback(button_gpio.port, &button_callback);
179 	if (err) {
180 		LOG_ERR("failed to add button callback: %d", err);
181 		return;
182 	}
183 
184 	err = gpio_pin_interrupt_configure_dt(&button_gpio,
185 					      GPIO_INT_EDGE_TO_ACTIVE);
186 	if (err) {
187 		LOG_ERR("failed to enable button callback: %d", err);
188 		return;
189 	}
190 }
191 
192 /**
193  * @brief Main application entry point.
194  *
195  * The main application thread is responsible for initializing the
196  * CANopen stack and doing the non real-time processing.
197  */
main(void)198 int main(void)
199 {
200 	CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
201 	CO_ReturnError_t err;
202 	struct canopen_context can;
203 	uint16_t timeout;
204 	uint32_t elapsed;
205 	int64_t timestamp;
206 #ifdef CONFIG_CANOPENNODE_STORAGE
207 	int ret;
208 #endif /* CONFIG_CANOPENNODE_STORAGE */
209 
210 	can.dev = CAN_INTERFACE;
211 	if (!device_is_ready(can.dev)) {
212 		LOG_ERR("CAN interface not ready");
213 		return 0;
214 	}
215 
216 #ifdef CONFIG_CANOPENNODE_STORAGE
217 	ret = settings_subsys_init();
218 	if (ret) {
219 		LOG_ERR("failed to initialize settings subsystem (err = %d)",
220 			ret);
221 		return 0;
222 	}
223 
224 	ret = settings_load();
225 	if (ret) {
226 		LOG_ERR("failed to load settings (err = %d)", ret);
227 		return 0;
228 	}
229 #endif /* CONFIG_CANOPENNODE_STORAGE */
230 
231 	OD_powerOnCounter++;
232 
233 	config_button();
234 
235 	while (reset != CO_RESET_APP) {
236 		elapsed =  0U; /* milliseconds */
237 
238 		err = CO_init(&can, CONFIG_CANOPEN_NODE_ID, CAN_BITRATE);
239 		if (err != CO_ERROR_NO) {
240 			LOG_ERR("CO_init failed (err = %d)", err);
241 			return 0;
242 		}
243 
244 		LOG_INF("CANopen stack initialized");
245 
246 #ifdef CONFIG_CANOPENNODE_STORAGE
247 		canopen_storage_attach(CO->SDO[0], CO->em);
248 #endif /* CONFIG_CANOPENNODE_STORAGE */
249 
250 		config_leds(CO->NMT);
251 		CO_OD_configure(CO->SDO[0], OD_2102_buttonPressCounter,
252 				odf_2102, NULL, 0U, 0U);
253 
254 		if (IS_ENABLED(CONFIG_CANOPENNODE_PROGRAM_DOWNLOAD)) {
255 			canopen_program_download_attach(CO->NMT, CO->SDO[0],
256 							CO->em);
257 		}
258 
259 		CO_CANsetNormalMode(CO->CANmodule[0]);
260 
261 		while (true) {
262 			timeout = 1U; /* default timeout in milliseconds */
263 			timestamp = k_uptime_get();
264 			reset = CO_process(CO, (uint16_t)elapsed, &timeout);
265 
266 			if (reset != CO_RESET_NOT) {
267 				break;
268 			}
269 
270 			if (timeout > 0) {
271 				CO_LOCK_OD();
272 				OD_buttonPressCounter = counter;
273 				CO_UNLOCK_OD();
274 
275 #ifdef CONFIG_CANOPENNODE_STORAGE
276 				ret = canopen_storage_save(
277 					CANOPEN_STORAGE_EEPROM);
278 				if (ret) {
279 					LOG_ERR("failed to save EEPROM");
280 				}
281 #endif /* CONFIG_CANOPENNODE_STORAGE */
282 				/*
283 				 * Try to sleep for as long as the
284 				 * stack requested and calculate the
285 				 * exact time elapsed.
286 				 */
287 				k_sleep(K_MSEC(timeout));
288 				elapsed = (uint32_t)k_uptime_delta(&timestamp);
289 			} else {
290 				/*
291 				 * Do not sleep, more processing to be
292 				 * done by the stack.
293 				 */
294 				elapsed = 0U;
295 			}
296 		}
297 
298 		if (reset == CO_RESET_COMM) {
299 			LOG_INF("Resetting communication");
300 		}
301 	}
302 
303 	LOG_INF("Resetting device");
304 
305 	CO_delete(&can);
306 	sys_reboot(SYS_REBOOT_COLD);
307 }
308