Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

111 feature request add index enable to stepgen #124

Merged
merged 10 commits into from
Jan 9, 2025
65 changes: 59 additions & 6 deletions src/litexcnc/config/modules/stepgen.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Imports for creating a json-definition
import math
import os
try:
from typing import ClassVar, List, Literal, Union
from typing import ClassVar, List, Literal, Optional, Union
except ImportError:
# Imports for Python <3.8
from typing import ClassVar, List, Union
Expand All @@ -15,7 +16,26 @@


class StepGenPinoutStepDirBaseConfig(ModuleInstanceBaseModel):
...
index_pin: Optional[str] = Field(
None,
description="The pin on the FPGA-card for the index signal for the stepgen."
)
io_standard: str = Field(
"LVCMOS33",
description="The IO Standard (voltage) to use for the pins."
)

def convert_to_signal(self):
"""
Creates the pins for this layout.
"""
# Deferred imports to prevent importing Litex while installing the driver
from litex.build.generic_platform import IOStandard, Pins, Subsignal
return (
Subsignal("index", Pins(self.index_pin), IOStandard(self.io_standard)),
)



class StepGenPinoutStepDirConfig(StepGenPinoutStepDirBaseConfig):
stepgen_type: Literal['step_dir']
Expand Down Expand Up @@ -43,7 +63,7 @@ def convert_to_signal(self):
"""
# Deferred imports to prevent importing Litex while installing the driver
from litex.build.generic_platform import IOStandard, Pins, Subsignal
return (
return super().convert_to_signal() + (
Subsignal("step", Pins(self.step_pin), IOStandard(self.io_standard)),
Subsignal("dir", Pins(self.dir_pin), IOStandard(self.io_standard))
)
Expand Down Expand Up @@ -98,7 +118,7 @@ def convert_to_signal(self):
"""
# Deferred imports to prevent importing Litex while installing the driver
from litex.build.generic_platform import IOStandard, Pins, Subsignal
return (
return super().convert_to_signal() + (
Subsignal("step_pos", Pins(self.step_pos_pin), IOStandard(self.io_standard)),
Subsignal("step_neg", Pins(self.step_neg_pin), IOStandard(self.io_standard)),
Subsignal("dir_pos", Pins(self.dir_pos_pin), IOStandard(self.io_standard)),
Expand Down Expand Up @@ -140,6 +160,14 @@ class StepgenInstanceConfig(BaseModel):
...,
description="The configuration of the stepper type and pin-out."
)
max_frequency: int = Field(
400e3,
description="The guaranteed maximum frequency the stepgen can generate in Hz. "
"The actual value can be larger then this value, as this is dependent on "
"the clock-frequency and scaling. Choosing a smaller value, close to the "
"limits of your drivers, gives a higher resolution in the velocity. Default "
"value is 400,000 Hz (400 kHz)."
)
soft_stop: bool = Field(
False,
description="When False, the stepgen will directly stop when the stepgen is "
Expand Down Expand Up @@ -170,6 +198,18 @@ class StepgenInstanceConfig(BaseModel):
'dir-hold-time',
]

def calculate_shift(self, clock_frequency):
"""Calculates the shift required to get the desired maximum frequency
for the stepgen instance. The shift is maximum 4 bits, so the maximum
frequency division is 16 times the clock frequency.

Args:
clock_frequency (int): The clock frequency of the FPGA.

Returns:
int: The shift required to get the desired maximum frequency.
"""
return (int(math.log2(clock_frequency / self.max_frequency)) - 1) & 0x0F

class StepgenModuleConfig(ModuleBaseModel):
"""
Expand Down Expand Up @@ -209,14 +249,27 @@ def add_mmio_read_registers(self, mmio):

@property
def config_size(self):
return 4
"""Calculates the number DWORDS required to store the shift data
of the stepgen instances. The first byte is the number of instances,
followed by the shifts required for each instance to get the desired
maximum frequency.
"""
return math.ceil((1 + len(self.instances)) / 4) * 4

def store_config(self, mmio):
# Deferred imports to prevent importing Litex while installing the driver
from litex.soc.interconnect.csr import CSRStatus
# - store the number of instances
config = len(self.instances) << ((self.config_size - 1) * 8)
# - calculate and store the shift for each instance plus the other settings
clock_frequency = mmio.clock_frequency.status.reset.value
for index, instance in enumerate(self.instances):
config_byte = instance.calculate_shift(clock_frequency)
config_byte += (1 if instance.pins.index_pin is not None else 0) << 7
config += config_byte << ((self.config_size - 2 - index) * 8)
# Store the config
mmio.stepgen_config_data = CSRStatus(
size=self.config_size*8,
reset=len(self.instances),
description=f"The config of the Stepgen module."
)

19 changes: 17 additions & 2 deletions src/litexcnc/driver/modules/litexcnc_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ size_t litexcnc_encoder_init(litexcnc_module_instance_t **module, litexcnc_t *li

// Create the params
LITEXCNC_CREATE_HAL_PARAM("position-scale", float, HAL_RW, &(instance->hal.param.position_scale))
LITEXCNC_CREATE_HAL_PARAM("position-offset", float, HAL_RW, &(instance->hal.param.position_offset))
LITEXCNC_CREATE_HAL_PARAM("x4-mode", bit, HAL_RW, &(instance->hal.param.x4_mode))
}

Expand Down Expand Up @@ -284,7 +285,7 @@ int litexcnc_encoder_process_read(void *module, uint8_t **data, int period) {
// as it is known the encoder is reset to 0 and it is not possible to roll-over
// within one period (assumption is that the period is less then 15 minutes).
if (*(instance->hal.pin.index_pulse)) {
*(instance->hal.pin.position) = *(instance->hal.pin.counts) * instance->data.position_scale_recip;
*(instance->hal.pin.position) = instance->hal.param.position_offset + *(instance->hal.pin.counts) * instance->data.position_scale_recip;
*(instance->hal.pin.overflow_occurred) = false;
} else {
// Roll-over detection; it assumed when the the difference between previous value
Expand All @@ -311,12 +312,26 @@ int litexcnc_encoder_process_read(void *module, uint8_t **data, int period) {
}
}
if (*(instance->hal.pin.overflow_occurred)) {
// Determine the position relative to previous position
*(instance->hal.pin.position) = *(instance->hal.pin.position) + difference * instance->data.position_scale_recip;
} else {
*(instance->hal.pin.position) = *(instance->hal.pin.counts) * instance->data.position_scale_recip;
// Determine the position absolute
*(instance->hal.pin.position) = instance->hal.param.position_offset + *(instance->hal.pin.counts) * instance->data.position_scale_recip;
}
}

// Check whether the position-offset has changed. When in relative mode (overflow occurred)
// we must compensate for this change
if (instance->hal.param.position_offset != instance->memo.position_offset) {
// Are we in relative mode
if (*(instance->hal.pin.overflow_occurred)) {
// Correct the offset
*(instance->hal.pin.position) -= instance->memo.position_offset;
*(instance->hal.pin.position) += instance->hal.param.position_offset;
}
instance->memo.position_offset = instance->hal.param.position_offset;
}

// Calculate the new speed based on the new position (running average). The
// running average is not modified when an index-pulse is received, as this
// means there is a large jump in position and thus to a large theoretical
Expand Down
5 changes: 5 additions & 0 deletions src/litexcnc/driver/modules/litexcnc_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ typedef struct {
* then 1000 counts of the encoder will be reported as a position of 2.0 units.
*/
hal_float_t position_scale;
/* Position offset in scaled units. When the encoder count is 0, this will be the
* reported position. Can be used to create a digital potentiometer.
*/
hal_float_t position_offset;
/* Enables times-4 mode. When true (the default), the counter counts each edge of the
* quadrature waveform (four counts per full cycle). When false, it only counts once
* per full cycle. NOTE: this function is implemented by dividing applying an integer
Expand All @@ -134,6 +138,7 @@ typedef struct {
struct {
hal_s32_t position_reset;
hal_float_t position_scale;
hal_float_t position_offset;
hal_float_t velocity[LITEXCNC_ENCODER_POSITION_AVERAGE_SIZE];
size_t velocity_pointer;
} memo;
Expand Down
Loading