From c5d00d7b5570760420d0a7bf33341e2f23711cbc Mon Sep 17 00:00:00 2001 From: Victor Chavez Date: Sat, 25 May 2024 17:28:41 +0200 Subject: [PATCH] initial draft for alpha version --- CMakeLists.txt | 2 + include/iolm/iolm.h | 7 + include/iolm/transciever.h | 1 + include/iolm/utils.h | 20 + samples/buzzer/CMakeLists.txt | 13 + samples/read_pd/CMakeLists.txt | 10 + samples/read_pd/README.md | 21 + .../esp32_devkitc_wrover_procpu.overlay | 16 + .../boards/nrf52833dk_nrf52833.overlay | 39 ++ samples/read_pd/prj.conf | 13 + samples/read_pd/src/main.c | 14 + src/iolm.c | 602 ++++++++++++++++++ src/utils.c | 89 +++ 13 files changed, 847 insertions(+) create mode 100644 include/iolm/iolm.h create mode 100644 include/iolm/utils.h create mode 100644 samples/buzzer/CMakeLists.txt create mode 100644 samples/read_pd/CMakeLists.txt create mode 100644 samples/read_pd/README.md create mode 100644 samples/read_pd/boards/esp32_devkitc_wrover_procpu.overlay create mode 100644 samples/read_pd/boards/nrf52833dk_nrf52833.overlay create mode 100644 samples/read_pd/prj.conf create mode 100644 samples/read_pd/src/main.c create mode 100644 src/iolm.c create mode 100644 src/utils.c diff --git a/CMakeLists.txt b/CMakeLists.txt index df19c14..e9a2464 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,8 @@ if(CONFIG_IOLINK) ${STACK_SRC}/iolink_sm.c src/osal/osal.c src/osal/osal_log.c + src/iolm.c + src/utils.c src/transciever.c) #zephyr_library_sources_ifdef(CONFIG_XX xx) diff --git a/include/iolm/iolm.h b/include/iolm/iolm.h new file mode 100644 index 0000000..243c9fc --- /dev/null +++ b/include/iolm/iolm.h @@ -0,0 +1,7 @@ +/* Copyright (c) 2024 Victor Chavez + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#pragma once + + +int iolm_init(void); \ No newline at end of file diff --git a/include/iolm/transciever.h b/include/iolm/transciever.h index c0f8715..b22b535 100644 --- a/include/iolm/transciever.h +++ b/include/iolm/transciever.h @@ -2,6 +2,7 @@ SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once +#include #include #include diff --git a/include/iolm/utils.h b/include/iolm/utils.h new file mode 100644 index 0000000..2cdf94d --- /dev/null +++ b/include/iolm/utils.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +/** + * @brief Decode IOL-Device cycle time + * @details See IO-Link v1.1.3 Table B.15 + * + * @param encoded_time Encoded Cycle Time + * @return uint32_t Cycle time in microseconds + */ +uint32_t cyctime_decode_us(uint8_t encoded_time); + +/** + * @brief Encode IOL-Device cycle time + * @details See IO-Link v1.1.3 Table B.15 + * @param CycleTimeUs Cycle time in microseconds + * @return uint8_t Encoded Cycle Time according to spec + */ +uint8_t cyctime_encode(uint32_t cycletime_us); \ No newline at end of file diff --git a/samples/buzzer/CMakeLists.txt b/samples/buzzer/CMakeLists.txt new file mode 100644 index 0000000..5520232 --- /dev/null +++ b/samples/buzzer/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Victor Chavez +# SPDX-License-Identifier: GPL-3.0-or-later + +cmake_minimum_required(VERSION 3.20.0) +set(IOLINK_MODULE ${CMAKE_CURRENT_SOURCE_DIR}/../..) +set(ZEPHYR_EXTRA_MODULES ${IOLINK_MODULE}) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(iolm_demo) + +target_sources(app PRIVATE src/iolink_smi.c + src/iolink_handler.c + src/demo.c +) diff --git a/samples/read_pd/CMakeLists.txt b/samples/read_pd/CMakeLists.txt new file mode 100644 index 0000000..72f429f --- /dev/null +++ b/samples/read_pd/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Victor Chavez +# SPDX-License-Identifier: GPL-3.0-or-later + +cmake_minimum_required(VERSION 3.20.0) +set(IOLINK_MODULE ${CMAKE_CURRENT_SOURCE_DIR}/../..) +set(ZEPHYR_EXTRA_MODULES ${IOLINK_MODULE}) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(iolm_demo) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/read_pd/README.md b/samples/read_pd/README.md new file mode 100644 index 0000000..b81dee7 --- /dev/null +++ b/samples/read_pd/README.md @@ -0,0 +1,21 @@ +# Zephyr IOLM Read Process Data Input + +A sample application that demonstrates how to read PDIn from an IO-Link device + + +## Build and Flash + +``` +git clone --recursive https://github.com/vChavezB/zephyr-iolm/ +cd YOUR_ZEPHYR_WEST_WORKSPACE +west build zephyr-iolm/samples/read_pd -b YOUR_BOARD +west flash +``` + +Where: + +- YOUR_ZEPHYR_WEST_WORKSPACE: Is the installation of your Zephyr SDK (i.e. `west init ..`). + Note: This has only been tested with Zephyr revision v3.6.99 commit `34c84eccec0508b16f5001b20fa369f387d856df` +- `YOUR_BOARD`: `esp32`, `nrf52833dk_nrf52833` + +## Expected Output diff --git a/samples/read_pd/boards/esp32_devkitc_wrover_procpu.overlay b/samples/read_pd/boards/esp32_devkitc_wrover_procpu.overlay new file mode 100644 index 0000000..8b2e6f3 --- /dev/null +++ b/samples/read_pd/boards/esp32_devkitc_wrover_procpu.overlay @@ -0,0 +1,16 @@ +&uart0 { + current-speed = <460800>; +}; + +&spi2 { + status = "okay"; + cs-gpios = <&gpio0 19 GPIO_ACTIVE_LOW>; + max:maxim14819@0 { + spi-max-frequency = <8000000>; + status = "okay"; + irq-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; + compatible = "iolm,maxim14819"; + reg = <0x00>; + chip-address = <0x00>; + }; +}; diff --git a/samples/read_pd/boards/nrf52833dk_nrf52833.overlay b/samples/read_pd/boards/nrf52833dk_nrf52833.overlay new file mode 100644 index 0000000..c505f6d --- /dev/null +++ b/samples/read_pd/boards/nrf52833dk_nrf52833.overlay @@ -0,0 +1,39 @@ +&uart0 { + current-speed = <460800>; +}; + +&pinctrl { + + spi1_default: spi1_default { + group1 { + psels = , + , + ; + }; + }; + + spi1_sleep: spi1_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + +//#include + +&spi1 { + status = "okay"; + pinctrl-names = "default", "sleep"; + cs-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; + max:maxim14819@0 { + spi-max-frequency = <10000000>; + status = "okay"; + irq-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>; + compatible = "iolm,maxim14819"; + reg = <0x00>; + chip-address = <0x00>; + }; +}; diff --git a/samples/read_pd/prj.conf b/samples/read_pd/prj.conf new file mode 100644 index 0000000..7ddd4ee --- /dev/null +++ b/samples/read_pd/prj.conf @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Victor Chavez +# SPDX-License-Identifier: GPL-3.0-or-later + +CONFIG_THREAD_NAME=y +CONFIG_LOG=y +CONFIG_IOLINK_LOG_LEVEL_DBG=y +CONFIG_ASSERT=y +CONFIG_MINIMAL_LIBC=y +#CONFIG_LOG_BUFFER_SIZE=16384 +CONFIG_IOLINK=y +CONFIG_IOLINK_MAIN_STACK_SIZE=1024 +CONFIG_IOLINK_DL_STACK_SIZE=1024 +CONFIG_IOLINK_NUM_PORTS=2 \ No newline at end of file diff --git a/samples/read_pd/src/main.c b/samples/read_pd/src/main.c new file mode 100644 index 0000000..72d0101 --- /dev/null +++ b/samples/read_pd/src/main.c @@ -0,0 +1,14 @@ +/* Copyright (c) 2024 Victor Chavez + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include +#include + +int main(void) +{ + int rc = iolm_init(); + if (rc != 0) { + printk("Failed to initialize IO-Link Master: %d\n",rc); + } + return rc; +} \ No newline at end of file diff --git a/src/iolm.c b/src/iolm.c new file mode 100644 index 0000000..bc58efb --- /dev/null +++ b/src/iolm.c @@ -0,0 +1,602 @@ + +/* Copyright (c) 2024 Victor Chavez + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include +#include +#include +#include +#include +#include +#include +#include + + +LOG_MODULE_DECLARE(iol_master, CONFIG_IOLINK_LOG_LEVEL); +/* +#include + +#include "osal_irq.h" + +#include "iolink.h" +#include "iolink_max14819.h" +#include "iolink_handler.h" + +*/ + +#define IOLINK_HANDLER_THREAD_STACK_SIZE (1024) +#define IOLINK_HANDLER_THREAD_PRIO CONFIG_IOLINK_MASTER_PRIO+1 +static K_THREAD_STACK_DEFINE(iolm_handler_stack, IOLINK_HANDLER_THREAD_STACK_SIZE); + +static iolink_port_cfg_t port_cfgs[CONFIG_IOLINK_NUM_PORTS]; +static iolink_pl_mode_t init_mode[] = +{ + iolink_mode_SDCI, + iolink_mode_INACTIVE, +}; + +static iolink_m_cfg_t iolink_cfg = { + .cb_arg = NULL, + .cb_smi = NULL, + .cb_pd = NULL, +}; + +static const char * port_status_msg [] = +{ + "No Device", + "Deactivated", + "Port Diagnostics", + "Preoperate", + "Operate", + "DI", + "DO", + "Power Off", + "Not Available" +}; + +static iolink_m_t * master; +// Retry comm timer, see figure 33 spec v1.1.3 +static struct k_timer tsd_timr[IOLINK_NUM_PORTS]; + +static const uint32_t SMI_PORT_CFG_TIMEOUT = 1000; + + +static arg_block_portstatuslist_t port_status[CONFIG_IOLINK_NUM_PORTS]; + +enum evt_type { + ComLost, + PortStatusChange, + PortStatusList, + PortCnf +}; + +typedef struct iolm_msgq_evt { + enum evt_type type; + uint8_t port_no; +}iolm_msgq_evt_t; + +#define MAX_MSGS 50 +#define MSG_TIMEOUT_MS 100 +K_MSGQ_DEFINE(iol_msgq, sizeof(iolm_msgq_evt_t), MAX_MSGS, 1); + +static void handle_smi_portevent (uint8_t port_no,diag_entry_t * event) +{ + iolm_msgq_evt_t msg_evt; + int rc; + + msg_evt.port_no = port_no ; + //uint8_t portnumber = app_port->portnumber; + //iolink_app_master_ctx_t * app_master = app_port->app_master; + //uint8_t port_index = portnumber - 1; + + + /* + if ( + ((app_port->type == GOLDEN) || (app_port->type == UNKNOWN)) && + event->event_code != IOLINK_EVENTCODE_NO_DEV) + */ + { + uint8_t event_index; + + //os_mutex_lock (app_port->event_mtx); + // event_index = app_port->events.count++; + //CC_ASSERT (event_index < PORT_EVENT_COUNT); + //app_port->events.diag_entry[event_index].event_qualifier = + // event->event_qualifier; + //app_port->events.diag_entry[event_index].event_code = event->event_code; + //os_mutex_unlock (app_port->event_mtx); + } + + /* + if (event->event_code != IOLINK_EVENTCODE_NO_DEV) + { + LOG_INFO ( + LOG_STATE_ON, + "%s (%d): type = %d, event_code = 0x%04X, count = %d\n", + __func__, + portnumber, + app_port->type, + event->event_code, + app_port->events.count); + } + */ + + switch (event->event_code) + { + case IOLINK_EVENTCODE_PORT_STATUS_CHANGE: + LOG_INF("Port [%u] status changed", port_no); + msg_evt.type = PortStatusChange; + rc = k_msgq_put(&iol_msgq, &msg_evt, K_MSEC(MSG_TIMEOUT_MS)); + if (rc != 0) { + LOG_ERR("Failed to queue evt %d", rc); + } + break; + case IOLINK_EVENTCODE_BAD_DID: + case IOLINK_EVENTCODE_BAD_VID: + //LOG_WARNING ( + // LOG_STATE_ON, + // "%s: Port %u: portevent bad vid/did.\n", + // __func__, + // portnumber); + /* Port status changed - Use SMI_PortStatus() for details */ + //os_event_set (app_master->app_event, (EVENT_PORTE_0 << port_index)); + break; + case IOLINK_EVENTCODE_NO_DEV: /* COMLOST */ + LOG_WRN("Port[%u]: COM Lost", port_no); + msg_evt.type = ComLost; + rc = k_msgq_put(&iol_msgq, &msg_evt, K_MSEC(MSG_TIMEOUT_MS)); + if (rc != 0) { + LOG_ERR("Failed to queue evt %d", rc); + } + break; + case IOLINK_EVENTCODE_BACKUP_INCON: + case IOLINK_EVENTCODE_BACKUP_INCON_SIZE: + case IOLINK_EVENTCODE_BACKUP_INCON_ID: + case IOLINK_EVENTCODE_BACKUP_INCON_UL: + case IOLINK_EVENTCODE_BACKUP_INCON_DL: + // TODO add evt + break; + default: + LOG_WRN ("Port[%u]: Unknown SMI_PortEvent code 0x%04x\n", port_no, event->event_code); + break; + } +} + + +static void SMI_cnf_cb (void * arg, + uint8_t portnumber, + iolink_arg_block_id_t ref_arg_block_id, + uint16_t arg_block_len, + arg_block_t * arg_block) +{ +/* + iolink_app_master_ctx_t * app_m = arg; + iolink_app_port_ctx_t * app_port = &app_m->app_port[portnumber - 1]; + + + + CC_ASSERT (arg_block != NULL); +*/ + + iolm_msgq_evt_t msg_evt; + msg_evt.port_no = portnumber; + int rc; + __ASSERT_NO_MSG(arg_block != NULL); + bool match_found = true; + switch (arg_block->id) { + case IOLINK_ARG_BLOCK_ID_JOB_ERROR: + //handle_smi_joberror (app_port, ref_arg_block_id, (arg_block_joberror_t *)arg_block); + //LOG_DBG ("%s: IOLINK_ARG_BLOCK_ID_JOB_ERROR\n", __func__); + break; + case IOLINK_ARG_BLOCK_ID_PORT_STATUS_LIST: + { + arg_block_portstatuslist_t * port_status_list = + (arg_block_portstatuslist_t *)arg_block; + uint8_t port_idx = portnumber - 1; + port_status[port_idx].port_status_info = port_status_list->port_status_info; + port_status[port_idx].port_quality_info = port_status_list->port_quality_info; + port_status[port_idx].revision_id = port_status_list->revision_id; + port_status[port_idx].transmission_rate = port_status_list->transmission_rate; + port_status[port_idx].master_cycle_time = port_status_list->master_cycle_time; + port_status[port_idx].vendorid = port_status_list->vendorid; + port_status[port_idx].deviceid = port_status_list->deviceid; + + msg_evt.type = PortStatusList; + rc = k_msgq_put(&iol_msgq, &msg_evt, K_MSEC(MSG_TIMEOUT_MS)); + if (rc != 0) { + LOG_ERR("Failed to queue evt %d", rc); + } + break; + } + case IOLINK_ARG_BLOCK_ID_PORT_EVENT:; + /* SMI_PortEvent_ind */ + arg_block_portevent_t * arg_block_portevent = (arg_block_portevent_t *)arg_block; + handle_smi_portevent (portnumber, &arg_block_portevent->event); + break; + case IOLINK_ARG_BLOCK_ID_DEV_EVENT: + // SMI_DeviceEvent_ind + //LOG_DEBUG (LOG_STATE_ON, "%s: IOLINK_ARG_BLOCK_ID_DEV_EVENT\n", __func__); + //handle_smi_deviceevent (app_port, (arg_block_devevent_t *)arg_block); + break; + case IOLINK_ARG_BLOCK_ID_OD_RD: + // SMI_DeviceRead_cnf + //LOG_DEBUG (LOG_STATE_ON, "%s: IOLINK_ARG_BLOCK_ID_OD_RD\n", __func__); + //app_port->param_read.data_len = arg_block_len - sizeof (arg_block_od_t); + //os_event_set (app_port->event, SMI_READ_CNF); + break; + case IOLINK_ARG_BLOCK_ID_PD_IN: + // SMI_PDIn_cnf + //arg_block_pdin_t * arg_block_pdin = (arg_block_pdin_t *)arg_block; + //memcpy (app_port->pdin.data, arg_block_pdin->data, arg_block_pdin->h.len); + //app_port->pdin.data_len = arg_block_pdin->h.len; + //app_port->pdin.pqi = arg_block_pdin->h.port_qualifier_info; + //os_event_set (iolink_app_master.app_event, (SMI_PDIN_CNF << portnumber-1)); + break; + case IOLINK_ARG_BLOCK_ID_PD_IN_OUT: + // SMI_PDInOut_cnf + //LOG_DEBUG (LOG_STATE_ON, "%s: IOLINK_ARG_BLOCK_ID_PD_IN_OUT\n", __func__); + break; + case IOLINK_ARG_BLOCK_ID_MASTERIDENT:; + // SMI_MasterIdentification_cnf() + //arg_block_masterident_t * arg_block_masterident = (arg_block_masterident_t *)arg_block; + //LOG_DEBUG (LOG_STATE_ON, "%s: IOLINK_ARG_BLOCK_ID_MASTERIDENT\n", __func__); + //app_m->vendorid = arg_block_masterident->h.vendorid; + //app_m->masterid = arg_block_masterident->h.masterid; + //os_event_set (app_port->event, SMI_MASTERIDENT_CNF); + break; + default: + match_found = false; + break; + } + + if (!match_found) + { + switch (ref_arg_block_id) { + case IOLINK_ARG_BLOCK_ID_PD_OUT: + // SMI_DeviceWrite_cnf + //LOG_DEBUG (LOG_STATE_ON, "%s: IOLINK_ARG_BLOCK_ID_PD_OUT\n", __func__); + break; + case IOLINK_ARG_BLOCK_ID_DS_DATA: + // SMI_ParServToDS_cnf + //LOG_DEBUG (LOG_STATE_ON, "%s: IOLINK_ARG_BLOCK_ID_DS_DATA\n", __func__); + //os_event_set (app_port->event, SMI_PARSERV_TO_DS_CNF); + break; + case IOLINK_ARG_BLOCK_ID_OD_WR: + // SMI_DeviceWrite_cnf + //LOG_DEBUG (LOG_STATE_ON, "%s: IOLINK_ARG_BLOCK_ID_OD_WR\n", __func__); + //os_event_set (app_port->event, SMI_WRITE_CNF); + break; + case IOLINK_ARG_BLOCK_ID_PORT_CFG_LIST: + // SMI_PortConfiguration_cnf + //LOG_DEBUG ( + // LOG_STATE_ON, + // "%s: IOLINK_ARG_BLOCK_ID_PORT_CFG_LIST\n", + // __func__); + msg_evt.type = PortCnf; + rc = k_msgq_put(&iol_msgq, &msg_evt, K_MSEC(MSG_TIMEOUT_MS)); + if (rc != 0) { + LOG_ERR("Failed to queue evt %d", rc); + } + break; + default: + /*LOG_WARNING ( + LOG_STATE_ON, + "%s: Port %u: Unexpected ref_arg_block_id 0x%04x and arg_block_id " + "0x%04x\n", + __func__, + portnumber, + ref_arg_block_id, + arg_block->id); + */ + break; + } + } +} + + +static void PD_cb ( + uint8_t portnumber, + void * arg, + uint8_t data_len, + const uint8_t * inputdata) +{ + /* + uint8_t port_index = portnumber - 1; + iolink_app_master_ctx_t * app_master = (iolink_app_master_ctx_t *)arg; + iolink_app_port_ctx_t * app_port = &iolink_app_master.app_port[port_index]; + + os_event_set (app_master->app_event, EVENT_PD_0 << port_index); + memcpy (app_port->pdin.data, inputdata, data_len); + app_port->pdin.data_len = data_len; + */ +} + +struct iol_port { + +}; + + +struct k_thread hdl_thread; + + +static void iolm_hdl_thread(void * p1, void * p2, void * p3); + + +static void iolink_common_config ( + arg_block_portconfiglist_t * port_cfg, + uint16_t vid, + uint32_t did, + uint8_t cycle_time, + iolink_portmode_t portmode, + iolink_validation_check_t validation) +{ + port_cfg->arg_block.id = IOLINK_ARG_BLOCK_ID_PORT_CFG_LIST; + port_cfg->configlist.port_cycle_time = cycle_time; + port_cfg->configlist.vendorid = vid; + port_cfg->configlist.deviceid = did; + port_cfg->configlist.portmode = portmode; + port_cfg->configlist.validation_backup = validation; + port_cfg->configlist.iq_behavior = IOLINK_IQ_BEHAVIOR_NO_SUPPORT; +} + +/** + * @brief Blocking wait for smi cnf + * + * @param evt_mask + * @param ms + * @return iolink_smi_errortypes_t + */ +iolink_smi_errortypes_t wait_smi_cnf (enum evt_type wait_evt, uint32_t ms) +{ + iolm_msgq_evt_t msg_evt; + iolink_smi_errortypes_t errortype = IOLINK_SMI_ERRORTYPE_NONE; + + /* Wait for SMI_xxx_cnf() */ + if(k_msgq_get(&iol_msgq, &msg_evt, K_MSEC(ms)) < 0) { + return IOLINK_SMI_ERRORTYPE_APP_DEV; + } + if (msg_evt.type != wait_evt) { + return IOLINK_SMI_ERRORTYPE_APP_DEV; + } + + /* + if ((evt_mask == SMI_WRITE_CNF) || (evt_mask == SMI_PARSERV_TO_DS_CNF)) + { + errortype = app_port->errortype; + + if (errortype != IOLINK_SMI_ERRORTYPE_NONE) + { + app_port->errortype = IOLINK_SMI_ERRORTYPE_NONE; + } + } + */ + return errortype; +} + + +static uint8_t iolink_config_port_sdci_auto(uint8_t port_no) +{ + arg_block_portconfiglist_t port_cfg; + + iolink_common_config ( + &port_cfg, + 0, + 0, + 0 /* AFAP (As fast as possible) */, // TODO ADD API to set Cycle time and encode it + IOLINK_PORTMODE_IOL_AUTO, + IOLINK_VALIDATION_CHECK_NO); + + iolink_error_t err = SMI_PortConfiguration_req ( + port_no+1, + IOLINK_ARG_BLOCK_ID_VOID_BLOCK, + sizeof (arg_block_portconfiglist_t), + (arg_block_t *)&port_cfg); + + + if ( (err != IOLINK_ERROR_NONE) || + (wait_smi_cnf ( PortCnf, SMI_PORT_CFG_TIMEOUT) != IOLINK_SMI_ERRORTYPE_NONE)) + { + return 1; + } + + return 0; +} + +static uint8_t iolink_config_port_dido (uint8_t portno, bool di) +{ + arg_block_portconfiglist_t port_cfg; + + memset (&port_cfg, 0, sizeof (arg_block_portconfiglist_t)); + + iolink_common_config ( + &port_cfg, + 0, + 0, + 0 /* AFAP (As fast as possible) */, + (di) ? IOLINK_PORTMODE_DI_CQ : IOLINK_PORTMODE_DO_CQ, + (di) ? IOLINK_IQ_BEHAVIOR_DI : IOLINK_IQ_BEHAVIOR_DO); + + iolink_error_t err = SMI_PortConfiguration_req ( + portno+1, + IOLINK_ARG_BLOCK_ID_VOID_BLOCK, + sizeof (arg_block_portconfiglist_t), + (arg_block_t *)&port_cfg); + + if ( (err != IOLINK_ERROR_NONE) || + (wait_smi_cnf (PortCnf, SMI_PORT_CFG_TIMEOUT) != IOLINK_SMI_ERRORTYPE_NONE)) + { + return 1; + } + + return 0; +} + +static uint8_t iolink_config_port (uint8_t port_no, + iolink_pl_mode_t port_mode) +{ + uint8_t res = 0; + switch (port_mode) + { + case iolink_mode_SDCI: + res = iolink_config_port_sdci_auto (port_no); + break; + case iolink_mode_DO: + res = iolink_config_port_dido (port_no, false); + break; + case iolink_mode_DI: + res = iolink_config_port_dido (port_no, true); + break; + case iolink_mode_INACTIVE: + break; + } + return res; +} + +int iolm_init(){ + for (int i = 0; i < CONFIG_IOLINK_NUM_PORTS; i++) { + iolink_hw_drv_t * drv = get_drv(i); + if (drv == NULL) { + LOG_ERR("No driver found for port %d", i); + return -ENODEV; + } + port_cfgs[i].drv = drv; + port_cfgs[i].name = "dummy"; // not used in stack at all + port_cfgs[i].mode = &init_mode[0]; + const uint8_t ch_no = i%TRANSCIEVER_MAX_PORTS; + port_cfgs[i].arg = (void*)(ch_no); + } + + iolink_cfg.port_cnt = CONFIG_IOLINK_NUM_PORTS; + iolink_cfg.port_cfgs = port_cfgs; + + iolink_m_cfg_t m_cfg = { + .port_cnt = CONFIG_IOLINK_NUM_PORTS, + .port_cfgs = port_cfgs, + .cb_arg = NULL, + .cb_smi = SMI_cnf_cb, + .cb_pd = PD_cb + }; + + master = iolink_m_init (&m_cfg); + if (master == NULL) { + LOG_ERR("iolink_m_init() failed!"); + return -ENODEV; + } + + for (uint8_t port_no = 0; port_no < CONFIG_IOLINK_NUM_PORTS; port_no++) { + k_timer_init(&tsd_timr[port_no], NULL, NULL); + } + + for (uint8_t port_no = 0; port_no < CONFIG_IOLINK_NUM_PORTS; port_no++) { + iolink_port_t * port = iolink_get_port (master, port_no); + // Note priority and stack are statically defined in Kconfig + // see CONFIG_IOLINK_DL_STACK_SIZE, + iolink_dl_instantiate (port,0,0); + + // TODO Add API for initial config of port + uint8_t port_res = iolink_config_port (port_no, iolink_mode_SDCI); + if (port_res != 0) { + LOG_ERR("Port %d config failed", port_no); + return -EIO; + } + } + + k_thread_create(&hdl_thread, + iolm_handler_stack, + K_THREAD_STACK_SIZEOF(iolm_handler_stack), + iolm_hdl_thread, + NULL,NULL,NULL, + CONFIG_IOLINK_MASTER_PRIO+1, + 0, // thread options + K_NO_WAIT); // start immediately + return 0; +} + +void iolm_hdl_thread(void * p1, void * p2, void * p3) { + while(true) { + iolm_msgq_evt_t evt; + + k_msgq_get(&iol_msgq, &evt, K_FOREVER); + + + switch(evt.type) { + case ComLost: + // handle com lost + // send event to user + LOG_WRN("Port[%u]: COM Lost", evt.port_no); + break; + case PortStatusChange: + { + arg_block_void_t arg_block_void; + + memset (&arg_block_void, 0, sizeof (arg_block_void_t)); + arg_block_void.arg_block.id = IOLINK_ARG_BLOCK_ID_VOID_BLOCK; + + iolink_error_t err = SMI_PortStatus_req ( + evt.port_no, + IOLINK_ARG_BLOCK_ID_PORT_STATUS_LIST, + sizeof (arg_block_void_t), + (arg_block_t *)&arg_block_void); + if (err != IOLINK_ERROR_NONE) { + LOG_ERR("SMI_PortStatus_req failed: %d", err); + } + break; + } + case PortCnf: + // notify user of port configuration success + break; + case PortStatusList: + // handle port status list + // Inform user of change in port status + const iolink_port_status_info_t status = port_status[evt.port_no].port_status_info; + uint8_t msg_idx = status; + if (msg_idx >= IOLINK_PORT_STATUS_INFO_POWER_OFF) { + // Last two status are at the end + msg_idx -= IOLINK_PORT_STATUS_INFO_POWER_OFF; + msg_idx += (IOLINK_PORT_STATUS_INFO_DO + 1); + } + LOG_INF("Port [%u] status changed to %s", + evt.port_no, port_status_msg[status]); + break; + } + } + + /* + if (((EVENT_RETRY_ESTCOM_0 << i) & event_value) != 0) + { + if (app_port->app_port_state == IOL_STATE_WU_RETRY_WAIT_TSD) + { + iolink_dl_reset (iolink_get_port ( + iolink_app_master.master, + app_port->portnumber)); + if (iolink_config_port (app_port, port_mode[i]) != 0) + { + LOG_WARNING ( + LOG_STATE_ON, + "%s: Failed to config port %lu\n", + __func__, + i + 1); + } + } + } + + if ((EVT_COMLOST << port) & evt) { + k_timer_stop(&tsd_timr[port]); + //if (app_port->app_port_state == IOL_STATE_STARTING) + { + /* Wait 500ms before sending new WURQ + k_timer_start(&tsd_timr[port], K_MSEC(500), K_NO_WAIT); + } + //else if (app_port->app_port_state != IOL_STATE_STOPPING) + { + /* Send WURQ immediately + //iolink_retry_estcom (NULL, (void *)i); + // retry_estcom will trigger EVENT_RETRY_ESTCOM_0 + } + //else // (app_port->app_port_state == IOL_STATE_STOPPING) + { + //app_port->app_port_state = IOL_STATE_INACTIVE; + } + } + */ +} \ No newline at end of file diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..0495073 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,89 @@ +#include + +#define CYC_TIME_ENCODINGS 3 + +static uint8_t time_base_offset = 6; +static uint8_t RANGE_MIN_IDX = 0; +static uint8_t RANGE_MAX_IDX = 1; +static uint8_t MULT_MASKING = 0x3F; /* Multiplier masking */ + +static uint8_t MAX_CYC_MULT=63; /* Max Cycletime multiplier factor*/ + +/* Cycle time ranges according to IOLink Spec */ +static const uint32_t RangeUs[CYC_TIME_ENCODINGS][2]= +{ + {400,6300}, + {6400,31600}, + {32000,132800} +}; + +const uint16_t TimeBaseUs[CYC_TIME_ENCODINGS]={100,400,1600}; +const uint16_t TimeOffsetUs[CYC_TIME_ENCODINGS]={0,6400,32000}; + +static inline uint32_t calculate_cyctime(uint8_t mult,uint8_t encode_idx) +{ + return (TimeOffsetUs[encode_idx] + (TimeBaseUs[encode_idx]*mult)); +} + +uint32_t cyctime_decode_us(uint8_t encoded_time) +{ + const uint8_t time_base_idx = encoded_time >> time_base_offset; + const uint8_t multiplier = encoded_time & 0x3F; + return calculate_cyctime(multiplier,time_base_idx); +} + + +uint8_t cyctime_encode(uint32_t CycleTimeUs) +{ + uint8_t encoded_cycle_time=0; + do { + if(CycleTimeUs <= RangeUs[0][RANGE_MIN_IDX]) { + encoded_cycle_time= 0x04; //Minimum of 0.4 ms cycle time + break; + } + if(CycleTimeUs >= RangeUs[2][RANGE_MAX_IDX]) { + //Maximum 132.8ms cycle time + encoded_cycle_time= 0xBF; + break; + } + + uint8_t encoding_idx=0xFF; + for(uint8_t i=0;i=RangeUs[i][RANGE_MIN_IDX] + && CycleTimeUs<=RangeUs[i][RANGE_MAX_IDX]) { + encoding_idx=i; + break; + } + if(i != (CYC_TIME_ENCODINGS-1)) { + /* if cycle time between two ranges */ + if(CycleTimeUs cyc1 && CycleTimeUs< cyc2) /* If not within the range */ + ||CycleTimeUs == cyc2) { /* or equals next cycle time */ + required_mult = mult+1; + break; + } + } + + encoded_cycle_time= encoding_idx<