Code:
"tuxcnc" I feel like when I calling Microsoft customer's support... "We dont know what is your problem, but it works with us..." I know how Linuxcnc does the threading, and know why it is acceptable with LPT and not acceptable with devices using ethernet link. LPT uses base_thread, ethernet uses servo_thread, so LPT can be ten times faster. I told you few posts ago, the servo_thread=1000000 (1 ms period) can make delay that can produce 18 degrees positon error at spindle rotates 3000 rpm. 3000 rpm => 50 rps => 20 ms per revolution. It is mathematics, everyone can make this calculations. The solution is not send the extended index in hope the Linuxcnc see it, and resets position to zero when received it. The solution is send raw_count latched at every real encoder's index edge. We can restore encoder's index (if we need) from (old != new) at the component. And we can reset position not to zero, but to real position captured at index edge. We can be confused, because we use "index" word for different meanings (encoder's output, program variable, hal pin) but we can not change this. Detailed explanation: Let's assume the encoder's index edge occurs at 1000 raw_counts. Time goes, counter counts A/B signals. The component requests data. At this moment raw_counts is 1050. We send both values, counted and latched. The component knows, the index was 50 counts ago and sets position to 50/scale not to zero. (of course only when hal index_enable pin is set) What don't you understand?
You're thinking along the right lines, and this can definitely be improved.
Interrupts break determinism and can introduce jitter into the system. The jitter might be small, but it's still there because the core has to stop processing, handle the ISR, and then resume the program.
We need to get rid of interrupts entirely for this.
Configure the timer like this:
TIM4->SMCR = (TIM_SMCR_SMS_0 | TIM_SMCR_SMS_1); // ENCODER MODE (TI1 & TI2)
TIM4->DIER = 0; // NO INTERRUPTS
TIM4->CCMR1 = (TIM_CCMR1_CC1S_0 | // (CH-A) CC1S = 01 (TI1FP1 --> TI1). CH1
TIM_CCMR1_IC1F | // F SAMPLING. CH1
TIM_CCMR1_CC2S_0 | // (CH-

CC2S = 01 (TI2FP2 --> TI2). CH2
TIM_CCMR1_IC2F); // F SAMPLING. CH2
TIM4->CCMR2 = (TIM_CCMR2_CC3S_0 | // (CH-Z) CC3S = 01 (TI2FP3 --> TI3). CH3
TIM_CCMR2_IC3F); // F SAMPLING. CH3
TIM4->CCER |= TIM_CCER_CC3E; // Capture/Compare enable CH3
TIM4->PSC = 0; // PRESCALER = 0
TIM4->ARR = 0xFFFF; // 65535
The timer will operate as an encoder counter (Channels 1 & 2) and **simultaneously** Channel 3 will be configured for **Input Capture mode**. The Index pulse (Z-channel) is connected to Channel 3.
**What do we get from this?**
* `TIM4->CNT`: The current raw encoder count.
* `TIM4->CCR3`: The **exact hardware state** of `CNT` latched at the moment of the rising or falling edge of the Index pulse.
This gives us an extremely accurate hardware snapshot of the counter at the exact moment the index pulse occurs.
**Next Steps:**
We just need to extend the communication protocol and send both `CNT` and `CCR3` for each encoder.
Then, on the **LinuxCNC side**, `CCR3` should be processed similarly to how the Mesa driver handles `CNT`.
When Motion sets `index_enable` in the driver, we simply do:
if(CCR3 != CCR3_prev) {
// HURRAY, THIS IS THE TRUE INDEX!
// Now we can correct the raw counter based on the current CCR3 value.
}
Result: We get precise index homing without needing to send an `index_enable` request down to the STM32. Everything is handled cleanly on the LinuxCNC side.
Hope that makes sense.