- Configuring LinuxCNC
- Advanced Configuration
- EtherCAT
- Ethercat compatible Encoder board tentative STM32F4 + LAN9252
Ethercat compatible Encoder board tentative STM32F4 + LAN9252
- Hakan
- Offline
- Platinum Member
-
Less
More
- Posts: 1107
- Thank you received: 390
18 Dec 2025 09:44 - 18 Dec 2025 10:05 #340277
by Hakan
Replied by Hakan on topic Ethercat compatible Encoder board tentative STM32F4 + LAN9252
Dc synchronization for the slave is at the same time very simple and complex.
Your slave is now running in free run mode. As fast and frequently as possible it executes the ecat_slv()This function does the following
ecat_slv_poll() checks with the LAN9252 for events, mailboxes and other housekeeping things.
DIG_process() reads from the LAN9252 all pdos, sets user variables to the value of the pdos in cb_set_outputs(), calls a processing function if defined in application_hook, and finally captures user variables into pdos cb_get_inputs() and uploads them to the LAN9252.
The data is then sent to the ethercat master with the next packet passing by.
In free-run this happens at the speed of the processor and one can expect the read value to have been read inside some 0-300 microseconds of the packet passing by. Sometime this is enough, sometimes not. For my plasma cutter it is definitely enough with a voltage reading every so often. For precise motion control this normally is not enough. Predictability of exactly when the value is read is important here.
That's where DC synchronization comes in. In DC sync the LAN9252 will tell you the exact reference time by setting the SYNC0 line high (or low).
That's all actually.
It is now up to you to do something useful with that signal.
There isn't one and only one way to handle this. For the stepper generator cards I have a somewhat elaborate time measurement built in and there is quite a bit of processing at every cycle when the target position changes.
In this case though I think it is as easy as it can be. Just read the encoder counter, store it in a value. Kick off the DIG_process are quickly as possible to have the data sent over to linuxcnc. Hal should have the read value next servo cycle.
To do this, set up an interrupt handler on the SYNC0 line and read the encoder value. Set a flag to start the DIG_process.
Start with the config structure
So there are many changes. Make good use of git so you can come back.
In loop() I have tried to make it work with both Dc sync and free run by waiting for 5 cycles without sync0 interrupts and then start polling.
You can find this code in E3000 main.cpp that's where I copied it from.
Your slave is now running in free run mode. As fast and frequently as possible it executes the ecat_slv()
void loop(void)
{
ecat_slv();
}void ecat_slv(void)
{
ecat_slv_poll();
DIG_process(ESC_ALeventread(), DIG_PROCESS_WD_FLAG | DIG_PROCESS_OUTPUTS_FLAG | DIG_PROCESS_APP_HOOK_FLAG | DIG_PROCESS_INPUTS_FLAG);
}ecat_slv_poll() checks with the LAN9252 for events, mailboxes and other housekeeping things.
DIG_process() reads from the LAN9252 all pdos, sets user variables to the value of the pdos in cb_set_outputs(), calls a processing function if defined in application_hook, and finally captures user variables into pdos cb_get_inputs() and uploads them to the LAN9252.
The data is then sent to the ethercat master with the next packet passing by.
In free-run this happens at the speed of the processor and one can expect the read value to have been read inside some 0-300 microseconds of the packet passing by. Sometime this is enough, sometimes not. For my plasma cutter it is definitely enough with a voltage reading every so often. For precise motion control this normally is not enough. Predictability of exactly when the value is read is important here.
That's where DC synchronization comes in. In DC sync the LAN9252 will tell you the exact reference time by setting the SYNC0 line high (or low).
That's all actually.
It is now up to you to do something useful with that signal.
There isn't one and only one way to handle this. For the stepper generator cards I have a somewhat elaborate time measurement built in and there is quite a bit of processing at every cycle when the target position changes.
In this case though I think it is as easy as it can be. Just read the encoder counter, store it in a value. Kick off the DIG_process are quickly as possible to have the data sent over to linuxcnc. Hal should have the read value next servo cycle.
To do this, set up an interrupt handler on the SYNC0 line and read the encoder value. Set a flag to start the DIG_process.
Start with the config structure
static esc_cfg_t config = {
.user_arg = NULL,
.use_interrupt = 0,
.watchdog_cnt = 150,
.set_defaults_hook = NULL,
.pre_state_change_hook = NULL,
.post_state_change_hook = NULL,
.application_hook = NULL,
.safeoutput_override = NULL,
.pre_object_download_hook = NULL,
.post_object_download_hook = NULL,
.rxpdo_override = NULL,
.txpdo_override = NULL,
.esc_hw_interrupt_enable = ESC_interrupt_enable,
.esc_hw_interrupt_disable = ESC_interrupt_disable,
.esc_hw_eep_handler = NULL,
.esc_check_dc_handler = dc_checker,
};// Enable interrupts
void ESC_interrupt_enable(uint32_t mask)
{
// Enable interrupt for SYNC0 or SM2 or SM3
uint32_t user_int_mask = ESCREG_ALEVENT_DC_SYNC0 | ESCREG_ALEVENT_SM2 | ESCREG_ALEVENT_SM3;
if (mask & user_int_mask) {
ESC_ALeventmaskwrite(ESC_ALeventmaskread() | (mask & user_int_mask));
ESC_ALeventmaskwrite(ESC_ALeventmaskread() &
~(ESCREG_ALEVENT_DC_SYNC0 | ESCREG_ALEVENT_SM3));
attachInterrupt(digitalPinToInterrupt(SYNC0), sync0Handler, RISING);
// Set LAN9252 interrupt pin driver as push-pull active high
uint32_t bits = 0x00000111;
ESC_write(0x54, &bits, 4);
// Enable LAN9252 interrupt
bits = 0x00000001;
ESC_write(0x5c, &bits, 4);
}
}// Disable interrupts
void ESC_interrupt_disable(uint32_t mask)
{
// Enable interrupt for SYNC0 or SM2 or SM3
uint32_t user_int_mask = ESCREG_ALEVENT_DC_SYNC0 | ESCREG_ALEVENT_SM2 | ESCREG_ALEVENT_SM3;
if (mask & user_int_mask) { // Disable interrupt from SYNC0 etc
ESC_ALeventmaskwrite(ESC_ALeventmaskread() & ~(mask & user_int_mask));
detachInterrupt(digitalPinToInterrupt(SYNC0));
// Disable LAN9252 interrupt
uint32_t bits = 0x00000000;
ESC_write(0x5c, &bits, 4);
}
}// Setup of DC
uint16_t dc_checker(void)
{
// Indicate we run DC
ESCvar.dcsync = 1;
return 0;
}void sync0Handler(void)
{
ALEventIRQ = ESC_ALeventread();
serveIRQ = 1;
encoderCounter = <read encoder counter value here>;
}void loop(void)
{
uint64_t dTime;
if (serveIRQ) {
DIG_process(DIG_PROCESS_WD_FLAG | DIG_PROCESS_OUTPUTS_FLAG |
DIG_PROCESS_APP_HOOK_FLAG | DIG_PROCESS_INPUTS_FLAG);
serveIRQ = 0;
ESCvar.PrevTime = ESCvar.Time;
ecat_slv_poll();
}
dTime = longTime.extendTime(micros()) - irqTime;
if (dTime > 5000) // Don't run ecat_slv_poll when expecting to serve interrupt
ecat_slv();
}So there are many changes. Make good use of git so you can come back.
In loop() I have tried to make it work with both Dc sync and free run by waiting for 5 cycles without sync0 interrupts and then start polling.
You can find this code in E3000 main.cpp that's where I copied it from.
Last edit: 18 Dec 2025 10:05 by Hakan. Reason: Worst bbcode disaster yet
The following user(s) said Thank You: tommylight, vibram
Please Log in or Create an account to join the conversation.
- Configuring LinuxCNC
- Advanced Configuration
- EtherCAT
- Ethercat compatible Encoder board tentative STM32F4 + LAN9252
Time to create page: 0.053 seconds