/* * Copyright (c) 2019 Vestas Wind Systems A/S * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr/kernel.h> #include <zephyr/drivers/gpio.h> #include <zephyr/sys/reboot.h> #include <zephyr/settings/settings.h> #include <canopennode.h> #define LOG_LEVEL CONFIG_CANOPEN_LOG_LEVEL #include <zephyr/logging/log.h> LOG_MODULE_REGISTER(app); #define CAN_INTERFACE DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus)) #define CAN_BITRATE (DT_PROP_OR(DT_CHOSEN(zephyr_canbus), bitrate, \ DT_PROP_OR(DT_CHOSEN(zephyr_canbus), bus_speed, \ CONFIG_CAN_DEFAULT_BITRATE)) / 1000) static struct gpio_dt_spec led_green_gpio = GPIO_DT_SPEC_GET_OR( DT_ALIAS(green_led), gpios, {0}); static struct gpio_dt_spec led_red_gpio = GPIO_DT_SPEC_GET_OR( DT_ALIAS(red_led), gpios, {0}); static struct gpio_dt_spec button_gpio = GPIO_DT_SPEC_GET_OR( DT_ALIAS(sw0), gpios, {0}); static struct gpio_callback button_callback; struct led_indicator { const struct device *dev; gpio_pin_t pin; }; static uint32_t counter; /** * @brief Callback for setting LED indicator state. * * @param value true if the LED indicator shall be turned on, false otherwise. * @param arg argument that was passed when LEDs were initialized. */ static void led_callback(bool value, void *arg) { struct gpio_dt_spec *led_gpio = arg; if (!led_gpio || !led_gpio->port) { return; } gpio_pin_set_dt(led_gpio, value); } /** * @brief Configure LED indicators pins and callbacks. * * This routine configures the GPIOs for the red and green LEDs (if * available). * * @param nmt CANopenNode NMT object. */ static void config_leds(CO_NMT_t *nmt) { int err; if (!led_green_gpio.port) { LOG_INF("Green LED not available"); } else if (!gpio_is_ready_dt(&led_green_gpio)) { LOG_ERR("Green LED device not ready"); led_green_gpio.port = NULL; } else { err = gpio_pin_configure_dt(&led_green_gpio, GPIO_OUTPUT_INACTIVE); if (err) { LOG_ERR("failed to configure Green LED gpio: %d", err); led_green_gpio.port = NULL; } } if (!led_red_gpio.port) { LOG_INF("Red LED not available"); } else if (!gpio_is_ready_dt(&led_red_gpio)) { LOG_ERR("Red LED device not ready"); led_red_gpio.port = NULL; } else { err = gpio_pin_configure_dt(&led_red_gpio, GPIO_OUTPUT_INACTIVE); if (err) { LOG_ERR("failed to configure Red LED gpio: %d", err); led_red_gpio.port = NULL; } } canopen_leds_init(nmt, led_callback, &led_green_gpio, led_callback, &led_red_gpio); } /** * @brief Button press counter object dictionary handler function. * * This function is called upon SDO access to the button press counter * object (index 0x2102) in the object dictionary. * * @param odf_arg object dictionary function argument. * * @return SDO abort code. */ static CO_SDO_abortCode_t odf_2102(CO_ODF_arg_t *odf_arg) { uint32_t value; value = CO_getUint32(odf_arg->data); if (odf_arg->reading) { return CO_SDO_AB_NONE; } if (odf_arg->subIndex != 0U) { return CO_SDO_AB_NONE; } if (value != 0) { /* Preserve old value */ memcpy(odf_arg->data, odf_arg->ODdataStorage, sizeof(uint32_t)); return CO_SDO_AB_DATA_TRANSF; } LOG_INF("Resetting button press counter"); counter = 0; return CO_SDO_AB_NONE; } /** * @brief Button press interrupt callback. * * @param port GPIO device struct. * @param cb GPIO callback struct. * @param pins GPIO pin mask that triggered the interrupt. */ static void button_isr_callback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { counter++; } /** * @brief Configure button GPIO pin and callback. * * This routine configures the GPIO for the button (if available). */ static void config_button(void) { int err; if (button_gpio.port == NULL) { LOG_INF("Button not available"); return; } if (!gpio_is_ready_dt(&button_gpio)) { LOG_ERR("Button device not ready"); return; } err = gpio_pin_configure_dt(&button_gpio, GPIO_INPUT); if (err) { LOG_ERR("failed to configure button gpio: %d", err); return; } gpio_init_callback(&button_callback, button_isr_callback, BIT(button_gpio.pin)); err = gpio_add_callback(button_gpio.port, &button_callback); if (err) { LOG_ERR("failed to add button callback: %d", err); return; } err = gpio_pin_interrupt_configure_dt(&button_gpio, GPIO_INT_EDGE_TO_ACTIVE); if (err) { LOG_ERR("failed to enable button callback: %d", err); return; } } /** * @brief Main application entry point. * * The main application thread is responsible for initializing the * CANopen stack and doing the non real-time processing. */ int main(void) { CO_NMT_reset_cmd_t reset = CO_RESET_NOT; CO_ReturnError_t err; struct canopen_context can; uint16_t timeout; uint32_t elapsed; int64_t timestamp; #ifdef CONFIG_CANOPENNODE_STORAGE int ret; #endif /* CONFIG_CANOPENNODE_STORAGE */ can.dev = CAN_INTERFACE; if (!device_is_ready(can.dev)) { LOG_ERR("CAN interface not ready"); return 0; } #ifdef CONFIG_CANOPENNODE_STORAGE ret = settings_subsys_init(); if (ret) { LOG_ERR("failed to initialize settings subsystem (err = %d)", ret); return 0; } ret = settings_load(); if (ret) { LOG_ERR("failed to load settings (err = %d)", ret); return 0; } #endif /* CONFIG_CANOPENNODE_STORAGE */ OD_powerOnCounter++; config_button(); while (reset != CO_RESET_APP) { elapsed = 0U; /* milliseconds */ err = CO_init(&can, CONFIG_CANOPEN_NODE_ID, CAN_BITRATE); if (err != CO_ERROR_NO) { LOG_ERR("CO_init failed (err = %d)", err); return 0; } LOG_INF("CANopen stack initialized"); #ifdef CONFIG_CANOPENNODE_STORAGE canopen_storage_attach(CO->SDO[0], CO->em); #endif /* CONFIG_CANOPENNODE_STORAGE */ config_leds(CO->NMT); CO_OD_configure(CO->SDO[0], OD_2102_buttonPressCounter, odf_2102, NULL, 0U, 0U); if (IS_ENABLED(CONFIG_CANOPENNODE_PROGRAM_DOWNLOAD)) { canopen_program_download_attach(CO->NMT, CO->SDO[0], CO->em); } CO_CANsetNormalMode(CO->CANmodule[0]); while (true) { timeout = 1U; /* default timeout in milliseconds */ timestamp = k_uptime_get(); reset = CO_process(CO, (uint16_t)elapsed, &timeout); if (reset != CO_RESET_NOT) { break; } if (timeout > 0) { CO_LOCK_OD(); OD_buttonPressCounter = counter; CO_UNLOCK_OD(); #ifdef CONFIG_CANOPENNODE_STORAGE ret = canopen_storage_save( CANOPEN_STORAGE_EEPROM); if (ret) { LOG_ERR("failed to save EEPROM"); } #endif /* CONFIG_CANOPENNODE_STORAGE */ /* * Try to sleep for as long as the * stack requested and calculate the * exact time elapsed. */ k_sleep(K_MSEC(timeout)); elapsed = (uint32_t)k_uptime_delta(×tamp); } else { /* * Do not sleep, more processing to be * done by the stack. */ elapsed = 0U; } } if (reset == CO_RESET_COMM) { LOG_INF("Resetting communication"); } } LOG_INF("Resetting device"); CO_delete(&can); sys_reboot(SYS_REBOOT_COLD); }