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