File syn_iolink.c¶
File List > proto > syn_iolink.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_IOLINK) || SYN_USE_IOLINK
#include "../port/syn_port_system.h"
#include "syn_iolink.h"
#include <string.h>
#define SYN_IOLINK_DEFAULT_CYCLE_MS 5U
#define SYN_IOLINK_CRC6_POLY 0x3DU
#define SYN_IOLINK_CRC6_SEED 0x1FU
/* ── Checksum & CRC Calculation ─────────────────────────────────────────── */
uint8_t syn_iolink_calc_checksum(const uint8_t *data, size_t len)
{
if (data == NULL || len == 0U) {
return 0U;
}
uint8_t crc = SYN_IOLINK_CRC6_SEED;
for (size_t i = 0U; i < len; i++) {
crc ^= data[i];
for (uint8_t bit = 0U; bit < 8U; bit++) {
if ((crc & 0x80U) != 0U) {
crc = (uint8_t)((crc << 1U) ^ SYN_IOLINK_CRC6_POLY);
} else {
crc = (uint8_t)(crc << 1U);
}
}
}
crc = (crc >> 2U) & 0x3FU; /* 6-bit CRC in bits 0..5 */
/* Calculate parity bits: bit 7 = even parity, bit 6 = odd parity */
uint8_t p0 = (crc ^ (crc >> 2U) ^ (crc >> 4U)) & 0x01U;
uint8_t p1 = ~((crc >> 1U) ^ (crc >> 3U) ^ (crc >> 5U)) & 0x01U;
return (uint8_t)(crc | (p1 << 6U) | (p0 << 7U));
}
/* ── Master Implementation ───────────────────────────────────────────────── */
SYN_Status syn_iolink_master_init(SYN_IOLink_Master *master, const SYN_IOLink_MasterConfig *cfg)
{
if (master == NULL || cfg == NULL) {
return SYN_INVALID_PARAM;
}
if (cfg->transport == NULL || cfg->rx_buf == NULL || cfg->tx_buf == NULL ||
cfg->rx_buf_size < 32U || cfg->tx_buf_size < 32U) {
return SYN_INVALID_PARAM;
}
(void)memset(master, 0, sizeof(*master));
master->cfg = *cfg;
if (master->cfg.cycle_time_ms == 0U) {
master->cfg.cycle_time_ms = SYN_IOLINK_DEFAULT_CYCLE_MS;
}
master->state = SYN_IOLINK_PORT_INACTIVE;
master->mseq_type = SYN_IOLINK_TYPE_0;
return SYN_OK;
}
static SYN_Status master_read_direct_param(SYN_IOLink_Master *master, uint8_t addr,
uint8_t *out_val)
{
uint8_t tx[3];
tx[0] = (uint8_t)(0x80U | (addr & 0x3FU)); /* Read direct parameter (Bit 7=1, Bit 6=0) */
tx[1] = syn_iolink_calc_checksum(&tx[0], 1U);
if (!syn_transport_send(master->cfg.transport, tx, 2U)) {
return SYN_ERROR;
}
size_t rx_len = 0U;
if (!syn_transport_recv(master->cfg.transport, master->cfg.rx_buf, master->cfg.rx_buf_size,
&rx_len) ||
rx_len < 2U) {
return SYN_ERROR;
}
uint8_t expected_cks = syn_iolink_calc_checksum(master->cfg.rx_buf, rx_len - 1U);
if (master->cfg.rx_buf[rx_len - 1U] != expected_cks) {
return SYN_ERROR;
}
if (out_val != NULL) {
*out_val = master->cfg.rx_buf[0];
}
return SYN_OK;
}
SYN_Status syn_iolink_master_start(SYN_IOLink_Master *master)
{
if (master == NULL || master->cfg.transport == NULL) {
return SYN_INVALID_PARAM;
}
master->state = SYN_IOLINK_PORT_STARTUP;
/* 1. Read Minimum Cycle Time */
uint8_t min_cycle = 0U;
if (master_read_direct_param(master, SYN_IOLINK_PAGE_MIN_CYCLE_TIME, &min_cycle) != SYN_OK) {
master->state = SYN_IOLINK_PORT_FAULT;
return SYN_ERROR;
}
master->dev_params.min_cycle_time = min_cycle;
/* 2. Read Revision ID */
uint8_t rev_id = 0U;
if (master_read_direct_param(master, SYN_IOLINK_PAGE_REVISION_ID, &rev_id) != SYN_OK) {
master->state = SYN_IOLINK_PORT_FAULT;
return SYN_ERROR;
}
master->dev_params.revision_id = rev_id;
/* 3. Read Vendor ID (2 bytes) */
uint8_t v1 = 0U;
uint8_t v2 = 0U;
(void)master_read_direct_param(master, SYN_IOLINK_PAGE_VENDOR_ID_1, &v1);
(void)master_read_direct_param(master, SYN_IOLINK_PAGE_VENDOR_ID_2, &v2);
master->dev_params.vendor_id = (uint16_t)(((uint16_t)v1 << 8U) | v2);
/* 4. Read Device ID (3 bytes) */
uint8_t d1 = 0U;
uint8_t d2 = 0U;
uint8_t d3 = 0U;
(void)master_read_direct_param(master, SYN_IOLINK_PAGE_DEVICE_ID_1, &d1);
(void)master_read_direct_param(master, SYN_IOLINK_PAGE_DEVICE_ID_2, &d2);
(void)master_read_direct_param(master, SYN_IOLINK_PAGE_DEVICE_ID_3, &d3);
master->dev_params.device_id = ((uint32_t)d1 << 16U) | ((uint32_t)d2 << 8U) | (uint32_t)d3;
/* 5. Read Process Data Lengths */
uint8_t pd_in_len = 0U;
uint8_t pd_out_len = 0U;
(void)master_read_direct_param(master, SYN_IOLINK_PAGE_PD_IN_LEN, &pd_in_len);
(void)master_read_direct_param(master, SYN_IOLINK_PAGE_PD_OUT_LEN, &pd_out_len);
master->dev_params.pd_in_len =
(pd_in_len > 0U && pd_in_len <= SYN_IOLINK_MAX_PD_LEN) ? pd_in_len : 1U;
master->dev_params.pd_out_len = (pd_out_len <= SYN_IOLINK_MAX_PD_LEN) ? pd_out_len : 1U;
master->state = SYN_IOLINK_PORT_OPERATE;
return SYN_OK;
}
SYN_Status syn_iolink_master_exchange_pd(SYN_IOLink_Master *master, const uint8_t *pd_out,
size_t out_len, uint8_t *pd_in, size_t *in_len)
{
if (master == NULL || master->cfg.transport == NULL) {
return SYN_INVALID_PARAM;
}
uint8_t tx[SYN_IOLINK_MAX_PD_LEN + 4U];
tx[0] = 0x00U; /* Process Data Mode (Bit 7=0, Bit 6=0) */
size_t tx_pos = 1U;
if (pd_out != NULL && out_len > 0U) {
size_t copy_len = (out_len <= SYN_IOLINK_MAX_PD_LEN) ? out_len : SYN_IOLINK_MAX_PD_LEN;
(void)memcpy(&tx[tx_pos], pd_out, copy_len);
tx_pos += copy_len;
}
uint8_t ckt = syn_iolink_calc_checksum(tx, tx_pos);
tx[tx_pos++] = ckt;
if (!syn_transport_send(master->cfg.transport, tx, tx_pos)) {
master->state = SYN_IOLINK_PORT_FAULT;
return SYN_ERROR;
}
size_t rx_len = 0U;
if (!syn_transport_recv(master->cfg.transport, master->cfg.rx_buf, master->cfg.rx_buf_size,
&rx_len) ||
rx_len < 2U) {
master->state = SYN_IOLINK_PORT_FAULT;
return SYN_ERROR;
}
uint8_t expected_cks = syn_iolink_calc_checksum(master->cfg.rx_buf, rx_len - 1U);
if (master->cfg.rx_buf[rx_len - 1U] != expected_cks) {
master->state = SYN_IOLINK_PORT_FAULT;
return SYN_ERROR;
}
size_t pld_len = rx_len - 1U;
if (pd_in != NULL && in_len != NULL) {
(void)memcpy(pd_in, master->cfg.rx_buf, pld_len);
*in_len = pld_len;
}
(void)memcpy(master->current_pd_in, master->cfg.rx_buf, pld_len);
master->current_pd_in_len = pld_len;
return SYN_OK;
}
SYN_Status syn_iolink_master_read_isdu(SYN_IOLink_Master *master, uint16_t index, uint8_t subindex,
uint8_t *data, size_t max_len, size_t *out_len)
{
if (master == NULL || master->cfg.transport == NULL || data == NULL || out_len == NULL) {
return SYN_INVALID_PARAM;
}
uint8_t tx[8];
tx[0] = 0xC0U; /* ISDU Read (Bit 7=1, Bit 6=1) */
tx[1] = (uint8_t)((index >> 8U) & 0xFFU);
tx[2] = (uint8_t)(index & 0xFFU);
tx[3] = subindex;
tx[4] = syn_iolink_calc_checksum(tx, 4U);
if (!syn_transport_send(master->cfg.transport, tx, 5U)) {
return SYN_ERROR;
}
size_t rx_len = 0U;
if (!syn_transport_recv(master->cfg.transport, master->cfg.rx_buf, master->cfg.rx_buf_size,
&rx_len) ||
rx_len < 2U) {
return SYN_ERROR;
}
uint8_t expected_cks = syn_iolink_calc_checksum(master->cfg.rx_buf, rx_len - 1U);
if (master->cfg.rx_buf[rx_len - 1U] != expected_cks) {
return SYN_ERROR;
}
size_t pld_len = rx_len - 1U;
size_t copy_len = (pld_len < max_len) ? pld_len : max_len;
(void)memcpy(data, master->cfg.rx_buf, copy_len);
*out_len = copy_len;
return SYN_OK;
}
SYN_Status syn_iolink_master_write_isdu(SYN_IOLink_Master *master, uint16_t index, uint8_t subindex,
const uint8_t *data, size_t len)
{
if (master == NULL || master->cfg.transport == NULL || (data == NULL && len > 0U)) {
return SYN_INVALID_PARAM;
}
uint8_t tx[SYN_IOLINK_MAX_ISDU_LEN + 8U];
tx[0] = 0x40U; /* ISDU Write (Bit 7=0, Bit 6=1) */
tx[1] = (uint8_t)((index >> 8U) & 0xFFU);
tx[2] = (uint8_t)(index & 0xFFU);
tx[3] = subindex;
size_t tx_pos = 4U;
if (len > 0U) {
size_t copy_len = (len <= SYN_IOLINK_MAX_ISDU_LEN) ? len : SYN_IOLINK_MAX_ISDU_LEN;
(void)memcpy(&tx[tx_pos], data, copy_len);
tx_pos += copy_len;
}
tx[tx_pos] = syn_iolink_calc_checksum(tx, tx_pos);
tx_pos++;
if (!syn_transport_send(master->cfg.transport, tx, tx_pos)) {
return SYN_ERROR;
}
size_t rx_len = 0U;
if (!syn_transport_recv(master->cfg.transport, master->cfg.rx_buf, master->cfg.rx_buf_size,
&rx_len) ||
rx_len < 2U) {
return SYN_ERROR;
}
uint8_t expected_cks = syn_iolink_calc_checksum(master->cfg.rx_buf, rx_len - 1U);
if (master->cfg.rx_buf[rx_len - 1U] != expected_cks) {
return SYN_ERROR;
}
return SYN_OK;
}
SYN_Status syn_iolink_master_step(SYN_IOLink_Master *master, uint32_t now_ms)
{
if (master == NULL || master->cfg.transport == NULL) {
return SYN_INVALID_PARAM;
}
if (master->state == SYN_IOLINK_PORT_OPERATE &&
(now_ms - master->last_cycle_ms >= master->cfg.cycle_time_ms ||
master->last_cycle_ms == 0U)) {
master->last_cycle_ms = now_ms;
uint8_t dummy_in[SYN_IOLINK_MAX_PD_LEN];
size_t in_len = 0U;
(void)syn_iolink_master_exchange_pd(master, master->current_pd_out,
master->current_pd_out_len, dummy_in, &in_len);
}
return SYN_OK;
}
SYN_PT_Status syn_iolink_master_pt(SYN_PT *pt, SYN_Task *task)
{
if (pt == NULL || task == NULL || task->user_data == NULL) {
return PT_ENDED;
}
SYN_IOLink_Master *master = (SYN_IOLink_Master *)task->user_data;
PT_BEGIN(pt);
for (;;) {
uint32_t now = syn_port_get_tick_ms();
if (syn_iolink_master_step(master, now) != SYN_OK) {
break;
}
PT_YIELD(pt);
}
PT_END(pt);
}
/* ── Device Implementation ───────────────────────────────────────────────── */
SYN_Status syn_iolink_device_init(SYN_IOLink_Device *dev, const SYN_IOLink_DeviceConfig *cfg)
{
if (dev == NULL || cfg == NULL) {
return SYN_INVALID_PARAM;
}
(void)memset(dev, 0, sizeof(*dev));
dev->cfg = *cfg;
dev->state = SYN_IOLINK_PORT_OPERATE;
return SYN_OK;
}
SYN_Status syn_iolink_device_set_pd_in(SYN_IOLink_Device *dev, const uint8_t *data, size_t len)
{
if (dev == NULL || (data == NULL && len > 0U)) {
return SYN_INVALID_PARAM;
}
size_t copy_len = (len <= SYN_IOLINK_MAX_PD_LEN) ? len : SYN_IOLINK_MAX_PD_LEN;
if (copy_len > 0U) {
(void)memcpy(dev->pd_in, data, copy_len);
}
return SYN_OK;
}
SYN_Status syn_iolink_device_get_pd_out(const SYN_IOLink_Device *dev, uint8_t *out_buf,
size_t max_len, size_t *out_len)
{
if (dev == NULL || out_buf == NULL || out_len == NULL) {
return SYN_INVALID_PARAM;
}
size_t copy_len =
(dev->cfg.params.pd_out_len <= max_len) ? dev->cfg.params.pd_out_len : max_len;
(void)memcpy(out_buf, dev->pd_out, copy_len);
*out_len = copy_len;
return SYN_OK;
}
SYN_Status syn_iolink_device_process_frame(SYN_IOLink_Device *dev, const uint8_t *rx, size_t rx_len,
uint8_t *tx, size_t max_tx, size_t *out_len)
{
if (dev == NULL || rx == NULL || tx == NULL || out_len == NULL || rx_len < 2U) {
return SYN_INVALID_PARAM;
}
*out_len = 0U;
uint8_t ckt = rx[rx_len - 1U];
uint8_t expected_ckt = syn_iolink_calc_checksum(rx, rx_len - 1U);
if (ckt != expected_ckt) {
return SYN_ERROR;
}
uint8_t mc = rx[0];
bool is_read = ((mc & 0x80U) != 0U);
bool is_isdu = ((mc & 0x40U) != 0U);
uint8_t addr = (mc & 0x3FU);
/* 1. Direct Parameter Page 1 Access */
if (!is_isdu && is_read) {
uint8_t param_val = 0U;
switch (addr) {
case SYN_IOLINK_PAGE_MIN_CYCLE_TIME:
param_val = dev->cfg.params.min_cycle_time;
break;
case SYN_IOLINK_PAGE_REVISION_ID:
param_val = dev->cfg.params.revision_id;
break;
case SYN_IOLINK_PAGE_VENDOR_ID_1:
param_val = (uint8_t)((dev->cfg.params.vendor_id >> 8U) & 0xFFU);
break;
case SYN_IOLINK_PAGE_VENDOR_ID_2:
param_val = (uint8_t)(dev->cfg.params.vendor_id & 0xFFU);
break;
case SYN_IOLINK_PAGE_DEVICE_ID_1:
param_val = (uint8_t)((dev->cfg.params.device_id >> 16U) & 0xFFU);
break;
case SYN_IOLINK_PAGE_DEVICE_ID_2:
param_val = (uint8_t)((dev->cfg.params.device_id >> 8U) & 0xFFU);
break;
case SYN_IOLINK_PAGE_DEVICE_ID_3:
param_val = (uint8_t)(dev->cfg.params.device_id & 0xFFU);
break;
case SYN_IOLINK_PAGE_PD_IN_LEN:
param_val = dev->cfg.params.pd_in_len;
break;
case SYN_IOLINK_PAGE_PD_OUT_LEN:
param_val = dev->cfg.params.pd_out_len;
break;
default:
param_val = 0U;
break;
}
tx[0] = param_val;
tx[1] = syn_iolink_calc_checksum(tx, 1U);
*out_len = 2U;
return SYN_OK;
}
/* 2. Process Data Exchange */
if (!is_isdu && !is_read) {
size_t pd_out_len = rx_len - 2U;
if (pd_out_len > 0U && pd_out_len <= SYN_IOLINK_MAX_PD_LEN) {
(void)memcpy(dev->pd_out, &rx[1], pd_out_len);
}
size_t pd_in_len =
(dev->cfg.params.pd_in_len > 0U && dev->cfg.params.pd_in_len <= SYN_IOLINK_MAX_PD_LEN)
? dev->cfg.params.pd_in_len
: 1U;
if (pd_in_len + 1U > max_tx) {
return SYN_ERROR;
}
(void)memcpy(tx, dev->pd_in, pd_in_len);
tx[pd_in_len] = syn_iolink_calc_checksum(tx, pd_in_len);
*out_len = pd_in_len + 1U;
return SYN_OK;
}
/* 3. ISDU Transactions */
if (is_isdu && rx_len >= 5U) {
uint16_t index = (uint16_t)(((uint16_t)rx[1] << 8U) | rx[2]);
uint8_t subindex = rx[3];
if (is_read && dev->cfg.on_read != NULL) {
size_t pld_len = 0U;
SYN_Status st =
dev->cfg.on_read(index, subindex, tx, max_tx - 1U, &pld_len, dev->cfg.user_data);
if (st == SYN_OK) {
tx[pld_len] = syn_iolink_calc_checksum(tx, pld_len);
*out_len = pld_len + 1U;
return SYN_OK;
}
} else if (!is_read && dev->cfg.on_write != NULL) {
size_t pld_len = rx_len - 5U;
SYN_Status st = dev->cfg.on_write(index, subindex, &rx[4], pld_len, dev->cfg.user_data);
if (st == SYN_OK) {
tx[0] = 0x00U; /* ACK */
tx[1] = syn_iolink_calc_checksum(tx, 1U);
*out_len = 2U;
return SYN_OK;
}
}
}
return SYN_ERROR;
}
#endif /* SYN_USE_IOLINK */