-
-
Notifications
You must be signed in to change notification settings - Fork 54
[WIP] Expose escape hatch for manual PDI configuration, misc other changes. #243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
david-boles
wants to merge
1
commit into
ethercrab-rs:master
Choose a base branch
from
david-boles:expose_pdi_escape_hatch
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| //! Discover devices connected to the network. | ||
|
|
||
| use env_logger::Env; | ||
| use ethercrab::{ | ||
| error::Error, std::{ethercat_now, tx_rx_task}, ConfigureSubdevicePdi, MainDevice, MainDeviceConfig, PdiSegment, PdoDirection, PduStorage, SyncManagerType, Timeouts | ||
| }; | ||
| use std::sync::Arc; | ||
|
|
||
| /// Maximum number of SubDevices that can be stored. This must be a power of 2 greater than 1. | ||
| const MAX_SUBDEVICES: usize = 128; | ||
| /// Maximum PDU data payload size - set this to the max PDI size or higher. | ||
| const MAX_PDU_DATA: usize = PduStorage::element_size(1100); | ||
| /// Maximum number of EtherCAT frames that can be in flight at any one time. | ||
| const MAX_FRAMES: usize = 16; | ||
| /// Maximum total PDI length. | ||
| const PDI_LEN: usize = 128; | ||
|
|
||
| static PDU_STORAGE: PduStorage<MAX_FRAMES, MAX_PDU_DATA> = PduStorage::new(); | ||
|
|
||
| fn main() { | ||
| env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); | ||
|
|
||
| let interface = std::env::args() | ||
| .nth(1) | ||
| .expect("Provide network interface as first argument."); | ||
|
|
||
| log::info!("Discovering EtherCAT devices on {}...", interface); | ||
|
|
||
| let (tx, rx, pdu_loop) = PDU_STORAGE.try_split().expect("can only split once"); | ||
|
|
||
| let maindevice = Arc::new(MainDevice::new( | ||
| pdu_loop, | ||
| Timeouts::default(), | ||
| MainDeviceConfig { | ||
| dc_static_sync_iterations: 0, | ||
| ..MainDeviceConfig::default() | ||
| }, | ||
| )); | ||
|
|
||
| smol::block_on(async { | ||
| smol::spawn(tx_rx_task(&interface, tx, rx).expect("spawn TX/RX task")).detach(); | ||
|
|
||
| let mut group = maindevice | ||
| .init_single_group::<MAX_SUBDEVICES, PDI_LEN>(ethercat_now) | ||
| .await | ||
| .expect("Init"); | ||
|
|
||
| log::info!("Discovered {} SubDevices", group.len()); | ||
|
|
||
| for subdevice in group.iter(&maindevice) { | ||
| log::info!( | ||
| "--> SubDevice {:#06x} {} {}", | ||
| subdevice.configured_address(), | ||
| subdevice.name(), | ||
| subdevice.identity() | ||
| ); | ||
| } | ||
|
|
||
| let group = group.into_pre_op_pdi_with_callback(&maindevice, ConfigureKistler5074B).await.expect("Pre-op PDI"); | ||
|
|
||
| group.into_op(&maindevice).await.expect("Op"); | ||
| }); | ||
|
|
||
| log::info!("Done."); | ||
| } | ||
|
|
||
| struct ConfigureKistler5074B; | ||
|
|
||
| impl ConfigureSubdevicePdi for ConfigureKistler5074B { | ||
| type Error = Error; | ||
| async fn configure<'a, S>( | ||
| &mut self, | ||
| subdevice: &mut ethercrab::SubDeviceRef<'a, S>, | ||
| sync_managers: &[ethercrab::SyncManager], | ||
| _fmmu_usage: &[ethercrab::FmmuUsage], | ||
| direction: ethercrab::PdoDirection, | ||
| global_offset: &mut ethercrab::PdiOffset, | ||
| ) -> Result<ethercrab::PdiSegment, Self::Error> | ||
| where | ||
| S: std::ops::DerefMut<Target = ethercrab::SubDevice> { | ||
|
|
||
| let start_offset = *global_offset; | ||
|
|
||
| // XXX: The charge amp isn't spec-compliant, PDO mapping SDOs (e.g. 0x1600 and | ||
| // 0x1A00) are not readable which breaks Ethercrab's default PDI setup. | ||
| pub const SM_ASSIGNMENT_BASE_ADDRESS: u16 = 0x1C10; | ||
| let (len_bytes, sm_idx, fmmu_idx) = match direction { | ||
| PdoDirection::MasterRead => { | ||
| const LEN_BYTES: u16 = 4 + (4 * 4 * 4); | ||
| const SM_IDX: u8 = 3; | ||
| const SM_ASSIGNMENT: u16 = SM_ASSIGNMENT_BASE_ADDRESS + (SM_IDX as u16); | ||
| const FMMU_IDX: usize = 1; | ||
|
|
||
| // PDO mapping | ||
| // All PDOs are fixed, nothing to do. | ||
|
|
||
| // PDO assignments | ||
| // XXX: You have to play this little song and dance with the CoE array's length. | ||
| subdevice | ||
| .sdo_write::<u8>(SM_ASSIGNMENT, 0, 0) | ||
| .await | ||
| .expect("zeroing pdo assignment count"); | ||
| // Status ChX - BYTE | ||
| // Scaled Value ChX - REAL | ||
| for channel_idx in 0..4u8 { | ||
| let sdo_subindex = 0x1A00_u16 + (u16::from(channel_idx) << 4); | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, (channel_idx * 5) + 1, sdo_subindex) | ||
| .await | ||
| .expect("setting CH1 status PDO assignment"); | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, (channel_idx * 5) + 2, sdo_subindex + 1_u16) | ||
| .await | ||
| .expect("setting CH1 value PDO assignment"); | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, (channel_idx * 5) + 3, sdo_subindex + 2_u16) | ||
| .await | ||
| .expect("setting CH1 peak min PDO assignment"); | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, (channel_idx * 5) + 4, sdo_subindex + 3_u16) | ||
| .await | ||
| .expect("setting CH1 peak max PDO assignment"); | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, (channel_idx * 5) + 5, sdo_subindex + 4_u16) | ||
| .await | ||
| .expect("setting CH1 integral PDO assignment"); | ||
| } | ||
| // Donzo | ||
| subdevice | ||
| .sdo_write::<u8>(SM_ASSIGNMENT, 0, 4 * 5) | ||
| .await | ||
| .expect("setting PDO assignment count"); | ||
|
|
||
| (LEN_BYTES, SM_IDX, FMMU_IDX) | ||
| } | ||
| PdoDirection::MasterWrite => { | ||
| const LEN_BYTES: u16 = 4; | ||
| const SM_IDX: u8 = 2; | ||
| const SM_ASSIGNMENT: u16 = SM_ASSIGNMENT_BASE_ADDRESS + (SM_IDX as u16); | ||
| const FMMU_IDX: usize = 0; | ||
|
|
||
| // PDO mapping: All PDOs are fixed, nothing to do. | ||
|
|
||
| // PDO assignments | ||
| // XXX: You have to play this little song and dance with the CoE array's length. | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, 0, 0_u8) | ||
| .await | ||
| .expect("zeroing pdo assignment count"); | ||
| // Control Ch - BYTE | ||
| // XXX: Even though these are required PDO assignments, you do have to reassign | ||
| // them. This may have been what was breaking EtherCrab's EEPROM based setup. | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, 1, 0x1600_u16) | ||
| .await | ||
| .expect("setting CH1 control PDO assignment"); | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, 2, 0x1610_u16) | ||
| .await | ||
| .expect("setting CH2 control PDO assignment"); | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, 3, 0x1620_u16) | ||
| .await | ||
| .expect("setting CH3 control PDO assignment"); | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, 4, 0x1630_u16) | ||
| .await | ||
| .expect("setting CH4 control PDO assignment"); | ||
| // Donzo | ||
| subdevice | ||
| .sdo_write(SM_ASSIGNMENT, 0, 4_u8) | ||
| .await | ||
| .expect("setting PDO assignment count"); | ||
|
|
||
| (LEN_BYTES, SM_IDX, FMMU_IDX) | ||
| } | ||
| }; | ||
|
|
||
| let len_bits = 8 * len_bytes; | ||
|
|
||
| let sm_config = subdevice | ||
| .write_sm_config(sm_idx, &sync_managers[usize::from(sm_idx)], len_bytes) | ||
| .await | ||
| .expect("configuring sync manager"); | ||
| let desired_sm_type = match direction { | ||
| PdoDirection::MasterRead => SyncManagerType::ProcessDataRead, | ||
| PdoDirection::MasterWrite => SyncManagerType::ProcessDataWrite, | ||
| }; | ||
| subdevice | ||
| .write_fmmu_config(len_bits, fmmu_idx, global_offset, desired_sm_type, &sm_config) | ||
| .await | ||
| .expect("configuring fmmu"); | ||
|
|
||
| Ok(PdiSegment { | ||
| bit_len: usize::from(len_bits), | ||
| bytes: start_offset.up_to(*global_offset), | ||
| }) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| [toolchain] | ||
| channel = "1.77" | ||
| channel = "1.79" | ||
| profile = "default" | ||
| targets = ["x86_64-unknown-linux-gnu"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -262,10 +262,14 @@ impl From<PdoType> for CategoryType { | |
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| #[repr(u8)] | ||
| pub enum FmmuUsage { | ||
| /// TODO | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll obviously document these properly if you agree with exposing these types publicly. |
||
| #[wire(alternatives = [0xff])] | ||
| Unused = 0x00, | ||
| /// TODO | ||
| Outputs = 0x01, | ||
| /// TODO | ||
| Inputs = 0x02, | ||
| /// TODO | ||
| SyncManagerStatus = 0x03, | ||
| } | ||
|
|
||
|
|
@@ -435,20 +439,26 @@ impl EtherCrabWireRead for CoeDetails { | |
| } | ||
| } | ||
|
|
||
| /// TODO | ||
| #[derive(Copy, Clone, PartialEq, Eq, ethercrab_wire::EtherCrabWireRead)] | ||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| #[wire(bytes = 8)] | ||
| pub struct SyncManager { | ||
| /// TODO | ||
| #[wire(bytes = 2)] | ||
| pub(crate) start_addr: u16, | ||
| pub start_addr: u16, | ||
| /// TODO | ||
| #[wire(bytes = 2)] | ||
| pub(crate) length: u16, | ||
| pub length: u16, | ||
| /// TODO | ||
| #[wire(bytes = 1, post_skip_bytes = 1)] | ||
| pub(crate) control: sync_manager_channel::Control, | ||
| pub control: sync_manager_channel::Control, | ||
| /// TODO | ||
| #[wire(bytes = 1)] | ||
| pub(crate) enable: SyncManagerEnable, | ||
| pub enable: SyncManagerEnable, | ||
| /// TODO | ||
| #[wire(bytes = 1)] | ||
| pub(crate) usage_type: SyncManagerType, | ||
| pub usage_type: SyncManagerType, | ||
| } | ||
|
|
||
| impl core::fmt::Debug for SyncManager { | ||
|
|
@@ -464,6 +474,7 @@ impl core::fmt::Debug for SyncManager { | |
| } | ||
|
|
||
| bitflags::bitflags! { | ||
| /// TODO | ||
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| pub struct SyncManagerEnable: u8 { | ||
| /// Bit 0: enable. | ||
|
|
@@ -502,6 +513,7 @@ impl defmt::Format for SyncManagerEnable { | |
| } | ||
| } | ||
|
|
||
| /// TODO | ||
| #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, ethercrab_wire::EtherCrabWireRead)] | ||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| #[repr(u8)] | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the important lines that this PR facilitates; the ability for a user to configure the SMs and FMMUs of their subdevices themselves.
While avoiding substantially changing the existing EtherCrab PDI setup flow.