///|
/// EtherCAT register addresses used during discovery and configuration.
pub let reg_type : UInt = 0x0000U
///|
pub let reg_revision : UInt = 0x0001U
///|
pub let reg_build : UInt = 0x0002U
///|
/// DL Status register: link status, loop status, signal detection.
pub let reg_dl_status : UInt = 0x0110U
///|
/// DL Information register: supported features (DC, FMMU count, SM count, etc.).
pub let reg_dl_information : UInt = 0x0008U
///|
/// DL Control register.
pub let reg_dl_control : UInt = 0x0100U
///|
/// DL Port control register (forwarding / loop-back per port).
pub let reg_dl_port : UInt = 0x0101U
///|
/// DL Control bit used to enable station alias addressing.
pub let dl_control_alias_enable_bit : Int = 24
///|
/// AL Status register: current ESM state and error indication.
pub let reg_al_status : UInt = 0x0130U
///|
/// AL Status Code register: error code for failed ESM transitions.
pub let reg_al_status_code : UInt = 0x0134U
///|
/// AL Control register: request ESM state transition.
pub let reg_al_control : UInt = 0x0120U
///|
/// IRQ Mask register (0x0200).
pub let reg_irq_mask : UInt = 0x0200U
///|
/// Configured Station Address register.
pub let reg_station_address : UInt = 0x0010U
///|
/// Configured Station Alias register.
pub let reg_station_alias : UInt = 0x0012U
///|
/// SII (EEPROM) control/status register.
pub let reg_sii_config : UInt = 0x0500U
///|
/// SII (EEPROM) control/status register.
pub let reg_sii_control : UInt = 0x0502U
///|
/// SII address register (word address for EEPROM read/write).
pub let reg_sii_address : UInt = 0x0504U
///|
/// SII data register (EEPROM data, 4 or 8 bytes depending on operation).
pub let reg_sii_data : UInt = 0x0508U
///|
/// FMMU base register (FMMU0 starts at 0x0600, each FMMU is 16 bytes).
pub let reg_fmmu_base : UInt = 0x0600U
///|
/// FMMU entry size in bytes.
pub let fmmu_entry_size : Int = 16
///|
/// Watchdog Divider register (ESC clock divider for both PDI and SM watchdogs).
pub let reg_watchdog_divider : UInt = 0x0400U
///|
/// PDI Watchdog Time register (master-to-PDI communication timeout).
pub let reg_watchdog_time_pdi : UInt = 0x0410U
///|
/// SM (Process Data) Watchdog Time register (process data arrival timeout).
pub let reg_watchdog_time_pdata : UInt = 0x0420U
///|
/// SM Watchdog Status register (bit 0: 0=expired, 1=active/reloaded).
pub let reg_watchdog_status_pdata : UInt = 0x0440U
///|
/// SM Watchdog Counter register (increments on each watchdog event).
pub let reg_watchdog_counter_pdata : UInt = 0x0442U
///|
/// RX Error Counter base register (0x0300–0x0307, 8 bytes: frame+physical per port).
pub let reg_rx_error_counter : UInt = 0x0300U
///|
/// Forwarded RX Error Counter base register (0x0308–0x030B, 4 bytes: one per port).
pub let reg_fwd_rx_error_counter : UInt = 0x0308U
///|
/// EtherCAT Processing Unit Error Counter register (0x030C).
pub let reg_ecat_pu_error_counter : UInt = 0x030CU
///|
/// PDI Error Counter register (0x030D).
pub let reg_pdi_error_counter : UInt = 0x030DU
///|
/// Lost Link Counter base register (0x0310–0x0313, 4 bytes: one per port).
pub let reg_lost_link_counter : UInt = 0x0310U
///|
/// Sync Manager base register (SM0 starts at 0x0800, each SM is 8 bytes).
pub let reg_sm_base : UInt = 0x0800U
///|
/// Sync Manager entry size in bytes.
pub let sm_entry_size : Int = 8
///|
/// DC receive time register (port 0). Broadcast write latches all port times.
pub let reg_dc_time0 : UInt = 0x0900U
///|
/// DC receive time register (port 1).
pub let reg_dc_time1 : UInt = 0x0904U
///|
/// DC receive time register (port 2).
pub let reg_dc_time2 : UInt = 0x0908U
///|
/// DC receive time register (port 3).
pub let reg_dc_time3 : UInt = 0x090CU
///|
/// DC system time register.
pub let reg_dc_system_time : UInt = 0x0910U
///|
/// DC receive timestamp register used for offset compensation.
pub let reg_dc_sof : UInt = 0x0918U
///|
/// DC system time offset register.
pub let reg_dc_system_offset : UInt = 0x0920U
///|
/// DC propagation delay register.
pub let reg_dc_system_delay : UInt = 0x0928U
///|
/// DC sync window/system difference register.
pub let reg_dc_system_diff : UInt = 0x092CU
///|
/// DC speed counter start register (0x0930).
pub let reg_dc_speed_count : UInt = 0x0930U
///|
/// DC time loop filter register (0x0934).
pub let reg_dc_time_filter : UInt = 0x0934U
///|
/// DC control unit register.
pub let reg_dc_cuc : UInt = 0x0980U
///|
/// DC sync activate register.
pub let reg_dc_sync_act : UInt = 0x0981U
///|
/// DC SYNC0 start time register.
pub let reg_dc_start0 : UInt = 0x0990U
///|
/// DC SYNC0 cycle time register.
pub let reg_dc_cycle0 : UInt = 0x09A0U
///|
/// DC SYNC1 cycle time register.
pub let reg_dc_cycle1 : UInt = 0x09A4U
///|
/// SlaveInfo represents a discovered slave on the network.
pub(all) struct SlaveInfo {
/// Position on the bus (0 = first slave)
position : Int
/// Configured station address (assigned during configuration)
configured_address : UInt
/// Identity from SII EEPROM
identity : @hal.SlaveIdentity
/// Current ESM state
al_status : EsmState
/// DL status (link/signal)
dl_status : UInt
/// SII data read from EEPROM
sii : @mailbox.SiiData?
} derive(Debug)
///|
/// Result of a bus scan operation.
pub(all) struct ScanResult {
/// Number of slaves found
slave_count : Int
/// Detailed information per slave
slaves : Array[SlaveInfo]
} derive(Debug)
///|
/// Count the number of slaves on the bus by sending a BRD to register 0x0000.
/// The working counter indicates how many slaves responded.
pub fn[N : @hal.Nic] count_slaves(
nic : N,
timeout : @hal.Duration,
) -> Int raise @hal.EcError {
let pdu = transact_pdu(
nic,
EcCommand::BRD,
broadcast_address(reg_type),
Bytes::new(1),
timeout,
)
pdu.wkc.reinterpret_as_int()
}
///|
/// Read a register from a slave using auto-increment addressing.
pub fn[N : @hal.Nic] read_register_ap(
nic : N,
position : UInt,
register : UInt,
length : Int,
timeout : @hal.Duration,
) -> Pdu raise @hal.EcError {
let addr = auto_increment_address(position) | (register << 16)
transact_pdu(nic, EcCommand::APRD, addr, Bytes::new(length), timeout)
}
///|
/// Write a register to a slave using auto-increment addressing.
pub fn[N : @hal.Nic] write_register_ap(
nic : N,
position : UInt,
register : UInt,
data : Bytes,
timeout : @hal.Duration,
) -> Pdu raise @hal.EcError {
let addr = auto_increment_address(position) | (register << 16)
transact_pdu(nic, EcCommand::APWR, addr, data, timeout)
}
///|
/// Read a register from a slave using configured station addressing.
pub fn[N : @hal.Nic] read_register_fp(
nic : N,
station : UInt,
register : UInt,
length : Int,
timeout : @hal.Duration,
) -> Pdu raise @hal.EcError {
let addr = configured_address(station, register)
transact_pdu(nic, EcCommand::FPRD, addr, Bytes::new(length), timeout)
}
///|
/// Write a register to a slave using configured station addressing.
pub fn[N : @hal.Nic] write_register_fp(
nic : N,
station : UInt,
register : UInt,
data : Bytes,
timeout : @hal.Duration,
) -> Pdu raise @hal.EcError {
let addr = configured_address(station, register)
transact_pdu(nic, EcCommand::FPWR, addr, data, timeout)
}
///|
/// Assign a configured station address to a slave at the given position.
pub fn[N : @hal.Nic] assign_station_address(
nic : N,
position : UInt,
station_address : UInt,
timeout : @hal.Duration,
) -> Unit raise @hal.EcError {
let buf = Buffer(size_hint=2)
buf.write_byte((station_address & 0xFFU).reinterpret_as_int().to_byte())
buf.write_byte(
((station_address >> 8) & 0xFFU).reinterpret_as_int().to_byte(),
)
let pdu = write_register_ap(
nic,
position,
reg_station_address,
buf.to_bytes(),
timeout,
)
if pdu.wkc == 0U {
raise @hal.EcError::SendFailed(
"station address assignment failed for position " +
position.reinterpret_as_int().to_string(),
)
}
}
///|
/// Read configured station address register (0x0010) using auto-increment addressing.
pub fn[N : @hal.Nic] read_station_address_ap(
nic : N,
position : UInt,
timeout : @hal.Duration,
) -> UInt raise @hal.EcError {
let pdu = read_register_ap(nic, position, reg_station_address, 2, timeout)
if pdu.data.length() < 2 {
raise @hal.EcError::InvalidPdu("station address response too short")
}
pdu.data[0].to_uint() | (pdu.data[1].to_uint() << 8)
}
///|
/// Read station alias register (0x0012) using configured station addressing.
pub fn[N : @hal.Nic] read_station_alias(
nic : N,
station : UInt,
timeout : @hal.Duration,
) -> UInt raise @hal.EcError {
let pdu = read_register_fp(nic, station, reg_station_alias, 2, timeout)
if pdu.data.length() < 2 {
raise @hal.EcError::InvalidPdu("station alias response too short")
}
pdu.data[0].to_uint() | (pdu.data[1].to_uint() << 8)
}
///|
/// Write station alias register (0x0012) using configured station addressing.
pub fn[N : @hal.Nic] write_station_alias(
nic : N,
station : UInt,
station_alias : UInt,
timeout : @hal.Duration,
) -> Unit raise @hal.EcError {
let buf = Bytes::from_array([
(station_alias & 0xFFU).to_byte(),
((station_alias >> 8) & 0xFFU).to_byte(),
])
let _ = write_register_fp(nic, station, reg_station_alias, buf, timeout)
}
///|
/// Set DL Control Bit24 to enable alias addressing for one slave.
pub fn[N : @hal.Nic] enable_alias_addressing(
nic : N,
station : UInt,
timeout : @hal.Duration,
) -> Unit raise @hal.EcError {
let dl = read_register_fp(nic, station, reg_dl_control, 4, timeout)
if dl.data.length() < 4 {
raise @hal.EcError::InvalidPdu("DL control response too short")
}
let current = dl.data[0].to_uint() |
(dl.data[1].to_uint() << 8) |
(dl.data[2].to_uint() << 16) |
(dl.data[3].to_uint() << 24)
let updated = current | (1U << dl_control_alias_enable_bit)
let buf = Buffer(size_hint=4)
buf.write_byte((updated & 0xFFU).reinterpret_as_int().to_byte())
buf.write_byte(((updated >> 8) & 0xFFU).reinterpret_as_int().to_byte())
buf.write_byte(((updated >> 16) & 0xFFU).reinterpret_as_int().to_byte())
buf.write_byte(((updated >> 24) & 0xFFU).reinterpret_as_int().to_byte())
let pdu = write_register_fp(
nic,
station,
reg_dl_control,
buf.to_bytes(),
timeout,
)
if pdu.wkc == 0U {
raise @hal.EcError::SendFailed(
"enable alias addressing failed for station " +
station.reinterpret_as_int().to_string(),
)
}
}