mirror of
https://github.com/Klipper3d/klipper.git
synced 2026-02-08 01:01:06 -07:00
Merge branch 'Klipper3d:master' into gc9a01
This commit is contained in:
commit
906453cd5a
130 changed files with 4484 additions and 2115 deletions
2
.github/workflows/klipper3d-deploy.yaml
vendored
2
.github/workflows/klipper3d-deploy.yaml
vendored
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- name: Install dependencies
|
||||
run: pip install -r docs/_klipper3d/mkdocs-requirements.txt
|
||||
- name: Build MkDocs Pages
|
||||
run: docs/_klipper3d/build-translations.sh
|
||||
run: docs/_klipper3d/build-website.sh
|
||||
- name: Deploy
|
||||
uses: JamesIves/github-pages-deploy-action@v4.4.3
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# This file contains pin mappings for the stock 2020 Creality Ender 5
|
||||
# Pro with the 32-bit Creality 4.2.2 board. To use this config, during
|
||||
# "make menuconfig" select the STM32F103 with a "28KiB bootloader" and
|
||||
# with "Use USB for communication" disabled.
|
||||
# communication interface set to "Serial (on USART1 PA10/PA9)".
|
||||
|
||||
# If you prefer a direct serial connection, in "make menuconfig"
|
||||
# select "Enable extra low-level configuration options" and select the
|
||||
|
|
|
|||
52
config/sample-cartographer-v3.cfg
Normal file
52
config/sample-cartographer-v3.cfg
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# This file contains common pin mappings for the Cartographer board V3
|
||||
# To use this config, the firmware should be compiled for the
|
||||
# STM32F042 with "24 MHz crystal" and
|
||||
# "USB (on PA9/PA10)" or "CAN bus (on PA9/PA10)".
|
||||
# CAN bus requires PA1 GPIO pin to be set at micro-controller start-up
|
||||
# The "carto" micro-controller will be used to control
|
||||
# the components on the board.
|
||||
|
||||
# See docs/Config_Reference.md for a description of parameters.
|
||||
|
||||
[mcu carto]
|
||||
serial: /dev/serial/by-id/usb-Klipper_stm32f042x6_29000380114330394D363620-if00
|
||||
#canbus_uuid: 92cf532ef122
|
||||
|
||||
#[adxl345 carto]
|
||||
#cs_pin: carto:PA3
|
||||
#spi_bus: spi1_PA6_PA7_PA5
|
||||
#axes_map: x,y,z
|
||||
|
||||
[thermistor 50k]
|
||||
temperature1: 25
|
||||
resistance1: 50000
|
||||
temperature2: 50
|
||||
resistance2: 17940
|
||||
temperature3: 100
|
||||
resistance3: 3090
|
||||
|
||||
[temperature_probe carto]
|
||||
pullup_resistor: 10000
|
||||
sensor_type: 50k
|
||||
sensor_pin: carto:PA4
|
||||
min_temp: 0
|
||||
max_temp: 125
|
||||
|
||||
[led carto_led]
|
||||
white_pin: carto:PB5
|
||||
initial_WHITE: 0.03
|
||||
|
||||
[output_pin _LDC1612_en]
|
||||
pin: carto:PA15
|
||||
value: 0 # enable
|
||||
|
||||
[static_pwm_clock ldc1612_clk_in]
|
||||
pin: carto:PB4
|
||||
frequency: 24000000
|
||||
|
||||
[probe_eddy_current carto]
|
||||
sensor_type: ldc1612
|
||||
frequency: 24000000
|
||||
i2c_address: 42
|
||||
i2c_mcu: carto
|
||||
i2c_bus: i2c1_PB6_PB7
|
||||
|
|
@ -8,9 +8,29 @@ All dates in this document are approximate.
|
|||
|
||||
## Changes
|
||||
|
||||
20251122: An option `axis` has been added to `[carriage <name>]` sections
|
||||
for `generic_cartesian` kinematics, allowing arbitrary names for primary
|
||||
carriages. Users are encouraged to explicitly specify `axis` option now.
|
||||
20260109: The status value `{printer.probe.last_z_result}` is
|
||||
deprecated; it will be removed in the near future. Use
|
||||
`{printer.probe.last_probe_position}` instead, and note that this new
|
||||
value already has the probe's configured xyz offsets applied.
|
||||
|
||||
20260109: The g-code console text output from the `PROBE`,
|
||||
`PROBE_ACCURACY`, and similar commands has changed. Now Z heights are
|
||||
reported relative to the nominal bed Z position instead of relative to
|
||||
the probe's configured `z_offset`. Similarly, intermediate probe x and
|
||||
y console reports will also have the probe's configured `x_offset` and
|
||||
`y_offset` applied.
|
||||
|
||||
20260109: The `[screws_tilt_adjust]` module now reports the status
|
||||
variable `{printer.screws_tilt_adjust.result.screw1.z}` with the
|
||||
probe's `z_offset` applied. That is, one would previously need to
|
||||
subtract the probe's configured `z_offset` to find the absolute Z
|
||||
deviation at the given screw location and now one must not apply the
|
||||
`z_offset`.
|
||||
|
||||
20251122: An option `axis` has been added to `[carriage <name>]`
|
||||
sections for `generic_cartesian` kinematics, allowing arbitrary names
|
||||
for primary carriages. Users are encouraged to explicitly specify
|
||||
`axis` option now.
|
||||
|
||||
20251106: The status fields `{printer.toolhead.position}`,
|
||||
`{printer.gcode_move.position}`,
|
||||
|
|
|
|||
|
|
@ -743,7 +743,7 @@ max_accel:
|
|||
|
||||
```
|
||||
|
||||
Then a user must define three carriages for X, Y, and Z axes, e.g.:
|
||||
Then a user must define three primary carriages for X, Y, and Z axes, e.g.:
|
||||
```
|
||||
[carriage carriage_x]
|
||||
axis:
|
||||
|
|
@ -2293,6 +2293,9 @@ sensor_type: ldc1612
|
|||
#samples_tolerance:
|
||||
#samples_tolerance_retries:
|
||||
# See the "probe" section for information on these parameters.
|
||||
#tap_threshold: 0
|
||||
# Noise cutoff/stop trigger threshold delta Hz per sample
|
||||
# See the Eddy_Probe.md for explanation
|
||||
```
|
||||
|
||||
### [axis_twist_compensation]
|
||||
|
|
@ -2452,10 +2455,16 @@ Please note that in this case the `[dual_carriage]` configuration deviates
|
|||
from the configuration described above:
|
||||
```
|
||||
[dual_carriage my_dc_carriage]
|
||||
primary_carriage:
|
||||
# Defines the matching primary carriage of this dual carriage and
|
||||
# the corresponding IDEX axis. Must match a name of a defined `[carriage]`.
|
||||
# This parameter must be provided.
|
||||
#primary_carriage:
|
||||
# Defines the matching carriage on the same gantry as this dual carriage and
|
||||
# the corresponding dual axis. Must match a name of a defined `[carriage]` or
|
||||
# another independent `[dual_carriage]`. If not set, which is a default,
|
||||
# defines a dual carriage independent of a `[carriage]` with the same axis
|
||||
# as this one (e.g. on a different gantry).
|
||||
#axis:
|
||||
# Axis of a carriage, either x or y. If 'primary_carriage' is defined, then
|
||||
# this parameter defaults to the 'axis' parameter of that primary carriage,
|
||||
# otherwise this parameter must be defined.
|
||||
#safe_distance:
|
||||
# The minimum distance (in mm) to enforce between the dual and the primary
|
||||
# carriages. If a G-Code command is executed that will bring the carriages
|
||||
|
|
@ -2464,7 +2473,8 @@ primary_carriage:
|
|||
# position_min and position_max for the dual and primary carriages. If set
|
||||
# to 0 (or safe_distance is unset and position_min and position_max are
|
||||
# identical for the primary and dual carriages), the carriages proximity
|
||||
# checks will be disabled.
|
||||
# checks will be disabled. Only valid for a dual_carriage with a defined
|
||||
# 'primary_carriage'.
|
||||
endstop_pin:
|
||||
#position_min:
|
||||
position_endstop:
|
||||
|
|
@ -3616,6 +3626,20 @@ pin:
|
|||
# These options are deprecated and should no longer be specified.
|
||||
```
|
||||
|
||||
### [static_pwm_clock]
|
||||
|
||||
Static configurable output pin (one may define any number of
|
||||
sections with an "static_pwm_clock" prefix).
|
||||
Pins configured here will be set up as clock output pins.
|
||||
Generally used to provide clock input to other hardware on the board.
|
||||
```
|
||||
[static_pwm_clock my_pin]
|
||||
pin:
|
||||
# The pin to configure as an output. This parameter must be provided.
|
||||
#frequency: 100
|
||||
# Target output frequency.
|
||||
```
|
||||
|
||||
### [pwm_tool]
|
||||
|
||||
Pulse width modulation digital output pins capable of high speed
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ This document describes how to use an
|
|||
[eddy current](https://en.wikipedia.org/wiki/Eddy_current) inductive
|
||||
probe in Klipper.
|
||||
|
||||
Currently, an eddy current probe can not be used for Z homing. The
|
||||
sensor can only be used for Z probing.
|
||||
Currently, an eddy current probe can not precisely home Z (i.e., `G28 Z`).
|
||||
The sensor can precisely do Z probing (i.e., `PROBE ...`).
|
||||
Look at the [homing correction](Eddy_Probe.md#homing-correction-macros)
|
||||
for further details.
|
||||
|
||||
Start by declaring a
|
||||
[probe_eddy_current config section](Config_Reference.md#probe_eddy_current)
|
||||
|
|
@ -24,6 +26,7 @@ named `[probe_eddy_current my_eddy_probe]` then one would run
|
|||
complete in a few seconds. After it completes, issue a `SAVE_CONFIG`
|
||||
command to save the results to the printer.cfg and restart.
|
||||
|
||||
Eddy current is used as a proximity/distance sensor (similar to a laser ruler).
|
||||
The second step in calibration is to correlate the sensor readings to
|
||||
the corresponding Z heights. Home the printer and navigate the
|
||||
toolhead so that the nozzle is near the center of the bed. Then run a
|
||||
|
|
@ -35,7 +38,17 @@ those steps are complete one can `ACCEPT` the position. The tool will
|
|||
then move the toolhead so that the sensor is above the point where the
|
||||
nozzle used to be and run a series of movements to correlate the
|
||||
sensor to Z positions. This will take a couple of minutes. After the
|
||||
tool completes, issue a `SAVE_CONFIG` command to save the results to
|
||||
tool completes it will output the sensor performance data:
|
||||
```
|
||||
probe_eddy_current: noise 0.000642mm, MAD_Hz=11.314 in 2525 queries
|
||||
Total frequency range: 45000.012 Hz
|
||||
z_offset: 0.250 # noise 0.000200mm, MAD_Hz=11.000
|
||||
z_offset: 0.530 # noise 0.000300mm, MAD_Hz=12.000
|
||||
z_offset: 1.010 # noise 0.000400mm, MAD_Hz=14.000
|
||||
z_offset: 2.010 # noise 0.000600mm, MAD_Hz=12.000
|
||||
z_offset: 3.010 # noise 0.000700mm, MAD_Hz=9.000
|
||||
```
|
||||
issue a `SAVE_CONFIG` command to save the results to
|
||||
the printer.cfg and restart.
|
||||
|
||||
After initial calibration it is a good idea to verify that the
|
||||
|
|
@ -55,6 +68,134 @@ surface temperature or sensor hardware temperature can skew the
|
|||
results. It is important that calibration and probing is only done
|
||||
when the printer is at a stable temperature.
|
||||
|
||||
## Homing correction macros
|
||||
|
||||
Because of current limitations, homing and probing
|
||||
are implemented differently for the eddy sensors.
|
||||
As a result, homing suffers from an offset error,
|
||||
while probing handles this correctly.
|
||||
|
||||
To correct the homing offset.
|
||||
One can use the suggested macro inside the homing override or
|
||||
inside the starting G-Code.
|
||||
|
||||
[Force move](Config_Reference.md#force_move) section
|
||||
have to be defined in the config.
|
||||
|
||||
```
|
||||
[gcode_macro _RELOAD_Z_OFFSET_FROM_PROBE]
|
||||
gcode:
|
||||
{% set Z = printer.toolhead.position.z %}
|
||||
SET_KINEMATIC_POSITION Z={Z - printer.probe.last_probe_position.z}
|
||||
|
||||
[gcode_macro SET_Z_FROM_PROBE]
|
||||
gcode:
|
||||
{% set METHOD = params.METHOD | default("automatic") %}
|
||||
PROBE METHOD={METHOD}
|
||||
_RELOAD_Z_OFFSET_FROM_PROBE
|
||||
G0 Z5
|
||||
```
|
||||
|
||||
## Tap calibration
|
||||
|
||||
The Eddy probe measures the resonance frequency of the coil.
|
||||
By the absolute value of the frequency and the calibration curve from
|
||||
`PROBE_EDDY_CURRENT_CALIBRATE`, it is therefore possible to detect
|
||||
where the bed is without physical contact.
|
||||
|
||||
By use of the same knowledge, we know that frequency changes with
|
||||
the distance. It is possible to track that change in real time and
|
||||
detect the time/position where contact happens - a change of frequency
|
||||
starts to change in a different way.
|
||||
For example, stopped to change because of the collision.
|
||||
|
||||
Because eddy output is not perfect: there is sensor noise,
|
||||
mechanical oscillation, thermal expansion and other discrepancies,
|
||||
it is required to calibrate the stop threshold for your machine.
|
||||
Practically, it ensures that the Eddy's output data absolute value
|
||||
change per second (velocity) is high enough - higher than the noise level,
|
||||
and that upon collision it always decreases by at least this value.
|
||||
|
||||
```
|
||||
[probe_eddy_current my_probe]
|
||||
# eddy probe configuration...
|
||||
# Recommended starting values for the tap
|
||||
#samples: 3
|
||||
#samples_tolerance: 0.025
|
||||
#samples_tolerance_retries: 3
|
||||
tap_threshold: 0 # 0 means tap is disabled
|
||||
```
|
||||
|
||||
Before setting it to any other value, it is necessary to install `scipy`:
|
||||
|
||||
```bash
|
||||
~/klippy-env/bin/pip install scipy
|
||||
```
|
||||
|
||||
The suggested calibration routine works as follows:
|
||||
1. Home Z
|
||||
2. Place the toolhead at the center of the bed.
|
||||
3. Move the Z axis far away (30 mm, for example).
|
||||
4. Run `PROBE METHOD=tap`
|
||||
5. If it stops before colliding, increase the `tap_threshold`.
|
||||
|
||||
Repeat until the nozzle softly touches the bed.
|
||||
This is easier to do with a clean nozzle and
|
||||
by visually inspecting the process.
|
||||
|
||||
You can streamline the process by placing the toolhead in the center once.
|
||||
Then, upon config restart, trick the machine into thinking that Z is homed.
|
||||
```
|
||||
SET_KINEMATIC_POSITION X=<middle> Y=<middle> Z=0
|
||||
G0 Z5 # Optional retract
|
||||
PROBE METHOD=tap
|
||||
```
|
||||
|
||||
Here is an example sequence of threshold values to test:
|
||||
```
|
||||
1 -> 5 -> 10 -> 20 -> 40 -> 80 -> 160
|
||||
160 -> 120 -> 100
|
||||
```
|
||||
Your value will normally be between those.
|
||||
- Too high a value leaves a less safe margin for early collision -
|
||||
if something is between the nozzle and the bed, or if the nozzle
|
||||
is too close to the bed before the tap.
|
||||
- Too low - can make the toolhead stop in mid-air
|
||||
because of the noise.
|
||||
|
||||
You can estimate the initial threshold value by analyzing your own
|
||||
calibration routine output:
|
||||
```
|
||||
probe_eddy_current: noise 0.000642mm, MAD_Hz=11.314
|
||||
...
|
||||
z_offset: 1.010 # noise 0.000400mm, MAD_Hz=14.000
|
||||
```
|
||||
The estimation will be:
|
||||
```
|
||||
MAD_Hz * 2
|
||||
11.314 * 2 = 22.628
|
||||
```
|
||||
|
||||
To further fine tune threshold, one can use `PROBE_ACCURACY METHOD=tap`.
|
||||
The range is expected to be about 0.02 mm,
|
||||
with the default probe speed of 5 mm/s.
|
||||
Elevated coil temperature may increase noise and may require additional tuning.
|
||||
|
||||
You can validate the tap precision by measuring the paper thickness
|
||||
from the initial calibration guide. It is expected to be ~0.1mm.
|
||||
|
||||
Tap precision is limited by the sampling frequency and
|
||||
the speed of the descent.
|
||||
If you take 24 photos per second of the moving train, you can only estimate
|
||||
where the train was between photos.
|
||||
|
||||
It is possible to reduce the descending speed. It may require decrease of
|
||||
absolute `tap_threshold` value.
|
||||
|
||||
It is possible to tap over non-conductive surfaces as long as there is metal
|
||||
behind it within the sensor's sensitivity range.
|
||||
Max distance can be approximated to be about 1.5x of the coil's narrowest part.
|
||||
|
||||
## Thermal Drift Calibration
|
||||
|
||||
As with all inductive probes, eddy current probes are subject to
|
||||
|
|
@ -144,3 +285,38 @@ to perform thermal drift calibration:
|
|||
|
||||
As one may conclude, the calibration process outlined above is more challenging
|
||||
and time consuming than most other procedures. It may require practice and several attempts to achieve an optimal calibration.
|
||||
|
||||
## Errors description
|
||||
|
||||
Possible homing errors and actionables:
|
||||
|
||||
- Sensor error
|
||||
- Check logs for detailed error
|
||||
- Eddy I2C STATUS/DATA error.
|
||||
- Check loose wiring.
|
||||
- Try software I2C/decrease I2C rate
|
||||
- Invalid read data
|
||||
- Same as I2C
|
||||
|
||||
Possible sensor errors and actionables:
|
||||
- Frequency over valid hard range
|
||||
- Check frequency configuration
|
||||
- Hardware fault
|
||||
- Frequency over valid soft range
|
||||
- Check frequency configuration
|
||||
- Conversion Watchdog timeout
|
||||
- Hardware fault
|
||||
|
||||
Amplitude Low/High warning messages can mean:
|
||||
- Sensor close to the bed
|
||||
- Sensor far from the bed
|
||||
- Higher temperature than was at the current calibration
|
||||
- Capacitor missing
|
||||
|
||||
On some sensors, it is not possible to completely avoid amplitude
|
||||
warning indicator.
|
||||
|
||||
You can try to redo the `LDC_CALIBRATE_DRIVE_CURRENT` calibration at work
|
||||
temperature or increase `reg_drive_current` by 1-2 from the calibrated value.
|
||||
|
||||
Generally, it is like an engine check light. It may indicate an issue.
|
||||
|
|
|
|||
|
|
@ -350,8 +350,9 @@ reference a defined primary or dual carriage for `generic_cartesian`
|
|||
kinematics or be 0 (for primary carriage) or 1 (for dual carriage)
|
||||
for all other kinematics supporting IDEX. Setting the mode to `PRIMARY`
|
||||
deactivates the other carriage and makes the specified carriage execute
|
||||
subsequent G-Code commands as-is. `COPY` and `MIRROR` modes are supported
|
||||
only for dual carriages. When set to either of these modes, dual carriage
|
||||
subsequent G-Code commands as-is. Before activating `COPY` or `MIRROR`
|
||||
mode for a carriage, a different one must be activated as `PRIMARY` on
|
||||
the same axis. When set to either of these two modes, the carriage
|
||||
will then track the subsequent moves of its primary carriage and either
|
||||
copy relative movements of it (in `COPY` mode) or execute them in the
|
||||
opposite (mirror) direction (in `MIRROR` mode).
|
||||
|
|
@ -1160,23 +1161,25 @@ The following commands are available when a
|
|||
see the [probe calibrate guide](Probe_Calibrate.md)).
|
||||
|
||||
#### PROBE
|
||||
`PROBE [PROBE_SPEED=<mm/s>] [LIFT_SPEED=<mm/s>] [SAMPLES=<count>]
|
||||
[SAMPLE_RETRACT_DIST=<mm>] [SAMPLES_TOLERANCE=<mm>]
|
||||
`PROBE [METHOD=<value>] [PROBE_SPEED=<mm/s>] [LIFT_SPEED=<mm/s>]
|
||||
[SAMPLES=<count>] [SAMPLE_RETRACT_DIST=<mm>] [SAMPLES_TOLERANCE=<mm>]
|
||||
[SAMPLES_TOLERANCE_RETRIES=<count>] [SAMPLES_RESULT=median|average]`:
|
||||
Move the nozzle downwards until the probe triggers. If any of the
|
||||
optional parameters are provided they override their equivalent
|
||||
setting in the [probe config section](Config_Reference.md#probe).
|
||||
The optional parameter `METHOD` is probe-specific.
|
||||
|
||||
#### QUERY_PROBE
|
||||
`QUERY_PROBE`: Report the current status of the probe ("triggered" or
|
||||
"open").
|
||||
|
||||
#### PROBE_ACCURACY
|
||||
`PROBE_ACCURACY [PROBE_SPEED=<mm/s>] [SAMPLES=<count>]
|
||||
`PROBE_ACCURACY [METHOD=<value>] [PROBE_SPEED=<mm/s>] [SAMPLES=<count>]
|
||||
[SAMPLE_RETRACT_DIST=<mm>]`: Calculate the maximum, minimum, average,
|
||||
median, and standard deviation of multiple probe samples. By default,
|
||||
10 SAMPLES are taken. Otherwise the optional parameters default to
|
||||
their equivalent setting in the probe config section.
|
||||
The optional parameter `METHOD` is probe-specific.
|
||||
|
||||
#### PROBE_CALIBRATE
|
||||
`PROBE_CALIBRATE [SPEED=<speed>] [<probe_parameter>=<value>]`: Run a
|
||||
|
|
@ -1237,13 +1240,14 @@ The following commands are available when the
|
|||
is enabled.
|
||||
|
||||
#### QUAD_GANTRY_LEVEL
|
||||
`QUAD_GANTRY_LEVEL [RETRIES=<value>] [RETRY_TOLERANCE=<value>]
|
||||
`QUAD_GANTRY_LEVEL [METHOD=<value>] [RETRIES=<value>] [RETRY_TOLERANCE=<value>]
|
||||
[HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This command
|
||||
will probe the points specified in the config and then make
|
||||
independent adjustments to each Z stepper to compensate for tilt. See
|
||||
the PROBE command for details on the optional probe parameters. The
|
||||
optional `RETRIES`, `RETRY_TOLERANCE`, and `HORIZONTAL_MOVE_Z` values
|
||||
override those options specified in the config file.
|
||||
The optional parameter `METHOD` is probe-specific.
|
||||
|
||||
### [query_adc]
|
||||
|
||||
|
|
@ -1672,10 +1676,11 @@ The following commands are available when the
|
|||
[z_tilt config section](Config_Reference.md#z_tilt) is enabled.
|
||||
|
||||
#### Z_TILT_ADJUST
|
||||
`Z_TILT_ADJUST [RETRIES=<value>] [RETRY_TOLERANCE=<value>]
|
||||
`Z_TILT_ADJUST [METHOD=<value>] [RETRIES=<value>] [RETRY_TOLERANCE=<value>]
|
||||
[HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This command
|
||||
will probe the points specified in the config and then make
|
||||
independent adjustments to each Z stepper to compensate for tilt. See
|
||||
the PROBE command for details on the optional probe parameters. The
|
||||
optional `RETRIES`, `RETRY_TOLERANCE`, and `HORIZONTAL_MOVE_Z` values
|
||||
override those options specified in the config file.
|
||||
The optional parameter `METHOD` is probe-specific.
|
||||
|
|
|
|||
|
|
@ -252,12 +252,12 @@ macro. This requires setting up
|
|||
Here is a simple macro that can accomplish this. Note that the
|
||||
`_HOME_Z_FROM_LAST_PROBE` macro has to be separate because of the way macros
|
||||
work. The sub-call is needed so that the `_HOME_Z_FROM_LAST_PROBE` macro can
|
||||
see the result of the probe in `printer.probe.last_z_result`.
|
||||
see the result of the probe in `printer.probe.last_probe_position`.
|
||||
|
||||
```gcode
|
||||
[gcode_macro _HOME_Z_FROM_LAST_PROBE]
|
||||
gcode:
|
||||
{% set z_probed = printer.probe.last_z_result %}
|
||||
{% set z_probed = printer.probe.last_probe_position.z %}
|
||||
{% set z_position = printer.toolhead.position[2] %}
|
||||
{% set z_actual = z_position - z_probed %}
|
||||
SET_KINEMATIC_POSITION Z={z_actual}
|
||||
|
|
|
|||
|
|
@ -419,10 +419,18 @@ is defined):
|
|||
during the last QUERY_PROBE command. Note, if this is used in a
|
||||
macro, due to the order of template expansion, the QUERY_PROBE
|
||||
command must be run prior to the macro containing this reference.
|
||||
- `last_z_result`: Returns the Z result value of the last PROBE
|
||||
command. Note, if this is used in a macro, due to the order of
|
||||
template expansion, the PROBE (or similar) command must be run prior
|
||||
to the macro containing this reference.
|
||||
- `last_probe_position`: The results of the last `PROBE` command. This
|
||||
value is encoded as a [coordinate](#accessing-coordinates). The
|
||||
probe hardware estimates that if one were to command the toolhead to
|
||||
XY position `last_probe_position.x`,`last_probe_position.y` and
|
||||
descend then the tip of the toolhead would first contact the bed at
|
||||
a Z height of `last_probe_position.z`. These coordinates are
|
||||
relative to the frame (that is, they use the coordinate system
|
||||
specified in the config file). Note, if this is used in a macro,
|
||||
due to the order of template expansion, the `PROBE` command must be
|
||||
run prior to the macro containing this reference.
|
||||
- `last_z_result`: This value is deprecated; it will be removed in the
|
||||
near future.
|
||||
|
||||
## pwm_cycle_time
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#!/bin/bash
|
||||
# This script extracts the Klipper translations and builds multiple
|
||||
# mdocs sites - one for each supported language. See the README file
|
||||
# for additional details.
|
||||
# This script creates the main klipper3d.org website hosted on github.
|
||||
# It extracts the Klipper translations and builds multiple mdocs sites
|
||||
# - one for each supported language. See the README file for
|
||||
# additional details.
|
||||
|
||||
MKDOCS_DIR="docs/_klipper3d/"
|
||||
WORK_DIR="work/"
|
||||
|
|
@ -22,6 +23,11 @@ done < <(egrep -v '^ *(#|$)' ${TRANS_FILE})
|
|||
echo "building site for en"
|
||||
mkdocs build -f ${MKDOCS_MAIN}
|
||||
|
||||
# Cleanup files (mkdocs copies _klipper3d dir and its sitemap doesn't work)
|
||||
rm -rf ${PWD}/site/_klipper3d/__pycache__
|
||||
find ${PWD}/site/_klipper3d ! -name '*.css' -type f -delete
|
||||
rm ${PWD}/site/sitemap.xml ${PWD}/site/sitemap.xml.gz
|
||||
|
||||
# Build each additional language website
|
||||
while IFS="," read dirname langsite langdesc langsearch; do
|
||||
new_docs_dir="${WORK_DIR}lang/${langsite}/docs/"
|
||||
|
|
@ -81,4 +87,10 @@ while IFS="," read dirname langsite langdesc langsearch; do
|
|||
mkdir -p "${PWD}/site/${langsite}/"
|
||||
ln -sf "${PWD}/site/${langsite}/" "${WORK_DIR}lang/${langsite}/site"
|
||||
mkdocs build -f "${new_mkdocs_file}"
|
||||
|
||||
# Cleanup files (mkdocs copies _klipper3d dir and its sitemap doesn't work)
|
||||
rm -rf "${PWD}/site/${langsite}/_klipper3d/__pycache__"
|
||||
find "${PWD}/site/${langsite}/_klipper3d" ! -name '*.css' -type f -delete
|
||||
rm "${PWD}/site/${langsite}/sitemap.xml" "${PWD}/site/${langsite}/sitemap.xml.gz"
|
||||
|
||||
done < <(egrep -v '^ *(#|$)' ${TRANS_FILE})
|
||||
|
|
@ -107,7 +107,7 @@ struct serialqueue {
|
|||
#define MIN_RTO 0.025
|
||||
#define MAX_RTO 5.000
|
||||
#define MAX_PENDING_BLOCKS 12
|
||||
#define MIN_REQTIME_DELTA 0.250
|
||||
#define MIN_REQTIME_DELTA 0.100
|
||||
#define MIN_BACKGROUND_DELTA 0.005
|
||||
#define IDLE_QUERY_TIME 1.0
|
||||
|
||||
|
|
@ -886,9 +886,10 @@ serialqueue_send_batch(struct serialqueue *sq, struct command_queue *cq
|
|||
int len = 0;
|
||||
struct queue_message *qm;
|
||||
list_for_each_entry(qm, msgs, node) {
|
||||
if (qm->min_clock + (1LL<<31) < qm->req_clock
|
||||
if (qm->min_clock + (3LL<<29) < qm->req_clock
|
||||
&& qm->req_clock != BACKGROUND_PRIORITY_CLOCK)
|
||||
qm->min_clock = qm->req_clock - (1LL<<31);
|
||||
// Avoid mcu clock comparison 31-bit overflow issues
|
||||
qm->min_clock = qm->req_clock - (3LL<<29);
|
||||
len += qm->len;
|
||||
}
|
||||
if (! len)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ class ADS1220:
|
|||
self.printer, self._process_batch, self._start_measurements,
|
||||
self._finish_measurements, UPDATE_INTERVAL)
|
||||
# Command Configuration
|
||||
self.attach_probe_cmd = None
|
||||
mcu.add_config_cmd(
|
||||
"config_ads1220 oid=%d spi_oid=%d data_ready_pin=%s"
|
||||
% (self.oid, self.spi.get_oid(), self.data_ready_pin))
|
||||
|
|
@ -105,12 +104,15 @@ class ADS1220:
|
|||
mcu.register_config_callback(self._build_config)
|
||||
self.query_ads1220_cmd = None
|
||||
|
||||
def setup_trigger_analog(self, trigger_analog_oid):
|
||||
self.mcu.add_config_cmd(
|
||||
"ads1220_attach_trigger_analog oid=%d trigger_analog_oid=%d"
|
||||
% (self.oid, trigger_analog_oid), is_init=True)
|
||||
|
||||
def _build_config(self):
|
||||
cmdqueue = self.spi.get_command_queue()
|
||||
self.query_ads1220_cmd = self.mcu.lookup_command(
|
||||
"query_ads1220 oid=%c rest_ticks=%u", cq=cmdqueue)
|
||||
self.attach_probe_cmd = self.mcu.lookup_command(
|
||||
"ads1220_attach_load_cell_probe oid=%c load_cell_probe_oid=%c")
|
||||
self.ffreader.setup_query_command("query_ads1220_status oid=%c",
|
||||
oid=self.oid, cq=cmdqueue)
|
||||
|
||||
|
|
@ -120,6 +122,9 @@ class ADS1220:
|
|||
def get_samples_per_second(self):
|
||||
return self.sps
|
||||
|
||||
def lookup_sensor_error(self, error_code):
|
||||
return "Unknown ads1220 error" % (error_code,)
|
||||
|
||||
# returns a tuple of the minimum and maximum value of the sensor, used to
|
||||
# detect if a data value is saturated
|
||||
def get_range(self):
|
||||
|
|
@ -129,9 +134,6 @@ class ADS1220:
|
|||
def add_client(self, callback):
|
||||
self.batch_bulk.add_client(callback)
|
||||
|
||||
def attach_load_cell_probe(self, load_cell_probe_oid):
|
||||
self.attach_probe_cmd.send([self.oid, load_cell_probe_oid])
|
||||
|
||||
# Measurement decoding
|
||||
def _convert_samples(self, samples):
|
||||
adc_factor = 1. / (1 << 23)
|
||||
|
|
@ -175,6 +177,8 @@ class ADS1220:
|
|||
# read startup register state and validate
|
||||
val = self.read_reg(0x0, 4)
|
||||
if val != RESET_STATE:
|
||||
if self.mcu.is_fileoutput():
|
||||
return
|
||||
raise self.printer.command_error(
|
||||
"Invalid ads1220 reset state (got %s vs %s).\n"
|
||||
"This is generally indicative of connection problems\n"
|
||||
|
|
@ -209,6 +213,8 @@ class ADS1220:
|
|||
self.spi.spi_send(write_command)
|
||||
stored_val = self.read_reg(reg, len(register_bytes))
|
||||
if bytearray(register_bytes) != stored_val:
|
||||
if self.mcu.is_fileoutput():
|
||||
return
|
||||
raise self.printer.command_error(
|
||||
"Failed to set ADS1220 register [0x%x] to %s: got %s. "
|
||||
"This may be a connection problem (e.g. faulty wiring)" % (
|
||||
|
|
|
|||
|
|
@ -51,21 +51,27 @@ class AxisTwistCompensation:
|
|||
self.printer.register_event_handler("probe:update_results",
|
||||
self._update_z_compensation_value)
|
||||
|
||||
def _update_z_compensation_value(self, pos):
|
||||
def _update_z_compensation_value(self, poslist):
|
||||
pos = poslist[0]
|
||||
zo = 0.
|
||||
if self.z_compensations:
|
||||
pos[2] += self._get_interpolated_z_compensation(
|
||||
pos[0], self.z_compensations,
|
||||
zo += self._get_interpolated_z_compensation(
|
||||
pos.test_x, self.z_compensations,
|
||||
self.compensation_start_x,
|
||||
self.compensation_end_x
|
||||
)
|
||||
|
||||
if self.zy_compensations:
|
||||
pos[2] += self._get_interpolated_z_compensation(
|
||||
pos[1], self.zy_compensations,
|
||||
zo += self._get_interpolated_z_compensation(
|
||||
pos.test_y, self.zy_compensations,
|
||||
self.compensation_start_y,
|
||||
self.compensation_end_y
|
||||
)
|
||||
|
||||
pos = manual_probe.ProbeResult(pos.bed_x, pos.bed_y, pos.bed_z + zo,
|
||||
pos.test_x, pos.test_y, pos.test_z)
|
||||
poslist[0] = pos
|
||||
|
||||
def _get_interpolated_z_compensation(
|
||||
self, coord, z_compensations,
|
||||
comp_start,
|
||||
|
|
@ -101,8 +107,7 @@ class Calibrater:
|
|||
self.gcode = self.printer.lookup_object('gcode')
|
||||
self.probe = None
|
||||
# probe settings are set to none, until they are available
|
||||
self.lift_speed, self.probe_x_offset, self.probe_y_offset, _ = \
|
||||
None, None, None, None
|
||||
self.lift_speed = None
|
||||
self.printer.register_event_handler("klippy:connect",
|
||||
self._handle_connect)
|
||||
self.speed = compensation.speed
|
||||
|
|
@ -129,8 +134,6 @@ class Calibrater:
|
|||
raise self.printer.config_error(
|
||||
"AXIS_TWIST_COMPENSATION requires [probe] to be defined")
|
||||
self.lift_speed = self.probe.get_probe_params()['lift_speed']
|
||||
self.probe_x_offset, self.probe_y_offset, _ = \
|
||||
self.probe.get_offsets()
|
||||
|
||||
def _register_gcode_handlers(self):
|
||||
# register gcode handlers
|
||||
|
|
@ -148,6 +151,7 @@ class Calibrater:
|
|||
|
||||
def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd):
|
||||
self.gcmd = gcmd
|
||||
probe_x_offset, probe_y_offset, _ = self.probe.get_offsets(gcmd)
|
||||
sample_count = gcmd.get_int('SAMPLE_COUNT', DEFAULT_SAMPLE_COUNT)
|
||||
axis = gcmd.get('AXIS', 'X')
|
||||
|
||||
|
|
@ -219,7 +223,7 @@ class Calibrater:
|
|||
"Invalid axis.")
|
||||
|
||||
probe_points = self._calculate_probe_points(
|
||||
nozzle_points, self.probe_x_offset, self.probe_y_offset)
|
||||
nozzle_points, probe_x_offset, probe_y_offset)
|
||||
|
||||
# verify no other manual probe is in progress
|
||||
manual_probe.verify_no_manual_probe(self.printer)
|
||||
|
|
@ -231,7 +235,7 @@ class Calibrater:
|
|||
self._calibration(probe_points, nozzle_points, interval_dist)
|
||||
|
||||
def _calculate_probe_points(self, nozzle_points,
|
||||
probe_x_offset, probe_y_offset):
|
||||
probe_x_offset, probe_y_offset):
|
||||
# calculate the points to put the nozzle at
|
||||
# returned as a list of tuples
|
||||
probe_points = []
|
||||
|
|
@ -267,7 +271,7 @@ class Calibrater:
|
|||
|
||||
# probe the point
|
||||
pos = probe.run_single_probe(self.probe, self.gcmd)
|
||||
self.current_measured_z = pos[2]
|
||||
self.current_measured_z = pos.bed_z
|
||||
|
||||
# horizontal_move_z (to prevent probe trigger or hitting bed)
|
||||
self._move_helper((None, None, self.horizontal_move_z))
|
||||
|
|
@ -286,14 +290,14 @@ class Calibrater:
|
|||
# returns a callback function for the manual probe
|
||||
is_end = self.current_point_index == len(probe_points) - 1
|
||||
|
||||
def callback(kin_pos):
|
||||
if kin_pos is None:
|
||||
def callback(mpresult):
|
||||
if mpresult is None:
|
||||
# probe was cancelled
|
||||
self.gcmd.respond_info(
|
||||
"AXIS_TWIST_COMPENSATION_CALIBRATE: Probe cancelled, "
|
||||
"calibration aborted")
|
||||
return
|
||||
z_offset = self.current_measured_z - kin_pos[2]
|
||||
z_offset = self.current_measured_z - mpresult.bed_z
|
||||
self.results.append(z_offset)
|
||||
if is_end:
|
||||
# end of calibration
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ class BedMesh:
|
|||
result["calibration"] = self.bmc.dump_calibration(gcmd)
|
||||
else:
|
||||
result["calibration"] = self.bmc.dump_calibration()
|
||||
offsets = [0, 0, 0] if prb is None else prb.get_offsets()
|
||||
offsets = [0, 0, 0] if prb is None else prb.get_offsets(gcmd)
|
||||
result["probe_offsets"] = offsets
|
||||
result["axis_minimum"] = th_sts["axis_minimum"]
|
||||
result["axis_maximum"] = th_sts["axis_maximum"]
|
||||
|
|
@ -651,9 +651,9 @@ class BedMeshCalibrate:
|
|||
except BedMeshError as e:
|
||||
raise gcmd.error(str(e))
|
||||
self.probe_mgr.start_probe(gcmd)
|
||||
def probe_finalize(self, offsets, positions):
|
||||
z_offset = offsets[2]
|
||||
positions = [[round(p[0], 2), round(p[1], 2), p[2]]
|
||||
def probe_finalize(self, positions):
|
||||
z_offset = 0.
|
||||
positions = [[round(p.bed_x, 2), round(p.bed_y, 2), p.bed_z]
|
||||
for p in positions]
|
||||
if self.probe_mgr.get_zero_ref_mode() == ZrefMode.PROBE:
|
||||
ref_pos = positions.pop()
|
||||
|
|
@ -682,7 +682,7 @@ class BedMeshCalibrate:
|
|||
idx_offset = 0
|
||||
start_idx = 0
|
||||
for i, pts in substitutes.items():
|
||||
fpt = [p - o for p, o in zip(base_points[i], offsets[:2])]
|
||||
fpt = list(base_points[i][:2])
|
||||
# offset the index to account for additional samples
|
||||
idx = i + idx_offset
|
||||
# Add "normal" points
|
||||
|
|
@ -702,7 +702,7 @@ class BedMeshCalibrate:
|
|||
|
||||
# validate length of result
|
||||
if len(base_points) != len(positions):
|
||||
self._dump_points(probed_pts, positions, offsets)
|
||||
self._dump_points(probed_pts, positions)
|
||||
raise self.gcode.error(
|
||||
"bed_mesh: invalid position list size, "
|
||||
"generated count: %d, probed count: %d"
|
||||
|
|
@ -713,7 +713,7 @@ class BedMeshCalibrate:
|
|||
row = []
|
||||
prev_pos = base_points[0]
|
||||
for pos, result in zip(base_points, positions):
|
||||
offset_pos = [p - o for p, o in zip(pos, offsets[:2])]
|
||||
offset_pos = pos[:2]
|
||||
if (
|
||||
not isclose(offset_pos[0], result[0], abs_tol=.5) or
|
||||
not isclose(offset_pos[1], result[1], abs_tol=.5)
|
||||
|
|
@ -786,7 +786,7 @@ class BedMeshCalibrate:
|
|||
self.gcode.respond_info("Mesh Bed Leveling Complete")
|
||||
if self._profile_name is not None:
|
||||
self.bedmesh.save_profile(self._profile_name)
|
||||
def _dump_points(self, probed_pts, corrected_pts, offsets):
|
||||
def _dump_points(self, probed_pts, corrected_pts):
|
||||
# logs generated points with offset applied, points received
|
||||
# from the finalize callback, and the list of corrected points
|
||||
points = self.probe_mgr.get_base_points()
|
||||
|
|
@ -797,7 +797,7 @@ class BedMeshCalibrate:
|
|||
for i in list(range(max_len)):
|
||||
gen_pt = probed_pt = corr_pt = ""
|
||||
if i < len(points):
|
||||
off_pt = [p - o for p, o in zip(points[i], offsets[:2])]
|
||||
off_pt = points[i][:2]
|
||||
gen_pt = "(%.2f, %.2f)" % tuple(off_pt)
|
||||
if i < len(probed_pts):
|
||||
probed_pt = "(%.2f, %.2f, %.4f)" % tuple(probed_pts[i])
|
||||
|
|
@ -1209,7 +1209,7 @@ class RapidScanHelper:
|
|||
gcmd_params["SAMPLE_TIME"] = half_window * 2
|
||||
self._raise_tool(gcmd, scan_height)
|
||||
probe_session = pprobe.start_probe_session(gcmd)
|
||||
offsets = pprobe.get_offsets()
|
||||
offsets = pprobe.get_offsets(gcmd)
|
||||
initial_move = True
|
||||
for pos, is_probe_pt in self.probe_manager.iter_rapid_path():
|
||||
pos = self._apply_offsets(pos[:2], offsets)
|
||||
|
|
@ -1221,7 +1221,7 @@ class RapidScanHelper:
|
|||
probe_session.run_probe(gcmd)
|
||||
results = probe_session.pull_probed_results()
|
||||
toolhead.get_last_move_time()
|
||||
self.finalize_callback(offsets, results)
|
||||
self.finalize_callback(results)
|
||||
probe_session.end_probe_session()
|
||||
|
||||
def _raise_tool(self, gcmd, scan_height):
|
||||
|
|
|
|||
|
|
@ -58,19 +58,17 @@ class BedTiltCalibrate:
|
|||
cmd_BED_TILT_CALIBRATE_help = "Bed tilt calibration script"
|
||||
def cmd_BED_TILT_CALIBRATE(self, gcmd):
|
||||
self.probe_helper.start_probe(gcmd)
|
||||
def probe_finalize(self, offsets, positions):
|
||||
def probe_finalize(self, positions):
|
||||
# Setup for coordinate descent analysis
|
||||
z_offset = offsets[2]
|
||||
logging.info("Calculating bed_tilt with: %s", positions)
|
||||
params = { 'x_adjust': self.bedtilt.x_adjust,
|
||||
'y_adjust': self.bedtilt.y_adjust,
|
||||
'z_adjust': z_offset }
|
||||
'z_adjust': 0. }
|
||||
logging.info("Initial bed_tilt parameters: %s", params)
|
||||
# Perform coordinate descent
|
||||
def adjusted_height(pos, params):
|
||||
x, y, z = pos
|
||||
return (z - x*params['x_adjust'] - y*params['y_adjust']
|
||||
- params['z_adjust'])
|
||||
return (pos.bed_z - pos.bed_x*params['x_adjust']
|
||||
- pos.bed_y*params['y_adjust'] - params['z_adjust'])
|
||||
def errorfunc(params):
|
||||
total_error = 0.
|
||||
for pos in positions:
|
||||
|
|
@ -81,8 +79,7 @@ class BedTiltCalibrate:
|
|||
# Update current bed_tilt calculations
|
||||
x_adjust = new_params['x_adjust']
|
||||
y_adjust = new_params['y_adjust']
|
||||
z_adjust = (new_params['z_adjust'] - z_offset
|
||||
- x_adjust * offsets[0] - y_adjust * offsets[1])
|
||||
z_adjust = new_params['z_adjust']
|
||||
self.bedtilt.update_adjust(x_adjust, y_adjust, z_adjust)
|
||||
# Log and report results
|
||||
logging.info("Calculated bed_tilt parameters: %s", new_params)
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ class BLTouchProbe:
|
|||
config, self, self.mcu_endstop.query_endstop)
|
||||
self.probe_offsets = probe.ProbeOffsetsHelper(config)
|
||||
self.param_helper = probe.ProbeParameterHelper(config)
|
||||
self.homing_helper = probe.HomingViaProbeHelper(config, self,
|
||||
self.param_helper)
|
||||
self.homing_helper = probe.HomingViaProbeHelper(
|
||||
config, self, self.probe_offsets, self.param_helper)
|
||||
self.probe_session = probe.ProbeSessionHelper(
|
||||
config, self.param_helper, self.homing_helper.start_probe_session)
|
||||
# Register BLTOUCH_DEBUG command
|
||||
|
|
@ -80,8 +80,8 @@ class BLTouchProbe:
|
|||
self.handle_connect)
|
||||
def get_probe_params(self, gcmd=None):
|
||||
return self.param_helper.get_probe_params(gcmd)
|
||||
def get_offsets(self):
|
||||
return self.probe_offsets.get_offsets()
|
||||
def get_offsets(self, gcmd=None):
|
||||
return self.probe_offsets.get_offsets(gcmd)
|
||||
def get_status(self, eventtime):
|
||||
return self.cmd_helper.get_status(eventtime)
|
||||
def start_probe_session(self, gcmd):
|
||||
|
|
|
|||
|
|
@ -240,6 +240,10 @@ def MCU_I2C_from_config(config, default_addr=None, default_speed=100000):
|
|||
for name in ['scl', 'sda']]
|
||||
sw_pin_params = [ppins.lookup_pin(config.get(name), share_type=name)
|
||||
for name in sw_pin_names]
|
||||
for pin_params in sw_pin_params:
|
||||
if pin_params['chip'] != i2c_mcu:
|
||||
raise ppins.error("%s: i2c pins must be on same mcu" % (
|
||||
config.get_name(),))
|
||||
sw_pins = tuple([pin_params['pin'] for pin_params in sw_pin_params])
|
||||
bus = None
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -152,12 +152,12 @@ class DeltaCalibrate:
|
|||
"%.3f,%.3f,%.3f" % tuple(spos1))
|
||||
configfile.set(section, "distance%d_pos2" % (i,),
|
||||
"%.3f,%.3f,%.3f" % tuple(spos2))
|
||||
def probe_finalize(self, offsets, positions):
|
||||
def probe_finalize(self, positions):
|
||||
# Convert positions into (z_offset, stable_position) pairs
|
||||
z_offset = offsets[2]
|
||||
kin = self.printer.lookup_object('toolhead').get_kinematics()
|
||||
delta_params = kin.get_calibration()
|
||||
probe_positions = [(z_offset, delta_params.calc_stable_position(p))
|
||||
csp = kin.get_calibration().calc_stable_position
|
||||
probe_positions = [(p.test_z - p.bed_z,
|
||||
csp([p.test_x, p.test_y, p.test_z]))
|
||||
for p in positions]
|
||||
# Perform analysis
|
||||
self.calculate_params(probe_positions, self.last_distances)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class ExcludeObject:
|
|||
offset = [0.] * num_coord
|
||||
self.extrusion_offsets[ename] = offset
|
||||
if len(offset) < num_coord:
|
||||
offset.extend([0.] * (len(num_coord) - len(offset)))
|
||||
offset.extend([0.] * (num_coord - len(offset)))
|
||||
return offset
|
||||
|
||||
def get_position(self):
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class Fan:
|
|||
self.last_req_value = value
|
||||
self.last_fan_value = self.max_power
|
||||
self.mcu_fan.set_pwm(print_time, self.max_power)
|
||||
return "delay", self.kick_start_time
|
||||
return "repeat", print_time + self.kick_start_time
|
||||
self.last_fan_value = self.last_req_value = value
|
||||
self.mcu_fan.set_pwm(print_time, value)
|
||||
def set_speed(self, value, print_time=None):
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class GCodeMove:
|
|||
def _get_gcode_position(self):
|
||||
p = [lp - bp for lp, bp in zip(self.last_position, self.base_position)]
|
||||
p[3] /= self.extrude_factor
|
||||
return p[:4]
|
||||
return p
|
||||
def _get_gcode_speed(self):
|
||||
return self.speed / self.speed_factor
|
||||
def _get_gcode_speed_override(self):
|
||||
|
|
@ -191,7 +191,7 @@ class GCodeMove:
|
|||
def cmd_M114(self, gcmd):
|
||||
# Get Current Position
|
||||
p = self._get_gcode_position()
|
||||
gcmd.respond_raw("X:%.3f Y:%.3f Z:%.3f E:%.3f" % tuple(p))
|
||||
gcmd.respond_raw("X:%.3f Y:%.3f Z:%.3f E:%.3f" % tuple(p[:4]))
|
||||
def cmd_M220(self, gcmd):
|
||||
# Set speed factor override percentage
|
||||
value = gcmd.get_float('S', 100., above=0.) / (60. * 100.)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ class HX71xBase:
|
|||
self._finish_measurements, UPDATE_INTERVAL)
|
||||
# Command Configuration
|
||||
self.query_hx71x_cmd = None
|
||||
self.attach_probe_cmd = None
|
||||
mcu.add_config_cmd(
|
||||
"config_hx71x oid=%d gain_channel=%d dout_pin=%s sclk_pin=%s"
|
||||
% (self.oid, self.gain_channel, self.dout_pin, self.sclk_pin))
|
||||
|
|
@ -62,14 +61,17 @@ class HX71xBase:
|
|||
|
||||
mcu.register_config_callback(self._build_config)
|
||||
|
||||
def setup_trigger_analog(self, trigger_analog_oid):
|
||||
self.mcu.add_config_cmd(
|
||||
"hx71x_attach_trigger_analog oid=%d trigger_analog_oid=%d"
|
||||
% (self.oid, trigger_analog_oid), is_init=True)
|
||||
|
||||
def _build_config(self):
|
||||
cmd_queue = self.mcu.alloc_command_queue()
|
||||
self.query_hx71x_cmd = self.mcu.lookup_command(
|
||||
"query_hx71x oid=%c rest_ticks=%u")
|
||||
self.attach_probe_cmd = self.mcu.lookup_command(
|
||||
"hx71x_attach_load_cell_probe oid=%c load_cell_probe_oid=%c")
|
||||
"query_hx71x oid=%c rest_ticks=%u", cq=cmd_queue)
|
||||
self.ffreader.setup_query_command("query_hx71x_status oid=%c",
|
||||
oid=self.oid,
|
||||
cq=self.mcu.alloc_command_queue())
|
||||
oid=self.oid, cq=cmd_queue)
|
||||
|
||||
|
||||
def get_mcu(self):
|
||||
|
|
@ -78,6 +80,9 @@ class HX71xBase:
|
|||
def get_samples_per_second(self):
|
||||
return self.sps
|
||||
|
||||
def lookup_sensor_error(self, error_code):
|
||||
return "Unknown hx71x error %d" % (error_code,)
|
||||
|
||||
# returns a tuple of the minimum and maximum value of the sensor, used to
|
||||
# detect if a data value is saturated
|
||||
def get_range(self):
|
||||
|
|
@ -87,9 +92,6 @@ class HX71xBase:
|
|||
def add_client(self, callback):
|
||||
self.batch_bulk.add_client(callback)
|
||||
|
||||
def attach_load_cell_probe(self, load_cell_probe_oid):
|
||||
self.attach_probe_cmd.send([self.oid, load_cell_probe_oid])
|
||||
|
||||
# Measurement decoding
|
||||
def _convert_samples(self, samples):
|
||||
adc_factor = 1. / (1 << 23)
|
||||
|
|
|
|||
|
|
@ -84,11 +84,15 @@ class LDC1612:
|
|||
default_addr=LDC1612_ADDR,
|
||||
default_speed=400000)
|
||||
self.mcu = mcu = self.i2c.get_mcu()
|
||||
self._sensor_errors = {}
|
||||
self.oid = oid = mcu.create_oid()
|
||||
self.query_ldc1612_cmd = None
|
||||
self.ldc1612_setup_home_cmd = self.query_ldc1612_home_state_cmd = None
|
||||
self.frequency = config.getint("frequency", DEFAULT_LDC1612_FREQ,
|
||||
2000000, 40000000)
|
||||
self.clock_freq = config.getint("frequency", DEFAULT_LDC1612_FREQ,
|
||||
2000000, 40000000)
|
||||
# Coil frequency divider, assume 12MHz is BTT Eddy
|
||||
# BTT Eddy's coil frequency is > 1/4 of reference clock
|
||||
self.sensor_div = 1 if self.clock_freq != DEFAULT_LDC1612_FREQ else 2
|
||||
self.freq_conv = float(self.clock_freq * self.sensor_div) / (1<<28)
|
||||
if config.get('intb_pin', None) is not None:
|
||||
ppins = config.get_printer().lookup_object("pins")
|
||||
pin_params = ppins.lookup_pin(config.get('intb_pin'))
|
||||
|
|
@ -115,22 +119,25 @@ class LDC1612:
|
|||
hdr = ('time', 'frequency', 'z')
|
||||
self.batch_bulk.add_mux_endpoint("ldc1612/dump_ldc1612", "sensor",
|
||||
self.name, {'header': hdr})
|
||||
def setup_trigger_analog(self, trigger_analog_oid):
|
||||
self.mcu.add_config_cmd(
|
||||
"ldc1612_attach_trigger_analog oid=%d trigger_analog_oid=%d"
|
||||
% (self.oid, trigger_analog_oid), is_init=True)
|
||||
def _build_config(self):
|
||||
cmdqueue = self.i2c.get_command_queue()
|
||||
self.query_ldc1612_cmd = self.mcu.lookup_command(
|
||||
"query_ldc1612 oid=%c rest_ticks=%u", cq=cmdqueue)
|
||||
self.ffreader.setup_query_command("query_status_ldc1612 oid=%c",
|
||||
oid=self.oid, cq=cmdqueue)
|
||||
self.ldc1612_setup_home_cmd = self.mcu.lookup_command(
|
||||
"ldc1612_setup_home oid=%c clock=%u threshold=%u"
|
||||
" trsync_oid=%c trigger_reason=%c error_reason=%c", cq=cmdqueue)
|
||||
self.query_ldc1612_home_state_cmd = self.mcu.lookup_query_command(
|
||||
"query_ldc1612_home_state oid=%c",
|
||||
"ldc1612_home_state oid=%c homing=%c trigger_clock=%u",
|
||||
oid=self.oid, cq=cmdqueue)
|
||||
errors = self.mcu.get_enumerations().get("ldc1612_error:", {})
|
||||
self._sensor_errors = {v: k for k, v in errors.items()}
|
||||
def get_mcu(self):
|
||||
return self.i2c.get_mcu()
|
||||
def get_samples_per_second(self):
|
||||
return self.data_rate
|
||||
def read_reg(self, reg):
|
||||
if self.mcu.is_fileoutput():
|
||||
return 0
|
||||
params = self.i2c.i2c_read([reg], 2)
|
||||
response = bytearray(params['response'])
|
||||
return (response[0] << 8) | response[1]
|
||||
|
|
@ -139,48 +146,61 @@ class LDC1612:
|
|||
minclock=minclock)
|
||||
def add_client(self, cb):
|
||||
self.batch_bulk.add_client(cb)
|
||||
# Homing
|
||||
def setup_home(self, print_time, trigger_freq,
|
||||
trsync_oid, hit_reason, err_reason):
|
||||
clock = self.mcu.print_time_to_clock(print_time)
|
||||
tfreq = int(trigger_freq * (1<<28) / float(self.frequency) + 0.5)
|
||||
self.ldc1612_setup_home_cmd.send(
|
||||
[self.oid, clock, tfreq, trsync_oid, hit_reason, err_reason])
|
||||
def clear_home(self):
|
||||
self.ldc1612_setup_home_cmd.send([self.oid, 0, 0, 0, 0, 0])
|
||||
if self.mcu.is_fileoutput():
|
||||
return 0.
|
||||
params = self.query_ldc1612_home_state_cmd.send([self.oid])
|
||||
tclock = self.mcu.clock32_to_clock64(params['trigger_clock'])
|
||||
return self.mcu.clock_to_print_time(tclock)
|
||||
def lookup_sensor_error(self, error):
|
||||
return self._sensor_errors.get(error, "Unknown ldc1612 error")
|
||||
def convert_frequency(self, freq):
|
||||
return int(freq / self.freq_conv + 0.5)
|
||||
# Measurement decoding
|
||||
def _convert_samples(self, samples):
|
||||
freq_conv = float(self.frequency) / (1<<28)
|
||||
freq_conv = self.freq_conv
|
||||
count = 0
|
||||
errors = {}
|
||||
def log_once(msg):
|
||||
if not errors.get(msg, 0):
|
||||
errors[msg] = 0
|
||||
errors[msg] += 1
|
||||
for ptime, val in samples:
|
||||
mv = val & 0x0fffffff
|
||||
if mv != val:
|
||||
if val > 0x03ffffff or val == 0x0:
|
||||
self.last_error_count += 1
|
||||
if (val >> 16 & 0xffff) == 0xffff:
|
||||
# Encoded error from sensor_ldc1612.c
|
||||
log_once(self.lookup_sensor_error(val & 0xffff))
|
||||
continue
|
||||
error_bits = (val >> 28) & 0x0f
|
||||
if error_bits & 0x8 or mv == 0x0000000:
|
||||
log_once("Frequency under valid range")
|
||||
if error_bits & 0x4 or mv > 0x3ffffff:
|
||||
type = "hard" if error_bits & 0x4 else "soft"
|
||||
log_once("Frequency over valid %s range" % (type))
|
||||
if error_bits & 0x2:
|
||||
log_once("Conversion Watchdog timeout")
|
||||
if error_bits & 0x1:
|
||||
log_once("Amplitude Low/High warning")
|
||||
samples[count] = (round(ptime, 6), round(freq_conv * mv, 3), 999.9)
|
||||
count += 1
|
||||
del samples[count:]
|
||||
for msg in errors:
|
||||
logging.error("%s: %s (%d)" % (self.name, msg, errors[msg]))
|
||||
# Start, stop, and process message batches
|
||||
def _start_measurements(self):
|
||||
# In case of miswiring, testing LDC1612 device ID prevents treating
|
||||
# noise or wrong signal as a correctly initialized device
|
||||
manuf_id = self.read_reg(REG_MANUFACTURER_ID)
|
||||
dev_id = self.read_reg(REG_DEVICE_ID)
|
||||
if manuf_id != LDC1612_MANUF_ID or dev_id != LDC1612_DEV_ID:
|
||||
if ((manuf_id != LDC1612_MANUF_ID or dev_id != LDC1612_DEV_ID)
|
||||
and not self.mcu.is_fileoutput()):
|
||||
raise self.printer.command_error(
|
||||
"Invalid ldc1612 id (got %x,%x vs %x,%x).\n"
|
||||
"This is generally indicative of connection problems\n"
|
||||
"(e.g. faulty wiring) or a faulty ldc1612 chip."
|
||||
% (manuf_id, dev_id, LDC1612_MANUF_ID, LDC1612_DEV_ID))
|
||||
# Setup chip in requested query rate
|
||||
rcount0 = self.frequency / (16. * (self.data_rate - 4))
|
||||
rcount0 = self.clock_freq / (16. * self.data_rate)
|
||||
self.set_reg(REG_RCOUNT0, int(rcount0 + 0.5))
|
||||
self.set_reg(REG_OFFSET0, 0)
|
||||
self.set_reg(REG_SETTLECOUNT0, int(SETTLETIME*self.frequency/16. + .5))
|
||||
self.set_reg(REG_CLOCK_DIVIDERS0, (1 << 12) | 1)
|
||||
self.set_reg(REG_SETTLECOUNT0, int(SETTLETIME*self.clock_freq/16. + .5))
|
||||
self.set_reg(REG_CLOCK_DIVIDERS0, (self.sensor_div << 12) | 1)
|
||||
self.set_reg(REG_ERROR_CONFIG, (0x1f << 11) | 1)
|
||||
self.set_reg(REG_MUX_CONFIG, 0x0208 | DEGLITCH)
|
||||
self.set_reg(REG_CONFIG, 0x001 | (1<<12) | (1<<10) | (1<<9))
|
||||
|
|
|
|||
|
|
@ -313,6 +313,8 @@ class LoadCellSampleCollector:
|
|||
self._errors = 0
|
||||
overflows = self._overflows
|
||||
self._overflows = 0
|
||||
if self._mcu.is_fileoutput():
|
||||
samples = [(0., 0., 0.)]
|
||||
return samples, (errors, overflows) if errors or overflows else 0
|
||||
|
||||
def _collect_until(self, timeout):
|
||||
|
|
@ -324,6 +326,8 @@ class LoadCellSampleCollector:
|
|||
raise self._printer.command_error(
|
||||
"LoadCellSampleCollector timed out! Errors: %i,"
|
||||
" Overflows: %i" % (self._errors, self._overflows))
|
||||
if self._mcu.is_fileoutput():
|
||||
break
|
||||
self._reactor.pause(now + RETRY_DELAY)
|
||||
return self._finish_collecting()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,15 +5,12 @@
|
|||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging, math
|
||||
import mcu
|
||||
from . import probe, sos_filter, load_cell, hx71x, ads1220
|
||||
from . import probe, trigger_analog, load_cell, hx71x, ads1220
|
||||
|
||||
np = None # delay NumPy import until configuration time
|
||||
|
||||
# constants for fixed point numbers
|
||||
Q2_INT_BITS = 2
|
||||
Q2_FRAC_BITS = (32 - (1 + Q2_INT_BITS))
|
||||
Q16_INT_BITS = 16
|
||||
Q16_FRAC_BITS = (32 - (1 + Q16_INT_BITS))
|
||||
# MCU SOS filter scaled to "fractional grams" for consistent sensor precision
|
||||
FRAC_GRAMS_CONV = 32768.0
|
||||
|
||||
|
||||
class TapAnalysis:
|
||||
|
|
@ -163,19 +160,16 @@ class ContinuousTareFilter:
|
|||
|
||||
# create a filter design from the parameters
|
||||
def design_filter(self, error_func):
|
||||
design = sos_filter.DigitalFilter(self.sps, error_func, self.drift,
|
||||
return trigger_analog.DigitalFilter(self.sps, error_func, self.drift,
|
||||
self.drift_delay, self.buzz, self.buzz_delay, self.notches,
|
||||
self.notch_quality)
|
||||
fixed_filter = sos_filter.FixedPointSosFilter(
|
||||
design.get_filter_sections(), design.get_initial_state(),
|
||||
Q2_INT_BITS, Q16_INT_BITS)
|
||||
return fixed_filter
|
||||
|
||||
|
||||
# Combine ContinuousTareFilter and SosFilter into an easy-to-use class
|
||||
class ContinuousTareFilterHelper:
|
||||
def __init__(self, config, sensor, cmd_queue):
|
||||
def __init__(self, config, sensor, sos_filter):
|
||||
self._sensor = sensor
|
||||
self._sos_filter = sos_filter
|
||||
self._sps = self._sensor.get_samples_per_second()
|
||||
max_filter_frequency = math.floor(self._sps / 2.)
|
||||
# setup filter parameters
|
||||
|
|
@ -200,8 +194,8 @@ class ContinuousTareFilterHelper:
|
|||
self._config_design = self._build_filter()
|
||||
# filter design currently inside the MCU
|
||||
self._active_design = self._config_design
|
||||
self._sos_filter = self._create_filter(
|
||||
self._active_design.design_filter(config.error), cmd_queue)
|
||||
design = self._active_design.design_filter(config.error)
|
||||
self._sos_filter.set_filter_design(design)
|
||||
|
||||
def _build_filter(self, gcmd=None):
|
||||
drift = self._drift_param.get(gcmd)
|
||||
|
|
@ -214,21 +208,14 @@ class ContinuousTareFilterHelper:
|
|||
return ContinuousTareFilter(self._sps, drift, drift_delay, buzz,
|
||||
buzz_delay, notches, notch_quality)
|
||||
|
||||
def _create_filter(self, fixed_filter, cmd_queue):
|
||||
return sos_filter.SosFilter(self._sensor.get_mcu(), cmd_queue,
|
||||
fixed_filter, 4)
|
||||
|
||||
def update_from_command(self, gcmd, cq=None):
|
||||
gcmd_filter = self._build_filter(gcmd)
|
||||
# if filters are identical, no change required
|
||||
if self._active_design == gcmd_filter:
|
||||
return
|
||||
# update MCU filter from GCode command
|
||||
self._sos_filter.change_filter(
|
||||
self._active_design.design_filter(gcmd.error))
|
||||
|
||||
def get_sos_filter(self):
|
||||
return self._sos_filter
|
||||
design = self._active_design.design_filter(gcmd.error)
|
||||
self._sos_filter.set_filter_design(design)
|
||||
|
||||
|
||||
# check results from the collector for errors and raise an exception is found
|
||||
|
|
@ -246,7 +233,6 @@ class LoadCellProbeConfigHelper:
|
|||
self._printer = config.get_printer()
|
||||
self._load_cell = load_cell_inst
|
||||
self._sensor = load_cell_inst.get_sensor()
|
||||
self._rest_time = 1. / float(self._sensor.get_samples_per_second())
|
||||
# Collect 4 x 60hz power cycles of data to average across power noise
|
||||
self._tare_time_param = floatParamHelper(config, 'tare_time',
|
||||
default=4. / 60., minval=0.01, maxval=1.0)
|
||||
|
|
@ -267,9 +253,6 @@ class LoadCellProbeConfigHelper:
|
|||
def get_safety_limit_grams(self, gcmd=None):
|
||||
return self._force_safety_limit_param.get(gcmd)
|
||||
|
||||
def get_rest_time(self):
|
||||
return self._rest_time
|
||||
|
||||
def get_safety_range(self, gcmd=None):
|
||||
counts_per_gram = self._load_cell.get_counts_per_gram()
|
||||
# calculate the safety band
|
||||
|
|
@ -284,137 +267,33 @@ class LoadCellProbeConfigHelper:
|
|||
raise cmd_err("Load cell force_safety_limit exceeds sensor range!")
|
||||
return safety_min, safety_max
|
||||
|
||||
# calculate 1/counts_per_gram in Q2 fixed point
|
||||
# calculate 1/counts_per_gram
|
||||
def get_grams_per_count(self):
|
||||
counts_per_gram = self._load_cell.get_counts_per_gram()
|
||||
# The counts_per_gram could be so large that it becomes 0.0 when
|
||||
# converted to Q2 format. This would mean the ADC range only measures a
|
||||
# few grams which seems very unlikely. Treat this as an error:
|
||||
if counts_per_gram >= 2**Q2_FRAC_BITS:
|
||||
# sent to the mcu. This would mean the ADC range only measures
|
||||
# a few grams which seems very unlikely. Treat this as an error:
|
||||
if counts_per_gram >= (1<<29):
|
||||
raise OverflowError("counts_per_gram value is too large to filter")
|
||||
return sos_filter.to_fixed_32((1. / counts_per_gram), Q2_INT_BITS)
|
||||
return 1. / counts_per_gram
|
||||
|
||||
|
||||
# McuLoadCellProbe is the interface to `load_cell_probe` on the MCU
|
||||
# This also manages the SosFilter so all commands use one command queue
|
||||
class McuLoadCellProbe:
|
||||
WATCHDOG_MAX = 3
|
||||
ERROR_SAFETY_RANGE = mcu.MCU_trsync.REASON_COMMS_TIMEOUT + 1
|
||||
ERROR_OVERFLOW = mcu.MCU_trsync.REASON_COMMS_TIMEOUT + 2
|
||||
ERROR_WATCHDOG = mcu.MCU_trsync.REASON_COMMS_TIMEOUT + 3
|
||||
|
||||
def __init__(self, config, load_cell_inst, sos_filter_inst, config_helper,
|
||||
trigger_dispatch):
|
||||
self._printer = config.get_printer()
|
||||
self._load_cell = load_cell_inst
|
||||
self._sos_filter = sos_filter_inst
|
||||
self._config_helper = config_helper
|
||||
self._sensor = load_cell_inst.get_sensor()
|
||||
self._mcu = self._sensor.get_mcu()
|
||||
# configure MCU objects
|
||||
self._dispatch = trigger_dispatch
|
||||
self._cmd_queue = self._dispatch.get_command_queue()
|
||||
self._oid = self._mcu.create_oid()
|
||||
self._config_commands()
|
||||
self._home_cmd = None
|
||||
self._query_cmd = None
|
||||
self._set_range_cmd = None
|
||||
self._mcu.register_config_callback(self._build_config)
|
||||
self._printer.register_event_handler("klippy:connect", self._on_connect)
|
||||
|
||||
def _config_commands(self):
|
||||
self._sos_filter.create_filter()
|
||||
self._mcu.add_config_cmd(
|
||||
"config_load_cell_probe oid=%d sos_filter_oid=%d" % (
|
||||
self._oid, self._sos_filter.get_oid()))
|
||||
|
||||
def _build_config(self):
|
||||
# Lookup commands
|
||||
self._query_cmd = self._mcu.lookup_query_command(
|
||||
"load_cell_probe_query_state oid=%c",
|
||||
"load_cell_probe_state oid=%c is_homing_trigger=%c "
|
||||
"trigger_ticks=%u", oid=self._oid, cq=self._cmd_queue)
|
||||
self._set_range_cmd = self._mcu.lookup_command(
|
||||
"load_cell_probe_set_range"
|
||||
" oid=%c safety_counts_min=%i safety_counts_max=%i tare_counts=%i"
|
||||
" trigger_grams=%u grams_per_count=%i", cq=self._cmd_queue)
|
||||
self._home_cmd = self._mcu.lookup_command(
|
||||
"load_cell_probe_home oid=%c trsync_oid=%c trigger_reason=%c"
|
||||
" error_reason=%c clock=%u rest_ticks=%u timeout=%u",
|
||||
cq=self._cmd_queue)
|
||||
|
||||
# the sensor data stream is connected on the MCU at the ready event
|
||||
def _on_connect(self):
|
||||
self._sensor.attach_load_cell_probe(self._oid)
|
||||
|
||||
def get_oid(self):
|
||||
return self._oid
|
||||
|
||||
def get_mcu(self):
|
||||
return self._mcu
|
||||
|
||||
def get_load_cell(self):
|
||||
return self._load_cell
|
||||
|
||||
def get_dispatch(self):
|
||||
return self._dispatch
|
||||
|
||||
def set_endstop_range(self, tare_counts, gcmd=None):
|
||||
# update the load cell so it reflects the new tare value
|
||||
self._load_cell.tare(tare_counts)
|
||||
# update internal tare value
|
||||
safety_min, safety_max = self._config_helper.get_safety_range(gcmd)
|
||||
args = [self._oid, safety_min, safety_max, int(tare_counts),
|
||||
self._config_helper.get_trigger_force_grams(gcmd),
|
||||
self._config_helper.get_grams_per_count()]
|
||||
self._set_range_cmd.send(args)
|
||||
self._sos_filter.reset_filter()
|
||||
|
||||
def home_start(self, print_time):
|
||||
clock = self._mcu.print_time_to_clock(print_time)
|
||||
rest_time = self._config_helper.get_rest_time()
|
||||
rest_ticks = self._mcu.seconds_to_clock(rest_time)
|
||||
self._home_cmd.send([self._oid, self._dispatch.get_oid(),
|
||||
mcu.MCU_trsync.REASON_ENDSTOP_HIT, self.ERROR_SAFETY_RANGE, clock,
|
||||
rest_ticks, self.WATCHDOG_MAX], reqclock=clock)
|
||||
|
||||
def clear_home(self):
|
||||
params = self._query_cmd.send([self._oid])
|
||||
# The time of the first sample that triggered is in "trigger_ticks"
|
||||
trigger_ticks = self._mcu.clock32_to_clock64(params['trigger_ticks'])
|
||||
# clear trsync from load_cell_endstop
|
||||
self._home_cmd.send([self._oid, 0, 0, 0, 0, 0, 0, 0])
|
||||
return self._mcu.clock_to_print_time(trigger_ticks)
|
||||
|
||||
|
||||
# Execute probing moves using the McuLoadCellProbe
|
||||
# Execute probing moves using the MCU_trigger_analog
|
||||
class LoadCellProbingMove:
|
||||
ERROR_MAP = {
|
||||
mcu.MCU_trsync.REASON_COMMS_TIMEOUT: "Communication timeout during "
|
||||
"homing",
|
||||
McuLoadCellProbe.ERROR_SAFETY_RANGE: "Load Cell Probe Error: load "
|
||||
"exceeds safety limit",
|
||||
McuLoadCellProbe.ERROR_OVERFLOW: "Load Cell Probe Error: fixed point "
|
||||
"math overflow",
|
||||
McuLoadCellProbe.ERROR_WATCHDOG: "Load Cell Probe Error: timed out "
|
||||
"waiting for sensor data"
|
||||
}
|
||||
|
||||
def __init__(self, config, mcu_load_cell_probe, param_helper,
|
||||
def __init__(self, config, load_cell_inst, mcu_trigger_analog, param_helper,
|
||||
continuous_tare_filter_helper, config_helper):
|
||||
self._printer = config.get_printer()
|
||||
self._mcu_load_cell_probe = mcu_load_cell_probe
|
||||
self._load_cell = load_cell_inst
|
||||
self._mcu_trigger_analog = mcu_trigger_analog
|
||||
self._param_helper = param_helper
|
||||
self._continuous_tare_filter_helper = continuous_tare_filter_helper
|
||||
self._config_helper = config_helper
|
||||
self._mcu = mcu_load_cell_probe.get_mcu()
|
||||
self._load_cell = mcu_load_cell_probe.get_load_cell()
|
||||
self._mcu = mcu_trigger_analog.get_mcu()
|
||||
self._z_min_position = probe.lookup_minimum_z(config)
|
||||
self._dispatch = mcu_load_cell_probe.get_dispatch()
|
||||
probe.LookupZSteppers(config, self._dispatch.add_stepper)
|
||||
dispatch = mcu_trigger_analog.get_dispatch()
|
||||
probe.LookupZSteppers(config, dispatch.add_stepper)
|
||||
# internal state tracking
|
||||
self._tare_counts = 0
|
||||
self._last_trigger_time = 0
|
||||
|
||||
def _start_collector(self):
|
||||
toolhead = self._printer.lookup_object('toolhead')
|
||||
|
|
@ -436,37 +315,22 @@ class LoadCellProbingMove:
|
|||
tare_counts = np.average(np.array(tare_samples)[:, 2].astype(float))
|
||||
# update sos_filter with any gcode parameter changes
|
||||
self._continuous_tare_filter_helper.update_from_command(gcmd)
|
||||
self._mcu_load_cell_probe.set_endstop_range(tare_counts, gcmd)
|
||||
# update the load cell so it reflects the new tare value
|
||||
self._load_cell.tare(tare_counts)
|
||||
# update raw range
|
||||
safety_min, safety_max = self._config_helper.get_safety_range(gcmd)
|
||||
self._mcu_trigger_analog.set_raw_range(safety_min, safety_max)
|
||||
# update internal tare value
|
||||
gpc = self._config_helper.get_grams_per_count() * FRAC_GRAMS_CONV
|
||||
sos_filter = self._mcu_trigger_analog.get_sos_filter()
|
||||
Q17_14_FRAC_BITS = 14
|
||||
sos_filter.set_offset_scale(int(-tare_counts), gpc, Q17_14_FRAC_BITS)
|
||||
# update trigger
|
||||
trigger_val = self._config_helper.get_trigger_force_grams(gcmd)
|
||||
trigger_frac_grams = trigger_val * FRAC_GRAMS_CONV
|
||||
self._mcu_trigger_analog.set_trigger("abs_ge", trigger_frac_grams)
|
||||
|
||||
def _home_start(self, print_time):
|
||||
# start trsync
|
||||
trigger_completion = self._dispatch.start(print_time)
|
||||
self._mcu_load_cell_probe.home_start(print_time)
|
||||
return trigger_completion
|
||||
|
||||
def home_start(self, print_time, sample_time, sample_count, rest_time,
|
||||
triggered=True):
|
||||
return self._home_start(print_time)
|
||||
|
||||
def home_wait(self, home_end_time):
|
||||
self._dispatch.wait_end(home_end_time)
|
||||
# trigger has happened, now to find out why...
|
||||
res = self._dispatch.stop()
|
||||
# clear the homing state so it stops processing samples
|
||||
self._last_trigger_time = self._mcu_load_cell_probe.clear_home()
|
||||
if res >= mcu.MCU_trsync.REASON_COMMS_TIMEOUT:
|
||||
error = "Load Cell Probe Error: unknown reason code %i" % (res,)
|
||||
if res in self.ERROR_MAP:
|
||||
error = self.ERROR_MAP[res]
|
||||
raise self._printer.command_error(error)
|
||||
if res != mcu.MCU_trsync.REASON_ENDSTOP_HIT:
|
||||
return 0.
|
||||
return self._last_trigger_time
|
||||
|
||||
def get_steppers(self):
|
||||
return self._dispatch.get_steppers()
|
||||
|
||||
# Probe towards z_min until the load_cell_probe on the MCU triggers
|
||||
# Probe towards z_min until the trigger_analog on the MCU triggers
|
||||
def probing_move(self, gcmd):
|
||||
# do not permit probing if the load cell is not calibrated
|
||||
if not self._load_cell.is_calibrated():
|
||||
|
|
@ -482,20 +346,22 @@ class LoadCellProbingMove:
|
|||
# start collector after tare samples are consumed
|
||||
collector = self._start_collector()
|
||||
# do homing move
|
||||
return phoming.probing_move(self, pos, speed), collector
|
||||
epos = phoming.probing_move(self._mcu_trigger_analog, pos, speed)
|
||||
return epos, collector
|
||||
|
||||
# Wait for the MCU to trigger with no movement
|
||||
def probing_test(self, gcmd, timeout):
|
||||
self._pause_and_tare(gcmd)
|
||||
toolhead = self._printer.lookup_object('toolhead')
|
||||
print_time = toolhead.get_last_move_time()
|
||||
self._home_start(print_time)
|
||||
return self.home_wait(print_time + timeout)
|
||||
self._mcu_trigger_analog.home_start(print_time, 0., 0, 0.)
|
||||
return self._mcu_trigger_analog.home_wait(print_time + timeout)
|
||||
|
||||
def get_status(self, eventtime):
|
||||
trig_time = self._mcu_trigger_analog.get_last_trigger_time()
|
||||
return {
|
||||
'tare_counts': self._tare_counts,
|
||||
'last_trigger_time': self._last_trigger_time,
|
||||
'last_trigger_time': trig_time,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -542,9 +408,11 @@ class TappingMove:
|
|||
|
||||
# ProbeSession that implements Tap logic
|
||||
class TapSession:
|
||||
def __init__(self, config, tapping_move, probe_params_helper):
|
||||
def __init__(self, config, tapping_move,
|
||||
probe_offsets, probe_params_helper):
|
||||
self._printer = config.get_printer()
|
||||
self._tapping_move = tapping_move
|
||||
self._probe_offsets = probe_offsets
|
||||
self._probe_params_helper = probe_params_helper
|
||||
# Session state
|
||||
self._results = []
|
||||
|
|
@ -558,7 +426,8 @@ class TapSession:
|
|||
# probe until a single good sample is returned or retries are exhausted
|
||||
def run_probe(self, gcmd):
|
||||
epos, is_good = self._tapping_move.run_tap(gcmd)
|
||||
self._results.append(epos)
|
||||
res = self._probe_offsets.create_probe_result(epos)
|
||||
self._results.append(res)
|
||||
|
||||
def pull_probed_results(self):
|
||||
res = self._results
|
||||
|
|
@ -615,22 +484,23 @@ class LoadCellPrinterProbe:
|
|||
# Read all user configuration and build modules
|
||||
config_helper = LoadCellProbeConfigHelper(config, self._load_cell)
|
||||
self._mcu = self._load_cell.get_sensor().get_mcu()
|
||||
trigger_dispatch = mcu.TriggerDispatch(self._mcu)
|
||||
continuous_tare_filter_helper = ContinuousTareFilterHelper(config,
|
||||
sensor, trigger_dispatch.get_command_queue())
|
||||
self._mcu_trigger_analog = trigger_analog.MCU_trigger_analog(sensor)
|
||||
cmd_queue = self._mcu_trigger_analog.get_dispatch().get_command_queue()
|
||||
sos_filter = trigger_analog.MCU_SosFilter(self._mcu, cmd_queue, 4)
|
||||
self._mcu_trigger_analog.setup_sos_filter(sos_filter)
|
||||
continuous_tare_filter_helper = ContinuousTareFilterHelper(
|
||||
config, sensor, sos_filter)
|
||||
# Probe Interface
|
||||
self._param_helper = probe.ProbeParameterHelper(config)
|
||||
self._cmd_helper = probe.ProbeCommandHelper(config, self)
|
||||
self._probe_offsets = probe.ProbeOffsetsHelper(config)
|
||||
self._mcu_load_cell_probe = McuLoadCellProbe(config, self._load_cell,
|
||||
continuous_tare_filter_helper.get_sos_filter(), config_helper,
|
||||
trigger_dispatch)
|
||||
load_cell_probing_move = LoadCellProbingMove(config,
|
||||
self._mcu_load_cell_probe, self._param_helper,
|
||||
load_cell_probing_move = LoadCellProbingMove(config, self._load_cell,
|
||||
self._mcu_trigger_analog, self._param_helper,
|
||||
continuous_tare_filter_helper, config_helper)
|
||||
self._tapping_move = TappingMove(config, load_cell_probing_move,
|
||||
config_helper)
|
||||
tap_session = TapSession(config, self._tapping_move, self._param_helper)
|
||||
tap_session = TapSession(config, self._tapping_move,
|
||||
self._probe_offsets, self._param_helper)
|
||||
self._probe_session = probe.ProbeSessionHelper(config,
|
||||
self._param_helper, tap_session.start_probe_session)
|
||||
# printer integration
|
||||
|
|
@ -641,8 +511,8 @@ class LoadCellPrinterProbe:
|
|||
def get_probe_params(self, gcmd=None):
|
||||
return self._param_helper.get_probe_params(gcmd)
|
||||
|
||||
def get_offsets(self):
|
||||
return self._probe_offsets.get_offsets()
|
||||
def get_offsets(self, gcmd=None):
|
||||
return self._probe_offsets.get_offsets(gcmd)
|
||||
|
||||
def start_probe_session(self, gcmd):
|
||||
return self._probe_session.start_probe_session(gcmd)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
# Helper script for manual z height probing
|
||||
#
|
||||
# Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2019-2025 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging, bisect
|
||||
import logging, bisect, collections
|
||||
|
||||
# Main probe results tuple. The probe estimates that if the toollhead
|
||||
# is commanded to xy position (bed_x, bed_y) and then descends, the
|
||||
# nozzle will contact the bed at a toolhead z position of bed_z. The
|
||||
# probe test itself was performed while the toolhead was at xyz
|
||||
# position (test_x, test_y, test_z). All coordinates are relative to
|
||||
# the frame (the coordinate system used in the config file).
|
||||
ProbeResult = collections.namedtuple('probe_result', [
|
||||
'bed_x', 'bed_y', 'bed_z', 'test_x', 'test_y', 'test_z'])
|
||||
|
||||
# Helper to lookup the Z stepper config section
|
||||
def lookup_z_endstop_config(config):
|
||||
|
|
@ -62,9 +71,9 @@ class ManualProbe:
|
|||
self.cmd_Z_OFFSET_APPLY_DELTA_ENDSTOPS,
|
||||
desc=self.cmd_Z_OFFSET_APPLY_ENDSTOP_help)
|
||||
self.reset_status()
|
||||
def manual_probe_finalize(self, kin_pos):
|
||||
if kin_pos is not None:
|
||||
self.gcode.respond_info("Z position is %.3f" % (kin_pos[2],))
|
||||
def manual_probe_finalize(self, mpresult):
|
||||
if mpresult is not None:
|
||||
self.gcode.respond_info("Z position is %.3f" % (mpresult.bed_z,))
|
||||
def reset_status(self):
|
||||
self.status = {
|
||||
'is_active': False,
|
||||
|
|
@ -77,10 +86,10 @@ class ManualProbe:
|
|||
cmd_MANUAL_PROBE_help = "Start manual probe helper script"
|
||||
def cmd_MANUAL_PROBE(self, gcmd):
|
||||
ManualProbeHelper(self.printer, gcmd, self.manual_probe_finalize)
|
||||
def z_endstop_finalize(self, kin_pos):
|
||||
if kin_pos is None:
|
||||
def z_endstop_finalize(self, mpresult):
|
||||
if mpresult is None:
|
||||
return
|
||||
z_pos = self.z_position_endstop - kin_pos[2]
|
||||
z_pos = self.z_position_endstop - mpresult.bed_z
|
||||
self.gcode.respond_info(
|
||||
"%s: position_endstop: %.3f\n"
|
||||
"The SAVE_CONFIG command will update the printer config file\n"
|
||||
|
|
@ -271,10 +280,11 @@ class ManualProbeHelper:
|
|||
self.gcode.register_command('NEXT', None)
|
||||
self.gcode.register_command('ABORT', None)
|
||||
self.gcode.register_command('TESTZ', None)
|
||||
kin_pos = None
|
||||
mpresult = None
|
||||
if success:
|
||||
kin_pos = self.get_kinematics_pos()
|
||||
self.finalize_callback(kin_pos)
|
||||
mpresult = ProbeResult(*(kin_pos[:3] + kin_pos[:3]))
|
||||
self.finalize_callback(mpresult)
|
||||
|
||||
def load_config(config):
|
||||
return ManualProbe(config)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ class PrinterMultiPin:
|
|||
def set_digital(self, print_time, value):
|
||||
for mcu_pin in self.mcu_pins:
|
||||
mcu_pin.set_digital(print_time, value)
|
||||
def next_aligned_print_time(self, print_time, allow_early=0.):
|
||||
return print_time
|
||||
def set_pwm(self, print_time, value):
|
||||
for mcu_pin in self.mcu_pins:
|
||||
mcu_pin.set_pwm(print_time, value)
|
||||
|
|
|
|||
|
|
@ -38,18 +38,23 @@ class GCodeRequestQueue:
|
|||
pos += 1
|
||||
req_pt, req_val = rqueue[pos]
|
||||
# Invoke callback for the request
|
||||
min_wait = 0.
|
||||
ret = self.callback(next_time, req_val)
|
||||
if ret is not None:
|
||||
# Handle special cases
|
||||
action, min_wait = ret
|
||||
action, next_min_time = ret
|
||||
self.next_min_flush_time = max(self.next_min_flush_time,
|
||||
next_min_time)
|
||||
if action == "discard":
|
||||
del rqueue[:pos+1]
|
||||
continue
|
||||
if action == "delay":
|
||||
if action == "reschedule":
|
||||
del rqueue[:pos]
|
||||
continue
|
||||
if action == "repeat":
|
||||
pos -= 1
|
||||
del rqueue[:pos+1]
|
||||
self.next_min_flush_time = next_time + max(min_wait, min_sched_time)
|
||||
self.next_min_flush_time = max(self.next_min_flush_time,
|
||||
next_time + min_sched_time)
|
||||
# Ensure following queue items are flushed
|
||||
self.motion_queuing.note_mcu_movequeue_activity(
|
||||
self.next_min_flush_time, is_step_gen=False)
|
||||
|
|
@ -68,15 +73,20 @@ class GCodeRequestQueue:
|
|||
while 1:
|
||||
next_time = max(print_time, self.next_min_flush_time)
|
||||
# Invoke callback for the request
|
||||
action, min_wait = "normal", 0.
|
||||
action, next_min_time = "normal", 0.
|
||||
ret = self.callback(next_time, value)
|
||||
if ret is not None:
|
||||
# Handle special cases
|
||||
action, min_wait = ret
|
||||
action, next_min_time = ret
|
||||
self.next_min_flush_time = max(self.next_min_flush_time,
|
||||
next_min_time)
|
||||
if action == "discard":
|
||||
break
|
||||
self.next_min_flush_time = next_time + max(min_wait, min_sched_time)
|
||||
if action != "delay":
|
||||
if action == "reschedule":
|
||||
continue
|
||||
self.next_min_flush_time = max(self.next_min_flush_time,
|
||||
next_time + min_sched_time)
|
||||
if action != "repeat":
|
||||
break
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,12 @@ can travel further (the Z minimum position can be negative).
|
|||
def calc_probe_z_average(positions, method='average'):
|
||||
if method != 'median':
|
||||
# Use mean average
|
||||
count = float(len(positions))
|
||||
return [sum([pos[i] for pos in positions]) / count
|
||||
for i in range(3)]
|
||||
inv_count = 1. / float(len(positions))
|
||||
return manual_probe.ProbeResult(
|
||||
*[sum([pos[i] for pos in positions]) * inv_count
|
||||
for i in range(len(positions[0]))])
|
||||
# Use median
|
||||
z_sorted = sorted(positions, key=(lambda p: p[2]))
|
||||
z_sorted = sorted(positions, key=(lambda p: p.bed_z))
|
||||
middle = len(positions) // 2
|
||||
if (len(positions) & 1) == 1:
|
||||
# odd number of samples
|
||||
|
|
@ -36,7 +37,8 @@ def calc_probe_z_average(positions, method='average'):
|
|||
|
||||
# Helper to implement common probing commands
|
||||
class ProbeCommandHelper:
|
||||
def __init__(self, config, probe, query_endstop=None):
|
||||
def __init__(self, config, probe, query_endstop=None,
|
||||
can_set_z_offset=True):
|
||||
self.printer = config.get_printer()
|
||||
self.probe = probe
|
||||
self.query_endstop = query_endstop
|
||||
|
|
@ -47,24 +49,28 @@ class ProbeCommandHelper:
|
|||
gcode.register_command('QUERY_PROBE', self.cmd_QUERY_PROBE,
|
||||
desc=self.cmd_QUERY_PROBE_help)
|
||||
# PROBE command
|
||||
self.last_probe_position = gcode.Coord((0., 0., 0.))
|
||||
self.last_z_result = 0.
|
||||
gcode.register_command('PROBE', self.cmd_PROBE,
|
||||
desc=self.cmd_PROBE_help)
|
||||
# PROBE_CALIBRATE command
|
||||
self.probe_calibrate_z = 0.
|
||||
gcode.register_command('PROBE_CALIBRATE', self.cmd_PROBE_CALIBRATE,
|
||||
desc=self.cmd_PROBE_CALIBRATE_help)
|
||||
self.probe_calibrate_info = None
|
||||
if can_set_z_offset:
|
||||
gcode.register_command('PROBE_CALIBRATE', self.cmd_PROBE_CALIBRATE,
|
||||
desc=self.cmd_PROBE_CALIBRATE_help)
|
||||
# Other commands
|
||||
gcode.register_command('PROBE_ACCURACY', self.cmd_PROBE_ACCURACY,
|
||||
desc=self.cmd_PROBE_ACCURACY_help)
|
||||
gcode.register_command('Z_OFFSET_APPLY_PROBE',
|
||||
self.cmd_Z_OFFSET_APPLY_PROBE,
|
||||
desc=self.cmd_Z_OFFSET_APPLY_PROBE_help)
|
||||
if can_set_z_offset:
|
||||
gcode.register_command('Z_OFFSET_APPLY_PROBE',
|
||||
self.cmd_Z_OFFSET_APPLY_PROBE,
|
||||
desc=self.cmd_Z_OFFSET_APPLY_PROBE_help)
|
||||
def _move(self, coord, speed):
|
||||
self.printer.lookup_object('toolhead').manual_move(coord, speed)
|
||||
def get_status(self, eventtime):
|
||||
return {'name': self.name,
|
||||
'last_query': self.last_state,
|
||||
'last_probe_position': self.last_probe_position,
|
||||
'last_z_result': self.last_z_result}
|
||||
cmd_QUERY_PROBE_help = "Return the status of the z-probe"
|
||||
def cmd_QUERY_PROBE(self, gcmd):
|
||||
|
|
@ -78,12 +84,18 @@ class ProbeCommandHelper:
|
|||
cmd_PROBE_help = "Probe Z-height at current XY position"
|
||||
def cmd_PROBE(self, gcmd):
|
||||
pos = run_single_probe(self.probe, gcmd)
|
||||
gcmd.respond_info("Result is z=%.6f" % (pos[2],))
|
||||
self.last_z_result = pos[2]
|
||||
def probe_calibrate_finalize(self, kin_pos):
|
||||
if kin_pos is None:
|
||||
gcmd.respond_info("Result: at %.3f,%.3f estimate contact at z=%.6f"
|
||||
% (pos.bed_x, pos.bed_y, pos.bed_z))
|
||||
gcode = self.printer.lookup_object('gcode')
|
||||
self.last_probe_position = gcode.Coord((pos.bed_x, pos.bed_y,
|
||||
pos.bed_z))
|
||||
x_offset, y_offset, z_offset = self.probe.get_offsets(gcmd)
|
||||
self.last_z_result = pos.bed_z + z_offset # Deprecated
|
||||
def probe_calibrate_finalize(self, mpresult):
|
||||
if mpresult is None:
|
||||
return
|
||||
z_offset = self.probe_calibrate_z - kin_pos[2]
|
||||
ppos, offsets = self.probe_calibrate_info
|
||||
z_offset = offsets[2] - mpresult.bed_z + ppos.bed_z
|
||||
gcode = self.printer.lookup_object('gcode')
|
||||
gcode.respond_info(
|
||||
"%s: z_offset: %.3f\n"
|
||||
|
|
@ -96,17 +108,17 @@ class ProbeCommandHelper:
|
|||
manual_probe.verify_no_manual_probe(self.printer)
|
||||
params = self.probe.get_probe_params(gcmd)
|
||||
# Perform initial probe
|
||||
curpos = run_single_probe(self.probe, gcmd)
|
||||
ppos = run_single_probe(self.probe, gcmd)
|
||||
# Move away from the bed
|
||||
self.probe_calibrate_z = curpos[2]
|
||||
curpos = self.printer.lookup_object('toolhead').get_position()
|
||||
curpos[2] += 5.
|
||||
self._move(curpos, params['lift_speed'])
|
||||
# Move the nozzle over the probe point
|
||||
x_offset, y_offset, z_offset = self.probe.get_offsets()
|
||||
curpos[0] += x_offset
|
||||
curpos[1] += y_offset
|
||||
curpos[0] = ppos.bed_x
|
||||
curpos[1] = ppos.bed_y
|
||||
self._move(curpos, params['probe_speed'])
|
||||
# Start manual probe
|
||||
self.probe_calibrate_info = (ppos, self.probe.get_offsets(gcmd))
|
||||
manual_probe.ManualProbeHelper(self.printer, gcmd,
|
||||
self.probe_calibrate_finalize)
|
||||
cmd_PROBE_ACCURACY_help = "Probe Z-height accuracy at current XY position"
|
||||
|
|
@ -114,11 +126,11 @@ class ProbeCommandHelper:
|
|||
params = self.probe.get_probe_params(gcmd)
|
||||
sample_count = gcmd.get_int("SAMPLES", 10, minval=1)
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
pos = toolhead.get_position()
|
||||
start_pos = toolhead.get_position()
|
||||
gcmd.respond_info("PROBE_ACCURACY at X:%.3f Y:%.3f Z:%.3f"
|
||||
" (samples=%d retract=%.3f"
|
||||
" speed=%.1f lift_speed=%.1f)\n"
|
||||
% (pos[0], pos[1], pos[2],
|
||||
% (start_pos[0], start_pos[1], start_pos[2],
|
||||
sample_count, params['sample_retract_dist'],
|
||||
params['probe_speed'], params['lift_speed']))
|
||||
# Create dummy gcmd with SAMPLES=1
|
||||
|
|
@ -134,21 +146,21 @@ class ProbeCommandHelper:
|
|||
probe_session.run_probe(fo_gcmd)
|
||||
probe_num += 1
|
||||
# Retract
|
||||
pos = toolhead.get_position()
|
||||
liftpos = [None, None, pos[2] + params['sample_retract_dist']]
|
||||
lift_z = toolhead.get_position()[2] + params['sample_retract_dist']
|
||||
liftpos = [start_pos[0], start_pos[1], lift_z]
|
||||
self._move(liftpos, params['lift_speed'])
|
||||
positions = probe_session.pull_probed_results()
|
||||
probe_session.end_probe_session()
|
||||
# Calculate maximum, minimum and average values
|
||||
max_value = max([p[2] for p in positions])
|
||||
min_value = min([p[2] for p in positions])
|
||||
max_value = max([p.bed_z for p in positions])
|
||||
min_value = min([p.bed_z for p in positions])
|
||||
range_value = max_value - min_value
|
||||
avg_value = calc_probe_z_average(positions, 'average')[2]
|
||||
median = calc_probe_z_average(positions, 'median')[2]
|
||||
avg_value = calc_probe_z_average(positions, 'average').bed_z
|
||||
median = calc_probe_z_average(positions, 'median').bed_z
|
||||
# calculate the standard deviation
|
||||
deviation_sum = 0
|
||||
for i in range(len(positions)):
|
||||
deviation_sum += pow(positions[i][2] - avg_value, 2.)
|
||||
deviation_sum += pow(positions[i].bed_z - avg_value, 2.)
|
||||
sigma = (deviation_sum / len(positions)) ** 0.5
|
||||
# Show information
|
||||
gcmd.respond_info(
|
||||
|
|
@ -162,7 +174,7 @@ class ProbeCommandHelper:
|
|||
if offset == 0:
|
||||
gcmd.respond_info("Nothing to do: Z Offset is 0")
|
||||
return
|
||||
z_offset = self.probe.get_offsets()[2]
|
||||
z_offset = self.probe.get_offsets(gcmd)[2]
|
||||
new_calibrate = z_offset - offset
|
||||
gcmd.respond_info(
|
||||
"%s: z_offset: %.3f\n"
|
||||
|
|
@ -195,9 +207,10 @@ class LookupZSteppers:
|
|||
|
||||
# Homing via probe:z_virtual_endstop
|
||||
class HomingViaProbeHelper:
|
||||
def __init__(self, config, mcu_probe, param_helper):
|
||||
def __init__(self, config, mcu_probe, probe_offsets, param_helper):
|
||||
self.printer = config.get_printer()
|
||||
self.mcu_probe = mcu_probe
|
||||
self.probe_offsets = probe_offsets
|
||||
self.param_helper = param_helper
|
||||
self.multi_probe_pending = False
|
||||
self.z_min_position = lookup_minimum_z(config)
|
||||
|
|
@ -256,7 +269,9 @@ class HomingViaProbeHelper:
|
|||
pos[2] = self.z_min_position
|
||||
speed = self.param_helper.get_probe_params(gcmd)['probe_speed']
|
||||
phoming = self.printer.lookup_object('homing')
|
||||
self.results.append(phoming.probing_move(self.mcu_probe, pos, speed))
|
||||
ppos = phoming.probing_move(self.mcu_probe, pos, speed)
|
||||
res = self.probe_offsets.create_probe_result(ppos)
|
||||
self.results.append(res)
|
||||
def pull_probed_results(self):
|
||||
res = self.results
|
||||
self.results = []
|
||||
|
|
@ -364,12 +379,12 @@ class ProbeSessionHelper:
|
|||
reason += HINT_TIMEOUT
|
||||
raise self.printer.command_error(reason)
|
||||
# Allow axis_twist_compensation to update results
|
||||
self.printer.send_event("probe:update_results", epos)
|
||||
self.printer.send_event("probe:update_results", [epos])
|
||||
# Report results
|
||||
gcode = self.printer.lookup_object('gcode')
|
||||
gcode.respond_info("probe at %.3f,%.3f is z=%.6f"
|
||||
% (epos[0], epos[1], epos[2]))
|
||||
return epos[:3]
|
||||
gcode.respond_info("probe: at %.3f,%.3f bed will contact at z=%.6f"
|
||||
% (epos.bed_x, epos.bed_y, epos.bed_z))
|
||||
return epos
|
||||
def run_probe(self, gcmd):
|
||||
if self.hw_probe_session is None:
|
||||
self._probe_state_error()
|
||||
|
|
@ -384,7 +399,7 @@ class ProbeSessionHelper:
|
|||
pos = self._probe(gcmd)
|
||||
positions.append(pos)
|
||||
# Check samples tolerance
|
||||
z_positions = [p[2] for p in positions]
|
||||
z_positions = [p.bed_z for p in positions]
|
||||
if max(z_positions)-min(z_positions) > params['samples_tolerance']:
|
||||
if retries >= params['samples_tolerance_retries']:
|
||||
raise gcmd.error("Probe samples exceed samples_tolerance")
|
||||
|
|
@ -393,8 +408,9 @@ class ProbeSessionHelper:
|
|||
positions = []
|
||||
# Retract
|
||||
if len(positions) < sample_count:
|
||||
cur_z = toolhead.get_position()[2]
|
||||
toolhead.manual_move(
|
||||
probexy + [pos[2] + params['sample_retract_dist']],
|
||||
probexy + [cur_z + params['sample_retract_dist']],
|
||||
params['lift_speed'])
|
||||
# Calculate result
|
||||
epos = calc_probe_z_average(positions, params['samples_result'])
|
||||
|
|
@ -410,8 +426,12 @@ class ProbeOffsetsHelper:
|
|||
self.x_offset = config.getfloat('x_offset', 0.)
|
||||
self.y_offset = config.getfloat('y_offset', 0.)
|
||||
self.z_offset = config.getfloat('z_offset')
|
||||
def get_offsets(self):
|
||||
def get_offsets(self, gcmd=None):
|
||||
return self.x_offset, self.y_offset, self.z_offset
|
||||
def create_probe_result(self, test_pos):
|
||||
return manual_probe.ProbeResult(
|
||||
test_pos[0]+self.x_offset, test_pos[1]+self.y_offset,
|
||||
test_pos[2]-self.z_offset, test_pos[0], test_pos[1], test_pos[2])
|
||||
|
||||
|
||||
######################################################################
|
||||
|
|
@ -463,7 +483,7 @@ class ProbePointsHelper:
|
|||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.get_last_move_time()
|
||||
# Invoke callback
|
||||
res = self.finalize_callback(self.probe_offsets, results)
|
||||
res = self.finalize_callback(results)
|
||||
return res != "retry"
|
||||
def _move_next(self, probe_num):
|
||||
# Move to next XY probe point
|
||||
|
|
@ -489,7 +509,7 @@ class ProbePointsHelper:
|
|||
return
|
||||
# Perform automatic probing
|
||||
self.lift_speed = probe.get_probe_params(gcmd)['lift_speed']
|
||||
self.probe_offsets = probe.get_offsets()
|
||||
self.probe_offsets = probe.get_offsets(gcmd)
|
||||
if self.horizontal_move_z < self.probe_offsets[2]:
|
||||
raise gcmd.error("horizontal_move_z can't be less than"
|
||||
" probe's z_offset")
|
||||
|
|
@ -520,10 +540,10 @@ class ProbePointsHelper:
|
|||
gcmd = self.gcode.create_gcode_command("", "", {})
|
||||
manual_probe.ManualProbeHelper(self.printer, gcmd,
|
||||
self._manual_probe_finalize)
|
||||
def _manual_probe_finalize(self, kin_pos):
|
||||
if kin_pos is None:
|
||||
def _manual_probe_finalize(self, mpresult):
|
||||
if mpresult is None:
|
||||
return
|
||||
self.manual_results.append(kin_pos)
|
||||
self.manual_results.append(mpresult)
|
||||
self._manual_probe_start()
|
||||
|
||||
# Helper to obtain a single probe measurement
|
||||
|
|
@ -606,14 +626,14 @@ class PrinterProbe:
|
|||
self.mcu_probe.query_endstop)
|
||||
self.probe_offsets = ProbeOffsetsHelper(config)
|
||||
self.param_helper = ProbeParameterHelper(config)
|
||||
self.homing_helper = HomingViaProbeHelper(config, self.mcu_probe,
|
||||
self.param_helper)
|
||||
self.homing_helper = HomingViaProbeHelper(
|
||||
config, self.mcu_probe, self.probe_offsets, self.param_helper)
|
||||
self.probe_session = ProbeSessionHelper(
|
||||
config, self.param_helper, self.homing_helper.start_probe_session)
|
||||
def get_probe_params(self, gcmd=None):
|
||||
return self.param_helper.get_probe_params(gcmd)
|
||||
def get_offsets(self):
|
||||
return self.probe_offsets.get_offsets()
|
||||
def get_offsets(self, gcmd=None):
|
||||
return self.probe_offsets.get_offsets(gcmd)
|
||||
def get_status(self, eventtime):
|
||||
return self.cmd_helper.get_status(eventtime)
|
||||
def start_probe_session(self, gcmd):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging, math, bisect
|
||||
import mcu
|
||||
from . import ldc1612, probe, manual_probe
|
||||
from . import ldc1612, trigger_analog, probe, manual_probe
|
||||
|
||||
OUT_OF_RANGE = 99.9
|
||||
|
||||
|
|
@ -31,8 +31,15 @@ class EddyCalibration:
|
|||
gcode.register_mux_command("PROBE_EDDY_CURRENT_CALIBRATE", "CHIP",
|
||||
cname, self.cmd_EDDY_CALIBRATE,
|
||||
desc=self.cmd_EDDY_CALIBRATE_help)
|
||||
def is_calibrated(self):
|
||||
return len(self.cal_freqs) > 2
|
||||
gcode.register_command('Z_OFFSET_APPLY_PROBE',
|
||||
self.cmd_Z_OFFSET_APPLY_PROBE,
|
||||
desc=self.cmd_Z_OFFSET_APPLY_PROBE_help)
|
||||
def get_printer(self):
|
||||
return self.printer
|
||||
def verify_calibrated(self):
|
||||
if len(self.cal_freqs) <= 2:
|
||||
raise self.printer.command_error(
|
||||
"Must calibrate probe_eddy_current first")
|
||||
def load_calibration(self, cal):
|
||||
cal = sorted([(c[1], c[0]) for c in cal])
|
||||
self.cal_freqs = [c[0] for c in cal]
|
||||
|
|
@ -134,21 +141,80 @@ class EddyCalibration:
|
|||
raise self.printer.command_error(
|
||||
"Failed calibration - incomplete sensor data")
|
||||
return cal
|
||||
|
||||
def _median(self, values):
|
||||
values = sorted(values)
|
||||
n = len(values)
|
||||
if n % 2 == 0:
|
||||
return (values[n//2 - 1] + values[n//2]) / 2.0
|
||||
return values[n // 2]
|
||||
def calc_freqs(self, meas):
|
||||
total_count = total_variance = 0
|
||||
positions = {}
|
||||
for pos, freqs in meas.items():
|
||||
count = len(freqs)
|
||||
freq_avg = float(sum(freqs)) / count
|
||||
positions[pos] = freq_avg
|
||||
total_count += count
|
||||
total_variance += sum([(f - freq_avg)**2 for f in freqs])
|
||||
return positions, math.sqrt(total_variance / total_count), total_count
|
||||
def post_manual_probe(self, kin_pos):
|
||||
if kin_pos is None:
|
||||
mads = [abs(f - freq_avg) for f in freqs]
|
||||
mad = self._median(mads)
|
||||
positions[pos] = (freq_avg, mad, count)
|
||||
return positions
|
||||
def validate_calibration_data(self, positions):
|
||||
last_freq = 40000000.
|
||||
last_pos = last_mad = .0
|
||||
gcode = self.printer.lookup_object("gcode")
|
||||
filtered = []
|
||||
mad_hz_total = .0
|
||||
mad_mm_total = .0
|
||||
samples_count = 0
|
||||
for pos, (freq_avg, mad_hz, count) in sorted(positions.items()):
|
||||
if freq_avg > last_freq:
|
||||
gcode.respond_info(
|
||||
"Frequency stops decreasing at step %.3f" % (pos))
|
||||
break
|
||||
diff_mad = math.sqrt(last_mad**2 + mad_hz**2)
|
||||
# Calculate if samples have a significant difference
|
||||
freq_diff = last_freq - freq_avg
|
||||
last_freq = freq_avg
|
||||
if freq_diff < 2.5 * diff_mad:
|
||||
gcode.respond_info(
|
||||
"Frequency too noisy at step %.3f -> %.3f" % (
|
||||
last_pos, pos))
|
||||
gcode.respond_info(
|
||||
"Frequency diff: %.3f, MAD_Hz: %.3f -> MAD_Hz: %.3f" % (
|
||||
freq_diff, last_mad, mad_hz
|
||||
))
|
||||
break
|
||||
last_mad = mad_hz
|
||||
delta_dist = pos - last_pos
|
||||
last_pos = pos
|
||||
# MAD is Median Absolute Deviation to Frequency avg ~ delta_hz_1
|
||||
# Signal is delta_hz_2 / delta_dist
|
||||
# SNR ~= delta_hz_1 / (delta_hz_2 / delta_mm) = d_1 * d_mm / d_2
|
||||
mad_mm = mad_hz * delta_dist / freq_diff
|
||||
filtered.append((pos, freq_avg, mad_hz, mad_mm))
|
||||
mad_hz_total += mad_hz
|
||||
mad_mm_total += mad_mm
|
||||
samples_count += count
|
||||
avg_mad = mad_hz_total / len(filtered)
|
||||
avg_mad_mm = mad_mm_total / len(filtered)
|
||||
gcode.respond_info(
|
||||
"probe_eddy_current: noise %.6fmm, MAD_Hz=%.3f in %d queries\n" % (
|
||||
avg_mad_mm, avg_mad, samples_count))
|
||||
freq_list = [freq for _, freq, _, _ in filtered]
|
||||
freq_diff = max(freq_list) - min(freq_list)
|
||||
gcode.respond_info("Total frequency range: %.3f Hz\n" % (freq_diff))
|
||||
points = [0.25, 0.5, 1.0, 2.0, 3.0]
|
||||
for pos, _, mad_hz, mad_mm in filtered:
|
||||
if len(points) and points[0] <= pos:
|
||||
points.pop(0)
|
||||
msg = "z_offset: %.3f # noise %.6fmm, MAD_Hz=%.3f\n" % (
|
||||
pos, mad_mm, mad_hz)
|
||||
gcode.respond_info(msg)
|
||||
return filtered
|
||||
def post_manual_probe(self, mpresult):
|
||||
if mpresult is None:
|
||||
# Manual Probe was aborted
|
||||
return
|
||||
curpos = list(kin_pos)
|
||||
curpos = [mpresult.bed_x, mpresult.bed_y, mpresult.bed_z]
|
||||
move = self.printer.lookup_object('toolhead').manual_move
|
||||
# Move away from the bed
|
||||
probe_calibrate_z = curpos[2]
|
||||
|
|
@ -166,24 +232,30 @@ class EddyCalibration:
|
|||
# Perform calibration movement and capture
|
||||
cal = self.do_calibration_moves(self.probe_speed)
|
||||
# Calculate each sample position average and variance
|
||||
positions, std, total = self.calc_freqs(cal)
|
||||
last_freq = 0.
|
||||
for pos, freq in reversed(sorted(positions.items())):
|
||||
if freq <= last_freq:
|
||||
raise self.printer.command_error(
|
||||
"Failed calibration - frequency not increasing each step")
|
||||
last_freq = freq
|
||||
_positions = self.calc_freqs(cal)
|
||||
# Fix Z position offset
|
||||
positions = {}
|
||||
for k in _positions:
|
||||
v = _positions[k]
|
||||
k = k - probe_calibrate_z
|
||||
positions[k] = v
|
||||
filtered = self.validate_calibration_data(positions)
|
||||
if len(filtered) <= 8:
|
||||
raise self.printer.command_error(
|
||||
"Failed calibration - No usable data")
|
||||
z_freq_pairs = [(pos, freq) for pos, freq, _, _ in filtered]
|
||||
self._save_calibration(z_freq_pairs)
|
||||
def _save_calibration(self, z_freq_pairs):
|
||||
gcode = self.printer.lookup_object("gcode")
|
||||
gcode.respond_info(
|
||||
"probe_eddy_current: stddev=%.3f in %d queries\n"
|
||||
"The SAVE_CONFIG command will update the printer config file\n"
|
||||
"and restart the printer." % (std, total))
|
||||
"and restart the printer.")
|
||||
# Save results
|
||||
cal_contents = []
|
||||
for i, (pos, freq) in enumerate(sorted(positions.items())):
|
||||
for i, (pos, freq) in enumerate(z_freq_pairs):
|
||||
if not i % 3:
|
||||
cal_contents.append('\n')
|
||||
cal_contents.append("%.6f:%.3f" % (pos - probe_calibrate_z, freq))
|
||||
cal_contents.append("%.6f:%.3f" % (pos, freq))
|
||||
cal_contents.append(',')
|
||||
cal_contents.pop()
|
||||
configfile = self.printer.lookup_object('configfile')
|
||||
|
|
@ -194,54 +266,49 @@ class EddyCalibration:
|
|||
# Start manual probe
|
||||
manual_probe.ManualProbeHelper(self.printer, gcmd,
|
||||
self.post_manual_probe)
|
||||
cmd_Z_OFFSET_APPLY_PROBE_help = "Adjust the probe's z_offset"
|
||||
def cmd_Z_OFFSET_APPLY_PROBE(self, gcmd):
|
||||
gcode_move = self.printer.lookup_object("gcode_move")
|
||||
offset = gcode_move.get_status()['homing_origin'].z
|
||||
if offset == 0:
|
||||
gcmd.respond_info("Nothing to do: Z Offset is 0")
|
||||
return
|
||||
cal_zpos = [z - offset for z in self.cal_zpos]
|
||||
z_freq_pairs = zip(cal_zpos, self.cal_freqs)
|
||||
z_freq_pairs = sorted(z_freq_pairs)
|
||||
self._save_calibration(z_freq_pairs)
|
||||
def register_drift_compensation(self, comp):
|
||||
self.drift_comp = comp
|
||||
|
||||
# Tool to gather samples and convert them to probe positions
|
||||
class EddyGatherSamples:
|
||||
def __init__(self, printer, sensor_helper, calibration, z_offset):
|
||||
def __init__(self, printer, sensor_helper):
|
||||
self._printer = printer
|
||||
self._sensor_helper = sensor_helper
|
||||
self._calibration = calibration
|
||||
self._z_offset = z_offset
|
||||
# Results storage
|
||||
self._samples = []
|
||||
self._probe_times = []
|
||||
self._probe_results = []
|
||||
# Sensor reading
|
||||
self._sensor_messages = []
|
||||
self._need_stop = False
|
||||
# Probe request and results storage
|
||||
self._probe_requests = []
|
||||
self._analysis_results = []
|
||||
# Start samples
|
||||
if not self._calibration.is_calibrated():
|
||||
raise self._printer.command_error(
|
||||
"Must calibrate probe_eddy_current first")
|
||||
sensor_helper.add_client(self._add_measurement)
|
||||
def _add_measurement(self, msg):
|
||||
sensor_helper.add_client(self._add_sensor_message)
|
||||
# Sensor reading and measurement extraction
|
||||
def _add_sensor_message(self, msg):
|
||||
if self._need_stop:
|
||||
del self._samples[:]
|
||||
del self._sensor_messages[:]
|
||||
return False
|
||||
self._samples.append(msg)
|
||||
self._check_samples()
|
||||
self._sensor_messages.append(msg)
|
||||
self._check_sensor_messages()
|
||||
return True
|
||||
def finish(self):
|
||||
self._need_stop = True
|
||||
def _await_samples(self):
|
||||
# Make sure enough samples have been collected
|
||||
reactor = self._printer.get_reactor()
|
||||
mcu = self._sensor_helper.get_mcu()
|
||||
while self._probe_times:
|
||||
start_time, end_time, pos_time, toolhead_pos = self._probe_times[0]
|
||||
systime = reactor.monotonic()
|
||||
est_print_time = mcu.estimated_print_time(systime)
|
||||
if est_print_time > end_time + 1.0:
|
||||
raise self._printer.command_error(
|
||||
"probe_eddy_current sensor outage")
|
||||
reactor.pause(systime + 0.010)
|
||||
def _pull_freq(self, start_time, end_time):
|
||||
# Find average sensor frequency between time range
|
||||
def _pull_measurements(self, start_time, end_time):
|
||||
# Extract measurements from sensor messages for given time range
|
||||
measures = []
|
||||
msg_num = discard_msgs = 0
|
||||
samp_sum = 0.
|
||||
samp_count = 0
|
||||
while msg_num < len(self._samples):
|
||||
msg = self._samples[msg_num]
|
||||
while msg_num < len(self._sensor_messages):
|
||||
msg = self._sensor_messages[msg_num]
|
||||
msg_num += 1
|
||||
data = msg['data']
|
||||
if data[0][0] > end_time:
|
||||
|
|
@ -249,104 +316,107 @@ class EddyGatherSamples:
|
|||
if data[-1][0] < start_time:
|
||||
discard_msgs = msg_num
|
||||
continue
|
||||
for time, freq, z in data:
|
||||
if time >= start_time and time <= end_time:
|
||||
samp_sum += freq
|
||||
samp_count += 1
|
||||
del self._samples[:discard_msgs]
|
||||
if not samp_count:
|
||||
# No sensor readings - raise error in pull_probed()
|
||||
return 0.
|
||||
return samp_sum / samp_count
|
||||
def _lookup_toolhead_pos(self, pos_time):
|
||||
toolhead = self._printer.lookup_object('toolhead')
|
||||
kin = toolhead.get_kinematics()
|
||||
kin_spos = {s.get_name(): s.mcu_to_commanded_position(
|
||||
s.get_past_mcu_position(pos_time))
|
||||
for s in kin.get_steppers()}
|
||||
return kin.calc_position(kin_spos)
|
||||
def _check_samples(self):
|
||||
while self._samples and self._probe_times:
|
||||
start_time, end_time, pos_time, toolhead_pos = self._probe_times[0]
|
||||
if self._samples[-1]['data'][-1][0] < end_time:
|
||||
for measure in data:
|
||||
time = measure[0]
|
||||
if time < start_time:
|
||||
continue
|
||||
if time > end_time:
|
||||
break
|
||||
measures.append(measure)
|
||||
del self._sensor_messages[:discard_msgs]
|
||||
return measures
|
||||
def _check_sensor_messages(self):
|
||||
while self._sensor_messages and self._probe_requests:
|
||||
cb, start_time, end_time, args = self._probe_requests[0]
|
||||
if self._sensor_messages[-1]['data'][-1][0] < end_time:
|
||||
break
|
||||
freq = self._pull_freq(start_time, end_time)
|
||||
if pos_time is not None:
|
||||
toolhead_pos = self._lookup_toolhead_pos(pos_time)
|
||||
sensor_z = None
|
||||
if freq:
|
||||
sensor_z = self._calibration.freq_to_height(freq)
|
||||
self._probe_results.append((sensor_z, toolhead_pos))
|
||||
self._probe_times.pop(0)
|
||||
measures = self._pull_measurements(start_time, end_time)
|
||||
errmsg = res = None
|
||||
try:
|
||||
# Call analysis callback to process measurements
|
||||
res = cb(measures, *args)
|
||||
except self._printer.command_error as e:
|
||||
# Defer raising of errors to pull_probed()
|
||||
errmsg = str(e)
|
||||
self._analysis_results.append((res, errmsg))
|
||||
self._probe_requests.pop(0)
|
||||
def add_probe_request(self, cb, start_time, end_time, *args):
|
||||
self._probe_requests.append((cb, start_time, end_time, args))
|
||||
self._check_sensor_messages()
|
||||
# Extract probe results
|
||||
def _await_sensor_messages(self):
|
||||
# Make sure enough samples have been collected
|
||||
reactor = self._printer.get_reactor()
|
||||
mcu = self._sensor_helper.get_mcu()
|
||||
while self._probe_requests:
|
||||
cb, start_time, end_time, args = self._probe_requests[0]
|
||||
systime = reactor.monotonic()
|
||||
est_print_time = mcu.estimated_print_time(systime)
|
||||
if est_print_time > end_time + 1.0:
|
||||
raise self._printer.command_error(
|
||||
"probe_eddy_current sensor outage")
|
||||
if mcu.is_fileoutput():
|
||||
# In debugging mode - just create dummy response
|
||||
dummy_pr = manual_probe.ProbeResult(0., 0., 0., 0., 0., 0.)
|
||||
self._analysis_results.append((dummy_pr, None))
|
||||
self._probe_requests.pop(0)
|
||||
continue
|
||||
reactor.pause(systime + 0.010)
|
||||
def pull_probed(self):
|
||||
self._await_samples()
|
||||
self._await_sensor_messages()
|
||||
results = []
|
||||
for sensor_z, toolhead_pos in self._probe_results:
|
||||
if sensor_z is None:
|
||||
raise self._printer.command_error(
|
||||
"Unable to obtain probe_eddy_current sensor readings")
|
||||
if sensor_z <= -OUT_OF_RANGE or sensor_z >= OUT_OF_RANGE:
|
||||
raise self._printer.command_error(
|
||||
"probe_eddy_current sensor not in valid range")
|
||||
# Callers expect position relative to z_offset, so recalculate
|
||||
bed_deviation = toolhead_pos[2] - sensor_z
|
||||
toolhead_pos[2] = self._z_offset + bed_deviation
|
||||
results.append(toolhead_pos)
|
||||
del self._probe_results[:]
|
||||
for res, errmsg in self._analysis_results:
|
||||
if errmsg is not None:
|
||||
raise self._printer.command_error(errmsg)
|
||||
results.append(res)
|
||||
del self._analysis_results[:]
|
||||
return results
|
||||
def note_probe(self, start_time, end_time, toolhead_pos):
|
||||
self._probe_times.append((start_time, end_time, None, toolhead_pos))
|
||||
self._check_samples()
|
||||
def note_probe_and_position(self, start_time, end_time, pos_time):
|
||||
self._probe_times.append((start_time, end_time, pos_time, None))
|
||||
self._check_samples()
|
||||
|
||||
# Generate a ProbeResult from the average of a set of measurements
|
||||
def probe_results_from_avg(measures, toolhead_pos, calibration, offsets):
|
||||
cmderr = calibration.get_printer().command_error
|
||||
if not measures:
|
||||
raise cmderr("Unable to obtain probe_eddy_current sensor readings")
|
||||
# Determine average of measurements
|
||||
freq_sum = sum([m[1] for m in measures])
|
||||
freq_avg = freq_sum / len(measures)
|
||||
# Determine height associated with frequency
|
||||
sensor_z = calibration.freq_to_height(freq_avg)
|
||||
if sensor_z <= -OUT_OF_RANGE or sensor_z >= OUT_OF_RANGE:
|
||||
raise cmderr("probe_eddy_current sensor not in valid range")
|
||||
return manual_probe.ProbeResult(
|
||||
toolhead_pos[0] + offsets[0], toolhead_pos[1] + offsets[1],
|
||||
toolhead_pos[2] - sensor_z,
|
||||
toolhead_pos[0], toolhead_pos[1], toolhead_pos[2])
|
||||
|
||||
MAX_VALID_RAW_VALUE=0x03ffffff
|
||||
|
||||
# Helper for implementing PROBE style commands (descend until trigger)
|
||||
class EddyDescend:
|
||||
REASON_SENSOR_ERROR = mcu.MCU_trsync.REASON_COMMS_TIMEOUT + 1
|
||||
def __init__(self, config, sensor_helper, calibration, param_helper):
|
||||
def __init__(self, config, sensor_helper, calibration,
|
||||
probe_offsets, param_helper, trigger_analog):
|
||||
self._printer = config.get_printer()
|
||||
self._sensor_helper = sensor_helper
|
||||
self._mcu = sensor_helper.get_mcu()
|
||||
self._calibration = calibration
|
||||
self._probe_offsets = probe_offsets
|
||||
self._param_helper = param_helper
|
||||
self._trigger_analog = trigger_analog
|
||||
self._z_min_position = probe.lookup_minimum_z(config)
|
||||
self._z_offset = config.getfloat('z_offset', minval=0.)
|
||||
self._dispatch = mcu.TriggerDispatch(self._mcu)
|
||||
self._trigger_time = 0.
|
||||
self._gather = None
|
||||
probe.LookupZSteppers(config, self._dispatch.add_stepper)
|
||||
# Interface for phoming.probing_move()
|
||||
def get_steppers(self):
|
||||
return self._dispatch.get_steppers()
|
||||
def home_start(self, print_time, sample_time, sample_count, rest_time,
|
||||
triggered=True):
|
||||
self._trigger_time = 0.
|
||||
trigger_freq = self._calibration.height_to_freq(self._z_offset)
|
||||
trigger_completion = self._dispatch.start(print_time)
|
||||
self._sensor_helper.setup_home(
|
||||
print_time, trigger_freq, self._dispatch.get_oid(),
|
||||
mcu.MCU_trsync.REASON_ENDSTOP_HIT, self.REASON_SENSOR_ERROR)
|
||||
return trigger_completion
|
||||
def home_wait(self, home_end_time):
|
||||
self._dispatch.wait_end(home_end_time)
|
||||
trigger_time = self._sensor_helper.clear_home()
|
||||
res = self._dispatch.stop()
|
||||
if res >= mcu.MCU_trsync.REASON_COMMS_TIMEOUT:
|
||||
if res == mcu.MCU_trsync.REASON_COMMS_TIMEOUT:
|
||||
raise self._printer.command_error(
|
||||
"Communication timeout during homing")
|
||||
raise self._printer.command_error("Eddy current sensor error")
|
||||
if res != mcu.MCU_trsync.REASON_ENDSTOP_HIT:
|
||||
return 0.
|
||||
if self._mcu.is_fileoutput():
|
||||
return home_end_time
|
||||
self._trigger_time = trigger_time
|
||||
return trigger_time
|
||||
def _prep_trigger_analog(self):
|
||||
sos_filter = self._trigger_analog.get_sos_filter()
|
||||
sos_filter.set_filter_design(None)
|
||||
sos_filter.set_offset_scale(0, 1.)
|
||||
self._trigger_analog.set_raw_range(0, MAX_VALID_RAW_VALUE)
|
||||
z_offset = self._probe_offsets.get_offsets()[2]
|
||||
trigger_freq = self._calibration.height_to_freq(z_offset)
|
||||
conv_freq = self._sensor_helper.convert_frequency(trigger_freq)
|
||||
self._trigger_analog.set_trigger('gt', conv_freq)
|
||||
# Probe session interface
|
||||
def start_probe_session(self, gcmd):
|
||||
self._gather = EddyGatherSamples(self._printer, self._sensor_helper,
|
||||
self._calibration, self._z_offset)
|
||||
self._calibration.verify_calibrated()
|
||||
self._prep_trigger_analog()
|
||||
self._gather = EddyGatherSamples(self._printer, self._sensor_helper)
|
||||
return self
|
||||
def run_probe(self, gcmd):
|
||||
toolhead = self._printer.lookup_object('toolhead')
|
||||
|
|
@ -355,14 +425,15 @@ class EddyDescend:
|
|||
speed = self._param_helper.get_probe_params(gcmd)['probe_speed']
|
||||
# Perform probing move
|
||||
phoming = self._printer.lookup_object('homing')
|
||||
trig_pos = phoming.probing_move(self, pos, speed)
|
||||
if not self._trigger_time:
|
||||
return trig_pos
|
||||
trig_pos = phoming.probing_move(self._trigger_analog, pos, speed)
|
||||
# Extract samples
|
||||
start_time = self._trigger_time + 0.050
|
||||
start_time = self._trigger_analog.get_last_trigger_time() + 0.050
|
||||
end_time = start_time + 0.100
|
||||
toolhead_pos = toolhead.get_position()
|
||||
self._gather.note_probe(start_time, end_time, toolhead_pos)
|
||||
offsets = self._probe_offsets.get_offsets()
|
||||
self._gather.add_probe_request(probe_results_from_avg,
|
||||
start_time, end_time,
|
||||
toolhead_pos, self._calibration, offsets)
|
||||
def pull_probed_results(self):
|
||||
return self._gather.pull_probed()
|
||||
def end_probe_session(self):
|
||||
|
|
@ -382,13 +453,13 @@ class EddyEndstopWrapper:
|
|||
def add_stepper(self, stepper):
|
||||
pass
|
||||
def get_steppers(self):
|
||||
return self._eddy_descend.get_steppers()
|
||||
return self._eddy_descend._trigger_analog.get_steppers()
|
||||
def home_start(self, print_time, sample_time, sample_count, rest_time,
|
||||
triggered=True):
|
||||
return self._eddy_descend.home_start(
|
||||
return self._eddy_descend._trigger_analog.home_start(
|
||||
print_time, sample_time, sample_count, rest_time, triggered)
|
||||
def home_wait(self, home_end_time):
|
||||
return self._eddy_descend.home_wait(home_end_time)
|
||||
return self._eddy_descend._trigger_analog.home_wait(home_end_time)
|
||||
def query_endstop(self, print_time):
|
||||
return False # XXX
|
||||
# Interface for HomingViaProbeHelper
|
||||
|
|
@ -402,24 +473,165 @@ class EddyEndstopWrapper:
|
|||
def probe_finish(self, hmove):
|
||||
pass
|
||||
def get_position_endstop(self):
|
||||
return self._eddy_descend._z_offset
|
||||
z_offset = self._eddy_descend._probe_offsets.get_offsets()[2]
|
||||
return z_offset
|
||||
|
||||
# Probing helper for "tap" requests
|
||||
class EddyTap:
|
||||
def __init__(self, config, sensor_helper, param_helper, trigger_analog):
|
||||
self._printer = config.get_printer()
|
||||
self._sensor_helper = sensor_helper
|
||||
self._param_helper = param_helper
|
||||
self._trigger_analog = trigger_analog
|
||||
self._z_min_position = probe.lookup_minimum_z(config)
|
||||
self._gather = None
|
||||
self._filter_design = None
|
||||
self._tap_threshold = config.getfloat('tap_threshold', 0., minval=0.)
|
||||
if self._tap_threshold:
|
||||
self._setup_tap()
|
||||
# Setup for "tap" probe request
|
||||
def _setup_tap(self):
|
||||
# Create sos filter "design"
|
||||
cfg_error = self._printer.config_error
|
||||
sps = self._sensor_helper.get_samples_per_second()
|
||||
design = trigger_analog.DigitalFilter(sps, cfg_error,
|
||||
lowpass=25.0, lowpass_order=4)
|
||||
# Create the derivative (sample to sample difference) post filter
|
||||
self._filter_design = trigger_analog.DerivativeFilter(design)
|
||||
# Create SOS filter
|
||||
cmd_queue = self._trigger_analog.get_dispatch().get_command_queue()
|
||||
mcu = self._sensor_helper.get_mcu()
|
||||
sos_filter = trigger_analog.MCU_SosFilter(mcu, cmd_queue, 5)
|
||||
self._trigger_analog.setup_sos_filter(sos_filter)
|
||||
def _prep_trigger_analog_tap(self):
|
||||
if not self._tap_threshold:
|
||||
raise self._printer.command_error("Tap not configured")
|
||||
sos_filter = self._trigger_analog.get_sos_filter()
|
||||
sos_filter.set_filter_design(self._filter_design)
|
||||
sos_filter.set_offset_scale(0, 1., auto_offset=True)
|
||||
self._trigger_analog.set_raw_range(0, MAX_VALID_RAW_VALUE)
|
||||
convert_frequency = self._sensor_helper.convert_frequency
|
||||
raw_threshold = convert_frequency(self._tap_threshold)
|
||||
self._trigger_analog.set_trigger('diff_peak_gt', raw_threshold)
|
||||
# Measurement analysis to determine "tap" position
|
||||
def central_diff(self, times, values):
|
||||
velocity = [0.0] * len(values)
|
||||
for i in range(1, len(values) - 1):
|
||||
delta_v = (values[i+1] - values[i-1])
|
||||
delta_t = (times[i+1] - times[i-1])
|
||||
velocity[i] = delta_v / delta_t
|
||||
velocity[0] = (values[1] - values[0]) / (times[1] - times[0])
|
||||
velocity[-1] = (values[-1] - values[-2]) / (times[-1] - times[-2])
|
||||
return velocity
|
||||
def validate_samples_time(self, timestamps):
|
||||
sps = self._sensor_helper.get_samples_per_second()
|
||||
cycle_time = 1.0 / sps
|
||||
SYNC_SLACK = 0.001
|
||||
for i in range(1, len(timestamps)):
|
||||
tdiff = timestamps[i] - timestamps[i-1]
|
||||
if cycle_time + SYNC_SLACK < tdiff:
|
||||
logging.error("Eddy: Gaps in the data: %.3f < %.3f" % (
|
||||
(cycle_time + SYNC_SLACK, tdiff)
|
||||
))
|
||||
break
|
||||
if cycle_time - SYNC_SLACK > tdiff:
|
||||
logging.error(
|
||||
"Eddy: CLKIN frequency too low: %.3f > %.3f" % (
|
||||
(cycle_time - SYNC_SLACK, tdiff)
|
||||
))
|
||||
break
|
||||
def _pull_tap_time(self, measures):
|
||||
tap_time = []
|
||||
tap_value = []
|
||||
for time, freq, z in measures:
|
||||
tap_time.append(time)
|
||||
tap_value.append(freq)
|
||||
# If samples have gaps this will not produce adequate data
|
||||
self.validate_samples_time(tap_time)
|
||||
# Do the same filtering as on the MCU but without induced lag
|
||||
main_design = self._filter_design.get_main_filter()
|
||||
try:
|
||||
fvals = main_design.filtfilt(tap_value)
|
||||
except ValueError as e:
|
||||
raise self._printer.command_error(str(e))
|
||||
velocity = self.central_diff(tap_time, fvals)
|
||||
peak_velocity = max(velocity)
|
||||
i = velocity.index(peak_velocity)
|
||||
return tap_time[i]
|
||||
def _lookup_toolhead_pos(self, pos_time):
|
||||
toolhead = self._printer.lookup_object('toolhead')
|
||||
kin = toolhead.get_kinematics()
|
||||
kin_spos = {s.get_name(): s.mcu_to_commanded_position(
|
||||
s.get_past_mcu_position(pos_time))
|
||||
for s in kin.get_steppers()}
|
||||
return kin.calc_position(kin_spos)
|
||||
def _analyze_tap(self, measures):
|
||||
pos_time = self._pull_tap_time(measures)
|
||||
trig_pos = self._lookup_toolhead_pos(pos_time)
|
||||
return manual_probe.ProbeResult(trig_pos[0], trig_pos[1], trig_pos[2],
|
||||
trig_pos[0], trig_pos[1], trig_pos[2])
|
||||
# Probe session interface
|
||||
def start_probe_session(self, gcmd):
|
||||
self._prep_trigger_analog_tap()
|
||||
self._gather = EddyGatherSamples(self._printer, self._sensor_helper)
|
||||
return self
|
||||
def run_probe(self, gcmd):
|
||||
toolhead = self._printer.lookup_object('toolhead')
|
||||
pos = toolhead.get_position()
|
||||
pos[2] = self._z_min_position
|
||||
speed = self._param_helper.get_probe_params(gcmd)['probe_speed']
|
||||
move_start_time = toolhead.get_last_move_time()
|
||||
# Perform probing move
|
||||
phoming = self._printer.lookup_object('homing')
|
||||
trig_pos = phoming.probing_move(self._trigger_analog, pos, speed)
|
||||
# Extract samples
|
||||
trigger_time = self._trigger_analog.get_last_trigger_time()
|
||||
start_time = trigger_time - 0.250
|
||||
if start_time < move_start_time:
|
||||
# Filter short move
|
||||
start_time = move_start_time
|
||||
end_time = trigger_time
|
||||
self._gather.add_probe_request(self._analyze_tap, start_time, end_time)
|
||||
def pull_probed_results(self):
|
||||
return self._gather.pull_probed()
|
||||
def end_probe_session(self):
|
||||
self._gather.finish()
|
||||
self._gather = None
|
||||
|
||||
# Implementing probing with "METHOD=scan"
|
||||
class EddyScanningProbe:
|
||||
def __init__(self, printer, sensor_helper, calibration, z_offset, gcmd):
|
||||
self._printer = printer
|
||||
def __init__(self, config, sensor_helper, calibration, probe_offsets):
|
||||
self._printer = config.get_printer()
|
||||
self._sensor_helper = sensor_helper
|
||||
self._calibration = calibration
|
||||
self._z_offset = z_offset
|
||||
self._gather = EddyGatherSamples(printer, sensor_helper,
|
||||
calibration, z_offset)
|
||||
self._offsets = probe_offsets.get_offsets()
|
||||
self._gather = None
|
||||
self._sample_time_delay = 0.050
|
||||
self._sample_time = gcmd.get_float("SAMPLE_TIME", 0.100, above=0.0)
|
||||
self._is_rapid = gcmd.get("METHOD", "scan") == 'rapid_scan'
|
||||
self._sample_time = 0.
|
||||
self._is_rapid = False
|
||||
def _lookup_toolhead_pos(self, pos_time):
|
||||
toolhead = self._printer.lookup_object('toolhead')
|
||||
kin = toolhead.get_kinematics()
|
||||
kin_spos = {s.get_name(): s.mcu_to_commanded_position(
|
||||
s.get_past_mcu_position(pos_time))
|
||||
for s in kin.get_steppers()}
|
||||
return kin.calc_position(kin_spos)
|
||||
def _analyze_scan(self, measures, pos_time):
|
||||
toolhead_pos = self._lookup_toolhead_pos(pos_time)
|
||||
return probe_results_from_avg(measures, toolhead_pos,
|
||||
self._calibration, self._offsets)
|
||||
def _rapid_lookahead_cb(self, printtime):
|
||||
start_time = printtime - self._sample_time / 2
|
||||
self._gather.note_probe_and_position(
|
||||
start_time, start_time + self._sample_time, printtime)
|
||||
end_time = start_time + self._sample_time
|
||||
self._gather.add_probe_request(self._analyze_scan, start_time, end_time,
|
||||
printtime)
|
||||
# Probe session interface
|
||||
def start_probe_session(self, gcmd):
|
||||
self._calibration.verify_calibrated()
|
||||
self._gather = EddyGatherSamples(self._printer, self._sensor_helper)
|
||||
self._sample_time = gcmd.get_float("SAMPLE_TIME", 0.100, above=0.0)
|
||||
self._is_rapid = gcmd.get("METHOD", "scan") == 'rapid_scan'
|
||||
return self
|
||||
def run_probe(self, gcmd):
|
||||
toolhead = self._printer.lookup_object("toolhead")
|
||||
if self._is_rapid:
|
||||
|
|
@ -428,8 +640,9 @@ class EddyScanningProbe:
|
|||
printtime = toolhead.get_last_move_time()
|
||||
toolhead.dwell(self._sample_time_delay + self._sample_time)
|
||||
start_time = printtime + self._sample_time_delay
|
||||
self._gather.note_probe_and_position(
|
||||
start_time, start_time + self._sample_time, start_time)
|
||||
end_time = start_time + self._sample_time
|
||||
self._gather.add_probe_request(self._analyze_scan, start_time, end_time,
|
||||
start_time)
|
||||
def pull_probed_results(self):
|
||||
if self._is_rapid:
|
||||
# Flush lookahead (so all lookahead callbacks are invoked)
|
||||
|
|
@ -438,7 +651,7 @@ class EddyScanningProbe:
|
|||
results = self._gather.pull_probed()
|
||||
# Allow axis_twist_compensation to update results
|
||||
for epos in results:
|
||||
self._printer.send_event("probe:update_results", epos)
|
||||
self._printer.send_event("probe:update_results", [epos])
|
||||
return results
|
||||
def end_probe_session(self):
|
||||
self._gather.finish()
|
||||
|
|
@ -453,31 +666,51 @@ class PrinterEddyProbe:
|
|||
sensors = { "ldc1612": ldc1612.LDC1612 }
|
||||
sensor_type = config.getchoice('sensor_type', {s: s for s in sensors})
|
||||
self.sensor_helper = sensors[sensor_type](config, self.calibration)
|
||||
# Probe interface
|
||||
# Create trigger_analog interface
|
||||
trig_analog = trigger_analog.MCU_trigger_analog(self.sensor_helper)
|
||||
probe.LookupZSteppers(config, trig_analog.get_dispatch().add_stepper)
|
||||
# Basic probe requests
|
||||
self.probe_offsets = probe.ProbeOffsetsHelper(config)
|
||||
self.param_helper = probe.ProbeParameterHelper(config)
|
||||
self.eddy_descend = EddyDescend(
|
||||
config, self.sensor_helper, self.calibration, self.param_helper)
|
||||
self.cmd_helper = probe.ProbeCommandHelper(config, self)
|
||||
self.probe_offsets = probe.ProbeOffsetsHelper(config)
|
||||
self.probe_session = probe.ProbeSessionHelper(
|
||||
config, self.param_helper, self.eddy_descend.start_probe_session)
|
||||
config, self.sensor_helper, self.calibration, self.probe_offsets,
|
||||
self.param_helper, trig_analog)
|
||||
# Create wrapper to support Z homing with probe
|
||||
mcu_probe = EddyEndstopWrapper(self.sensor_helper, self.eddy_descend)
|
||||
probe.HomingViaProbeHelper(config, mcu_probe, self.param_helper)
|
||||
probe.HomingViaProbeHelper(config, mcu_probe,
|
||||
self.probe_offsets, self.param_helper)
|
||||
# Probing via "tap" interface
|
||||
self.eddy_tap = EddyTap(config, self.sensor_helper,
|
||||
self.param_helper, trig_analog)
|
||||
# Probing via "scan" and "rapid_scan" requests
|
||||
self.eddy_scan = EddyScanningProbe(config, self.sensor_helper,
|
||||
self.calibration, self.probe_offsets)
|
||||
# Register with main probe interface
|
||||
self.cmd_helper = probe.ProbeCommandHelper(config, self,
|
||||
can_set_z_offset=False)
|
||||
self.probe_session = probe.ProbeSessionHelper(
|
||||
config, self.param_helper, self._start_descend_wrapper)
|
||||
self.printer.add_object('probe', self)
|
||||
def add_client(self, cb):
|
||||
self.sensor_helper.add_client(cb)
|
||||
def get_probe_params(self, gcmd=None):
|
||||
return self.param_helper.get_probe_params(gcmd)
|
||||
def get_offsets(self):
|
||||
return self.probe_offsets.get_offsets()
|
||||
def get_offsets(self, gcmd=None):
|
||||
if gcmd is not None and gcmd.get('METHOD', '').lower() == "tap":
|
||||
return (0., 0., 0.)
|
||||
return self.probe_offsets.get_offsets(gcmd)
|
||||
def get_status(self, eventtime):
|
||||
return self.cmd_helper.get_status(eventtime)
|
||||
def _start_descend_wrapper(self, gcmd):
|
||||
method = gcmd.get('METHOD', 'automatic').lower()
|
||||
if method == "tap":
|
||||
return self.eddy_tap.start_probe_session(gcmd)
|
||||
return self.eddy_descend.start_probe_session(gcmd)
|
||||
def start_probe_session(self, gcmd):
|
||||
method = gcmd.get('METHOD', 'automatic').lower()
|
||||
if method in ('scan', 'rapid_scan'):
|
||||
z_offset = self.get_offsets()[2]
|
||||
return EddyScanningProbe(self.printer, self.sensor_helper,
|
||||
self.calibration, z_offset, gcmd)
|
||||
return self.eddy_scan.start_probe_session(gcmd)
|
||||
# For "tap" and normal, probe_session can average multiple attempts
|
||||
return self.probe_session.start_probe_session(gcmd)
|
||||
def register_drift_compensation(self, comp):
|
||||
self.calibration.register_drift_compensation(comp)
|
||||
|
|
|
|||
|
|
@ -51,34 +51,34 @@ class QuadGantryLevel:
|
|||
self.z_status.reset()
|
||||
self.retry_helper.start(gcmd)
|
||||
self.probe_helper.start_probe(gcmd)
|
||||
def probe_finalize(self, offsets, positions):
|
||||
def probe_finalize(self, positions):
|
||||
# Mirror our perspective so the adjustments make sense
|
||||
# from the perspective of the gantry
|
||||
z_positions = [self.horizontal_move_z - p[2] for p in positions]
|
||||
z_positions = [self.horizontal_move_z - p.bed_z for p in positions]
|
||||
points_message = "Gantry-relative probe points:\n%s\n" % (
|
||||
" ".join(["%s: %.6f" % (z_id, z_positions[z_id])
|
||||
for z_id in range(len(z_positions))]))
|
||||
self.gcode.respond_info(points_message)
|
||||
# Calculate slope along X axis between probe point 0 and 3
|
||||
ppx0 = [positions[0][0] + offsets[0], z_positions[0]]
|
||||
ppx3 = [positions[3][0] + offsets[0], z_positions[3]]
|
||||
ppx0 = [positions[0].bed_x, z_positions[0]]
|
||||
ppx3 = [positions[3].bed_x, z_positions[3]]
|
||||
slope_x_pp03 = self.linefit(ppx0, ppx3)
|
||||
# Calculate slope along X axis between probe point 1 and 2
|
||||
ppx1 = [positions[1][0] + offsets[0], z_positions[1]]
|
||||
ppx2 = [positions[2][0] + offsets[0], z_positions[2]]
|
||||
ppx1 = [positions[1].bed_x, z_positions[1]]
|
||||
ppx2 = [positions[2].bed_x, z_positions[2]]
|
||||
slope_x_pp12 = self.linefit(ppx1, ppx2)
|
||||
logging.info("quad_gantry_level f1: %s, f2: %s"
|
||||
% (slope_x_pp03, slope_x_pp12))
|
||||
# Calculate gantry slope along Y axis between stepper 0 and 1
|
||||
a1 = [positions[0][1] + offsets[1],
|
||||
a1 = [positions[0].bed_y,
|
||||
self.plot(slope_x_pp03, self.gantry_corners[0][0])]
|
||||
a2 = [positions[1][1] + offsets[1],
|
||||
a2 = [positions[1].bed_y,
|
||||
self.plot(slope_x_pp12, self.gantry_corners[0][0])]
|
||||
slope_y_s01 = self.linefit(a1, a2)
|
||||
# Calculate gantry slope along Y axis between stepper 2 and 3
|
||||
b1 = [positions[0][1] + offsets[1],
|
||||
b1 = [positions[0].bed_y,
|
||||
self.plot(slope_x_pp03, self.gantry_corners[1][0])]
|
||||
b2 = [positions[1][1] + offsets[1],
|
||||
b2 = [positions[1].bed_y,
|
||||
self.plot(slope_x_pp12, self.gantry_corners[1][0])]
|
||||
slope_y_s23 = self.linefit(b1, b2)
|
||||
logging.info("quad_gantry_level af: %s, bf: %s"
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ class pca9685_pwm:
|
|||
cmd_queue = self._mcu.alloc_command_queue()
|
||||
self._set_cmd = self._mcu.lookup_command(
|
||||
"queue_pca9685_out oid=%c clock=%u value=%hu", cq=cmd_queue)
|
||||
def next_aligned_print_time(self, print_time, allow_early=0.):
|
||||
return print_time
|
||||
def set_pwm(self, print_time, value):
|
||||
clock = self._mcu.print_time_to_clock(print_time)
|
||||
if self._invert:
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class ScrewsTiltAdjust:
|
|||
'max_deviation': self.max_diff,
|
||||
'results': self.results}
|
||||
|
||||
def probe_finalize(self, offsets, positions):
|
||||
def probe_finalize(self, positions):
|
||||
self.results = {}
|
||||
self.max_diff_error = False
|
||||
# Factors used for CW-M3, CCW-M3, CW-M4, CCW-M4, CW-M5, CCW-M5, CW-M6
|
||||
|
|
@ -79,15 +79,15 @@ class ScrewsTiltAdjust:
|
|||
or (not is_clockwise_thread and self.direction == 'CCW'))
|
||||
min_or_max = max if use_max else min
|
||||
i_base, z_base = min_or_max(
|
||||
enumerate([pos[2] for pos in positions]), key=lambda v: v[1])
|
||||
enumerate([pos.bed_z for pos in positions]), key=lambda v: v[1])
|
||||
else:
|
||||
# First screw is the base position used for comparison
|
||||
i_base, z_base = 0, positions[0][2]
|
||||
i_base, z_base = 0, positions[0].bed_z
|
||||
# Provide the user some information on how to read the results
|
||||
self.gcode.respond_info("01:20 means 1 full turn and 20 minutes, "
|
||||
"CW=clockwise, CCW=counter-clockwise")
|
||||
for i, screw in enumerate(self.screws):
|
||||
z = positions[i][2]
|
||||
z = positions[i].bed_z
|
||||
coord, name = screw
|
||||
if i == i_base:
|
||||
# Show the results
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
from . import output_pin
|
||||
|
||||
SERVO_SIGNAL_PERIOD = 0.020
|
||||
RESCHEDULE_SLACK = 0.000500
|
||||
|
||||
class PrinterServo:
|
||||
def __init__(self, config):
|
||||
|
|
@ -47,8 +48,12 @@ class PrinterServo:
|
|||
def _set_pwm(self, print_time, value):
|
||||
if value == self.last_value:
|
||||
return "discard", 0.
|
||||
aligned_ptime = self.mcu_servo.next_aligned_print_time(print_time,
|
||||
RESCHEDULE_SLACK)
|
||||
if aligned_ptime > print_time + RESCHEDULE_SLACK:
|
||||
return "reschedule", aligned_ptime
|
||||
self.last_value = value
|
||||
self.mcu_servo.set_pwm(print_time, value)
|
||||
self.mcu_servo.set_pwm(aligned_ptime, value)
|
||||
def _get_pwm_from_angle(self, angle):
|
||||
angle = max(0., min(self.max_angle, angle))
|
||||
width = self.min_width + angle * self.angle_to_width
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ class SmartEffectorProbe:
|
|||
config, self, self.probe_wrapper.query_endstop)
|
||||
self.probe_offsets = probe.ProbeOffsetsHelper(config)
|
||||
self.param_helper = probe.ProbeParameterHelper(config)
|
||||
self.homing_helper = probe.HomingViaProbeHelper(config, self,
|
||||
self.param_helper)
|
||||
self.homing_helper = probe.HomingViaProbeHelper(
|
||||
config, self, self.probe_offsets, self.param_helper)
|
||||
self.probe_session = probe.ProbeSessionHelper(
|
||||
config, self.param_helper, self.homing_helper.start_probe_session)
|
||||
# SmartEffector control
|
||||
|
|
@ -90,8 +90,8 @@ class SmartEffectorProbe:
|
|||
desc=self.cmd_SET_SMART_EFFECTOR_help)
|
||||
def get_probe_params(self, gcmd=None):
|
||||
return self.param_helper.get_probe_params(gcmd)
|
||||
def get_offsets(self):
|
||||
return self.probe_offsets.get_offsets()
|
||||
def get_offsets(self, gcmd=None):
|
||||
return self.probe_offsets.get_offsets(gcmd)
|
||||
def get_status(self, eventtime):
|
||||
return self.cmd_helper.get_status(eventtime)
|
||||
def start_probe_session(self, gcmd):
|
||||
|
|
|
|||
|
|
@ -1,234 +0,0 @@
|
|||
# Second Order Sections Filter
|
||||
#
|
||||
# Copyright (C) 2025 Gareth Farrington <gareth@waves.ky>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
MAX_INT32 = (2 ** 31)
|
||||
MIN_INT32 = -(2 ** 31) - 1
|
||||
def assert_is_int32(value, error):
|
||||
if value > MAX_INT32 or value < MIN_INT32:
|
||||
raise OverflowError(error)
|
||||
return value
|
||||
|
||||
# convert a floating point value to a 32 bit fixed point representation
|
||||
# checks for overflow
|
||||
def to_fixed_32(value, int_bits):
|
||||
fractional_bits = (32 - (1 + int_bits))
|
||||
fixed_val = int(value * (2 ** fractional_bits))
|
||||
return assert_is_int32(fixed_val, "Fixed point Q%i overflow"
|
||||
% (int_bits,))
|
||||
|
||||
|
||||
# Digital filter designer and container
|
||||
class DigitalFilter:
|
||||
def __init__(self, sps, cfg_error, highpass=None, highpass_order=1,
|
||||
lowpass=None, lowpass_order=1, notches=None, notch_quality=2.0):
|
||||
self.filter_sections = []
|
||||
self.initial_state = []
|
||||
self.sample_frequency = sps
|
||||
# an empty filter can be created without SciPi/numpy
|
||||
if not (highpass or lowpass or notches):
|
||||
return
|
||||
try:
|
||||
import scipy.signal as signal
|
||||
except:
|
||||
raise cfg_error("DigitalFilter require the SciPy module")
|
||||
if highpass:
|
||||
self.filter_sections.extend(
|
||||
self._butter(highpass, "highpass", highpass_order))
|
||||
if lowpass:
|
||||
self.filter_sections.extend(
|
||||
self._butter(lowpass, "lowpass", lowpass_order))
|
||||
if notches is None:
|
||||
notches = []
|
||||
for notch_freq in notches:
|
||||
self.filter_sections.append(self._notch(notch_freq, notch_quality))
|
||||
if len(self.filter_sections) > 0:
|
||||
self.initial_state = signal.sosfilt_zi(self.filter_sections)
|
||||
|
||||
def _butter(self, frequency, btype, order):
|
||||
import scipy.signal as signal
|
||||
return signal.butter(order, Wn=frequency, btype=btype,
|
||||
fs=self.sample_frequency, output='sos')
|
||||
|
||||
def _notch(self, freq, quality):
|
||||
import scipy.signal as signal
|
||||
b, a = signal.iirnotch(freq, Q=quality, fs=self.sample_frequency)
|
||||
return signal.tf2sos(b, a)[0]
|
||||
|
||||
def get_filter_sections(self):
|
||||
return self.filter_sections
|
||||
|
||||
def get_initial_state(self):
|
||||
return self.initial_state
|
||||
|
||||
# container that accepts SciPy formatted SOS filter data and converts it to a
|
||||
# selected fixed point representation. This data could come from DigitalFilter,
|
||||
# static data, config etc.
|
||||
class FixedPointSosFilter:
|
||||
# filter_sections is an array of SciPy formatted SOS filter sections (sos)
|
||||
# initial_state is an array of SciPy formatted SOS state sections (zi)
|
||||
def __init__(self, filter_sections=None, initial_state=None,
|
||||
coeff_int_bits=2, value_int_bits=15):
|
||||
filter_sections = [] if filter_sections is None else filter_sections
|
||||
initial_state = [] if initial_state is None else initial_state
|
||||
num_sections = len(filter_sections)
|
||||
num_state = len(initial_state)
|
||||
if num_state != num_sections:
|
||||
raise ValueError("The number of filter sections (%i) and state "
|
||||
"sections (%i) must be equal" % (
|
||||
num_sections, num_state))
|
||||
self._coeff_int_bits = self._validate_int_bits(coeff_int_bits)
|
||||
self._value_int_bits = self._validate_int_bits(value_int_bits)
|
||||
self._filter = self._convert_filter(filter_sections)
|
||||
self._state = self._convert_state(initial_state)
|
||||
|
||||
def get_filter_sections(self):
|
||||
return self._filter
|
||||
|
||||
def get_initial_state(self):
|
||||
return self._state
|
||||
|
||||
def get_coeff_int_bits(self):
|
||||
return self._coeff_int_bits
|
||||
|
||||
def get_value_int_bits(self):
|
||||
return self._value_int_bits
|
||||
|
||||
def get_num_sections(self):
|
||||
return len(self._filter)
|
||||
|
||||
def _validate_int_bits(self, int_bits):
|
||||
if int_bits < 1 or int_bits > 30:
|
||||
raise ValueError("The number of integer bits (%i) must be a"
|
||||
" value between 1 and 30" % (int_bits,))
|
||||
return int_bits
|
||||
|
||||
# convert the SciPi SOS filters to fixed point format
|
||||
def _convert_filter(self, filter_sections):
|
||||
sos_fixed = []
|
||||
for section in filter_sections:
|
||||
nun_coeff = len(section)
|
||||
if nun_coeff != 6:
|
||||
raise ValueError("The number of filter coefficients is %i"
|
||||
", must be 6" % (nun_coeff,))
|
||||
fixed_section = []
|
||||
for col, coeff in enumerate(section):
|
||||
if col != 3: # omit column 3
|
||||
fixed_coeff = to_fixed_32(coeff, self._coeff_int_bits)
|
||||
fixed_section.append(fixed_coeff)
|
||||
elif coeff != 1.0: # double check column 3 is always 1.0
|
||||
raise ValueError("Coefficient 3 is expected to be 1.0"
|
||||
" but was %f" % (coeff,))
|
||||
sos_fixed.append(fixed_section)
|
||||
return sos_fixed
|
||||
|
||||
# convert the SOS filter state matrix (zi) to fixed point format
|
||||
def _convert_state(self, filter_state):
|
||||
sos_state = []
|
||||
for section in filter_state:
|
||||
nun_states = len(section)
|
||||
if nun_states != 2:
|
||||
raise ValueError(
|
||||
"The number of state elements is %i, must be 2"
|
||||
% (nun_states,))
|
||||
fixed_state = []
|
||||
for col, value in enumerate(section):
|
||||
fixed_state.append(to_fixed_32(value, self._value_int_bits))
|
||||
sos_state.append(fixed_state)
|
||||
return sos_state
|
||||
|
||||
|
||||
# Control an `sos_filter` object on the MCU
|
||||
class SosFilter:
|
||||
# fixed_point_filter should be an FixedPointSosFilter instance. A filter of
|
||||
# size 0 will create a passthrough filter.
|
||||
# max_sections should be the largest number of sections you expect
|
||||
# to use at runtime. The default is the size of the fixed_point_filter.
|
||||
def __init__(self, mcu, cmd_queue, fixed_point_filter, max_sections=None):
|
||||
self._mcu = mcu
|
||||
self._cmd_queue = cmd_queue
|
||||
self._oid = self._mcu.create_oid()
|
||||
self._filter = fixed_point_filter
|
||||
self._max_sections = max_sections
|
||||
if self._max_sections is None:
|
||||
self._max_sections = self._filter.get_num_sections()
|
||||
self._cmd_set_section = [
|
||||
"sos_filter_set_section oid=%d section_idx=%d"
|
||||
" sos0=%i sos1=%i sos2=%i sos3=%i sos4=%i",
|
||||
"sos_filter_set_section oid=%c section_idx=%c"
|
||||
" sos0=%i sos1=%i sos2=%i sos3=%i sos4=%i"]
|
||||
self._cmd_config_state = [
|
||||
"sos_filter_set_state oid=%d section_idx=%d state0=%i state1=%i",
|
||||
"sos_filter_set_state oid=%c section_idx=%c state0=%i state1=%i"]
|
||||
self._cmd_activate = [
|
||||
"sos_filter_set_active oid=%d n_sections=%d coeff_int_bits=%d",
|
||||
"sos_filter_set_active oid=%c n_sections=%c coeff_int_bits=%c"]
|
||||
self._mcu.register_config_callback(self._build_config)
|
||||
|
||||
def _build_config(self):
|
||||
cmds = [self._cmd_set_section, self._cmd_config_state,
|
||||
self._cmd_activate]
|
||||
for cmd in cmds:
|
||||
cmd.append(self._mcu.lookup_command(cmd[1], cq=self._cmd_queue))
|
||||
|
||||
def get_oid(self):
|
||||
return self._oid
|
||||
|
||||
# create an uninitialized filter object
|
||||
def create_filter(self):
|
||||
self._mcu.add_config_cmd("config_sos_filter oid=%d max_sections=%d"
|
||||
% (self._oid, self._max_sections))
|
||||
self._configure_filter(is_init=True)
|
||||
|
||||
# either setup an init command or send the command based on a flag
|
||||
def _cmd(self, command, args, is_init=False):
|
||||
if is_init:
|
||||
self._mcu.add_config_cmd(command[0] % args, is_init=True)
|
||||
else:
|
||||
command[2].send(args)
|
||||
|
||||
def _set_filter_sections(self, is_init=False):
|
||||
for i, section in enumerate(self._filter.get_filter_sections()):
|
||||
args = (self._oid, i, section[0], section[1], section[2],
|
||||
section[3], section[4])
|
||||
self._cmd(self._cmd_set_section, args, is_init)
|
||||
|
||||
def _set_filter_state(self, is_init=False):
|
||||
for i, state in enumerate(self._filter.get_initial_state()):
|
||||
args = (self._oid, i, state[0], state[1])
|
||||
self._cmd(self._cmd_config_state, args, is_init)
|
||||
|
||||
def _activate_filter(self, is_init=False):
|
||||
args = (self._oid, self._filter.get_num_sections(),
|
||||
self._filter.get_coeff_int_bits())
|
||||
self._cmd(self._cmd_activate, args, is_init)
|
||||
|
||||
# configure the filter sections on the mcu
|
||||
# filters should be an array of filter sections in SciPi SOS format
|
||||
# sos_filter_state should be an array of zi filter state elements
|
||||
def _configure_filter(self, is_init=False):
|
||||
num_sections = self._filter.get_num_sections()
|
||||
if num_sections > self._max_sections:
|
||||
raise ValueError("Too many filter sections: %i, The max is %i"
|
||||
% (num_sections, self._max_sections,))
|
||||
# convert to fixed point to find errors
|
||||
# no errors, state is accepted
|
||||
# configure MCU filter and activate
|
||||
self._set_filter_sections(is_init)
|
||||
self._set_filter_state(is_init,)
|
||||
self._activate_filter(is_init)
|
||||
|
||||
# Change the filter coefficients and state at runtime
|
||||
# fixed_point_filter should be an FixedPointSosFilter instance
|
||||
# cq is an optional command queue to for command sequencing
|
||||
def change_filter(self, fixed_point_filter):
|
||||
self._filter = fixed_point_filter
|
||||
self._configure_filter(False)
|
||||
|
||||
# Resets the filter state back to initial conditions at runtime
|
||||
# cq is an optional command queue to for command sequencing
|
||||
def reset_filter(self):
|
||||
self._set_filter_state(False)
|
||||
self._activate_filter(False)
|
||||
41
klippy/extras/static_pwm_clock.py
Normal file
41
klippy/extras/static_pwm_clock.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Define GPIO as clock output
|
||||
#
|
||||
# Copyright (C) 2025 Timofey Titovets <nefelim4ag@gmail.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
import logging
|
||||
|
||||
class PrinterClockOutputPin:
|
||||
def __init__(self, config):
|
||||
self.name = config.get_name()
|
||||
self.printer = config.get_printer()
|
||||
ppins = self.printer.lookup_object('pins')
|
||||
self.mcu_pin = ppins.setup_pin('pwm', config.get('pin'))
|
||||
self.mcu = self.mcu_pin.get_mcu()
|
||||
self.frequency = config.getfloat('frequency', 100, above=(1/0.3),
|
||||
maxval=520000000)
|
||||
self.mcu_pin.setup_cycle_time(1. / self.frequency, True)
|
||||
self.mcu_pin.setup_max_duration(0.)
|
||||
self.mcu_pin.setup_start_value(0.5, 0.5)
|
||||
self.mcu.register_config_callback(self._build_config)
|
||||
def _build_config(self):
|
||||
mcu_freq = self.mcu.seconds_to_clock(1.0)
|
||||
cycle_ticks = int(mcu_freq // self.frequency)
|
||||
# validate frequency
|
||||
mcu_freq_rev = int(cycle_ticks * self.frequency)
|
||||
if mcu_freq_rev != mcu_freq:
|
||||
msg = """
|
||||
# Frequency output must be without remainder, %i != %i
|
||||
[%s]
|
||||
frequency = %f
|
||||
""" % (mcu_freq, mcu_freq_rev, self.name, self.frequency)
|
||||
raise self.printer.config_error(msg)
|
||||
value = int(0.5 * cycle_ticks)
|
||||
if value/cycle_ticks < 0.4:
|
||||
logging.warning("[%s] pulse width < 40%%" % (self.name))
|
||||
if value/cycle_ticks > 0.6:
|
||||
logging.warning("[%s] pulse width > 60%%" % (self.name))
|
||||
|
||||
def load_config_prefix(config):
|
||||
return PrinterClockOutputPin(config)
|
||||
|
|
@ -178,6 +178,8 @@ class SX1509_pwm(object):
|
|||
self._shutdown_value = max(0., min(1., shutdown_value))
|
||||
self._sx1509.set_register(self._i_on_reg,
|
||||
~int(255 * self._start_value) & 0xFF)
|
||||
def next_aligned_print_time(self, print_time, allow_early=0.):
|
||||
return print_time
|
||||
def set_pwm(self, print_time, value):
|
||||
self._sx1509.set_register(self._i_on_reg, ~int(255 * value)
|
||||
if not self._invert
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class PrinterSensorCombined:
|
|||
self.apply_mode = config.getchoice('combination_method', algos)
|
||||
# set default values
|
||||
self.last_temp = self.min_temp = self.max_temp = 0.0
|
||||
self.humidity = self.pressure = self.gas = None
|
||||
# add object
|
||||
self.printer.add_object("temperature_combined " + self.name, self)
|
||||
# time-controlled sensor update
|
||||
|
|
@ -96,13 +97,56 @@ class PrinterSensorCombined:
|
|||
def get_temp(self, eventtime):
|
||||
return self.last_temp, 0.
|
||||
|
||||
def update_additional(self, eventtime):
|
||||
values_humidity = []
|
||||
values_pressure = []
|
||||
values_gas = []
|
||||
for sensor in self.sensors:
|
||||
sensor_status = sensor.get_status(eventtime)
|
||||
if 'humidity' in sensor_status:
|
||||
sensor_humidity = sensor_status['humidity']
|
||||
if sensor_humidity is not None:
|
||||
values_humidity.append(sensor_humidity)
|
||||
if 'pressure' in sensor_status:
|
||||
sensor_pressure = sensor_status['pressure']
|
||||
if sensor_pressure is not None:
|
||||
values_pressure.append(sensor_pressure)
|
||||
if 'gas' in sensor_status:
|
||||
sensor_gas = sensor_status['gas']
|
||||
if sensor_gas is not None:
|
||||
values_gas.append(sensor_gas)
|
||||
|
||||
if values_humidity:
|
||||
humidity = self.apply_mode(values_humidity)
|
||||
if humidity:
|
||||
self.humidity = humidity
|
||||
|
||||
if values_pressure:
|
||||
pressure = self.apply_mode(values_pressure)
|
||||
if pressure:
|
||||
self.pressure = pressure
|
||||
|
||||
if values_gas:
|
||||
gas = self.apply_mode(values_gas)
|
||||
if gas:
|
||||
self.gas = gas
|
||||
|
||||
def get_status(self, eventtime):
|
||||
return {'temperature': round(self.last_temp, 2),
|
||||
}
|
||||
data = {
|
||||
'temperature': round(self.last_temp, 2),
|
||||
}
|
||||
if self.humidity is not None:
|
||||
data['humidity'] = self.humidity
|
||||
if self.pressure is not None:
|
||||
data['pressure'] = self.pressure
|
||||
if self.gas is not None:
|
||||
data['gas'] = self.gas
|
||||
return data
|
||||
|
||||
def _temperature_update_event(self, eventtime):
|
||||
# update sensor value
|
||||
self.update_temp(eventtime)
|
||||
self.update_additional(eventtime)
|
||||
|
||||
# check min / max temp values
|
||||
if self.last_temp < self.min_temp:
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ class TemperatureProbe:
|
|||
def get_temp(self, eventtime=None):
|
||||
return self.last_measurement[0], self.target_temp
|
||||
|
||||
def _collect_sample(self, kin_pos, tool_zero_z):
|
||||
def _collect_sample(self, mpresult, tool_zero_z):
|
||||
probe = self._get_probe()
|
||||
x_offset, y_offset, _ = probe.get_offsets()
|
||||
speeds = self._get_speeds()
|
||||
|
|
@ -198,7 +198,7 @@ class TemperatureProbe:
|
|||
cur_pos[0] -= x_offset
|
||||
cur_pos[1] -= y_offset
|
||||
toolhead.manual_move(cur_pos, move_speed)
|
||||
return self.cal_helper.collect_sample(kin_pos, tool_zero_z, speeds)
|
||||
return self.cal_helper.collect_sample(mpresult, tool_zero_z, speeds)
|
||||
|
||||
def _prepare_next_sample(self, last_temp, tool_zero_z):
|
||||
# Register our own abort command now that the manual
|
||||
|
|
@ -221,23 +221,23 @@ class TemperatureProbe:
|
|||
% (self.name, cnt, exp_cnt, last_temp, self.next_auto_temp)
|
||||
)
|
||||
|
||||
def _manual_probe_finalize(self, kin_pos):
|
||||
if kin_pos is None:
|
||||
def _manual_probe_finalize(self, mpresult):
|
||||
if mpresult is None:
|
||||
# Calibration aborted
|
||||
self._finalize_drift_cal(False)
|
||||
return
|
||||
if self.last_zero_pos is not None:
|
||||
z_diff = self.last_zero_pos[2] - kin_pos[2]
|
||||
z_diff = self.last_zero_pos - mpresult.bed_z
|
||||
self.total_expansion += z_diff
|
||||
logging.info(
|
||||
"Estimated Total Thermal Expansion: %.6f"
|
||||
% (self.total_expansion,)
|
||||
)
|
||||
self.last_zero_pos = kin_pos
|
||||
self.last_zero_pos = mpresult.bed_z
|
||||
toolhead = self.printer.lookup_object("toolhead")
|
||||
tool_zero_z = toolhead.get_position()[2]
|
||||
try:
|
||||
last_temp = self._collect_sample(kin_pos, tool_zero_z)
|
||||
last_temp = self._collect_sample(mpresult, tool_zero_z)
|
||||
except Exception:
|
||||
self._finalize_drift_cal(False)
|
||||
raise
|
||||
|
|
@ -562,7 +562,7 @@ class EddyDriftCompensation:
|
|||
% (self.name, self.cal_temp)
|
||||
)
|
||||
|
||||
def collect_sample(self, kin_pos, tool_zero_z, speeds):
|
||||
def collect_sample(self, mpresult, tool_zero_z, speeds):
|
||||
if self.calibration_samples is None:
|
||||
self.calibration_samples = [[] for _ in range(DRIFT_SAMPLE_COUNT)]
|
||||
move_times = []
|
||||
|
|
@ -616,7 +616,7 @@ class EddyDriftCompensation:
|
|||
zvals = [d[2] for d in data]
|
||||
avg_freq = sum(freqs) / len(freqs)
|
||||
avg_z = sum(zvals) / len(zvals)
|
||||
kin_z = i * .5 + .05 + kin_pos[2]
|
||||
kin_z = i * .5 + .05 + mpresult.bed_z
|
||||
logging.info(
|
||||
"Probe Values at Temp %.2fC, Z %.4fmm: Avg Freq = %.6f, "
|
||||
"Avg Measured Z = %.6f"
|
||||
|
|
|
|||
|
|
@ -323,24 +323,29 @@ class TMCCommandHelper:
|
|||
self.name = config.get_name().split()[-1]
|
||||
self.mcu_tmc = mcu_tmc
|
||||
self.current_helper = current_helper
|
||||
self.fields = mcu_tmc.get_fields()
|
||||
self.stepper = None
|
||||
# Stepper phase tracking
|
||||
self.mcu_phase_offset = None
|
||||
# Stepper enable/disable tracking
|
||||
self.toff = None
|
||||
self.stepper_enable = self.printer.load_object(config, "stepper_enable")
|
||||
self.enable_mutex = self.printer.get_reactor().mutex()
|
||||
# DUMP_TMC support
|
||||
self.read_registers = self.read_translate = None
|
||||
# Common tmc helpers
|
||||
self.echeck_helper = TMCErrorCheck(config, mcu_tmc)
|
||||
self.record_helper = TMCStallguardDump(config, mcu_tmc)
|
||||
self.fields = mcu_tmc.get_fields()
|
||||
self.read_registers = self.read_translate = None
|
||||
self.toff = None
|
||||
self.mcu_phase_offset = None
|
||||
self.stepper = None
|
||||
self.stepper_enable = self.printer.load_object(config, "stepper_enable")
|
||||
TMCMicrostepHelper(config, mcu_tmc)
|
||||
# Register callbacks
|
||||
self.printer.register_event_handler("stepper:sync_mcu_position",
|
||||
self._handle_sync_mcu_pos)
|
||||
self.printer.register_event_handler("stepper:set_sdir_inverted",
|
||||
self.printer.register_event_handler("stepper:set_dir_inverted",
|
||||
self._handle_sync_mcu_pos)
|
||||
self.printer.register_event_handler("klippy:mcu_identify",
|
||||
self._handle_mcu_identify)
|
||||
self.printer.register_event_handler("klippy:connect",
|
||||
self._handle_connect)
|
||||
# Set microstep config options
|
||||
TMCMicrostepHelper(config, mcu_tmc)
|
||||
# Register commands
|
||||
gcode = self.printer.lookup_object("gcode")
|
||||
gcode.register_mux_command("SET_TMC_FIELD", "STEPPER", self.name,
|
||||
|
|
@ -438,48 +443,48 @@ class TMCCommandHelper:
|
|||
self.mcu_phase_offset = moff
|
||||
# Stepper enable/disable tracking
|
||||
def _do_enable(self, print_time):
|
||||
try:
|
||||
if self.toff is not None:
|
||||
# Shared enable via comms handling
|
||||
self.fields.set_field("toff", self.toff)
|
||||
self._init_registers()
|
||||
did_reset = self.echeck_helper.start_checks()
|
||||
if did_reset:
|
||||
self.mcu_phase_offset = None
|
||||
# Calculate phase offset
|
||||
if self.toff is not None:
|
||||
# Shared enable via comms handling
|
||||
self.fields.set_field("toff", self.toff)
|
||||
self._init_registers()
|
||||
did_reset = self.echeck_helper.start_checks()
|
||||
if did_reset:
|
||||
self.mcu_phase_offset = None
|
||||
# Calculate phase offset
|
||||
if self.mcu_phase_offset is not None:
|
||||
return
|
||||
gcode = self.printer.lookup_object("gcode")
|
||||
with gcode.get_mutex():
|
||||
if self.mcu_phase_offset is not None:
|
||||
return
|
||||
gcode = self.printer.lookup_object("gcode")
|
||||
with gcode.get_mutex():
|
||||
if self.mcu_phase_offset is not None:
|
||||
return
|
||||
logging.info("Pausing toolhead to calculate %s phase offset",
|
||||
self.stepper_name)
|
||||
self.printer.lookup_object('toolhead').wait_moves()
|
||||
self._handle_sync_mcu_pos(self.stepper)
|
||||
except self.printer.command_error as e:
|
||||
self.printer.invoke_shutdown(str(e))
|
||||
logging.info("Pausing toolhead to calculate %s phase offset",
|
||||
self.stepper_name)
|
||||
self.printer.lookup_object('toolhead').wait_moves()
|
||||
self._handle_sync_mcu_pos(self.stepper)
|
||||
def _do_disable(self, print_time):
|
||||
try:
|
||||
if self.toff is not None:
|
||||
val = self.fields.set_field("toff", 0)
|
||||
reg_name = self.fields.lookup_register("toff")
|
||||
self.mcu_tmc.set_register(reg_name, val, print_time)
|
||||
self.echeck_helper.stop_checks()
|
||||
except self.printer.command_error as e:
|
||||
self.printer.invoke_shutdown(str(e))
|
||||
if self.toff is not None:
|
||||
val = self.fields.set_field("toff", 0)
|
||||
reg_name = self.fields.lookup_register("toff")
|
||||
self.mcu_tmc.set_register(reg_name, val, print_time)
|
||||
self.echeck_helper.stop_checks()
|
||||
def _handle_stepper_enable(self, print_time, is_enable):
|
||||
def enable_disable_cb(eventtime):
|
||||
try:
|
||||
with self.enable_mutex:
|
||||
if is_enable:
|
||||
self._do_enable(print_time)
|
||||
else:
|
||||
self._do_disable(print_time)
|
||||
except self.printer.command_error as e:
|
||||
self.printer.invoke_shutdown(str(e))
|
||||
self.printer.get_reactor().register_callback(enable_disable_cb)
|
||||
# Initial startup handling
|
||||
def _handle_mcu_identify(self):
|
||||
# Lookup stepper object
|
||||
force_move = self.printer.lookup_object("force_move")
|
||||
self.stepper = force_move.lookup_stepper(self.stepper_name)
|
||||
# Note pulse duration and step_both_edge optimizations available
|
||||
self.stepper.setup_default_pulse_duration(.000000100, True)
|
||||
def _handle_stepper_enable(self, print_time, is_enable):
|
||||
if is_enable:
|
||||
cb = (lambda ev: self._do_enable(print_time))
|
||||
else:
|
||||
cb = (lambda ev: self._do_disable(print_time))
|
||||
self.printer.get_reactor().register_callback(cb)
|
||||
def _handle_connect(self):
|
||||
# Check if using step on both edges optimization
|
||||
pulse_duration, step_both_edge = self.stepper.get_pulse_duration()
|
||||
|
|
|
|||
380
klippy/extras/trigger_analog.py
Normal file
380
klippy/extras/trigger_analog.py
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
# Wrapper around mcu trigger_analog objects
|
||||
#
|
||||
# Copyright (C) 2025 Gareth Farrington <gareth@waves.ky>
|
||||
# Copyright (C) 2026 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import mcu
|
||||
|
||||
|
||||
######################################################################
|
||||
# SOS filters (Second Order Sectional)
|
||||
######################################################################
|
||||
|
||||
MAX_INT32 = (2 ** 31)
|
||||
MIN_INT32 = -(2 ** 31) - 1
|
||||
def assert_is_int32(value, frac_bits):
|
||||
if value > MAX_INT32 or value < MIN_INT32:
|
||||
raise OverflowError("Fixed point Q%d.%d overflow"
|
||||
% (31-frac_bits, frac_bits))
|
||||
return value
|
||||
|
||||
# convert a floating point value to a 32 bit fixed point representation
|
||||
# checks for overflow
|
||||
def to_fixed_32(value, frac_bits=0):
|
||||
fixed_val = int(value * (2**frac_bits))
|
||||
return assert_is_int32(fixed_val, frac_bits)
|
||||
|
||||
|
||||
# Digital filter designer and container
|
||||
class DigitalFilter:
|
||||
def __init__(self, sps, cfg_error, highpass=None, highpass_order=1,
|
||||
lowpass=None, lowpass_order=1, notches=None, notch_quality=2.0):
|
||||
self.filter_sections = []
|
||||
self.initial_state = []
|
||||
self.sample_frequency = sps
|
||||
# an empty filter can be created without SciPi/numpy
|
||||
if not (highpass or lowpass or notches):
|
||||
return
|
||||
try:
|
||||
import scipy.signal as signal
|
||||
import numpy
|
||||
except:
|
||||
raise cfg_error("DigitalFilter require the SciPy module")
|
||||
if highpass:
|
||||
self.filter_sections.extend(
|
||||
self._butter(highpass, "highpass", highpass_order))
|
||||
if lowpass:
|
||||
self.filter_sections.extend(
|
||||
self._butter(lowpass, "lowpass", lowpass_order))
|
||||
if notches is None:
|
||||
notches = []
|
||||
for notch_freq in notches:
|
||||
self.filter_sections.append(self._notch(notch_freq, notch_quality))
|
||||
if len(self.filter_sections) > 0:
|
||||
self.initial_state = signal.sosfilt_zi(self.filter_sections)
|
||||
|
||||
def _butter(self, frequency, btype, order):
|
||||
import scipy.signal as signal
|
||||
return signal.butter(order, Wn=frequency, btype=btype,
|
||||
fs=self.sample_frequency, output='sos')
|
||||
|
||||
def _notch(self, freq, quality):
|
||||
import scipy.signal as signal
|
||||
b, a = signal.iirnotch(freq, Q=quality, fs=self.sample_frequency)
|
||||
return signal.tf2sos(b, a)[0]
|
||||
|
||||
def get_filter_sections(self):
|
||||
return self.filter_sections
|
||||
|
||||
def get_initial_state(self):
|
||||
return self.initial_state
|
||||
|
||||
def filtfilt(self, data):
|
||||
import scipy.signal as signal
|
||||
import numpy
|
||||
data = numpy.array(data)
|
||||
return signal.sosfiltfilt(self.filter_sections, data)
|
||||
|
||||
# Produce sample to sample difference (derivative) of a DigitalFilter
|
||||
class DerivativeFilter:
|
||||
def __init__(self, main_filter):
|
||||
self._main_filter = main_filter
|
||||
|
||||
def get_main_filter(self):
|
||||
return self._main_filter
|
||||
|
||||
def get_filter_sections(self):
|
||||
s = list(self._main_filter.get_filter_sections())
|
||||
return s + [(1., -1., 0., 1., 0., 0.)]
|
||||
|
||||
def get_initial_state(self):
|
||||
s = list(self._main_filter.get_initial_state())
|
||||
return s + [(-1., 0.)]
|
||||
|
||||
# Control an `sos_filter` object on the MCU
|
||||
class MCU_SosFilter:
|
||||
# max_sections should be the largest number of sections you expect
|
||||
# to use at runtime.
|
||||
def __init__(self, mcu, cmd_queue, max_sections):
|
||||
self._mcu = mcu
|
||||
self._cmd_queue = cmd_queue
|
||||
self._max_sections = max_sections
|
||||
# SOS filter "design"
|
||||
self._design = None
|
||||
self._coeff_frac_bits = 0
|
||||
self._start_value = 0.
|
||||
# Offset and scaling
|
||||
self._offset = 0
|
||||
self._scale = 1.
|
||||
self._scale_frac_bits = 0
|
||||
self._auto_offset = False
|
||||
# MCU commands
|
||||
self._oid = self._mcu.create_oid()
|
||||
self._set_section_cmd = self._set_state_cmd = None
|
||||
self._set_active_cmd = self._set_offset_scale_cmd = None
|
||||
self._last_sent_coeffs = [None] * self._max_sections
|
||||
self._last_sent_offset_scale = None
|
||||
self._mcu.add_config_cmd("config_sos_filter oid=%d max_sections=%d"
|
||||
% (self._oid, self._max_sections))
|
||||
self._mcu.register_config_callback(self._build_config)
|
||||
|
||||
def _build_config(self):
|
||||
self._set_section_cmd = self._mcu.lookup_command(
|
||||
"sos_filter_set_section oid=%c section_idx=%c"
|
||||
" sos0=%i sos1=%i sos2=%i sos3=%i sos4=%i", cq=self._cmd_queue)
|
||||
self._set_state_cmd = self._mcu.lookup_command(
|
||||
"sos_filter_set_state oid=%c section_idx=%c state0=%i state1=%i",
|
||||
cq=self._cmd_queue)
|
||||
self._set_offset_scale_cmd = self._mcu.lookup_command(
|
||||
"sos_filter_set_offset_scale oid=%c offset=%i"
|
||||
" scale=%i scale_frac_bits=%c auto_offset=%c", cq=self._cmd_queue)
|
||||
self._set_active_cmd = self._mcu.lookup_command(
|
||||
"sos_filter_set_active oid=%c n_sections=%c coeff_frac_bits=%c",
|
||||
cq=self._cmd_queue)
|
||||
|
||||
def get_oid(self):
|
||||
return self._oid
|
||||
|
||||
# convert the SciPi SOS filters to fixed point format
|
||||
def _convert_filter(self):
|
||||
if self._design is None:
|
||||
return []
|
||||
filter_sections = self._design.get_filter_sections()
|
||||
sos_fixed = []
|
||||
for section in filter_sections:
|
||||
nun_coeff = len(section)
|
||||
if nun_coeff != 6:
|
||||
raise ValueError("The number of filter coefficients is %i"
|
||||
", must be 6" % (nun_coeff,))
|
||||
fixed_section = []
|
||||
for col, coeff in enumerate(section):
|
||||
if col != 3: # omit column 3
|
||||
fixed_coeff = to_fixed_32(coeff, self._coeff_frac_bits)
|
||||
fixed_section.append(fixed_coeff)
|
||||
elif coeff != 1.0: # double check column 3 is always 1.0
|
||||
raise ValueError("Coefficient 3 is expected to be 1.0"
|
||||
" but was %f" % (coeff,))
|
||||
sos_fixed.append(fixed_section)
|
||||
return sos_fixed
|
||||
|
||||
# convert the SOS filter state matrix (zi) to fixed point format
|
||||
def _convert_state(self):
|
||||
if self._design is None:
|
||||
return []
|
||||
filter_state = self._design.get_initial_state()
|
||||
sos_state = []
|
||||
for section in filter_state:
|
||||
nun_states = len(section)
|
||||
if nun_states != 2:
|
||||
raise ValueError(
|
||||
"The number of state elements is %i, must be 2"
|
||||
% (nun_states,))
|
||||
fixed_state = []
|
||||
for col, value in enumerate(section):
|
||||
adjval = value * self._start_value
|
||||
fixed_state.append(to_fixed_32(adjval))
|
||||
sos_state.append(fixed_state)
|
||||
return sos_state
|
||||
|
||||
# Set expected state when filter first starts (avoids filter
|
||||
# "ringing" if sensor data has a known static offset)
|
||||
def set_start_state(self, start_value):
|
||||
self._start_value = start_value
|
||||
|
||||
# Set conversion of a raw value 1 to a 1.0 value processed by sos filter
|
||||
def set_offset_scale(self, offset=0, scale=1., scale_frac_bits=0,
|
||||
auto_offset=False):
|
||||
self._offset = offset
|
||||
self._scale = scale
|
||||
self._scale_frac_bits = scale_frac_bits
|
||||
self._auto_offset = auto_offset
|
||||
|
||||
# Change the filter coefficients and state at runtime
|
||||
def set_filter_design(self, design, coeff_frac_bits=29):
|
||||
self._design = design
|
||||
self._coeff_frac_bits = coeff_frac_bits
|
||||
|
||||
# Resets the filter state back to initial conditions at runtime
|
||||
def reset_filter(self):
|
||||
# Generate filter parameters
|
||||
sos_fixed = self._convert_filter()
|
||||
sos_state = self._convert_state()
|
||||
num_sections = len(sos_fixed)
|
||||
if num_sections > self._max_sections:
|
||||
raise ValueError("Too many filter sections: %i, The max is %i"
|
||||
% (num_sections, self._max_sections,))
|
||||
if len(sos_state) != num_sections:
|
||||
raise ValueError("The number of filter sections (%i) and state "
|
||||
"sections (%i) must be equal"
|
||||
% (num_sections, len(sos_state)))
|
||||
# Send section coefficients (if they have changed)
|
||||
for i, section in enumerate(sos_fixed):
|
||||
args = (self._oid, i, section[0], section[1], section[2],
|
||||
section[3], section[4])
|
||||
if args == self._last_sent_coeffs[i]:
|
||||
continue
|
||||
self._set_section_cmd.send(args)
|
||||
self._last_sent_coeffs[i] = args
|
||||
# Send section initial states
|
||||
for i, state in enumerate(sos_state):
|
||||
self._set_state_cmd.send([self._oid, i, state[0], state[1]])
|
||||
# Send offset/scale (if they have changed)
|
||||
su = to_fixed_32(self._scale, self._scale_frac_bits)
|
||||
args = (self._oid, self._offset, su, self._scale_frac_bits,
|
||||
self._auto_offset)
|
||||
if args != self._last_sent_offset_scale or self._auto_offset:
|
||||
self._set_offset_scale_cmd.send(args)
|
||||
self._last_sent_offset_scale = args
|
||||
# Activate filter
|
||||
if self._max_sections:
|
||||
self._set_active_cmd.send([self._oid, num_sections,
|
||||
self._coeff_frac_bits])
|
||||
|
||||
|
||||
######################################################################
|
||||
# Trigger Analog
|
||||
######################################################################
|
||||
|
||||
# MCU_trigger_analog is the interface to `trigger_analog` on the MCU
|
||||
class MCU_trigger_analog:
|
||||
MONITOR_MAX = 3
|
||||
REASON_TRIGGER_ANALOG = mcu.MCU_trsync.REASON_COMMS_TIMEOUT + 1
|
||||
def __init__(self, sensor_inst):
|
||||
self._printer = sensor_inst.get_mcu().get_printer()
|
||||
self._sensor = sensor_inst
|
||||
self._mcu = self._sensor.get_mcu()
|
||||
self._sos_filter = None
|
||||
self._dispatch = mcu.TriggerDispatch(self._mcu)
|
||||
self._last_trigger_time = 0.
|
||||
# Raw range checking
|
||||
self._raw_min = self._raw_max = 0
|
||||
self._last_range_args = None
|
||||
# Trigger type
|
||||
self._trigger_type = "unspecified"
|
||||
self._trigger_value = 0.
|
||||
self._last_trigger_args = None
|
||||
# Error codes from MCU
|
||||
self._error_map = {}
|
||||
self._sensor_specific_error = 0
|
||||
# Configure MCU objects
|
||||
self._oid = self._mcu.create_oid()
|
||||
self._home_cmd = self._query_state_cmd = None
|
||||
self._set_raw_range_cmd = self._set_trigger_cmd = None
|
||||
self._mcu.register_config_callback(self._build_config)
|
||||
|
||||
def setup_sos_filter(self, sos_filter):
|
||||
self._sos_filter = sos_filter
|
||||
|
||||
def _build_config(self):
|
||||
self._sensor.setup_trigger_analog(self._oid)
|
||||
cmd_queue = self._dispatch.get_command_queue()
|
||||
if self._sos_filter is None:
|
||||
self.setup_sos_filter(MCU_SosFilter(self._mcu, cmd_queue, 0))
|
||||
self._mcu.add_config_cmd(
|
||||
"config_trigger_analog oid=%d sos_filter_oid=%d" % (
|
||||
self._oid, self._sos_filter.get_oid()))
|
||||
# Lookup commands
|
||||
self._query_state_cmd = self._mcu.lookup_query_command(
|
||||
"trigger_analog_query_state oid=%c",
|
||||
"trigger_analog_state oid=%c homing=%c homing_clock=%u",
|
||||
oid=self._oid, cq=cmd_queue)
|
||||
self._set_raw_range_cmd = self._mcu.lookup_command(
|
||||
"trigger_analog_set_raw_range oid=%c raw_min=%i raw_max=%i",
|
||||
cq=cmd_queue)
|
||||
self._set_trigger_cmd = self._mcu.lookup_command(
|
||||
"trigger_analog_set_trigger oid=%c trigger_analog_type=%c"
|
||||
" trigger_value=%i", cq=cmd_queue)
|
||||
self._home_cmd = self._mcu.lookup_command(
|
||||
"trigger_analog_home oid=%c trsync_oid=%c trigger_reason=%c"
|
||||
" error_reason=%c clock=%u monitor_ticks=%u monitor_max=%u",
|
||||
cq=cmd_queue)
|
||||
# Load errors from mcu
|
||||
errors = self._mcu.get_enumerations().get("trigger_analog_error:", {})
|
||||
self._error_map = {v: k for k, v in errors.items()}
|
||||
self._sensor_specific_error = errors.get("SENSOR_SPECIFIC", 0)
|
||||
|
||||
def get_oid(self):
|
||||
return self._oid
|
||||
|
||||
def get_mcu(self):
|
||||
return self._mcu
|
||||
|
||||
def get_sos_filter(self):
|
||||
return self._sos_filter
|
||||
|
||||
def get_dispatch(self):
|
||||
return self._dispatch
|
||||
|
||||
def get_last_trigger_time(self):
|
||||
return self._last_trigger_time
|
||||
|
||||
def set_trigger(self, trigger_type, trigger_value):
|
||||
self._trigger_type = trigger_type
|
||||
self._trigger_value = trigger_value
|
||||
|
||||
def set_raw_range(self, raw_min, raw_max):
|
||||
self._raw_min = raw_min
|
||||
self._raw_max = raw_max
|
||||
|
||||
def _reset_filter(self):
|
||||
# Update raw range parameters in mcu (if they have changed)
|
||||
args = [self._oid, self._raw_min, self._raw_max]
|
||||
if args != self._last_range_args:
|
||||
self._set_raw_range_cmd.send(args)
|
||||
self._last_range_args = args
|
||||
# Update trigger in mcu (if it has changed)
|
||||
args = [self._oid, self._trigger_type, to_fixed_32(self._trigger_value)]
|
||||
if args != self._last_trigger_args:
|
||||
self._set_trigger_cmd.send(args)
|
||||
self._last_trigger_args = args
|
||||
# Update sos filter in mcu
|
||||
self._sos_filter.reset_filter()
|
||||
|
||||
def _clear_home(self):
|
||||
self._home_cmd.send([self._oid, 0, 0, 0, 0, 0, 0, 0])
|
||||
params = self._query_state_cmd.send([self._oid])
|
||||
trigger_ticks = self._mcu.clock32_to_clock64(params['homing_clock'])
|
||||
return self._mcu.clock_to_print_time(trigger_ticks)
|
||||
|
||||
def get_steppers(self):
|
||||
return self._dispatch.get_steppers()
|
||||
|
||||
def home_start(self, print_time, sample_time, sample_count, rest_time,
|
||||
triggered=True):
|
||||
self._last_trigger_time = 0.
|
||||
self._reset_filter()
|
||||
trigger_completion = self._dispatch.start(print_time)
|
||||
clock = self._mcu.print_time_to_clock(print_time)
|
||||
sensor_update = 1. / self._sensor.get_samples_per_second()
|
||||
sm_ticks = self._mcu.seconds_to_clock(sensor_update)
|
||||
self._home_cmd.send([self._oid, self._dispatch.get_oid(),
|
||||
mcu.MCU_trsync.REASON_ENDSTOP_HIT, self.REASON_TRIGGER_ANALOG,
|
||||
clock, sm_ticks, self.MONITOR_MAX], reqclock=clock)
|
||||
return trigger_completion
|
||||
|
||||
def home_wait(self, home_end_time):
|
||||
self._dispatch.wait_end(home_end_time)
|
||||
# trigger has happened, now to find out why...
|
||||
res = self._dispatch.stop()
|
||||
# clear the homing state so it stops processing samples
|
||||
trigger_time = self._clear_home()
|
||||
if res >= mcu.MCU_trsync.REASON_COMMS_TIMEOUT:
|
||||
if res == mcu.MCU_trsync.REASON_COMMS_TIMEOUT:
|
||||
raise self._printer.command_error(
|
||||
"Communication timeout during homing")
|
||||
error_code = res - self.REASON_TRIGGER_ANALOG
|
||||
if error_code >= self._sensor_specific_error:
|
||||
sensor_err = error_code - self._sensor_specific_error
|
||||
error_msg = self._sensor.lookup_sensor_error(sensor_err)
|
||||
else:
|
||||
defmsg = "Unknown code %i" % (error_code,)
|
||||
error_msg = self._error_map.get(error_code, defmsg)
|
||||
raise self._printer.command_error("Trigger analog error: %s"
|
||||
% (error_msg,))
|
||||
if res != mcu.MCU_trsync.REASON_ENDSTOP_HIT:
|
||||
return 0.
|
||||
if self._mcu.is_fileoutput():
|
||||
trigger_time = home_end_time
|
||||
self._last_trigger_time = trigger_time
|
||||
return trigger_time
|
||||
|
|
@ -143,16 +143,14 @@ class ZTilt:
|
|||
self.z_status.reset()
|
||||
self.retry_helper.start(gcmd)
|
||||
self.probe_helper.start_probe(gcmd)
|
||||
def probe_finalize(self, offsets, positions):
|
||||
def probe_finalize(self, positions):
|
||||
# Setup for coordinate descent analysis
|
||||
z_offset = offsets[2]
|
||||
logging.info("Calculating bed tilt with: %s", positions)
|
||||
params = { 'x_adjust': 0., 'y_adjust': 0., 'z_adjust': z_offset }
|
||||
params = { 'x_adjust': 0., 'y_adjust': 0., 'z_adjust': 0. }
|
||||
# Perform coordinate descent
|
||||
def adjusted_height(pos, params):
|
||||
x, y, z = pos
|
||||
return (z - x*params['x_adjust'] - y*params['y_adjust']
|
||||
- params['z_adjust'])
|
||||
return (pos.bed_z - pos.bed_x*params['x_adjust']
|
||||
- pos.bed_y*params['y_adjust'] - params['z_adjust'])
|
||||
def errorfunc(params):
|
||||
total_error = 0.
|
||||
for pos in positions:
|
||||
|
|
@ -165,8 +163,7 @@ class ZTilt:
|
|||
logging.info("Calculated bed tilt parameters: %s", new_params)
|
||||
x_adjust = new_params['x_adjust']
|
||||
y_adjust = new_params['y_adjust']
|
||||
z_adjust = (new_params['z_adjust'] - z_offset
|
||||
- x_adjust * offsets[0] - y_adjust * offsets[1])
|
||||
z_adjust = new_params['z_adjust']
|
||||
adjustments = [x*x_adjust + y*y_adjust + z_adjust
|
||||
for x, y in self.z_positions]
|
||||
self.z_helper.adjust_steppers(adjustments, speed)
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ class CartKinematics:
|
|||
self.dc_module = idex_modes.DualCarriages(
|
||||
self.printer, [self.rails[self.dual_carriage_axis]],
|
||||
[self.rails[3]], axes=[self.dual_carriage_axis],
|
||||
safe_dist=dc_config.getfloat(
|
||||
'safe_distance', None, minval=0.))
|
||||
safe_dist=[dc_config.getfloat(
|
||||
'safe_distance', None, minval=0.)])
|
||||
for s in self.get_steppers():
|
||||
s.set_trapq(toolhead.get_trapq())
|
||||
# Setup boundary checks
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@
|
|||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
import copy, itertools, logging, math
|
||||
import collections, copy, itertools, logging, math
|
||||
import gcode, mathutil, stepper
|
||||
from . import idex_modes
|
||||
from . import kinematic_stepper as ks
|
||||
|
||||
VALID_AXES = ['x', 'y', 'z']
|
||||
|
||||
def mat_mul(a, b):
|
||||
if len(a[0]) != len(b):
|
||||
return None
|
||||
|
|
@ -35,13 +37,11 @@ class MainCarriage:
|
|||
def __init__(self, config):
|
||||
self.rail = stepper.GenericPrinterRail(config)
|
||||
carriage_name = self.rail.get_name(short=True)
|
||||
valid_axes = ['x', 'y', 'z']
|
||||
if carriage_name in valid_axes:
|
||||
axis_name = config.getchoice('axis', valid_axes, carriage_name)
|
||||
if carriage_name in VALID_AXES:
|
||||
self.axis_name = config.getchoice('axis', VALID_AXES, carriage_name)
|
||||
else:
|
||||
axis_name = config.getchoice('axis', valid_axes)
|
||||
self.axis = ord(axis_name) - ord('x')
|
||||
self.axis_name = axis_name
|
||||
self.axis_name = config.getchoice('axis', VALID_AXES)
|
||||
self.axis = ord(self.axis_name) - ord('x')
|
||||
self.dual_carriage = None
|
||||
def get_name(self):
|
||||
return self.rail.get_name(short=True)
|
||||
|
|
@ -56,9 +56,7 @@ class MainCarriage:
|
|||
return True
|
||||
return self.dual_carriage.get_dc_module().is_active(self.rail)
|
||||
def set_dual_carriage(self, carriage):
|
||||
old_dc = self.dual_carriage
|
||||
self.dual_carriage = carriage
|
||||
return old_dc
|
||||
def get_dual_carriage(self):
|
||||
return self.dual_carriage
|
||||
|
||||
|
|
@ -78,23 +76,49 @@ class ExtraCarriage:
|
|||
self.endstop_pin, self.name)
|
||||
|
||||
class DualCarriage:
|
||||
def __init__(self, config, carriages):
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
self.rail = stepper.GenericPrinterRail(config)
|
||||
self.primary_carriage = config.getchoice('primary_carriage', carriages)
|
||||
if self.primary_carriage.set_dual_carriage(self) is not None:
|
||||
raise config.error(
|
||||
"Redefinition of dual_carriage for carriage '%s'" %
|
||||
self.primary_carriage.get_name())
|
||||
self.primary_carriage_name = config.get('primary_carriage', None)
|
||||
if self.primary_carriage_name is None:
|
||||
self.axis_name = config.getchoice('axis', VALID_AXES)
|
||||
self.axis = ord(self.axis_name) - ord('x')
|
||||
self.safe_dist = None
|
||||
else:
|
||||
self.axis_name = config.getchoice('axis', VALID_AXES + [None], None)
|
||||
self.safe_dist = config.getfloat('safe_distance', None, minval=0.)
|
||||
self.primary_carriage = self.dual_carriage = None
|
||||
self.config_error = config.error
|
||||
def resolve_primary_carriage(self, carriages):
|
||||
if self.primary_carriage_name is None:
|
||||
return
|
||||
if self.primary_carriage_name not in carriages:
|
||||
raise self.config_error(
|
||||
"primary_carriage = '%s' for '%s' is not a valid choice"
|
||||
% (self.primary_carriage_name, self.get_name()))
|
||||
self.primary_carriage = carriages[self.primary_carriage_name]
|
||||
axis_name = self.axis_name or self.primary_carriage.axis_name
|
||||
if axis_name != self.primary_carriage.axis_name:
|
||||
raise self.config_error("Mismatching axes between carriage '%s' "
|
||||
"(axis=%s) and dual_carriage '%s' (axis=%s)"
|
||||
% (self.primary_carriage.get_name(),
|
||||
self.primary_carriage.axis_name,
|
||||
self.get_name(), axis_name))
|
||||
self.axis = ord(axis_name) - ord('x')
|
||||
if self.primary_carriage.get_dual_carriage():
|
||||
raise self.config_error(
|
||||
"Multiple dual carriages ('%s', '%s') for carriage '%s'" %
|
||||
(self.primary_carriage.get_dual_carriage().get_name(),
|
||||
self.get_name(), self.primary_carriage.get_name()))
|
||||
self.primary_carriage.set_dual_carriage(self)
|
||||
self.axis = self.primary_carriage.get_axis()
|
||||
if self.axis > 1:
|
||||
raise config.error("Invalid axis '%s' for dual_carriage" %
|
||||
"xyz"[self.axis])
|
||||
self.safe_dist = config.getfloat('safe_distance', None, minval=0.)
|
||||
raise self.config_error("Invalid axis '%s' for dual_carriage '%s'" %
|
||||
("xyz"[self.axis], self.get_name()))
|
||||
def get_name(self):
|
||||
return self.rail.get_name(short=True)
|
||||
def get_axis(self):
|
||||
return self.primary_carriage.get_axis()
|
||||
return self.axis
|
||||
def get_rail(self):
|
||||
return self.rail
|
||||
def get_safe_dist(self):
|
||||
|
|
@ -103,7 +127,13 @@ class DualCarriage:
|
|||
return self.printer.lookup_object('dual_carriage')
|
||||
def is_active(self):
|
||||
return self.get_dc_module().is_active(self.rail)
|
||||
def set_dual_carriage(self, carriage):
|
||||
self.dual_carriage = carriage
|
||||
def get_dual_carriage(self):
|
||||
if self.dual_carriage is not None:
|
||||
return self.dual_carriage
|
||||
return self.primary_carriage
|
||||
def get_primary_carriage(self):
|
||||
return self.primary_carriage
|
||||
def add_stepper(self, kin_stepper):
|
||||
self.rail.add_stepper(kin_stepper.get_stepper())
|
||||
|
|
@ -116,27 +146,38 @@ class GenericCartesianKinematics:
|
|||
s.set_trapq(toolhead.get_trapq())
|
||||
self.dc_module = None
|
||||
if self.dc_carriages:
|
||||
pcs = [dc.get_dual_carriage() for dc in self.dc_carriages]
|
||||
dc_axes = set(dc.get_axis() for dc in self.dc_carriages)
|
||||
pcs = ([pc for pc in self.primary_carriages
|
||||
if pc.get_axis() in dc_axes] +
|
||||
[dc for dc in self.dc_carriages
|
||||
if dc.get_primary_carriage() is None])
|
||||
dcs = [pc.get_dual_carriage() for pc in pcs]
|
||||
primary_rails = [pc.get_rail() for pc in pcs]
|
||||
dual_rails = [dc.get_rail() for dc in self.dc_carriages]
|
||||
axes = [dc.get_axis() for dc in self.dc_carriages]
|
||||
safe_dist = {dc.get_axis() : dc.get_safe_dist()
|
||||
for dc in self.dc_carriages}
|
||||
dual_rails = [dc.get_rail() if dc else None for dc in dcs]
|
||||
axes = [pc.get_axis() for pc in pcs]
|
||||
safe_dist = [dc.get_safe_dist() if dc else None for dc in dcs]
|
||||
self.dc_module = dc_module = idex_modes.DualCarriages(
|
||||
self.printer, primary_rails, dual_rails, axes, safe_dist)
|
||||
zero_pos = (0., 0.)
|
||||
for acs in itertools.product(*zip(pcs, self.dc_carriages)):
|
||||
for acs in itertools.product(*zip(pcs, dcs)):
|
||||
for c in acs:
|
||||
if c is None:
|
||||
continue
|
||||
dc_module.get_dc_rail_wrapper(c.get_rail()).activate(
|
||||
idex_modes.PRIMARY, zero_pos)
|
||||
dc_rail = c.get_dual_carriage().get_rail()
|
||||
dc_module.get_dc_rail_wrapper(dc_rail).inactivate(zero_pos)
|
||||
dc = c.get_dual_carriage()
|
||||
if dc is not None:
|
||||
dc_module.get_dc_rail_wrapper(dc.get_rail()).inactivate(
|
||||
zero_pos)
|
||||
self._check_kinematics(config.error)
|
||||
for c in pcs:
|
||||
dc_module.get_dc_rail_wrapper(c.get_rail()).activate(
|
||||
for dc in self.dc_carriages:
|
||||
dc_module.get_dc_rail_wrapper(dc.get_rail()).inactivate(
|
||||
zero_pos)
|
||||
for pc in self.primary_carriages:
|
||||
if pc.get_axis() not in dc_axes:
|
||||
continue
|
||||
dc_module.get_dc_rail_wrapper(pc.get_rail()).activate(
|
||||
idex_modes.PRIMARY, zero_pos)
|
||||
dc_rail = c.get_dual_carriage().get_rail()
|
||||
dc_module.get_dc_rail_wrapper(dc_rail).inactivate(zero_pos)
|
||||
else:
|
||||
self._check_kinematics(config.error)
|
||||
# Setup boundary checks
|
||||
|
|
@ -152,25 +193,32 @@ class GenericCartesianKinematics:
|
|||
self.cmd_SET_STEPPER_CARRIAGES,
|
||||
desc=self.cmd_SET_STEPPER_CARRIAGES_help)
|
||||
def _load_kinematics(self, config):
|
||||
carriages = {}
|
||||
primary_carriages = []
|
||||
for mcconfig in config.get_prefix_sections('carriage '):
|
||||
c = MainCarriage(mcconfig)
|
||||
axis = c.get_axis()
|
||||
dups = [mc for mc in carriages.values() if mc.get_axis() == axis]
|
||||
if dups:
|
||||
primary_carriages.append(MainCarriage(mcconfig))
|
||||
for axis, axis_name in enumerate(VALID_AXES):
|
||||
dups = [pc.get_name() for pc in primary_carriages
|
||||
if pc.get_axis() == axis]
|
||||
if len(dups) > 1:
|
||||
raise config.error(
|
||||
"Axis '%s' referenced by multiple carriages (%s, %s)"
|
||||
% ("xyz"[axis], c.get_name(), dups[0].get_name()))
|
||||
carriages[c.get_name()] = c
|
||||
"Axis '%s' is set for multiple primary carriages (%s)"
|
||||
% (axis_name, ', '.join(dups)))
|
||||
elif not dups:
|
||||
raise config.error(
|
||||
"No carriage defined for axis '%s'" % axis_name)
|
||||
dc_carriages = []
|
||||
for dcconfig in config.get_prefix_sections('dual_carriage '):
|
||||
dc_carriages.append(DualCarriage(dcconfig, carriages))
|
||||
for dc in dc_carriages:
|
||||
name = dc.get_name()
|
||||
dc_carriages.append(DualCarriage(dcconfig))
|
||||
carriages = {}
|
||||
for carriage in primary_carriages + dc_carriages:
|
||||
name = carriage.get_name()
|
||||
if name in carriages:
|
||||
raise config.error("Redefinition of carriage %s" % name)
|
||||
carriages[name] = dc
|
||||
carriages[name] = carriage
|
||||
for dc in dc_carriages:
|
||||
dc.resolve_primary_carriage(carriages)
|
||||
self.carriages = dict(carriages)
|
||||
self.primary_carriages = primary_carriages
|
||||
self.dc_carriages = dc_carriages
|
||||
ec_carriages = []
|
||||
for ecconfig in config.get_prefix_sections('extra_carriage '):
|
||||
|
|
@ -207,16 +255,19 @@ class GenericCartesianKinematics:
|
|||
def get_steppers(self):
|
||||
return [s.get_stepper() for s in self.kin_steppers]
|
||||
def get_primary_carriages(self):
|
||||
carriages = [None] * 3
|
||||
for carriage in self.carriages.values():
|
||||
a = carriage.get_axis()
|
||||
if carriage.get_dual_carriage() is not None:
|
||||
carriages = []
|
||||
for a in range(3):
|
||||
c = None
|
||||
if self.dc_module is not None and a in self.dc_module.get_axes():
|
||||
primary_rail = self.dc_module.get_primary_rail(a)
|
||||
for c in self.carriages.values():
|
||||
if c.get_rail() == primary_rail:
|
||||
carriages[a] = c
|
||||
break
|
||||
else:
|
||||
carriages[a] = carriage
|
||||
for c in self.primary_carriages:
|
||||
if c.get_axis() == a:
|
||||
break
|
||||
carriages.append(c)
|
||||
return carriages
|
||||
def _get_kinematics_coeffs(self):
|
||||
matr = {s.get_name() : list(s.get_kin_coeffs())
|
||||
|
|
@ -227,9 +278,9 @@ class GenericCartesianKinematics:
|
|||
[0. for s in self.kin_steppers])
|
||||
axes = [dc.get_axis() for dc in self.dc_carriages]
|
||||
orig_matr = copy.deepcopy(matr)
|
||||
for dc in self.dc_carriages:
|
||||
axis = dc.get_axis()
|
||||
for c in [dc.get_dual_carriage(), dc]:
|
||||
for c in self.carriages.values():
|
||||
axis = c.get_axis()
|
||||
if axis in self.dc_module.get_axes():
|
||||
m, o = self.dc_module.get_transform(c.get_rail())
|
||||
for s in c.get_rail().get_steppers():
|
||||
matr[s.get_name()][axis] *= m
|
||||
|
|
@ -289,16 +340,14 @@ class GenericCartesianKinematics:
|
|||
homing_state.home_rails([rail], forcepos, homepos)
|
||||
def home(self, homing_state):
|
||||
self._check_kinematics(self.printer.command_error)
|
||||
primary_carriages = self.get_primary_carriages()
|
||||
# Each axis is homed independently and in order
|
||||
for axis in homing_state.get_axes():
|
||||
for carriage in self.carriages.values():
|
||||
if carriage.get_axis() != axis:
|
||||
continue
|
||||
if carriage.get_dual_carriage() != None:
|
||||
self.dc_module.home(homing_state, axis)
|
||||
else:
|
||||
self.home_axis(homing_state, axis, carriage.get_rail())
|
||||
break
|
||||
if self.dc_module is not None and axis in self.dc_module.get_axes():
|
||||
self.dc_module.home(homing_state, axis)
|
||||
else:
|
||||
carriage = primary_carriages[axis]
|
||||
self.home_axis(homing_state, axis, carriage.get_rail())
|
||||
def _check_endstops(self, move):
|
||||
end_pos = move.end_pos
|
||||
for i in (0, 1, 2):
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ class HybridCoreXYKinematics:
|
|||
self.rails[3].setup_itersolve('corexy_stepper_alloc', b'+')
|
||||
self.dc_module = idex_modes.DualCarriages(
|
||||
self.printer, [self.rails[0]], [self.rails[3]], axes=[0],
|
||||
safe_dist=dc_config.getfloat(
|
||||
'safe_distance', None, minval=0.))
|
||||
safe_dist=[dc_config.getfloat(
|
||||
'safe_distance', None, minval=0.)])
|
||||
for s in self.get_steppers():
|
||||
s.set_trapq(toolhead.get_trapq())
|
||||
# Setup boundary checks
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ class HybridCoreXZKinematics:
|
|||
self.rails[3].setup_itersolve('corexz_stepper_alloc', b'+')
|
||||
self.dc_module = idex_modes.DualCarriages(
|
||||
self.printer, [self.rails[0]], [self.rails[3]], axes=[0],
|
||||
safe_dist=dc_config.getfloat(
|
||||
'safe_distance', None, minval=0.))
|
||||
safe_dist=[dc_config.getfloat(
|
||||
'safe_distance', None, minval=0.)])
|
||||
for s in self.get_steppers():
|
||||
s.set_trapq(toolhead.get_trapq())
|
||||
# Setup boundary checks
|
||||
|
|
|
|||
|
|
@ -14,36 +14,37 @@ MIRROR = 'MIRROR'
|
|||
|
||||
class DualCarriages:
|
||||
VALID_MODES = [PRIMARY, COPY, MIRROR]
|
||||
def __init__(self, printer, primary_rails, dual_rails, axes,
|
||||
safe_dist={}):
|
||||
def __init__(self, printer, primary_rails, dual_rails, axes, safe_dist):
|
||||
self.printer = printer
|
||||
self.axes = axes
|
||||
self._init_steppers(primary_rails + dual_rails)
|
||||
self.primary_rails = [
|
||||
DualCarriagesRail(printer, c, dual_rails[i],
|
||||
axes[i], active=True)
|
||||
for i, c in enumerate(primary_rails)]
|
||||
safe_dist = list(safe_dist)
|
||||
for i, dc in enumerate(dual_rails):
|
||||
if dc is None or safe_dist[i] is not None:
|
||||
continue
|
||||
pc = primary_rails[i]
|
||||
safe_dist[i] = min(abs(pc.position_min - dc.position_min),
|
||||
abs(pc.position_max - dc.position_max))
|
||||
self.primary_mode_dcs = [None] * 3
|
||||
self.primary_rails = []
|
||||
for i, c in enumerate(primary_rails):
|
||||
activate = self.primary_mode_dcs[axes[i]] is None
|
||||
dc_rail = DualCarriagesRail(
|
||||
printer, c, dual_rails[i], axes[i], safe_dist[i],
|
||||
active=activate)
|
||||
if activate:
|
||||
self.primary_mode_dcs[axes[i]] = dc_rail
|
||||
self.primary_rails.append(dc_rail)
|
||||
self.dual_rails = [
|
||||
DualCarriagesRail(printer, c, primary_rails[i],
|
||||
axes[i], active=False)
|
||||
axes[i], safe_dist[i], active=False)
|
||||
if c is not None else None
|
||||
for i, c in enumerate(dual_rails)]
|
||||
self.dc_rails = collections.OrderedDict(
|
||||
[(c.rail.get_name(short=True), c)
|
||||
for c in self.primary_rails + self.dual_rails])
|
||||
for c in self.primary_rails + self.dual_rails
|
||||
if c is not None])
|
||||
self.saved_states = {}
|
||||
self.safe_dist = {}
|
||||
for i, dc in enumerate(dual_rails):
|
||||
axis = axes[i]
|
||||
if isinstance(safe_dist, dict):
|
||||
if axis in safe_dist:
|
||||
self.safe_dist[axis] = safe_dist[axis]
|
||||
continue
|
||||
elif safe_dist is not None:
|
||||
self.safe_dist[axis] = safe_dist
|
||||
continue
|
||||
pc = primary_rails[i]
|
||||
self.safe_dist[axis] = min(abs(pc.position_min - dc.position_min),
|
||||
abs(pc.position_max - dc.position_max))
|
||||
self.axes = sorted(set(axes))
|
||||
self.printer.add_object('dual_carriage', self)
|
||||
self.printer.register_event_handler("klippy:ready", self._handle_ready)
|
||||
gcode = self.printer.lookup_object('gcode')
|
||||
|
|
@ -64,6 +65,8 @@ class DualCarriages:
|
|||
self.orig_stepper_kinematics = []
|
||||
steppers = set()
|
||||
for rail in rails:
|
||||
if rail is None:
|
||||
continue
|
||||
c_steppers = rail.get_steppers()
|
||||
if not c_steppers:
|
||||
raise self.printer.config_error(
|
||||
|
|
@ -80,10 +83,9 @@ class DualCarriages:
|
|||
def get_axes(self):
|
||||
return self.axes
|
||||
def get_primary_rail(self, axis):
|
||||
for dc_rail in self.dc_rails.values():
|
||||
if dc_rail.mode == PRIMARY and dc_rail.axis == axis:
|
||||
return dc_rail.rail
|
||||
return None
|
||||
if self.primary_mode_dcs[axis] is None:
|
||||
return None
|
||||
return self.primary_mode_dcs[axis].rail
|
||||
def get_dc_rail_wrapper(self, rail):
|
||||
for dc_rail in self.dc_rails.values():
|
||||
if dc_rail.rail == rail:
|
||||
|
|
@ -109,22 +111,27 @@ class DualCarriages:
|
|||
if target_dc.mode != PRIMARY:
|
||||
newpos = pos[:axis] + [target_dc.get_axis_position(pos)] \
|
||||
+ pos[axis+1:]
|
||||
self.primary_mode_dcs[axis] = target_dc
|
||||
target_dc.activate(PRIMARY, newpos, old_position=pos)
|
||||
toolhead.set_position(newpos)
|
||||
kin.update_limits(axis, target_dc.rail.get_range())
|
||||
def home(self, homing_state, axis):
|
||||
kin = self.printer.lookup_object('toolhead').get_kinematics()
|
||||
dcs = [dc for dc in self.dc_rails.values() if dc.axis == axis]
|
||||
if (self.get_dc_order(dcs[0], dcs[1]) > 0) != \
|
||||
dcs[0].rail.get_homing_info().positive_dir:
|
||||
# The second carriage must home first, because the carriages home in
|
||||
# the same direction and the first carriage homes on the second one
|
||||
dcs.reverse()
|
||||
for dc in dcs:
|
||||
self.toggle_active_dc_rail(dc)
|
||||
kin.home_axis(homing_state, axis, dc.rail)
|
||||
# Restore the original rails ordering
|
||||
self.activate_dc_mode(dcs[0], PRIMARY)
|
||||
homing_rails = [r for r in self.primary_rails if r.axis == axis]
|
||||
for dc_rail in homing_rails:
|
||||
dcs = [dc for dc in self.dc_rails.values()
|
||||
if dc_rail.rail in [dc.rail, dc.dual_rail]]
|
||||
if len(dcs) > 1 and (self.get_dc_order(dcs[0], dcs[1]) > 0) != \
|
||||
dcs[0].rail.get_homing_info().positive_dir:
|
||||
# The second carriage must home first, because the carriages
|
||||
# home in the same direction and the first carriage homes on
|
||||
# the second one, so reversing the oder
|
||||
dcs.reverse()
|
||||
for dc in dcs:
|
||||
self.toggle_active_dc_rail(dc)
|
||||
kin.home_axis(homing_state, dc.axis, dc.rail)
|
||||
# Restore the first rail as primary after all homed
|
||||
self.activate_dc_mode(homing_rails[0], PRIMARY)
|
||||
def get_status(self, eventtime=None):
|
||||
status = {'carriages' : {dc.get_name() : dc.mode
|
||||
for dc in self.dc_rails.values()}}
|
||||
|
|
@ -132,46 +139,62 @@ class DualCarriages:
|
|||
status.update({('carriage_%d' % (i,)) : dc.mode
|
||||
for i, dc in enumerate(self.dc_rails.values())})
|
||||
return status
|
||||
def get_kin_range(self, toolhead, mode, axis):
|
||||
def get_kin_range(self, toolhead, axis):
|
||||
pos = toolhead.get_position()
|
||||
dcs = [dc for dc in self.dc_rails.values() if dc.axis == axis]
|
||||
axes_pos = [dc.get_axis_position(pos) for dc in dcs]
|
||||
dc0_rail = dcs[0].rail
|
||||
dc1_rail = dcs[1].rail
|
||||
if mode != PRIMARY or dcs[0].is_active():
|
||||
range_min = dc0_rail.position_min
|
||||
range_max = dc0_rail.position_max
|
||||
else:
|
||||
range_min = dc1_rail.position_min
|
||||
range_max = dc1_rail.position_max
|
||||
safe_dist = self.safe_dist[axis]
|
||||
if not safe_dist:
|
||||
return (range_min, range_max)
|
||||
primary_carriage = self.primary_mode_dcs[axis]
|
||||
if primary_carriage is None:
|
||||
return (1.0, -1.0)
|
||||
primary_pos = primary_carriage.get_axis_position(pos)
|
||||
range_min = primary_carriage.rail.position_min
|
||||
range_max = primary_carriage.rail.position_max
|
||||
for carriage in self.dc_rails.values():
|
||||
if carriage.axis != axis:
|
||||
continue
|
||||
dcs = [carriage] + [dc for dc in self.dc_rails.values()
|
||||
if carriage.rail is dc.dual_rail]
|
||||
axes_pos = [dc.get_axis_position(pos) for dc in dcs]
|
||||
# Check how dcs[0] affects the motion range of primary_carriage
|
||||
if not dcs[0].is_active():
|
||||
continue
|
||||
elif dcs[0].mode == COPY:
|
||||
range_min = max(range_min, primary_pos
|
||||
+ dcs[0].rail.position_min - axes_pos[0])
|
||||
range_max = min(range_max, primary_pos
|
||||
+ dcs[0].rail.position_max - axes_pos[0])
|
||||
elif dcs[0].mode == MIRROR:
|
||||
range_min = max(range_min, primary_pos
|
||||
+ axes_pos[0] - dcs[0].rail.position_max)
|
||||
range_max = min(range_max, primary_pos
|
||||
+ axes_pos[0] - dcs[0].rail.position_min)
|
||||
safe_dist = dcs[0].safe_dist
|
||||
if not safe_dist or len(dcs) == 1:
|
||||
continue
|
||||
if dcs[0].mode == dcs[1].mode or \
|
||||
set((dcs[0].mode, dcs[1].mode)) == set((PRIMARY, COPY)):
|
||||
# dcs[0] and dcs[1] carriages move in the same direction and
|
||||
# cannot collide with each other
|
||||
continue
|
||||
|
||||
if mode == COPY:
|
||||
range_min = max(range_min,
|
||||
axes_pos[0] - axes_pos[1] + dc1_rail.position_min)
|
||||
range_max = min(range_max,
|
||||
axes_pos[0] - axes_pos[1] + dc1_rail.position_max)
|
||||
elif mode == MIRROR:
|
||||
# Compute how much dcs[0] can move towards dcs[1]
|
||||
dcs_dist = axes_pos[1] - axes_pos[0]
|
||||
if self.get_dc_order(dcs[0], dcs[1]) > 0:
|
||||
range_min = max(range_min,
|
||||
0.5 * (sum(axes_pos) + safe_dist))
|
||||
range_max = min(range_max,
|
||||
sum(axes_pos) - dc1_rail.position_min)
|
||||
safe_move_dist = dcs_dist + safe_dist
|
||||
else:
|
||||
range_max = min(range_max,
|
||||
0.5 * (sum(axes_pos) - safe_dist))
|
||||
range_min = max(range_min,
|
||||
sum(axes_pos) - dc1_rail.position_max)
|
||||
else:
|
||||
# mode == PRIMARY
|
||||
active_idx = 1 if dcs[1].is_active() else 0
|
||||
inactive_idx = 1 - active_idx
|
||||
if self.get_dc_order(dcs[active_idx], dcs[inactive_idx]) > 0:
|
||||
range_min = max(range_min, axes_pos[inactive_idx] + safe_dist)
|
||||
else:
|
||||
range_max = min(range_max, axes_pos[inactive_idx] - safe_dist)
|
||||
safe_move_dist = dcs_dist - safe_dist
|
||||
if dcs[1].is_active():
|
||||
safe_move_dist *= 0.5
|
||||
|
||||
if dcs[0].mode in (PRIMARY, COPY):
|
||||
if self.get_dc_order(dcs[0], dcs[1]) > 0:
|
||||
range_min = max(range_min, primary_pos + safe_move_dist)
|
||||
else:
|
||||
range_max = min(range_max, primary_pos + safe_move_dist)
|
||||
else: # dcs[0].mode == MIRROR
|
||||
if self.get_dc_order(dcs[0], dcs[1]) > 0:
|
||||
range_max = min(range_max, primary_pos - safe_move_dist)
|
||||
else:
|
||||
range_min = max(range_min, primary_pos - safe_move_dist)
|
||||
|
||||
if range_min > range_max:
|
||||
# During multi-MCU homing it is possible that the carriage
|
||||
# position will end up below position_min or above position_max
|
||||
|
|
@ -208,12 +231,13 @@ class DualCarriages:
|
|||
axis = dc.axis
|
||||
if mode == INACTIVE:
|
||||
dc.inactivate(toolhead.get_position())
|
||||
if self.primary_mode_dcs[axis] is dc:
|
||||
self.primary_mode_dcs[axis] = None
|
||||
elif mode == PRIMARY:
|
||||
self.toggle_active_dc_rail(dc)
|
||||
else:
|
||||
self.toggle_active_dc_rail(self.get_dc_rail_wrapper(dc.dual_rail))
|
||||
dc.activate(mode, toolhead.get_position())
|
||||
kin.update_limits(axis, self.get_kin_range(toolhead, mode, axis))
|
||||
kin.update_limits(axis, self.get_kin_range(toolhead, axis))
|
||||
def _handle_ready(self):
|
||||
for dc_rail in self.dc_rails.values():
|
||||
dc_rail.apply_transform()
|
||||
|
|
@ -241,10 +265,9 @@ class DualCarriages:
|
|||
if mode not in self.VALID_MODES:
|
||||
raise gcmd.error("Invalid mode=%s specified" % (mode,))
|
||||
if mode in [COPY, MIRROR]:
|
||||
if dc_rail in self.primary_rails:
|
||||
if self.primary_mode_dcs[dc_rail.axis] in [None, dc_rail]:
|
||||
raise gcmd.error(
|
||||
"Mode=%s is not supported for carriage=%s" % (
|
||||
mode, dc_rail.get_name()))
|
||||
"Must activate another carriage as PRIMARY first")
|
||||
curtime = self.printer.get_reactor().monotonic()
|
||||
kin = self.printer.lookup_object('toolhead').get_kinematics()
|
||||
axis = 'xyz'[dc_rail.axis]
|
||||
|
|
@ -291,18 +314,21 @@ class DualCarriages:
|
|||
for i, dc in enumerate(dcs)]
|
||||
for axis in self.axes:
|
||||
dc_ind = [i for i, dc in enumerate(dcs) if dc.axis == axis]
|
||||
if abs(dl[dc_ind[0]]) >= abs(dl[dc_ind[1]]):
|
||||
primary_ind, secondary_ind = dc_ind[0], dc_ind[1]
|
||||
else:
|
||||
primary_ind, secondary_ind = dc_ind[1], dc_ind[0]
|
||||
abs_dl = [abs(dl[i]) for i in dc_ind]
|
||||
primary_ind = dc_ind[abs_dl.index(max(abs_dl))]
|
||||
primary_dc = dcs[primary_ind]
|
||||
self.toggle_active_dc_rail(primary_dc)
|
||||
move_pos[axis] = carriage_positions[primary_dc.get_name()]
|
||||
dc_mode = INACTIVE if min(abs(dl[primary_ind]),
|
||||
abs(dl[secondary_ind])) < .000000001 \
|
||||
else COPY if dl[primary_ind] * dl[secondary_ind] > 0 \
|
||||
else MIRROR
|
||||
if dc_mode != INACTIVE:
|
||||
for secondary_ind in dc_ind:
|
||||
if secondary_ind == primary_ind:
|
||||
continue
|
||||
if min(abs(dl[primary_ind]),
|
||||
abs(dl[secondary_ind])) < .000000001:
|
||||
continue
|
||||
if dl[primary_ind] * dl[secondary_ind] > 0:
|
||||
dc_mode = COPY
|
||||
else:
|
||||
dc_mode = MIRROR
|
||||
dcs[secondary_ind].activate(dc_mode, cur_pos[primary_ind])
|
||||
dcs[secondary_ind].override_axis_scaling(
|
||||
abs(dl[secondary_ind] / dl[primary_ind]),
|
||||
|
|
@ -312,18 +338,26 @@ class DualCarriages:
|
|||
# Make sure the scaling coefficients are restored with the mode
|
||||
for dc in dcs:
|
||||
dc.inactivate(move_pos)
|
||||
saved_modes = saved_state['carriage_modes']
|
||||
saved_primary_dcs = [dc for dc in self.dc_rails.values()
|
||||
if saved_modes[dc.get_name()] == PRIMARY]
|
||||
# First activate all primary carriages
|
||||
for dc in saved_primary_dcs:
|
||||
self.activate_dc_mode(dc, PRIMARY)
|
||||
# Then set the modes the remaining carriages
|
||||
for dc in self.dc_rails.values():
|
||||
saved_mode = saved_state['carriage_modes'][dc.get_name()]
|
||||
self.activate_dc_mode(dc, saved_mode)
|
||||
if dc not in saved_primary_dcs:
|
||||
self.activate_dc_mode(dc, saved_modes[dc.get_name()])
|
||||
|
||||
class DualCarriagesRail:
|
||||
ENC_AXES = [b'x', b'y']
|
||||
def __init__(self, printer, rail, dual_rail, axis, active):
|
||||
def __init__(self, printer, rail, dual_rail, axis, safe_dist, active):
|
||||
self.printer = printer
|
||||
self.rail = rail
|
||||
self.dual_rail = dual_rail
|
||||
self.sks = [s.get_stepper_kinematics() for s in rail.get_steppers()]
|
||||
self.axis = axis
|
||||
self.safe_dist = safe_dist
|
||||
self.mode = (INACTIVE, PRIMARY)[active]
|
||||
self.offset = 0.
|
||||
self.scale = 1. if active else 0.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,20 @@ MAX_NOMINAL_DURATION = 3.0
|
|||
# Command transmit helper classes
|
||||
######################################################################
|
||||
|
||||
# Generate a dummy response to query commands when in debugging mode
|
||||
class DummyResponse:
|
||||
def __init__(self, serial, name, oid=None):
|
||||
params = {}
|
||||
if oid is not None:
|
||||
params['oid'] = oid
|
||||
msgparser = serial.get_msgparser()
|
||||
resp = msgparser.create_dummy_response(name, params)
|
||||
resp['#sent_time'] = 0.
|
||||
resp['#receive_time'] = 0.
|
||||
self._response = resp
|
||||
def get_response(self, cmds, cmd_queue, minclock=0, reqclock=0, retry=True):
|
||||
return dict(self._response)
|
||||
|
||||
# Class to retry sending of a query command until a given response is received
|
||||
class RetryAsyncCommand:
|
||||
TIMEOUT_TIME = 5.0
|
||||
|
|
@ -60,16 +74,18 @@ class RetryAsyncCommand:
|
|||
|
||||
# Wrapper around query commands
|
||||
class CommandQueryWrapper:
|
||||
def __init__(self, serial, msgformat, respformat, oid=None,
|
||||
cmd_queue=None, is_async=False, error=serialhdl.error):
|
||||
self._serial = serial
|
||||
def __init__(self, conn_helper, msgformat, respformat, oid=None,
|
||||
cmd_queue=None, is_async=False):
|
||||
self._serial = serial = conn_helper.get_serial()
|
||||
self._cmd = serial.get_msgparser().lookup_command(msgformat)
|
||||
serial.get_msgparser().lookup_command(respformat)
|
||||
self._response = respformat.split()[0]
|
||||
self._oid = oid
|
||||
self._error = error
|
||||
self._error = conn_helper.get_mcu().get_printer().command_error
|
||||
self._xmit_helper = serialhdl.SerialRetryCommand
|
||||
if is_async:
|
||||
if conn_helper.get_mcu().is_fileoutput():
|
||||
self._xmit_helper = DummyResponse
|
||||
elif is_async:
|
||||
self._xmit_helper = RetryAsyncCommand
|
||||
if cmd_queue is None:
|
||||
cmd_queue = serial.get_default_command_queue()
|
||||
|
|
@ -92,15 +108,15 @@ class CommandQueryWrapper:
|
|||
|
||||
# Wrapper around command sending
|
||||
class CommandWrapper:
|
||||
def __init__(self, serial, msgformat, cmd_queue=None, debugoutput=False):
|
||||
self._serial = serial
|
||||
def __init__(self, conn_helper, msgformat, cmd_queue=None):
|
||||
self._serial = serial = conn_helper.get_serial()
|
||||
msgparser = serial.get_msgparser()
|
||||
self._cmd = msgparser.lookup_command(msgformat)
|
||||
if cmd_queue is None:
|
||||
cmd_queue = serial.get_default_command_queue()
|
||||
self._cmd_queue = cmd_queue
|
||||
self._msgtag = msgparser.lookup_msgid(msgformat) & 0xffffffff
|
||||
if debugoutput:
|
||||
if conn_helper.get_mcu().is_fileoutput():
|
||||
# Can't use send_wait_ack when in debugging mode
|
||||
self.send_wait_ack = self.send
|
||||
def send(self, data=(), minclock=0, reqclock=0):
|
||||
|
|
@ -219,12 +235,11 @@ class MCU_trsync:
|
|||
self._mcu.register_response(self._handle_trsync_state,
|
||||
"trsync_state", self._oid)
|
||||
self._trsync_start_cmd.send([self._oid, report_clock, report_ticks,
|
||||
self.REASON_COMMS_TIMEOUT],
|
||||
reqclock=report_clock)
|
||||
self.REASON_COMMS_TIMEOUT], reqclock=clock)
|
||||
for s in self._steppers:
|
||||
self._stepper_stop_cmd.send([s.get_oid(), self._oid])
|
||||
self._trsync_set_timeout_cmd.send([self._oid, expire_clock],
|
||||
reqclock=expire_clock)
|
||||
reqclock=clock)
|
||||
def set_home_end_time(self, home_end_time):
|
||||
self._home_end_clock = self._mcu.print_time_to_clock(home_end_time)
|
||||
def stop(self):
|
||||
|
|
@ -422,6 +437,7 @@ class MCU_pwm:
|
|||
self._invert = pin_params['invert']
|
||||
self._start_value = self._shutdown_value = float(self._invert)
|
||||
self._last_clock = 0
|
||||
self._last_value = .0
|
||||
self._pwm_max = 0.
|
||||
self._set_cmd = None
|
||||
def get_mcu(self):
|
||||
|
|
@ -437,6 +453,7 @@ class MCU_pwm:
|
|||
shutdown_value = 1. - shutdown_value
|
||||
self._start_value = max(0., min(1., start_value))
|
||||
self._shutdown_value = max(0., min(1., shutdown_value))
|
||||
self._last_value = self._start_value
|
||||
def _build_config(self):
|
||||
if self._max_duration and self._start_value != self._shutdown_value:
|
||||
raise pins.error("Pin with max duration must have start"
|
||||
|
|
@ -488,6 +505,20 @@ class MCU_pwm:
|
|||
% (self._oid, self._last_clock, svalue), is_init=True)
|
||||
self._set_cmd = self._mcu.lookup_command(
|
||||
"queue_digital_out oid=%c clock=%u on_ticks=%u", cq=cmd_queue)
|
||||
def next_aligned_print_time(self, print_time, allow_early=0.):
|
||||
# Filter cases where there is no need to sync anything
|
||||
if self._hardware_pwm:
|
||||
return print_time
|
||||
if self._last_value == 1. or self._last_value == .0:
|
||||
return print_time
|
||||
# Simplify the calling and allow scheduling slightly earlier
|
||||
req_ptime = print_time - min(allow_early, 0.5 * self._cycle_time)
|
||||
cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time)
|
||||
req_clock = self._mcu.print_time_to_clock(req_ptime)
|
||||
last_clock = self._last_clock
|
||||
pulses = (req_clock - last_clock + cycle_ticks - 1) // cycle_ticks
|
||||
next_clock = last_clock + pulses * cycle_ticks
|
||||
return self._mcu.clock_to_print_time(next_clock)
|
||||
def set_pwm(self, print_time, value):
|
||||
if self._invert:
|
||||
value = 1. - value
|
||||
|
|
@ -496,6 +527,7 @@ class MCU_pwm:
|
|||
self._set_cmd.send([self._oid, clock, v],
|
||||
minclock=self._last_clock, reqclock=clock)
|
||||
self._last_clock = clock
|
||||
self._last_value = value
|
||||
|
||||
class MCU_adc:
|
||||
def __init__(self, mcu, pin_params):
|
||||
|
|
@ -1080,12 +1112,11 @@ class MCU:
|
|||
def max_nominal_duration(self):
|
||||
return MAX_NOMINAL_DURATION
|
||||
def lookup_command(self, msgformat, cq=None):
|
||||
return CommandWrapper(self._serial, msgformat, cq,
|
||||
debugoutput=self.is_fileoutput())
|
||||
return CommandWrapper(self._conn_helper, msgformat, cq)
|
||||
def lookup_query_command(self, msgformat, respformat, oid=None,
|
||||
cq=None, is_async=False):
|
||||
return CommandQueryWrapper(self._serial, msgformat, respformat, oid,
|
||||
cq, is_async, self._printer.command_error)
|
||||
return CommandQueryWrapper(self._conn_helper, msgformat, respformat,
|
||||
oid, cq, is_async)
|
||||
def try_lookup_command(self, msgformat):
|
||||
try:
|
||||
return self.lookup_command(msgformat)
|
||||
|
|
|
|||
|
|
@ -353,6 +353,27 @@ class MessageParser:
|
|||
#logging.exception("Unable to encode")
|
||||
self._error("Unable to encode: %s", msgname)
|
||||
return cmd
|
||||
def create_dummy_response(self, msgname, params={}):
|
||||
mp = self.messages_by_name.get(msgname)
|
||||
if mp is None:
|
||||
self._error("Unknown response: %s", msgname)
|
||||
argparts = dict(params)
|
||||
for name, t in mp.name_to_type.items():
|
||||
if name not in argparts:
|
||||
tval = 0
|
||||
if t.is_dynamic_string:
|
||||
tval = ()
|
||||
argparts[name] = tval
|
||||
try:
|
||||
msg = mp.encode_by_name(**argparts)
|
||||
except error as e:
|
||||
raise
|
||||
except:
|
||||
#logging.exception("Unable to encode")
|
||||
self._error("Unable to encode: %s", msgname)
|
||||
res, pos = mp.parse(msg, 0)
|
||||
res['#name'] = msgname
|
||||
return res
|
||||
def fill_enumerations(self, enumerations):
|
||||
for add_name, add_enums in enumerations.items():
|
||||
enums = self.enumerations.setdefault(add_name, {})
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ taken from the Drivers/CMSIS/Device/ST/STM32H7xx/ directory.
|
|||
|
||||
The pico-sdk directory contains code from the pico sdk:
|
||||
https://github.com/raspberrypi/pico-sdk.git
|
||||
version 2.0.0 (efe2103f9b28458a1615ff096054479743ade236). It has been
|
||||
version 2.2.0 (a1438dff1d38bd9c65dbd693f0e5db4b9ae91779). It has been
|
||||
modified so that it can build outside of the pico sdk. See
|
||||
pico-sdk.patch for the modifications.
|
||||
|
||||
|
|
|
|||
344
lib/pico-sdk/boot/bootrom_constants.h
Normal file
344
lib/pico-sdk/boot/bootrom_constants.h
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _BOOT_BOOTROM_CONSTANTS_H
|
||||
#define _BOOT_BOOTROM_CONSTANTS_H
|
||||
|
||||
#ifndef NO_PICO_PLATFORM
|
||||
#include "pico/platform.h"
|
||||
#endif
|
||||
|
||||
// ROOT ADDRESSES
|
||||
#define BOOTROM_MAGIC_OFFSET 0x10
|
||||
#define BOOTROM_FUNC_TABLE_OFFSET 0x14
|
||||
#if PICO_RP2040
|
||||
#define BOOTROM_DATA_TABLE_OFFSET 0x16
|
||||
#endif
|
||||
|
||||
#if PICO_RP2040
|
||||
#define BOOTROM_VTABLE_OFFSET 0x00
|
||||
#define BOOTROM_TABLE_LOOKUP_OFFSET 0x18
|
||||
#else
|
||||
#define BOOTROM_WELL_KNOWN_PTR_SIZE 2
|
||||
#if defined(__riscv)
|
||||
#define BOOTROM_ENTRY_OFFSET 0x7dfc
|
||||
#define BOOTROM_TABLE_LOOKUP_ENTRY_OFFSET (BOOTROM_ENTRY_OFFSET - BOOTROM_WELL_KNOWN_PTR_SIZE)
|
||||
#define BOOTROM_TABLE_LOOKUP_OFFSET (BOOTROM_ENTRY_OFFSET - BOOTROM_WELL_KNOWN_PTR_SIZE*2)
|
||||
#else
|
||||
#define BOOTROM_VTABLE_OFFSET 0x00
|
||||
#define BOOTROM_TABLE_LOOKUP_OFFSET (BOOTROM_FUNC_TABLE_OFFSET + BOOTROM_WELL_KNOWN_PTR_SIZE)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !PICO_RP2040 || PICO_COMBINED_DOCS
|
||||
|
||||
#define BOOTROM_OK 0
|
||||
//#define BOOTROM_ERROR_TIMEOUT (-1)
|
||||
//#define BOOTROM_ERROR_GENERIC (-2)
|
||||
//#define BOOTROM_ERROR_NO_DATA (-3) // E.g. read from an empty buffer/FIFO
|
||||
#define BOOTROM_ERROR_NOT_PERMITTED (-4) // Permission violation e.g. write to read-only flash partition
|
||||
#define BOOTROM_ERROR_INVALID_ARG (-5) // Argument is outside of range of supported values`
|
||||
//#define BOOTROM_ERROR_IO (-6)
|
||||
//#define BOOTROM_ERROR_BADAUTH (-7)
|
||||
//#define BOOTROM_ERROR_CONNECT_FAILED (-8)
|
||||
//#define BOOTROM_ERROR_INSUFFICIENT_RESOURCES (-9) // Dynamic allocation of resources failed
|
||||
#define BOOTROM_ERROR_INVALID_ADDRESS (-10) // Address argument was out-of-bounds or was determined to be an address that the caller may not access
|
||||
#define BOOTROM_ERROR_BAD_ALIGNMENT (-11) // Address modulo transfer chunk size was nonzero (e.g. word-aligned transfer with address % 4 != 0)
|
||||
#define BOOTROM_ERROR_INVALID_STATE (-12) // Something happened or failed to happen in the past, and consequently we (currently) can't service the request
|
||||
#define BOOTROM_ERROR_BUFFER_TOO_SMALL (-13) // A user-allocated buffer was too small to hold the result or working state of this function
|
||||
#define BOOTROM_ERROR_PRECONDITION_NOT_MET (-14) // This call failed because another ROM function must be called first
|
||||
#define BOOTROM_ERROR_MODIFIED_DATA (-15) // Cached data was determined to be inconsistent with the full version of the data it was calculated from
|
||||
#define BOOTROM_ERROR_INVALID_DATA (-16) // A data structure failed to validate
|
||||
#define BOOTROM_ERROR_NOT_FOUND (-17) // Attempted to access something that does not exist; or, a search failed
|
||||
#define BOOTROM_ERROR_UNSUPPORTED_MODIFICATION (-18) // Write is impossible based on previous writes; e.g. attempted to clear an OTP bit
|
||||
#define BOOTROM_ERROR_LOCK_REQUIRED (-19) // A required lock is not owned
|
||||
#define BOOTROM_ERROR_LAST (-19)
|
||||
|
||||
#define RT_FLAG_FUNC_RISCV 0x0001
|
||||
#define RT_FLAG_FUNC_RISCV_FAR 0x0003
|
||||
#define RT_FLAG_FUNC_ARM_SEC 0x0004
|
||||
// reserved for 32-bit pointer: 0x0008
|
||||
#define RT_FLAG_FUNC_ARM_NONSEC 0x0010
|
||||
// reserved for 32-bit pointer: 0x0020
|
||||
#define RT_FLAG_DATA 0x0040
|
||||
// reserved for 32-bit pointer: 0x0080
|
||||
|
||||
#define PARTITION_TABLE_MAX_PARTITIONS 16
|
||||
// note this is deliberately > MAX_PARTITIONs is likely to be, and also -1 as a signed byte
|
||||
#define PARTITION_TABLE_NO_PARTITION_INDEX 0xff
|
||||
|
||||
// todo these are duplicated in picoboot_constants.h
|
||||
// values 0-7 are secure/non-secure
|
||||
#define BOOT_TYPE_NORMAL 0
|
||||
#define BOOT_TYPE_BOOTSEL 2
|
||||
#define BOOT_TYPE_RAM_IMAGE 3
|
||||
#define BOOT_TYPE_FLASH_UPDATE 4
|
||||
|
||||
// values 8-15 are secure only
|
||||
#define BOOT_TYPE_PC_SP 0xd
|
||||
|
||||
// ORed in if a bootloader chained into the image
|
||||
#define BOOT_TYPE_CHAINED_FLAG 0x80
|
||||
|
||||
// call from NS to S
|
||||
#ifndef __riscv
|
||||
#define BOOTROM_API_CALLBACK_secure_call 0
|
||||
#endif
|
||||
#define BOOTROM_API_CALLBACK_COUNT 1
|
||||
|
||||
#define BOOTROM_LOCK_SHA_256 0
|
||||
#define BOOTROM_LOCK_FLASH_OP 1
|
||||
#define BOOTROM_LOCK_OTP 2
|
||||
#define BOOTROM_LOCK_MAX 2
|
||||
|
||||
#define BOOTROM_LOCK_ENABLE 7
|
||||
|
||||
#define BOOT_PARTITION_NONE (-1)
|
||||
#define BOOT_PARTITION_SLOT0 (-2)
|
||||
#define BOOT_PARTITION_SLOT1 (-3)
|
||||
#define BOOT_PARTITION_WINDOW (-4)
|
||||
|
||||
#define BOOT_DIAGNOSTIC_WINDOW_SEARCHED 0x01
|
||||
// note if both BOOT_DIAGNOSTIC_INVALID_BLOCK_LOOP and BOOT_DIAGNOSTIC_VALID_BLOCK_LOOP then the block loop was valid
|
||||
// but it has a PARTITION_TABLE which while it passed the initial verification (and hash/sig) had invalid contents
|
||||
// (discovered when it was later loaded)
|
||||
#define BOOT_DIAGNOSTIC_INVALID_BLOCK_LOOP 0x02
|
||||
#define BOOT_DIAGNOSTIC_VALID_BLOCK_LOOP 0x04
|
||||
#define BOOT_DIAGNOSTIC_VALID_IMAGE_DEF 0x08
|
||||
#define BOOT_DIAGNOSTIC_HAS_PARTITION_TABLE 0x10
|
||||
#define BOOT_DIAGNOSTIC_CONSIDERED 0x20
|
||||
#define BOOT_DIAGNOSTIC_CHOSEN 0x40
|
||||
#define BOOT_DIAGNOSTIC_PARTITION_TABLE_LSB 7
|
||||
#define BOOT_DIAGNOSTIC_PARTITION_TABLE_MATCHING_KEY_FOR_VERIFY 0x80
|
||||
#define BOOT_DIAGNOSTIC_PARTITION_TABLE_HASH_FOR_VERIFY 0x100
|
||||
#define BOOT_DIAGNOSTIC_PARTITION_TABLE_VERIFIED_OK 0x200
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_DEF_LSB 10
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_DEF_MATCHING_KEY_FOR_VERIFY 0x400
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_DEF_HASH_FOR_VERIFY 0x800
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_DEF_VERIFIED_OK 0x1000
|
||||
|
||||
#define BOOT_DIAGNOSTIC_LOAD_MAP_ENTRIES_LOADED 0x2000
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_LAUNCHED 0x4000
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_CONDITION_FAILURE 0x8000
|
||||
|
||||
#define BOOT_PARSED_BLOCK_DIAGNOSTIC_MATCHING_KEY_FOR_VERIFY 0x1 // if this is present and VERIFIED_OK isn't the sig check failed
|
||||
#define BOOT_PARSED_BLOCK_DIAGNOSTIC_HASH_FOR_VERIFY 0x2 // if this is present and VERIFIED_OL isn't then hash check failed
|
||||
#define BOOT_PARSED_BLOCK_DIAGNOSTIC_VERIFIED_OK 0x4
|
||||
|
||||
#define BOOT_TBYB_AND_UPDATE_FLAG_BUY_PENDING 0x1
|
||||
#define BOOT_TBYB_AND_UPDATE_FLAG_OTP_VERSION_APPLIED 0x2
|
||||
#define BOOT_TBYB_AND_UPDATE_FLAG_OTHER_ERASED 0x4
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
// Limited to 3 arguments in case of varm multiplex hint (trashes Arm r3)
|
||||
typedef int (*bootrom_api_callback_generic_t)(uint32_t r0, uint32_t r1, uint32_t r2);
|
||||
// Return negative for error, else number of bytes transferred:
|
||||
//typedef int (*bootrom_api_callback_stdout_put_blocking_t)(const uint8_t *buffer, uint32_t size);
|
||||
//typedef int (*bootrom_api_callback_stdin_get_t)(uint8_t *buffer, uint32_t size);
|
||||
//typedef void (*bootrom_api_callback_core1_security_setup_t)(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*! \brief Return a bootrom lookup code based on two ASCII characters
|
||||
* \ingroup pico_bootrom
|
||||
*
|
||||
* These codes are uses to lookup data or function addresses in the bootrom
|
||||
*
|
||||
* \param c1 the first character
|
||||
* \param c2 the second character
|
||||
* \return the 'code' to use in rom_func_lookup() or rom_data_lookup()
|
||||
*/
|
||||
#define ROM_TABLE_CODE(c1, c2) ((c1) | ((c2) << 8))
|
||||
|
||||
// ROM FUNCTIONS
|
||||
|
||||
// RP2040 & RP2350
|
||||
#define ROM_DATA_SOFTWARE_GIT_REVISION ROM_TABLE_CODE('G', 'R')
|
||||
#define ROM_FUNC_FLASH_ENTER_CMD_XIP ROM_TABLE_CODE('C', 'X')
|
||||
#define ROM_FUNC_FLASH_EXIT_XIP ROM_TABLE_CODE('E', 'X')
|
||||
#define ROM_FUNC_FLASH_FLUSH_CACHE ROM_TABLE_CODE('F', 'C')
|
||||
#define ROM_FUNC_CONNECT_INTERNAL_FLASH ROM_TABLE_CODE('I', 'F')
|
||||
#define ROM_FUNC_FLASH_RANGE_ERASE ROM_TABLE_CODE('R', 'E')
|
||||
#define ROM_FUNC_FLASH_RANGE_PROGRAM ROM_TABLE_CODE('R', 'P')
|
||||
|
||||
|
||||
#if PICO_RP2040
|
||||
// RP2040 only
|
||||
#define ROM_FUNC_MEMCPY44 ROM_TABLE_CODE('C', '4')
|
||||
#define ROM_DATA_COPYRIGHT ROM_TABLE_CODE('C', 'R')
|
||||
#define ROM_FUNC_CLZ32 ROM_TABLE_CODE('L', '3')
|
||||
#define ROM_FUNC_MEMCPY ROM_TABLE_CODE('M', 'C')
|
||||
#define ROM_FUNC_MEMSET ROM_TABLE_CODE('M', 'S')
|
||||
#define ROM_FUNC_POPCOUNT32 ROM_TABLE_CODE('P', '3')
|
||||
#define ROM_FUNC_REVERSE32 ROM_TABLE_CODE('R', '3')
|
||||
#define ROM_FUNC_MEMSET4 ROM_TABLE_CODE('S', '4')
|
||||
#define ROM_FUNC_CTZ32 ROM_TABLE_CODE('T', '3')
|
||||
#define ROM_FUNC_RESET_USB_BOOT ROM_TABLE_CODE('U', 'B')
|
||||
#endif
|
||||
|
||||
#if !PICO_RP2040 || PICO_COMBINED_DOCS
|
||||
// RP2350 only
|
||||
#define ROM_FUNC_PICK_AB_PARTITION ROM_TABLE_CODE('A', 'B')
|
||||
#define ROM_FUNC_CHAIN_IMAGE ROM_TABLE_CODE('C', 'I')
|
||||
#define ROM_FUNC_EXPLICIT_BUY ROM_TABLE_CODE('E', 'B')
|
||||
#define ROM_FUNC_FLASH_RUNTIME_TO_STORAGE_ADDR ROM_TABLE_CODE('F', 'A')
|
||||
#define ROM_DATA_FLASH_DEVINFO16_PTR ROM_TABLE_CODE('F', 'D')
|
||||
#define ROM_FUNC_FLASH_OP ROM_TABLE_CODE('F', 'O')
|
||||
#define ROM_FUNC_GET_B_PARTITION ROM_TABLE_CODE('G', 'B')
|
||||
#define ROM_FUNC_GET_PARTITION_TABLE_INFO ROM_TABLE_CODE('G', 'P')
|
||||
#define ROM_FUNC_GET_SYS_INFO ROM_TABLE_CODE('G', 'S')
|
||||
#define ROM_FUNC_GET_UF2_TARGET_PARTITION ROM_TABLE_CODE('G', 'U')
|
||||
#define ROM_FUNC_LOAD_PARTITION_TABLE ROM_TABLE_CODE('L', 'P')
|
||||
#define ROM_FUNC_OTP_ACCESS ROM_TABLE_CODE('O', 'A')
|
||||
#define ROM_DATA_PARTITION_TABLE_PTR ROM_TABLE_CODE('P', 'T')
|
||||
#define ROM_FUNC_FLASH_RESET_ADDRESS_TRANS ROM_TABLE_CODE('R', 'A')
|
||||
#define ROM_FUNC_REBOOT ROM_TABLE_CODE('R', 'B')
|
||||
#define ROM_FUNC_SET_ROM_CALLBACK ROM_TABLE_CODE('R', 'C')
|
||||
#define ROM_FUNC_SECURE_CALL ROM_TABLE_CODE('S', 'C')
|
||||
#define ROM_FUNC_SET_NS_API_PERMISSION ROM_TABLE_CODE('S', 'P')
|
||||
#define ROM_FUNC_BOOTROM_STATE_RESET ROM_TABLE_CODE('S', 'R')
|
||||
#define ROM_FUNC_SET_BOOTROM_STACK ROM_TABLE_CODE('S', 'S')
|
||||
#define ROM_DATA_SAVED_XIP_SETUP_FUNC_PTR ROM_TABLE_CODE('X', 'F')
|
||||
#define ROM_FUNC_FLASH_SELECT_XIP_READ_MODE ROM_TABLE_CODE('X', 'M')
|
||||
#define ROM_FUNC_VALIDATE_NS_BUFFER ROM_TABLE_CODE('V', 'B')
|
||||
#endif
|
||||
|
||||
// these form a bit set
|
||||
#define BOOTROM_STATE_RESET_CURRENT_CORE 0x01
|
||||
#define BOOTROM_STATE_RESET_OTHER_CORE 0x02
|
||||
#define BOOTROM_STATE_RESET_GLOBAL_STATE 0x04 // reset any global state (e.g. permissions)
|
||||
|
||||
// partition level stuff is returned first (note PT_INFO flags is only 16 bits)
|
||||
|
||||
// 3 words: pt_count, unpartitioned_perm_loc, unpartioned_perm_flags
|
||||
#define PT_INFO_PT_INFO 0x0001
|
||||
#define PT_INFO_SINGLE_PARTITION 0x8000 // marker to just include a single partition in the results)
|
||||
|
||||
// then in order per partition selected
|
||||
|
||||
// 2 words: unpartitioned_perm_loc, unpartioned_perm_flags
|
||||
#define PT_INFO_PARTITION_LOCATION_AND_FLAGS 0x0010
|
||||
// 2 words: id lsb first
|
||||
#define PT_INFO_PARTITION_ID 0x0020
|
||||
// n+1 words: n, family_id...
|
||||
#define PT_INFO_PARTITION_FAMILY_IDS 0x0040
|
||||
// (n+3)/4 words... bytes are: n (len), c0, c1, ... cn-1 padded to word boundary with zeroes
|
||||
#define PT_INFO_PARTITION_NAME 0x0080
|
||||
|
||||
// items are returned in order
|
||||
// 3 words package_id, device_id_lo, device_id_hi
|
||||
#define SYS_INFO_CHIP_INFO 0x0001
|
||||
// 1 word: chip specific critical bits
|
||||
#define SYS_INFO_CRITICAL 0x0002
|
||||
// 1 word: bytes: cpu_type, supported_cpu_type_bitfield
|
||||
#define SYS_INFO_CPU_INFO 0x0004
|
||||
// 1 word: same as FLASH_DEVINFO row in OTP
|
||||
#define SYS_INFO_FLASH_DEV_INFO 0x0008
|
||||
// 4 words
|
||||
#define SYS_INFO_BOOT_RANDOM 0x0010
|
||||
// 2 words lsb first
|
||||
#define SYS_INFO_NONCE 0x0020
|
||||
// 4 words boot_info, boot_diagnostic, boot_param0, boot_param1
|
||||
#define SYS_INFO_BOOT_INFO 0x0040
|
||||
|
||||
#define BOOTROM_NS_API_get_sys_info 0
|
||||
#define BOOTROM_NS_API_checked_flash_op 1
|
||||
#define BOOTROM_NS_API_flash_runtime_to_storage_addr 2
|
||||
#define BOOTROM_NS_API_get_partition_table_info 3
|
||||
#define BOOTROM_NS_API_secure_call 4
|
||||
#define BOOTROM_NS_API_otp_access 5
|
||||
#define BOOTROM_NS_API_reboot 6
|
||||
#define BOOTROM_NS_API_get_b_partition 7
|
||||
#define BOOTROM_NS_API_COUNT 8
|
||||
|
||||
#define OTP_CMD_ROW_BITS _u(0x0000ffff)
|
||||
#define OTP_CMD_ROW_LSB _u(0)
|
||||
#define OTP_CMD_WRITE_BITS _u(0x00010000)
|
||||
#define OTP_CMD_WRITE_LSB _u(16)
|
||||
#define OTP_CMD_ECC_BITS _u(0x00020000)
|
||||
#define OTP_CMD_ECC_LSB _u(17)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
static_assert(OTP_CMD_WRITE_BITS == (1 << OTP_CMD_WRITE_LSB), "");
|
||||
static_assert(OTP_CMD_ECC_BITS == (1 << OTP_CMD_ECC_LSB), "");
|
||||
|
||||
typedef struct {
|
||||
uint32_t permissions_and_location;
|
||||
uint32_t permissions_and_flags;
|
||||
} resident_partition_t;
|
||||
static_assert(sizeof(resident_partition_t) == 8, "");
|
||||
|
||||
typedef struct otp_cmd {
|
||||
uint32_t flags;
|
||||
} otp_cmd_t;
|
||||
|
||||
typedef enum {
|
||||
BOOTROM_XIP_MODE_03H_SERIAL = 0,
|
||||
BOOTROM_XIP_MODE_0BH_SERIAL,
|
||||
BOOTROM_XIP_MODE_BBH_DUAL,
|
||||
BOOTROM_XIP_MODE_EBH_QUAD,
|
||||
BOOTROM_XIP_MODE_N_MODES
|
||||
} bootrom_xip_mode_t;
|
||||
|
||||
// The checked flash API wraps the low-level flash routines from generic_flash, adding bounds
|
||||
// checking, permission checking against the resident partition table, and simple address
|
||||
// translation. The low-level API deals with flash offsets (i.e. distance from the start of the
|
||||
// first flash device, measured in bytes) but the checked flash API accepts one of two types of
|
||||
// address:
|
||||
//
|
||||
// - Flash runtime addresses: the address of some flash-resident data or code in the currently
|
||||
// running image. The flash addresses your binary is "linked at" by the linker.
|
||||
// - Flash storage addresses: a flash offset, plus the address base where QSPI hardware is first
|
||||
// mapped on the system bus (XIP_BASE constant from addressmap.h)
|
||||
//
|
||||
// These addresses are one and the same *if* the currently running program is stored at the
|
||||
// beginning of flash. They are different if the start of your image has been "rolled" by the flash
|
||||
// boot path to make it appear at the address it was linked at even though it is stored at a
|
||||
// different location in flash, which is necessary when you have A/B images for example.
|
||||
//
|
||||
// The address translation between flash runtime and flash storage addresses is configured in
|
||||
// hardware by the QMI_ATRANSx registers, and this API assumes those registers contain a valid
|
||||
// address mapping which it can use to translate runtime to storage addresses.
|
||||
|
||||
typedef struct cflash_flags {
|
||||
uint32_t flags;
|
||||
} cflash_flags_t;
|
||||
|
||||
#endif // #ifdef __ASSEMBLER__
|
||||
|
||||
// Bits which are permitted to be set in a flags variable -- any other bits being set is an error
|
||||
#define CFLASH_FLAGS_BITS 0x00070301u
|
||||
|
||||
// Used to tell checked flash API which space a given address belongs to
|
||||
#define CFLASH_ASPACE_BITS 0x00000001u
|
||||
#define CFLASH_ASPACE_LSB _u(0)
|
||||
#define CFLASH_ASPACE_VALUE_STORAGE _u(0)
|
||||
#define CFLASH_ASPACE_VALUE_RUNTIME _u(1)
|
||||
|
||||
// Used to tell checked flash APIs the effective security level of a flash access (may be forced to
|
||||
// one of these values for the NonSecure-exported version of this API)
|
||||
#define CFLASH_SECLEVEL_BITS 0x00000300u
|
||||
#define CFLASH_SECLEVEL_LSB _u(8)
|
||||
// Zero is not a valid security level:
|
||||
#define CFLASH_SECLEVEL_VALUE_SECURE _u(1)
|
||||
#define CFLASH_SECLEVEL_VALUE_NONSECURE _u(2)
|
||||
#define CFLASH_SECLEVEL_VALUE_BOOTLOADER _u(3)
|
||||
|
||||
#define CFLASH_OP_BITS 0x00070000u
|
||||
#define CFLASH_OP_LSB _u(16)
|
||||
// Erase size_bytes bytes of flash, starting at address addr. Both addr and size_bytes must be a
|
||||
// multiple of 4096 bytes (one flash sector).
|
||||
#define CFLASH_OP_VALUE_ERASE _u(0)
|
||||
// Program size_bytes bytes of flash, starting at address addr. Both addr and size_bytes must be a
|
||||
// multiple of 256 bytes (one flash page).
|
||||
#define CFLASH_OP_VALUE_PROGRAM _u(1)
|
||||
// Read size_bytes bytes of flash, starting at address addr. There are no alignment restrictions on
|
||||
// addr or size_bytes.
|
||||
#define CFLASH_OP_VALUE_READ _u(2)
|
||||
#define CFLASH_OP_MAX _u(2)
|
||||
|
||||
#endif
|
||||
|
|
@ -9,11 +9,11 @@
|
|||
|
||||
#define REBOOT2_TYPE_MASK 0x0f
|
||||
|
||||
// note these match REBOOT_TYPE in pico/bootrom_constants.h (also 0 is used for PC_SP for backwards compatibility with RP2040)
|
||||
// note these match REBOOT_TYPE in pico/bootrom_constants.h
|
||||
// values 0-7 are secure/non-secure
|
||||
#define REBOOT2_FLAG_REBOOT_TYPE_NORMAL 0x0 // param0 = diagnostic partition
|
||||
#define REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL 0x2 // param0 = bootsel_flags, param1 = gpio_config
|
||||
#define REBOOT2_FLAG_REBOOT_TYPE_RAM_IMAGE 0x3 // param0 = image_base, param1 = image_end
|
||||
#define REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL 0x2 // param0 = gpio_pin_number, param1 = flags
|
||||
#define REBOOT2_FLAG_REBOOT_TYPE_RAM_IMAGE 0x3 // param0 = image_region_base, param1 = image_region_size
|
||||
#define REBOOT2_FLAG_REBOOT_TYPE_FLASH_UPDATE 0x4 // param0 = update_base
|
||||
|
||||
// values 8-15 are secure only
|
||||
|
|
@ -39,4 +39,4 @@
|
|||
#define UF2_STATUS_ABORT_BAD_ADDRESS 0x20
|
||||
#define UF2_STATUS_ABORT_WRITE_ERROR 0x40
|
||||
#define UF2_STATUS_ABORT_REBOOT_FAILED 0x80
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,19 +20,30 @@
|
|||
#define UF2_MAGIC_START1 0x9E5D5157u
|
||||
#define UF2_MAGIC_END 0x0AB16F30u
|
||||
|
||||
#define UF2_FLAG_NOT_MAIN_FLASH 0x00000001u
|
||||
#define UF2_FLAG_FILE_CONTAINER 0x00001000u
|
||||
#define UF2_FLAG_FAMILY_ID_PRESENT 0x00002000u
|
||||
#define UF2_FLAG_MD5_PRESENT 0x00004000u
|
||||
#define UF2_FLAG_NOT_MAIN_FLASH 0x00000001u
|
||||
#define UF2_FLAG_FILE_CONTAINER 0x00001000u
|
||||
#define UF2_FLAG_FAMILY_ID_PRESENT 0x00002000u
|
||||
#define UF2_FLAG_MD5_PRESENT 0x00004000u
|
||||
#define UF2_FLAG_EXTENSION_FLAGS_PRESENT 0x00008000u
|
||||
|
||||
// Extra family IDs
|
||||
#define CYW43_FIRMWARE_FAMILY_ID 0xe48bff55u
|
||||
|
||||
// Bootrom supported family IDs
|
||||
#define RP2040_FAMILY_ID 0xe48bff56u
|
||||
#define ABSOLUTE_FAMILY_ID 0xe48bff57u
|
||||
#define DATA_FAMILY_ID 0xe48bff58u
|
||||
#define RP2350_ARM_S_FAMILY_ID 0xe48bff59u
|
||||
#define RP2350_RISCV_FAMILY_ID 0xe48bff5au
|
||||
#define RP2350_ARM_NS_FAMILY_ID 0xe48bff5bu
|
||||
#define FAMILY_ID_MAX 0xe48bff5bu
|
||||
#define BOOTROM_FAMILY_ID_MIN RP2040_FAMILY_ID
|
||||
#define BOOTROM_FAMILY_ID_MAX RP2350_ARM_NS_FAMILY_ID
|
||||
|
||||
// Defined for backwards compatibility
|
||||
#define FAMILY_ID_MAX BOOTROM_FAMILY_ID_MAX
|
||||
|
||||
// 04 e3 57 99
|
||||
#define UF2_EXTENSION_RP2_IGNORE_BLOCK 0x9957e304
|
||||
|
||||
struct uf2_block {
|
||||
// 32 byte header
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@
|
|||
#define NUM_ALARMS 4u
|
||||
|
||||
#define NUM_IRQS 32u
|
||||
#define NUM_USER_IRQS 6u
|
||||
#define FIRST_USER_IRQ (NUM_IRQS - NUM_USER_IRQS)
|
||||
#define VTABLE_FIRST_IRQ 16
|
||||
|
||||
#define NUM_SPIN_LOCKS 32u
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,29 @@
|
|||
This file summarizes the local changes from the upstream pico-sdk
|
||||
repository (version 2.2.0). In brief, the following steps can be used
|
||||
to recreate the code here from the main pico-sdk code:
|
||||
|
||||
cp /pico-sdk/src/common/boot_uf2_headers/include/boot/*.h boot/
|
||||
cp /pico-sdk/src/common/boot_picoboot_headers/include/boot/*.h boot/
|
||||
cp /pico-sdk/src/rp2_common/boot_bootrom_headers/include/boot/*.h boot/
|
||||
|
||||
cp /pico-sdk/src/rp2_common/hardware_base/include/hardware/*.h hardware/
|
||||
cp /pico-sdk/src/host/pico_platform/include/hardware/*.h hardware/
|
||||
|
||||
cp /pico-sdk/src/rp2_common/pico_bootrom/include/pico/bootrom_constants.h pico/
|
||||
cp /pico-sdk/src/host/pico_platform/include/pico/*.h pico/
|
||||
|
||||
cp -a /pico-sdk/src/rp2040/boot_stage2/ rp2040/
|
||||
cp /pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Device/RP2040/Include/*.h rp2040/cmsis_include/
|
||||
cp -a /pico-sdk/src/rp2040/hardware_regs/include/hardware rp2040/
|
||||
cp -a /pico-sdk/src/rp2040/hardware_structs/include/hardware rp2040/
|
||||
cp /pico-sdk/src/rp2040/pico_platform/include/pico/*.S rp2040/pico/
|
||||
|
||||
cp /pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Device/RP2350/Include/*.h rp2350/cmsis_include/
|
||||
cp /pico-sdk/src/rp2350/hardware_regs/include/hardware/regs/*.h rp2350/hardware/regs/
|
||||
cp /pico-sdk/src/rp2350/hardware_structs/include/hardware/structs/*.h rp2350/hardware/structs/
|
||||
|
||||
patch -p3 < pico-sdk.patch
|
||||
|
||||
diff --git a/lib/pico-sdk/hardware/address_mapped.h b/lib/pico-sdk/hardware/address_mapped.h
|
||||
index b384f5572..635a275b5 100644
|
||||
--- a/lib/pico-sdk/hardware/address_mapped.h
|
||||
|
|
|
|||
|
|
@ -4,339 +4,5 @@
|
|||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_BOOTROM_CONSTANTS_H
|
||||
#define _PICO_BOOTROM_CONSTANTS_H
|
||||
|
||||
#ifndef NO_PICO_PLATFORM
|
||||
#include "pico/platform.h"
|
||||
#endif
|
||||
|
||||
// ROOT ADDRESSES
|
||||
#define BOOTROM_MAGIC_OFFSET 0x10
|
||||
#define BOOTROM_FUNC_TABLE_OFFSET 0x14
|
||||
#if PICO_RP2040
|
||||
#define BOOTROM_DATA_TABLE_OFFSET 0x16
|
||||
#endif
|
||||
|
||||
#if PICO_RP2040
|
||||
#define BOOTROM_VTABLE_OFFSET 0x00
|
||||
#define BOOTROM_TABLE_LOOKUP_OFFSET 0x18
|
||||
#else
|
||||
// todo remove this (or #ifdef it for A1/A2)
|
||||
#define BOOTROM_IS_A2() ((*(volatile uint8_t *)0x13) == 2)
|
||||
#define BOOTROM_WELL_KNOWN_PTR_SIZE (BOOTROM_IS_A2() ? 2 : 4)
|
||||
#if defined(__riscv)
|
||||
#define BOOTROM_ENTRY_OFFSET 0x7dfc
|
||||
#define BOOTROM_TABLE_LOOKUP_ENTRY_OFFSET (BOOTROM_ENTRY_OFFSET - BOOTROM_WELL_KNOWN_PTR_SIZE)
|
||||
#define BOOTROM_TABLE_LOOKUP_OFFSET (BOOTROM_ENTRY_OFFSET - BOOTROM_WELL_KNOWN_PTR_SIZE*2)
|
||||
#else
|
||||
#define BOOTROM_VTABLE_OFFSET 0x00
|
||||
#define BOOTROM_TABLE_LOOKUP_OFFSET (BOOTROM_FUNC_TABLE_OFFSET + BOOTROM_WELL_KNOWN_PTR_SIZE)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !PICO_RP2040 || PICO_COMBINED_DOCS
|
||||
|
||||
#define BOOTROM_OK 0
|
||||
//#define BOOTROM_ERROR_TIMEOUT (-1)
|
||||
//#define BOOTROM_ERROR_GENERIC (-2)
|
||||
//#define BOOTROM_ERROR_NO_DATA (-3) // E.g. read from an empty buffer/FIFO
|
||||
#define BOOTROM_ERROR_NOT_PERMITTED (-4) // Permission violation e.g. write to read-only flash partition
|
||||
#define BOOTROM_ERROR_INVALID_ARG (-5) // Argument is outside of range of supported values`
|
||||
//#define BOOTROM_ERROR_IO (-6)
|
||||
//#define BOOTROM_ERROR_BADAUTH (-7)
|
||||
//#define BOOTROM_ERROR_CONNECT_FAILED (-8)
|
||||
//#define BOOTROM_ERROR_INSUFFICIENT_RESOURCES (-9) // Dynamic allocation of resources failed
|
||||
#define BOOTROM_ERROR_INVALID_ADDRESS (-10) // Address argument was out-of-bounds or was determined to be an address that the caller may not access
|
||||
#define BOOTROM_ERROR_BAD_ALIGNMENT (-11) // Address modulo transfer chunk size was nonzero (e.g. word-aligned transfer with address % 4 != 0)
|
||||
#define BOOTROM_ERROR_INVALID_STATE (-12) // Something happened or failed to happen in the past, and consequently we (currently) can't service the request
|
||||
#define BOOTROM_ERROR_BUFFER_TOO_SMALL (-13) // A user-allocated buffer was too small to hold the result or working state of this function
|
||||
#define BOOTROM_ERROR_PRECONDITION_NOT_MET (-14) // This call failed because another ROM function must be called first
|
||||
#define BOOTROM_ERROR_MODIFIED_DATA (-15) // Cached data was determined to be inconsistent with the full version of the data it was calculated from
|
||||
#define BOOTROM_ERROR_INVALID_DATA (-16) // A data structure failed to validate
|
||||
#define BOOTROM_ERROR_NOT_FOUND (-17) // Attempted to access something that does not exist; or, a search failed
|
||||
#define BOOTROM_ERROR_UNSUPPORTED_MODIFICATION (-18) // Write is impossible based on previous writes; e.g. attempted to clear an OTP bit
|
||||
#define BOOTROM_ERROR_LOCK_REQUIRED (-19) // A required lock is not owned
|
||||
#define BOOTROM_ERROR_LAST (-19)
|
||||
|
||||
#define RT_FLAG_FUNC_RISCV 0x0001
|
||||
#define RT_FLAG_FUNC_RISCV_FAR 0x0003
|
||||
#define RT_FLAG_FUNC_ARM_SEC 0x0004
|
||||
// reserved for 32-bit pointer: 0x0008
|
||||
#define RT_FLAG_FUNC_ARM_NONSEC 0x0010
|
||||
// reserved for 32-bit pointer: 0x0020
|
||||
#define RT_FLAG_DATA 0x0040
|
||||
// reserved for 32-bit pointer: 0x0080
|
||||
|
||||
#define PARTITION_TABLE_MAX_PARTITIONS 16
|
||||
// note this is deliberately > MAX_PARTITIONs is likely to be, and also -1 as a signed byte
|
||||
#define PARTITION_TABLE_NO_PARTITION_INDEX 0xff
|
||||
|
||||
// todo these are duplicated in picoboot_constants.h
|
||||
// values 0-7 are secure/non-secure
|
||||
#define BOOT_TYPE_NORMAL 0
|
||||
#define BOOT_TYPE_BOOTSEL 2
|
||||
#define BOOT_TYPE_RAM_IMAGE 3
|
||||
#define BOOT_TYPE_FLASH_UPDATE 4
|
||||
|
||||
// values 8-15 are secure only
|
||||
#define BOOT_TYPE_PC_SP 0xd
|
||||
|
||||
// ORed in if a bootloader chained into the image
|
||||
#define BOOT_TYPE_CHAINED_FLAG 0x80
|
||||
|
||||
// call from NS to S
|
||||
#ifndef __riscv
|
||||
#define BOOTROM_API_CALLBACK_secure_call 0
|
||||
#endif
|
||||
#define BOOTROM_API_CALLBACK_COUNT 1
|
||||
|
||||
#define BOOTROM_LOCK_SHA_256 0
|
||||
#define BOOTROM_LOCK_FLASH_OP 1
|
||||
#define BOOTROM_LOCK_OTP 2
|
||||
#define BOOTROM_LOCK_MAX 2
|
||||
|
||||
#define BOOTROM_LOCK_ENABLE 7
|
||||
|
||||
#define BOOT_PARTITION_NONE (-1)
|
||||
#define BOOT_PARTITION_SLOT0 (-2)
|
||||
#define BOOT_PARTITION_SLOT1 (-3)
|
||||
#define BOOT_PARTITION_WINDOW (-4)
|
||||
|
||||
#define BOOT_DIAGNOSTIC_WINDOW_SEARCHED 0x01
|
||||
// note if both BOOT_DIAGNOSTIC_INVALID_BLOCK_LOOP and BOOT_DIAGNOSTIC_VALID_BLOCK_LOOP then the block loop was valid
|
||||
// but it has a PARTITION_TABLE which while it passed the initial verification (and hash/sig) had invalid contents
|
||||
// (discovered when it was later loaded)
|
||||
#define BOOT_DIAGNOSTIC_INVALID_BLOCK_LOOP 0x02
|
||||
#define BOOT_DIAGNOSTIC_VALID_BLOCK_LOOP 0x04
|
||||
#define BOOT_DIAGNOSTIC_VALID_IMAGE_DEF 0x08
|
||||
#define BOOT_DIAGNOSTIC_HAS_PARTITION_TABLE 0x10
|
||||
#define BOOT_DIAGNOSTIC_CONSIDERED 0x20
|
||||
#define BOOT_DIAGNOSTIC_CHOSEN 0x40
|
||||
#define BOOT_DIAGNOSTIC_PARTITION_TABLE_LSB 7
|
||||
#define BOOT_DIAGNOSTIC_PARTITION_TABLE_MATCHING_KEY_FOR_VERIFY 0x80
|
||||
#define BOOT_DIAGNOSTIC_PARTITION_TABLE_HASH_FOR_VERIFY 0x100
|
||||
#define BOOT_DIAGNOSTIC_PARTITION_TABLE_VERIFIED_OK 0x200
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_DEF_LSB 10
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_DEF_MATCHING_KEY_FOR_VERIFY 0x400
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_DEF_HASH_FOR_VERIFY 0x800
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_DEF_VERIFIED_OK 0x1000
|
||||
|
||||
#define BOOT_DIAGNOSTIC_LOAD_MAP_ENTRIES_LOADED 0x2000
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_LAUNCHED 0x4000
|
||||
#define BOOT_DIAGNOSTIC_IMAGE_CONDITION_FAILURE 0x8000
|
||||
|
||||
#define BOOT_PARSED_BLOCK_DIAGNOSTIC_MATCHING_KEY_FOR_VERIFY 0x1 // if this is present and VERIFIED_OK isn't the sig check failed
|
||||
#define BOOT_PARSED_BLOCK_DIAGNOSTIC_HASH_FOR_VERIFY 0x2 // if this is present and VERIFIED_OL isn't then hash check failed
|
||||
#define BOOT_PARSED_BLOCK_DIAGNOSTIC_VERIFIED_OK 0x4
|
||||
|
||||
#define BOOT_TBYB_AND_UPDATE_FLAG_BUY_PENDING 0x1
|
||||
#define BOOT_TBYB_AND_UPDATE_FLAG_OTP_VERSION_APPLIED 0x2
|
||||
#define BOOT_TBYB_AND_UPDATE_FLAG_OTHER_ERASED 0x4
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
// Limited to 3 arguments in case of varm multiplex hint (trashes Arm r3)
|
||||
typedef int (*bootrom_api_callback_generic_t)(uint32_t r0, uint32_t r1, uint32_t r2);
|
||||
// Return negative for error, else number of bytes transferred:
|
||||
//typedef int (*bootrom_api_callback_stdout_put_blocking_t)(const uint8_t *buffer, uint32_t size);
|
||||
//typedef int (*bootrom_api_callback_stdin_get_t)(uint8_t *buffer, uint32_t size);
|
||||
//typedef void (*bootrom_api_callback_core1_security_setup_t)(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*! \brief Return a bootrom lookup code based on two ASCII characters
|
||||
* \ingroup pico_bootrom
|
||||
*
|
||||
* These codes are uses to lookup data or function addresses in the bootrom
|
||||
*
|
||||
* \param c1 the first character
|
||||
* \param c2 the second character
|
||||
* \return the 'code' to use in rom_func_lookup() or rom_data_lookup()
|
||||
*/
|
||||
#define ROM_TABLE_CODE(c1, c2) ((c1) | ((c2) << 8))
|
||||
|
||||
// ROM FUNCTIONS
|
||||
|
||||
// RP2040 & RP2350
|
||||
#define ROM_DATA_SOFTWARE_GIT_REVISION ROM_TABLE_CODE('G', 'R')
|
||||
#define ROM_FUNC_FLASH_ENTER_CMD_XIP ROM_TABLE_CODE('C', 'X')
|
||||
#define ROM_FUNC_FLASH_EXIT_XIP ROM_TABLE_CODE('E', 'X')
|
||||
#define ROM_FUNC_FLASH_FLUSH_CACHE ROM_TABLE_CODE('F', 'C')
|
||||
#define ROM_FUNC_CONNECT_INTERNAL_FLASH ROM_TABLE_CODE('I', 'F')
|
||||
#define ROM_FUNC_FLASH_RANGE_ERASE ROM_TABLE_CODE('R', 'E')
|
||||
#define ROM_FUNC_FLASH_RANGE_PROGRAM ROM_TABLE_CODE('R', 'P')
|
||||
|
||||
|
||||
#if PICO_RP2040
|
||||
// RP2040 only
|
||||
#define ROM_FUNC_MEMCPY44 ROM_TABLE_CODE('C', '4')
|
||||
#define ROM_DATA_COPYRIGHT ROM_TABLE_CODE('C', 'R')
|
||||
#define ROM_FUNC_CLZ32 ROM_TABLE_CODE('L', '3')
|
||||
#define ROM_FUNC_MEMCPY ROM_TABLE_CODE('M', 'C')
|
||||
#define ROM_FUNC_MEMSET ROM_TABLE_CODE('M', 'S')
|
||||
#define ROM_FUNC_POPCOUNT32 ROM_TABLE_CODE('P', '3')
|
||||
#define ROM_FUNC_REVERSE32 ROM_TABLE_CODE('R', '3')
|
||||
#define ROM_FUNC_MEMSET4 ROM_TABLE_CODE('S', '4')
|
||||
#define ROM_FUNC_CTZ32 ROM_TABLE_CODE('T', '3')
|
||||
#define ROM_FUNC_RESET_USB_BOOT ROM_TABLE_CODE('U', 'B')
|
||||
#endif
|
||||
|
||||
#if !PICO_RP2040 || PICO_COMBINED_DOCS
|
||||
// RP2350 only
|
||||
#define ROM_FUNC_PICK_AB_PARTITION ROM_TABLE_CODE('A', 'B')
|
||||
#define ROM_FUNC_CHAIN_IMAGE ROM_TABLE_CODE('C', 'I')
|
||||
#define ROM_FUNC_EXPLICIT_BUY ROM_TABLE_CODE('E', 'B')
|
||||
#define ROM_FUNC_FLASH_RUNTIME_TO_STORAGE_ADDR ROM_TABLE_CODE('F', 'A')
|
||||
#define ROM_DATA_FLASH_DEVINFO16_PTR ROM_TABLE_CODE('F', 'D')
|
||||
#define ROM_FUNC_FLASH_OP ROM_TABLE_CODE('F', 'O')
|
||||
#define ROM_FUNC_GET_B_PARTITION ROM_TABLE_CODE('G', 'B')
|
||||
#define ROM_FUNC_GET_PARTITION_TABLE_INFO ROM_TABLE_CODE('G', 'P')
|
||||
#define ROM_FUNC_GET_SYS_INFO ROM_TABLE_CODE('G', 'S')
|
||||
#define ROM_FUNC_GET_UF2_TARGET_PARTITION ROM_TABLE_CODE('G', 'U')
|
||||
#define ROM_FUNC_LOAD_PARTITION_TABLE ROM_TABLE_CODE('L', 'P')
|
||||
#define ROM_FUNC_OTP_ACCESS ROM_TABLE_CODE('O', 'A')
|
||||
#define ROM_DATA_PARTITION_TABLE_PTR ROM_TABLE_CODE('P', 'T')
|
||||
#define ROM_FUNC_FLASH_RESET_ADDRESS_TRANS ROM_TABLE_CODE('R', 'A')
|
||||
#define ROM_FUNC_REBOOT ROM_TABLE_CODE('R', 'B')
|
||||
#define ROM_FUNC_SET_ROM_CALLBACK ROM_TABLE_CODE('R', 'C')
|
||||
#define ROM_FUNC_SECURE_CALL ROM_TABLE_CODE('S', 'C')
|
||||
#define ROM_FUNC_SET_NS_API_PERMISSION ROM_TABLE_CODE('S', 'P')
|
||||
#define ROM_FUNC_BOOTROM_STATE_RESET ROM_TABLE_CODE('S', 'R')
|
||||
#define ROM_FUNC_SET_BOOTROM_STACK ROM_TABLE_CODE('S', 'S')
|
||||
#define ROM_DATA_SAVED_XIP_SETUP_FUNC_PTR ROM_TABLE_CODE('X', 'F')
|
||||
#define ROM_FUNC_FLASH_SELECT_XIP_READ_MODE ROM_TABLE_CODE('X', 'M')
|
||||
#define ROM_FUNC_VALIDATE_NS_BUFFER ROM_TABLE_CODE('V', 'B')
|
||||
#endif
|
||||
|
||||
// these form a bit set
|
||||
#define BOOTROM_STATE_RESET_CURRENT_CORE 0x01
|
||||
#define BOOTROM_STATE_RESET_OTHER_CORE 0x02
|
||||
#define BOOTROM_STATE_RESET_GLOBAL_STATE 0x04 // reset any global state (e.g. permissions)
|
||||
|
||||
// partition level stuff is returned first (note PT_INFO flags is only 16 bits)
|
||||
|
||||
// 3 words: pt_count, unpartitioned_perm_loc, unpartioned_perm_flags
|
||||
#define PT_INFO_PT_INFO 0x0001
|
||||
#define PT_INFO_SINGLE_PARTITION 0x8000 // marker to just include a single partition in the results)
|
||||
|
||||
// then in order per partition selected
|
||||
|
||||
// 2 words: unpartitioned_perm_loc, unpartioned_perm_flags
|
||||
#define PT_INFO_PARTITION_LOCATION_AND_FLAGS 0x0010
|
||||
// 2 words: id lsb first
|
||||
#define PT_INFO_PARTITION_ID 0x0020
|
||||
// n+1 words: n, family_id...
|
||||
#define PT_INFO_PARTITION_FAMILY_IDS 0x0040
|
||||
// (n+3)/4 words... bytes are: n (len), c0, c1, ... cn-1 padded to word boundary with zeroes
|
||||
#define PT_INFO_PARTITION_NAME 0x0080
|
||||
|
||||
// items are returned in order
|
||||
// 3 words package_id, device_id, wafer_id
|
||||
#define SYS_INFO_CHIP_INFO 0x0001
|
||||
// 1 word: chip specific critical bits
|
||||
#define SYS_INFO_CRITICAL 0x0002
|
||||
// 1 word: bytes: cpu_type, supported_cpu_type_bitfield
|
||||
#define SYS_INFO_CPU_INFO 0x0004
|
||||
// 1 word: same as FLASH_DEVINFO row in OTP
|
||||
#define SYS_INFO_FLASH_DEV_INFO 0x0008
|
||||
// 4 words
|
||||
#define SYS_INFO_BOOT_RANDOM 0x0010
|
||||
// 2 words lsb first
|
||||
#define SYS_INFO_NONCE 0x0020
|
||||
// 4 words boot_info, boot_diagnostic, boot_param0, boot_param1
|
||||
#define SYS_INFO_BOOT_INFO 0x0040
|
||||
|
||||
#define BOOTROM_NS_API_get_sys_info 0
|
||||
#define BOOTROM_NS_API_checked_flash_op 1
|
||||
#define BOOTROM_NS_API_flash_runtime_to_storage_addr 2
|
||||
#define BOOTROM_NS_API_get_partition_table_info 3
|
||||
#define BOOTROM_NS_API_secure_call 4
|
||||
#define BOOTROM_NS_API_otp_access 5
|
||||
#define BOOTROM_NS_API_reboot 6
|
||||
#define BOOTROM_NS_API_get_b_partition 7
|
||||
#define BOOTROM_NS_API_COUNT 8
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
typedef struct {
|
||||
uint32_t permissions_and_location;
|
||||
uint32_t permissions_and_flags;
|
||||
} resident_partition_t;
|
||||
static_assert(sizeof(resident_partition_t) == 8, "");
|
||||
|
||||
#define OTP_CMD_ROW_BITS 0x0000ffffu
|
||||
#define OTP_CMD_ROW_LSB 0u
|
||||
#define OTP_CMD_WRITE_BITS 0x00010000u
|
||||
#define OTP_CMD_ECC_BITS 0x00020000u
|
||||
|
||||
typedef struct otp_cmd {
|
||||
uint32_t flags;
|
||||
} otp_cmd_t;
|
||||
|
||||
typedef enum {
|
||||
BOOTROM_XIP_MODE_03H_SERIAL = 0,
|
||||
BOOTROM_XIP_MODE_0BH_SERIAL,
|
||||
BOOTROM_XIP_MODE_BBH_DUAL,
|
||||
BOOTROM_XIP_MODE_EBH_QUAD,
|
||||
BOOTROM_XIP_MODE_N_MODES
|
||||
} bootrom_xip_mode_t;
|
||||
|
||||
// The checked flash API wraps the low-level flash routines from generic_flash, adding bounds
|
||||
// checking, permission checking against the resident partition table, and simple address
|
||||
// translation. The low-level API deals with flash offsets (i.e. distance from the start of the
|
||||
// first flash device, measured in bytes) but the checked flash API accepts one of two types of
|
||||
// address:
|
||||
//
|
||||
// - Flash runtime addresses: the address of some flash-resident data or code in the currently
|
||||
// running image. The flash addresses your binary is "linked at" by the linker.
|
||||
// - Flash storage addresses: a flash offset, plus the address base where QSPI hardware is first
|
||||
// mapped on the system bus (XIP_BASE constant from addressmap.h)
|
||||
//
|
||||
// These addresses are one and the same *if* the currently running program is stored at the
|
||||
// beginning of flash. They are different if the start of your image has been "rolled" by the flash
|
||||
// boot path to make it appear at the address it was linked at even though it is stored at a
|
||||
// different location in flash, which is necessary when you have A/B images for example.
|
||||
//
|
||||
// The address translation between flash runtime and flash storage addresses is configured in
|
||||
// hardware by the QMI_ATRANSx registers, and this API assumes those registers contain a valid
|
||||
// address mapping which it can use to translate runtime to storage addresses.
|
||||
|
||||
typedef struct cflash_flags {
|
||||
uint32_t flags;
|
||||
} cflash_flags_t;
|
||||
|
||||
// Bits which are permitted to be set in a flags variable -- any other bits being set is an error
|
||||
#define CFLASH_FLAGS_BITS 0x00070301u
|
||||
|
||||
// Used to tell checked flash API which space a given address belongs to
|
||||
#define CFLASH_ASPACE_BITS 0x00000001u
|
||||
#define CFLASH_ASPACE_LSB 0u
|
||||
#define CFLASH_ASPACE_VALUE_STORAGE 0u
|
||||
#define CFLASH_ASPACE_VALUE_RUNTIME 1u
|
||||
|
||||
// Used to tell checked flash APIs the effective security level of a flash access (may be forced to
|
||||
// one of these values for the NonSecure-exported version of this API)
|
||||
#define CFLASH_SECLEVEL_BITS 0x00000300u
|
||||
#define CFLASH_SECLEVEL_LSB 8u
|
||||
// Zero is not a valid security level:
|
||||
#define CFLASH_SECLEVEL_VALUE_SECURE 1u
|
||||
#define CFLASH_SECLEVEL_VALUE_NONSECURE 2u
|
||||
#define CFLASH_SECLEVEL_VALUE_BOOTLOADER 3u
|
||||
|
||||
#define CFLASH_OP_BITS 0x00070000u
|
||||
#define CFLASH_OP_LSB 16u
|
||||
// Erase size_bytes bytes of flash, starting at address addr. Both addr and size_bytes must be a
|
||||
// multiple of 4096 bytes (one flash sector).
|
||||
#define CFLASH_OP_VALUE_ERASE 0u
|
||||
// Program size_bytes bytes of flash, starting at address addr. Both addr and size_bytes must be a
|
||||
// multiple of 256 bytes (one flash page).
|
||||
#define CFLASH_OP_VALUE_PROGRAM 1u
|
||||
// Read size_bytes bytes of flash, starting at address addr. There are no alignment restrictions on
|
||||
// addr or size_bytes.
|
||||
#define CFLASH_OP_VALUE_READ 2u
|
||||
#define CFLASH_OP_MAX 2u
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
// new location; this file kept for backwards compatibility
|
||||
#include "boot/bootrom_constants.h"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __unix__
|
||||
#if defined __unix__ && defined __GLIBC__
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ extern void tight_loop_contents();
|
|||
#define __STRING(x) #x
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#if !defined(_MSC_VER) || defined(__clang__)
|
||||
#ifndef __noreturn
|
||||
#define __noreturn __attribute((noreturn))
|
||||
#endif
|
||||
|
|
@ -60,6 +60,12 @@ extern void tight_loop_contents();
|
|||
#define __noinline __attribute__((noinline))
|
||||
#endif
|
||||
|
||||
#ifndef __force_inline
|
||||
// don't think it is critical to inline in host mode, and this is simpler than picking the
|
||||
// correct attribute incantation for always_inline on different compiler versions
|
||||
#define __force_inline inline
|
||||
#endif
|
||||
|
||||
#ifndef __aligned
|
||||
#define __aligned(x) __attribute__((aligned(x)))
|
||||
#endif
|
||||
|
|
@ -148,7 +154,11 @@ uint get_core_num();
|
|||
|
||||
static inline uint __get_current_exception(void) {
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void busy_wait_at_least_cycles(uint32_t minimum_cycles);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -76,6 +76,11 @@ cc_binary(
|
|||
copts = ["-fPIC"],
|
||||
# Incompatible with section garbage collection.
|
||||
features = ["-gc_sections"],
|
||||
# Platforms will commonly depend on bootloader components in every
|
||||
# binary via `link_extra_libs`, so we must drop these deps when
|
||||
# building the bootloader binaries themselves in order to avoid a
|
||||
# circular dependency.
|
||||
link_extra_lib = "//bazel:empty_cc_lib",
|
||||
linkopts = [
|
||||
"-Wl,--no-gc-sections",
|
||||
"-nostartfiles",
|
||||
|
|
|
|||
|
|
@ -30,10 +30,16 @@ pico_register_common_scope_var(PICO_DEFAULT_BOOT_STAGE2_FILE)
|
|||
# needed by function below
|
||||
set(PICO_BOOT_STAGE2_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "")
|
||||
|
||||
add_library(boot_stage2_headers INTERFACE)
|
||||
pico_add_library(boot_stage2_headers)
|
||||
target_include_directories(boot_stage2_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
|
||||
# by convention the first source file name without extension is used for the binary info name
|
||||
# pico_define_boot_stage2(NAME SOURCES)
|
||||
# \brief\ Define a boot stage 2 target.
|
||||
#
|
||||
# By convention the first source file name without extension is used for the binary info name
|
||||
#
|
||||
# \param\ NAME The name of the boot stage 2 target
|
||||
# \param\ SOURCES The source files to link into the boot stage 2
|
||||
function(pico_define_boot_stage2 NAME SOURCES)
|
||||
add_executable(${NAME}
|
||||
${SOURCES}
|
||||
|
|
@ -66,15 +72,12 @@ function(pico_define_boot_stage2 NAME SOURCES)
|
|||
add_custom_command(OUTPUT ${ORIGINAL_BIN} DEPENDS ${NAME} COMMAND ${CMAKE_OBJCOPY} -Obinary $<TARGET_FILE:${NAME}> ${ORIGINAL_BIN}
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(${NAME}_padded_checksummed_asm DEPENDS ${PADDED_CHECKSUMMED_ASM})
|
||||
add_custom_command(OUTPUT ${PADDED_CHECKSUMMED_ASM} DEPENDS ${ORIGINAL_BIN}
|
||||
COMMAND ${Python3_EXECUTABLE} ${PICO_BOOT_STAGE2_DIR}/pad_checksum -s 0xffffffff ${ORIGINAL_BIN} ${PADDED_CHECKSUMMED_ASM}
|
||||
VERBATIM)
|
||||
|
||||
add_library(${NAME}_library INTERFACE)
|
||||
add_dependencies(${NAME}_library ${NAME}_padded_checksummed_asm)
|
||||
# not strictly (or indeed actually) a link library, but this avoids dependency cycle
|
||||
target_link_libraries(${NAME}_library INTERFACE ${PADDED_CHECKSUMMED_ASM})
|
||||
add_library(${NAME}_library OBJECT ${PADDED_CHECKSUMMED_ASM})
|
||||
target_link_libraries(${NAME}_library INTERFACE "$<TARGET_OBJECTS:${NAME}_library>")
|
||||
target_link_libraries(${NAME}_library INTERFACE boot_stage2_headers)
|
||||
|
||||
list(GET SOURCES 0 FIRST_SOURCE)
|
||||
|
|
@ -100,7 +103,12 @@ endmacro()
|
|||
|
||||
pico_define_boot_stage2(bs2_default ${PICO_DEFAULT_BOOT_STAGE2_FILE})
|
||||
|
||||
# pico_clone_default_boot_stage2(NAME)
|
||||
# \brief_nodesc\ Clone the default boot stage 2 target.
|
||||
#
|
||||
# Create a new boot stage 2 target using the default implementation for the current build (PICO_BOARD derived)
|
||||
#
|
||||
# \param\ NAME The name of the new boot stage 2 target
|
||||
function(pico_clone_default_boot_stage2 NAME)
|
||||
pico_define_boot_stage2(${NAME} ${PICO_DEFAULT_BOOT_STAGE2_FILE})
|
||||
endfunction()
|
||||
|
|
|
|||
|
|
@ -144,10 +144,10 @@ regular_func _stage2_boot
|
|||
// status register and checking for the "RX FIFO Not Empty" flag to assert.
|
||||
|
||||
movs r1, #SSI_SR_RFNE_BITS
|
||||
00:
|
||||
1:
|
||||
ldr r0, [r3, #SSI_SR_OFFSET] // Read status register
|
||||
tst r0, r1 // RFNE status flag set?
|
||||
beq 00b // If not then wait
|
||||
beq 1b // If not then wait
|
||||
|
||||
// At this point CN# will be deasserted and the SPI clock will not be running.
|
||||
// The Winbond WX25X10CL device will be in continuous read, dual I/O mode and
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ MEMORY {
|
|||
SECTIONS {
|
||||
. = ORIGIN(SRAM);
|
||||
.text : {
|
||||
_start = .; /* make LLVM happy */
|
||||
*(.entry)
|
||||
*(.text)
|
||||
} >SRAM
|
||||
|
|
|
|||
|
|
@ -11,13 +11,15 @@
|
|||
|
||||
#include "pico.h"
|
||||
|
||||
// PICO_CONFIG: PICO_BUILD_BOOT_STAGE2_NAME, The name of the boot stage 2 if selected by the build, group=boot_stage2
|
||||
// PICO_CONFIG: PICO_FLASH_SPI_CLKDIV, Clock divider from clk_sys to use for serial flash communications in boot stage 2. On RP2040 this must be a multiple of 2. This define applies to compilation of the boot stage 2 not the main application, type=int, default=varies; often specified in board header, advanced=true, group=boot_stage2
|
||||
|
||||
// PICO_CONFIG: PICO_BUILD_BOOT_STAGE2_NAME, Name of the boot stage 2 if selected in the build system. This define applies to compilation of the boot stage 2 not the main application, group=boot_stage2
|
||||
#ifdef PICO_BUILD_BOOT_STAGE2_NAME
|
||||
#define _BOOT_STAGE2_SELECTED
|
||||
#else
|
||||
// check that multiple boot stage 2 options haven't been set...
|
||||
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_IS25LP080, Select boot2_is25lp080 as the boot stage 2 when no boot stage 2 selection is made by the CMake build, type=bool, default=0, group=boot_stage2
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_IS25LP080, Select boot2_is25lp080 as the boot stage 2 when no boot stage 2 selection is made by the CMake build. This define applies to compilation of the boot stage 2 not the main application, type=bool, default=0, group=boot_stage2
|
||||
#ifndef PICO_BOOT_STAGE2_CHOOSE_IS25LP080
|
||||
#define PICO_BOOT_STAGE2_CHOOSE_IS25LP080 0
|
||||
#elif PICO_BOOT_STAGE2_CHOOSE_IS25LP080
|
||||
|
|
@ -26,7 +28,7 @@
|
|||
#endif
|
||||
#define _BOOT_STAGE2_SELECTED
|
||||
#endif
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_W25Q080, Select boot2_w25q080 as the boot stage 2 when no boot stage 2 selection is made by the CMake build, type=bool, default=0, group=boot_stage2
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_W25Q080, Select boot2_w25q080 as the boot stage 2 when no boot stage 2 selection is made by the CMake build. This define applies to compilation of the boot stage 2 not the main application, type=bool, default=0, group=boot_stage2
|
||||
#ifndef PICO_BOOT_STAGE2_CHOOSE_W25Q080
|
||||
#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 0
|
||||
#elif PICO_BOOT_STAGE2_CHOOSE_W25Q080
|
||||
|
|
@ -35,7 +37,7 @@
|
|||
#endif
|
||||
#define _BOOT_STAGE2_SELECTED
|
||||
#endif
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_W25X10CL, Select boot2_w25x10cl as the boot stage 2 when no boot stage 2 selection is made by the CMake build, type=bool, default=0, group=boot_stage2
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_W25X10CL, Select boot2_w25x10cl as the boot stage 2 when no boot stage 2 selection is made by the CMake build. This define applies to compilation of the boot stage 2 not the main application, type=bool, default=0, group=boot_stage2
|
||||
#ifndef PICO_BOOT_STAGE2_CHOOSE_W25X10CL
|
||||
#define PICO_BOOT_STAGE2_CHOOSE_W25X10CL 0
|
||||
#elif PICO_BOOT_STAGE2_CHOOSE_W25X10CL
|
||||
|
|
@ -44,7 +46,7 @@
|
|||
#endif
|
||||
#define _BOOT_STAGE2_SELECTED
|
||||
#endif
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_AT25SF128A, Select boot2_at25sf128a as the boot stage 2 when no boot stage 2 selection is made by the CMake build, type=bool, default=0, group=boot_stage2
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_AT25SF128A, Select boot2_at25sf128a as the boot stage 2 when no boot stage 2 selection is made by the CMake build. This define applies to compilation of the boot stage 2 not the main application, type=bool, default=0, group=boot_stage2
|
||||
#ifndef PICO_BOOT_STAGE2_CHOOSE_AT25SF128A
|
||||
#define PICO_BOOT_STAGE2_CHOOSE_AT25SF128A 0
|
||||
#elif PICO_BOOT_STAGE2_CHOOSE_AT25SF128A
|
||||
|
|
@ -54,7 +56,7 @@
|
|||
#define _BOOT_STAGE2_SELECTED
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H, Select boot2_generic_03h as the boot stage 2 when no boot stage 2 selection is made by the CMake build, type=bool, default=1, group=boot_stage2
|
||||
// PICO_CONFIG: PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H, Select boot2_generic_03h as the boot stage 2 when no boot stage 2 selection is made by the CMake build. This define applies to compilation of the boot stage 2 not the main application, type=bool, default=1, group=boot_stage2
|
||||
#if defined(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H) && PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H
|
||||
#ifdef _BOOT_STAGE2_SELECTED
|
||||
#error multiple boot stage 2 options chosen
|
||||
|
|
|
|||
|
|
@ -46,6 +46,15 @@
|
|||
|
||||
#define HAS_SIO_DIVIDER 1
|
||||
#define HAS_RP2040_RTC 1
|
||||
|
||||
#ifndef FPGA_CLK_SYS_HZ
|
||||
#define FPGA_CLK_SYS_HZ (48 * MHZ)
|
||||
#endif
|
||||
|
||||
#ifndef FPGA_CLK_REF_HZ
|
||||
#define FPGA_CLK_REF_HZ (12 * MHZ)
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: XOSC_HZ, Crystal oscillator frequency in Hz, type=int, default=12000000, advanced=true, group=hardware_base
|
||||
// NOTE: The system and USB clocks are generated from the frequency using two PLLs.
|
||||
// If you override this define, or SYS_CLK_HZ/USB_CLK_HZ below, you will *also* need to add your own adjusted PLL set-up defines to
|
||||
|
|
@ -61,6 +70,11 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_USE_FASTEST_SUPPORTED_CLOCK, Use the fastest officially supported clock by default, type=bool, default=0, group=hardware_base
|
||||
#ifndef PICO_USE_FASTEST_SUPPORTED_CLOCK
|
||||
#define PICO_USE_FASTEST_SUPPORTED_CLOCK 0
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: SYS_CLK_HZ, System operating frequency in Hz, type=int, default=125000000, advanced=true, group=hardware_base
|
||||
#ifndef SYS_CLK_HZ
|
||||
#ifdef SYS_CLK_KHZ
|
||||
|
|
@ -68,9 +82,13 @@
|
|||
#elif defined(SYS_CLK_MHZ)
|
||||
#define SYS_CLK_HZ ((SYS_CLK_MHZ) * _u(1000000))
|
||||
#else
|
||||
#if PICO_USE_FASTEST_SUPPORTED_CLOCK
|
||||
#define SYS_CLK_HZ _u(200000000)
|
||||
#else
|
||||
#define SYS_CLK_HZ _u(125000000)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: USB_CLK_HZ, USB clock frequency. Must be 48MHz for the USB interface to operate correctly, type=int, default=48000000, advanced=true, group=hardware_base
|
||||
#ifndef USB_CLK_HZ
|
||||
|
|
@ -116,4 +134,6 @@
|
|||
#define FIRST_USER_IRQ (NUM_IRQS - NUM_USER_IRQS)
|
||||
#define VTABLE_FIRST_IRQ 16
|
||||
|
||||
#define REG_FIELD_WIDTH(f) (f ## _MSB + 1 - f ## _LSB)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@
|
|||
#define I2C0_IRQ 23
|
||||
#define I2C1_IRQ 24
|
||||
#define RTC_IRQ 25
|
||||
#define SPARE_IRQ_0 26
|
||||
#define SPARE_IRQ_1 27
|
||||
#define SPARE_IRQ_2 28
|
||||
#define SPARE_IRQ_3 29
|
||||
#define SPARE_IRQ_4 30
|
||||
#define SPARE_IRQ_5 31
|
||||
#else
|
||||
/**
|
||||
* \brief Interrupt numbers on RP2040 (used as typedef \ref irq_num_t)
|
||||
|
|
@ -71,6 +77,12 @@ typedef enum irq_num_rp2040 {
|
|||
I2C0_IRQ = 23, ///< Select I2C0's IRQ output
|
||||
I2C1_IRQ = 24, ///< Select I2C1's IRQ output
|
||||
RTC_IRQ = 25, ///< Select RTC's IRQ output
|
||||
SPARE_IRQ_0 = 26, ///< Select SPARE IRQ 0
|
||||
SPARE_IRQ_1 = 27, ///< Select SPARE IRQ 1
|
||||
SPARE_IRQ_2 = 28, ///< Select SPARE IRQ 2
|
||||
SPARE_IRQ_3 = 29, ///< Select SPARE IRQ 3
|
||||
SPARE_IRQ_4 = 30, ///< Select SPARE IRQ 4
|
||||
SPARE_IRQ_5 = 31, ///< Select SPARE IRQ 5
|
||||
IRQ_COUNT
|
||||
} irq_num_t;
|
||||
#endif
|
||||
|
|
@ -101,6 +113,12 @@ typedef enum irq_num_rp2040 {
|
|||
#define isr_i2c0 isr_irq23
|
||||
#define isr_i2c1 isr_irq24
|
||||
#define isr_rtc isr_irq25
|
||||
#define isr_spare_0 isr_irq26
|
||||
#define isr_spare_1 isr_irq27
|
||||
#define isr_spare_2 isr_irq28
|
||||
#define isr_spare_3 isr_irq29
|
||||
#define isr_spare_4 isr_irq30
|
||||
#define isr_spare_5 isr_irq31
|
||||
|
||||
#endif // _INTCTRL_H
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
// BITMASK [BITRANGE] FIELDNAME (RESETVALUE) DESCRIPTION
|
||||
|
||||
/** \brief Bus fabric performance counters on RP2040 (used as typedef \ref bus_ctrl_perf_counter_t)
|
||||
* \ingroup hardware_busctrl
|
||||
*/
|
||||
typedef enum bus_ctrl_perf_counter_rp2040 {
|
||||
arbiter_rom_perf_event_access = 19,
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
* @file src/rp2_common/cmsis/stub/CMSIS/Device/RP2350/Include/RP2350.h
|
||||
* @brief CMSIS HeaderFile
|
||||
* @version 0.1
|
||||
* @date Thu Aug 8 04:04:02 2024
|
||||
* @note Generated by SVDConv V3.3.47
|
||||
* from File 'src/rp2_common/cmsis/../../rp2350/hardware_regs/RP2350.svd',
|
||||
* last modified on Thu Aug 8 03:59:33 2024
|
||||
* @date Mon Jul 28 11:37:41 2025
|
||||
* @note Generated by SVDConv V3.3.45
|
||||
* from File 'src/rp2350/hardware_regs/RP2350.svd',
|
||||
* last modified on Mon Jul 28 11:35:05 2025
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -2028,7 +2028,7 @@ typedef struct { /*!< POWMAN Structure
|
|||
ignore the power down requests. To do nothing would risk
|
||||
entering an unrecoverable lock-up state. Invalid requests
|
||||
are: any combination of power up and power down requests
|
||||
any request that results in swcore boing powered and xip
|
||||
any request that results in swcore being powered and xip
|
||||
unpowered If the request is to power down the switched-core
|
||||
domain then POWMAN_STATE_WAITING stays active until the
|
||||
processors halt. During this time the POWMAN_STATE_REQ
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/*************************************************************************//**
|
||||
* @file system_RP2040.h
|
||||
* @file system_RP2350.h
|
||||
* @brief CMSIS-Core(M) Device Peripheral Access Layer Header File for
|
||||
* Device RP2040
|
||||
* @version V1.0.0
|
||||
* @date 5. May 2021
|
||||
* Device RP2350
|
||||
* @version V1.0.1
|
||||
* @date 6. Sep 2024
|
||||
*****************************************************************************/
|
||||
/*
|
||||
* Copyright (c) 2009-2021 Arm Limited. All rights reserved.
|
||||
|
|
@ -26,8 +26,8 @@
|
|||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _CMSIS_SYSTEM_RP2040_H
|
||||
#define _CMSIS_SYSTEM_RP2040_H
|
||||
#ifndef _CMSIS_SYSTEM_RP2350_H
|
||||
#define _CMSIS_SYSTEM_RP2350_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -62,4 +62,4 @@ extern void SystemCoreClockUpdate (void);
|
|||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CMSIS_SYSTEM_RP2040_H */
|
||||
#endif /* _CMSIS_SYSTEM_RP2350_H */
|
||||
|
|
|
|||
|
|
@ -615,7 +615,7 @@
|
|||
// Description : Clock control, can be changed on-the-fly (except for auxsrc)
|
||||
#define CLOCKS_CLK_SYS_CTRL_OFFSET _u(0x0000003c)
|
||||
#define CLOCKS_CLK_SYS_CTRL_BITS _u(0x000000e1)
|
||||
#define CLOCKS_CLK_SYS_CTRL_RESET _u(0x00000000)
|
||||
#define CLOCKS_CLK_SYS_CTRL_RESET _u(0x00000041)
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : CLOCKS_CLK_SYS_CTRL_AUXSRC
|
||||
// Description : Selects the auxiliary clock source, will glitch when switching
|
||||
|
|
@ -625,7 +625,7 @@
|
|||
// 0x3 -> xosc_clksrc
|
||||
// 0x4 -> clksrc_gpin0
|
||||
// 0x5 -> clksrc_gpin1
|
||||
#define CLOCKS_CLK_SYS_CTRL_AUXSRC_RESET _u(0x0)
|
||||
#define CLOCKS_CLK_SYS_CTRL_AUXSRC_RESET _u(0x2)
|
||||
#define CLOCKS_CLK_SYS_CTRL_AUXSRC_BITS _u(0x000000e0)
|
||||
#define CLOCKS_CLK_SYS_CTRL_AUXSRC_MSB _u(7)
|
||||
#define CLOCKS_CLK_SYS_CTRL_AUXSRC_LSB _u(5)
|
||||
|
|
@ -642,7 +642,7 @@
|
|||
// fly
|
||||
// 0x0 -> clk_ref
|
||||
// 0x1 -> clksrc_clk_sys_aux
|
||||
#define CLOCKS_CLK_SYS_CTRL_SRC_RESET _u(0x0)
|
||||
#define CLOCKS_CLK_SYS_CTRL_SRC_RESET _u(0x1)
|
||||
#define CLOCKS_CLK_SYS_CTRL_SRC_BITS _u(0x00000001)
|
||||
#define CLOCKS_CLK_SYS_CTRL_SRC_MSB _u(0)
|
||||
#define CLOCKS_CLK_SYS_CTRL_SRC_LSB _u(0)
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ typedef enum dreq_num_rp2350 {
|
|||
DREQ_PWM_WRAP7 = 39, ///< Select PWM Counter 7's Wrap Value as DREQ
|
||||
DREQ_PWM_WRAP8 = 40, ///< Select PWM Counter 8's Wrap Value as DREQ
|
||||
DREQ_PWM_WRAP9 = 41, ///< Select PWM Counter 9's Wrap Value as DREQ
|
||||
DREQ_PWM_WRAP10 = 42, ///< Select PWM Counter 0's Wrap Value as DREQ
|
||||
DREQ_PWM_WRAP11 = 43, ///< Select PWM Counter 1's Wrap Value as DREQ
|
||||
DREQ_PWM_WRAP10 = 42, ///< Select PWM Counter 10's Wrap Value as DREQ
|
||||
DREQ_PWM_WRAP11 = 43, ///< Select PWM Counter 11's Wrap Value as DREQ
|
||||
DREQ_I2C0_TX = 44, ///< Select I2C0's TX FIFO as DREQ
|
||||
DREQ_I2C0_RX = 45, ///< Select I2C0's RX FIFO as DREQ
|
||||
DREQ_I2C1_TX = 46, ///< Select I2C1's TX FIFO as DREQ
|
||||
|
|
@ -135,8 +135,8 @@ typedef enum dreq_num_rp2350 {
|
|||
DREQ_CORESIGHT = 53, ///< Select CORESIGHT as DREQ
|
||||
DREQ_SHA256 = 54, ///< Select SHA256 as DREQ
|
||||
DREQ_DMA_TIMER0 = 59, ///< Select DMA_TIMER0 as DREQ
|
||||
DREQ_DMA_TIMER1 = 60, ///< Select DMA_TIMER0 as DREQ
|
||||
DREQ_DMA_TIMER2 = 61, ///< Select DMA_TIMER1 as DREQ
|
||||
DREQ_DMA_TIMER1 = 60, ///< Select DMA_TIMER1 as DREQ
|
||||
DREQ_DMA_TIMER2 = 61, ///< Select DMA_TIMER2 as DREQ
|
||||
DREQ_DMA_TIMER3 = 62, ///< Select DMA_TIMER3 as DREQ
|
||||
DREQ_FORCE = 63, ///< Select FORCE as DREQ
|
||||
DREQ_COUNT
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@
|
|||
#define GLITCH_DETECTOR_ARM_VALUE_YES _u(0x0000)
|
||||
// =============================================================================
|
||||
// Register : GLITCH_DETECTOR_DISARM
|
||||
// Description : None
|
||||
// Forcibly disarm the glitch detectors, if they are armed by OTP.
|
||||
// Description : Forcibly disarm the glitch detectors, if they are armed by OTP.
|
||||
// Ignored if ARM is YES.
|
||||
//
|
||||
// This register is Secure read/write only.
|
||||
|
|
@ -142,8 +141,7 @@
|
|||
#define GLITCH_DETECTOR_SENSITIVITY_DET0_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : GLITCH_DETECTOR_LOCK
|
||||
// Description : None
|
||||
// Write any nonzero value to disable writes to ARM, DISARM,
|
||||
// Description : Write any nonzero value to disable writes to ARM, DISARM,
|
||||
// SENSITIVITY and LOCK. This register is Secure read/write only.
|
||||
#define GLITCH_DETECTOR_LOCK_OFFSET _u(0x0000000c)
|
||||
#define GLITCH_DETECTOR_LOCK_BITS _u(0x000000ff)
|
||||
|
|
|
|||
|
|
@ -59,12 +59,12 @@
|
|||
#define PLL_USB_IRQ 43
|
||||
#define POWMAN_IRQ_POW 44
|
||||
#define POWMAN_IRQ_TIMER 45
|
||||
#define SPAREIRQ_IRQ_0 46
|
||||
#define SPAREIRQ_IRQ_1 47
|
||||
#define SPAREIRQ_IRQ_2 48
|
||||
#define SPAREIRQ_IRQ_3 49
|
||||
#define SPAREIRQ_IRQ_4 50
|
||||
#define SPAREIRQ_IRQ_5 51
|
||||
#define SPARE_IRQ_0 46
|
||||
#define SPARE_IRQ_1 47
|
||||
#define SPARE_IRQ_2 48
|
||||
#define SPARE_IRQ_3 49
|
||||
#define SPARE_IRQ_4 50
|
||||
#define SPARE_IRQ_5 51
|
||||
#else
|
||||
/**
|
||||
* \brief Interrupt numbers on RP2350 (used as typedef \ref irq_num_t)
|
||||
|
|
@ -79,8 +79,8 @@ typedef enum irq_num_rp2350 {
|
|||
TIMER1_IRQ_1 = 5, ///< Select TIMER1's IRQ 1 output
|
||||
TIMER1_IRQ_2 = 6, ///< Select TIMER1's IRQ 2 output
|
||||
TIMER1_IRQ_3 = 7, ///< Select TIMER1's IRQ 3 output
|
||||
PWM_IRQ_WRAP_0 = 8, ///< Select PWM's IRQ_WRAP 0 output
|
||||
PWM_IRQ_WRAP_1 = 9, ///< Select PWM's IRQ_WRAP 1 output
|
||||
PWM_IRQ_WRAP_0 = 8, ///< Select PWM's WRAP_0 IRQ output
|
||||
PWM_IRQ_WRAP_1 = 9, ///< Select PWM's WRAP_1 IRQ output
|
||||
DMA_IRQ_0 = 10, ///< Select DMA's IRQ 0 output
|
||||
DMA_IRQ_1 = 11, ///< Select DMA's IRQ 1 output
|
||||
DMA_IRQ_2 = 12, ///< Select DMA's IRQ 2 output
|
||||
|
|
@ -96,27 +96,27 @@ typedef enum irq_num_rp2350 {
|
|||
IO_IRQ_BANK0_NS = 22, ///< Select IO_BANK0_NS's IRQ output
|
||||
IO_IRQ_QSPI = 23, ///< Select IO_QSPI's IRQ output
|
||||
IO_IRQ_QSPI_NS = 24, ///< Select IO_QSPI_NS's IRQ output
|
||||
SIO_IRQ_FIFO = 25, ///< Select SIO's IRQ_FIFO output
|
||||
SIO_IRQ_BELL = 26, ///< Select SIO's IRQ_BELL output
|
||||
SIO_IRQ_FIFO_NS = 27, ///< Select SIO_NS's IRQ_FIFO output
|
||||
SIO_IRQ_BELL_NS = 28, ///< Select SIO_NS's IRQ_BELL output
|
||||
SIO_IRQ_MTIMECMP = 29, ///< Select SIO_IRQ_MTIMECMP's IRQ output
|
||||
SIO_IRQ_FIFO = 25, ///< Select SIO's FIFO IRQ output
|
||||
SIO_IRQ_BELL = 26, ///< Select SIO's BELL IRQ output
|
||||
SIO_IRQ_FIFO_NS = 27, ///< Select SIO_NS's FIFO IRQ output
|
||||
SIO_IRQ_BELL_NS = 28, ///< Select SIO_NS's BELL IRQ output
|
||||
SIO_IRQ_MTIMECMP = 29, ///< Select SIO's MTIMECMP IRQ output
|
||||
CLOCKS_IRQ = 30, ///< Select CLOCKS's IRQ output
|
||||
SPI0_IRQ = 31, ///< Select SPI0's IRQ output
|
||||
SPI1_IRQ = 32, ///< Select SPI1's IRQ output
|
||||
UART0_IRQ = 33, ///< Select UART0's IRQ output
|
||||
UART1_IRQ = 34, ///< Select UART1's IRQ output
|
||||
ADC_IRQ_FIFO = 35, ///< Select ADC's IRQ_FIFO output
|
||||
ADC_IRQ_FIFO = 35, ///< Select ADC's FIFO IRQ output
|
||||
I2C0_IRQ = 36, ///< Select I2C0's IRQ output
|
||||
I2C1_IRQ = 37, ///< Select I2C1's IRQ output
|
||||
OTP_IRQ = 38, ///< Select OTP's IRQ output
|
||||
TRNG_IRQ = 39, ///< Select TRNG's IRQ output
|
||||
PROC0_IRQ_CTI = 40, ///< Select PROC0's IRQ_CTI output
|
||||
PROC1_IRQ_CTI = 41, ///< Select PROC1's IRQ_CTI output
|
||||
PROC0_IRQ_CTI = 40, ///< Select PROC0's CTI IRQ output
|
||||
PROC1_IRQ_CTI = 41, ///< Select PROC1's CTI IRQ output
|
||||
PLL_SYS_IRQ = 42, ///< Select PLL_SYS's IRQ output
|
||||
PLL_USB_IRQ = 43, ///< Select PLL_USB's IRQ output
|
||||
POWMAN_IRQ_POW = 44, ///< Select POWMAN's IRQ_POW output
|
||||
POWMAN_IRQ_TIMER = 45, ///< Select POWMAN's IRQ_TIMER output
|
||||
POWMAN_IRQ_POW = 44, ///< Select POWMAN's POW IRQ output
|
||||
POWMAN_IRQ_TIMER = 45, ///< Select POWMAN's TIMER IRQ output
|
||||
SPARE_IRQ_0 = 46, ///< Select SPARE IRQ 0
|
||||
SPARE_IRQ_1 = 47, ///< Select SPARE IRQ 1
|
||||
SPARE_IRQ_2 = 48, ///< Select SPARE IRQ 2
|
||||
|
|
|
|||
|
|
@ -461,8 +461,7 @@
|
|||
// =============================================================================
|
||||
// Register : PIO_DBG_PADOUT
|
||||
// Description : Read to sample the pad output values PIO is currently driving
|
||||
// to the GPIOs. On RP2040 there are 30 GPIOs, so the two most
|
||||
// significant bits are hardwired to 0.
|
||||
// to the GPIOs.
|
||||
#define PIO_DBG_PADOUT_OFFSET _u(0x0000003c)
|
||||
#define PIO_DBG_PADOUT_BITS _u(0xffffffff)
|
||||
#define PIO_DBG_PADOUT_RESET _u(0x00000000)
|
||||
|
|
@ -472,8 +471,7 @@
|
|||
// =============================================================================
|
||||
// Register : PIO_DBG_PADOE
|
||||
// Description : Read to sample the pad output enables (direction) PIO is
|
||||
// currently driving to the GPIOs. On RP2040 there are 30 GPIOs,
|
||||
// so the two most significant bits are hardwired to 0.
|
||||
// currently driving to the GPIOs.
|
||||
#define PIO_DBG_PADOE_OFFSET _u(0x00000040)
|
||||
#define PIO_DBG_PADOE_BITS _u(0xffffffff)
|
||||
#define PIO_DBG_PADOE_RESET _u(0x00000000)
|
||||
|
|
|
|||
|
|
@ -944,7 +944,7 @@
|
|||
// requests and ignore the power down requests. To do nothing
|
||||
// would risk entering an unrecoverable lock-up state. Invalid
|
||||
// requests are: any combination of power up and power down
|
||||
// requests any request that results in swcore boing powered and
|
||||
// requests any request that results in swcore being powered and
|
||||
// xip unpowered If the request is to power down the switched-core
|
||||
// domain then POWMAN_STATE_WAITING stays active until the
|
||||
// processors halt. During this time the POWMAN_STATE_REQ field
|
||||
|
|
@ -957,6 +957,7 @@
|
|||
#define POWMAN_STATE_RESET _u(0x0000000f)
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : POWMAN_STATE_CHANGING
|
||||
// Description : Indicates a power state change is in progress
|
||||
#define POWMAN_STATE_CHANGING_RESET _u(0x0)
|
||||
#define POWMAN_STATE_CHANGING_BITS _u(0x00002000)
|
||||
#define POWMAN_STATE_CHANGING_MSB _u(13)
|
||||
|
|
@ -964,6 +965,9 @@
|
|||
#define POWMAN_STATE_CHANGING_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : POWMAN_STATE_WAITING
|
||||
// Description : Indicates the power manager has received a state change request
|
||||
// and is waiting for other actions to complete before executing
|
||||
// it
|
||||
#define POWMAN_STATE_WAITING_RESET _u(0x0)
|
||||
#define POWMAN_STATE_WAITING_BITS _u(0x00001000)
|
||||
#define POWMAN_STATE_WAITING_MSB _u(12)
|
||||
|
|
@ -971,8 +975,8 @@
|
|||
#define POWMAN_STATE_WAITING_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : POWMAN_STATE_BAD_HW_REQ
|
||||
// Description : Bad hardware initiated state request. Went back to state 0
|
||||
// (i.e. everything powered up)
|
||||
// Description : Invalid hardware initiated state request, power up requests
|
||||
// actioned, power down requests ignored
|
||||
#define POWMAN_STATE_BAD_HW_REQ_RESET _u(0x0)
|
||||
#define POWMAN_STATE_BAD_HW_REQ_BITS _u(0x00000800)
|
||||
#define POWMAN_STATE_BAD_HW_REQ_MSB _u(11)
|
||||
|
|
@ -980,7 +984,7 @@
|
|||
#define POWMAN_STATE_BAD_HW_REQ_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : POWMAN_STATE_BAD_SW_REQ
|
||||
// Description : Bad software initiated state request. No action taken.
|
||||
// Description : Invalid software initiated state request ignored
|
||||
#define POWMAN_STATE_BAD_SW_REQ_RESET _u(0x0)
|
||||
#define POWMAN_STATE_BAD_SW_REQ_BITS _u(0x00000400)
|
||||
#define POWMAN_STATE_BAD_SW_REQ_MSB _u(10)
|
||||
|
|
@ -988,9 +992,8 @@
|
|||
#define POWMAN_STATE_BAD_SW_REQ_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : POWMAN_STATE_PWRUP_WHILE_WAITING
|
||||
// Description : Request ignored because of a pending pwrup request. See
|
||||
// current_pwrup_req. Note this blocks powering up AND powering
|
||||
// down.
|
||||
// Description : Indicates that a power state change request was ignored because
|
||||
// of a pending power state change request
|
||||
#define POWMAN_STATE_PWRUP_WHILE_WAITING_RESET _u(0x0)
|
||||
#define POWMAN_STATE_PWRUP_WHILE_WAITING_BITS _u(0x00000200)
|
||||
#define POWMAN_STATE_PWRUP_WHILE_WAITING_MSB _u(9)
|
||||
|
|
@ -998,6 +1001,8 @@
|
|||
#define POWMAN_STATE_PWRUP_WHILE_WAITING_ACCESS "WC"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : POWMAN_STATE_REQ_IGNORED
|
||||
// Description : Indicates that a software state change request was ignored
|
||||
// because it clashed with an ongoing hardware or debugger request
|
||||
#define POWMAN_STATE_REQ_IGNORED_RESET _u(0x0)
|
||||
#define POWMAN_STATE_REQ_IGNORED_BITS _u(0x00000100)
|
||||
#define POWMAN_STATE_REQ_IGNORED_MSB _u(8)
|
||||
|
|
@ -1005,6 +1010,8 @@
|
|||
#define POWMAN_STATE_REQ_IGNORED_ACCESS "WC"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : POWMAN_STATE_REQ
|
||||
// Description : This is written by software or hardware to request a new power
|
||||
// state
|
||||
#define POWMAN_STATE_REQ_RESET _u(0x0)
|
||||
#define POWMAN_STATE_REQ_BITS _u(0x000000f0)
|
||||
#define POWMAN_STATE_REQ_MSB _u(7)
|
||||
|
|
@ -1012,6 +1019,7 @@
|
|||
#define POWMAN_STATE_REQ_ACCESS "RW"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : POWMAN_STATE_CURRENT
|
||||
// Description : Indicates the current power state
|
||||
#define POWMAN_STATE_CURRENT_RESET _u(0xf)
|
||||
#define POWMAN_STATE_CURRENT_BITS _u(0x0000000f)
|
||||
#define POWMAN_STATE_CURRENT_MSB _u(3)
|
||||
|
|
@ -1019,8 +1027,7 @@
|
|||
#define POWMAN_STATE_CURRENT_ACCESS "RO"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_POW_FASTDIV
|
||||
// Description : None
|
||||
// divides the POWMAN clock to provide a tick for the delay module
|
||||
// Description : divides the POWMAN clock to provide a tick for the delay module
|
||||
// and state machines
|
||||
// when clk_pow is running from the slow clock it is not divided
|
||||
// when clk_pow is running from the fast clock it is divided by
|
||||
|
|
@ -1187,6 +1194,10 @@
|
|||
#define POWMAN_EXT_TIME_REF_SOURCE_SEL_MSB _u(1)
|
||||
#define POWMAN_EXT_TIME_REF_SOURCE_SEL_LSB _u(0)
|
||||
#define POWMAN_EXT_TIME_REF_SOURCE_SEL_ACCESS "RW"
|
||||
#define POWMAN_EXT_TIME_REF_SOURCE_SEL_VALUE_GPIO12 _u(0x0)
|
||||
#define POWMAN_EXT_TIME_REF_SOURCE_SEL_VALUE_GPIO20 _u(0x1)
|
||||
#define POWMAN_EXT_TIME_REF_SOURCE_SEL_VALUE_GPIO14 _u(0x2)
|
||||
#define POWMAN_EXT_TIME_REF_SOURCE_SEL_VALUE_GPIO22 _u(0x3)
|
||||
// =============================================================================
|
||||
// Register : POWMAN_LPOSC_FREQ_KHZ_INT
|
||||
// Description : Informs the AON Timer of the integer component of the clock
|
||||
|
|
@ -1241,8 +1252,7 @@
|
|||
#define POWMAN_XOSC_FREQ_KHZ_FRAC_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_SET_TIME_63TO48
|
||||
// Description : None
|
||||
// For setting the time, do not use for reading the time, use
|
||||
// Description : For setting the time, do not use for reading the time, use
|
||||
// POWMAN_READ_TIME_UPPER and POWMAN_READ_TIME_LOWER. This field
|
||||
// must only be written when POWMAN_TIMER_RUN=0
|
||||
#define POWMAN_SET_TIME_63TO48_OFFSET _u(0x00000060)
|
||||
|
|
@ -1253,8 +1263,7 @@
|
|||
#define POWMAN_SET_TIME_63TO48_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_SET_TIME_47TO32
|
||||
// Description : None
|
||||
// For setting the time, do not use for reading the time, use
|
||||
// Description : For setting the time, do not use for reading the time, use
|
||||
// POWMAN_READ_TIME_UPPER and POWMAN_READ_TIME_LOWER. This field
|
||||
// must only be written when POWMAN_TIMER_RUN=0
|
||||
#define POWMAN_SET_TIME_47TO32_OFFSET _u(0x00000064)
|
||||
|
|
@ -1265,8 +1274,7 @@
|
|||
#define POWMAN_SET_TIME_47TO32_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_SET_TIME_31TO16
|
||||
// Description : None
|
||||
// For setting the time, do not use for reading the time, use
|
||||
// Description : For setting the time, do not use for reading the time, use
|
||||
// POWMAN_READ_TIME_UPPER and POWMAN_READ_TIME_LOWER. This field
|
||||
// must only be written when POWMAN_TIMER_RUN=0
|
||||
#define POWMAN_SET_TIME_31TO16_OFFSET _u(0x00000068)
|
||||
|
|
@ -1277,8 +1285,7 @@
|
|||
#define POWMAN_SET_TIME_31TO16_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_SET_TIME_15TO0
|
||||
// Description : None
|
||||
// For setting the time, do not use for reading the time, use
|
||||
// Description : For setting the time, do not use for reading the time, use
|
||||
// POWMAN_READ_TIME_UPPER and POWMAN_READ_TIME_LOWER. This field
|
||||
// must only be written when POWMAN_TIMER_RUN=0
|
||||
#define POWMAN_SET_TIME_15TO0_OFFSET _u(0x0000006c)
|
||||
|
|
@ -1289,8 +1296,7 @@
|
|||
#define POWMAN_SET_TIME_15TO0_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_READ_TIME_UPPER
|
||||
// Description : None
|
||||
// For reading bits 63:32 of the timer. When reading all 64 bits
|
||||
// Description : For reading bits 63:32 of the timer. When reading all 64 bits
|
||||
// it is possible for the LOWER count to rollover during the read.
|
||||
// It is recommended to read UPPER, then LOWER, then re-read UPPER
|
||||
// and, if it has changed, re-read LOWER.
|
||||
|
|
@ -1302,8 +1308,7 @@
|
|||
#define POWMAN_READ_TIME_UPPER_ACCESS "RO"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_READ_TIME_LOWER
|
||||
// Description : None
|
||||
// For reading bits 31:0 of the timer.
|
||||
// Description : For reading bits 31:0 of the timer.
|
||||
#define POWMAN_READ_TIME_LOWER_OFFSET _u(0x00000074)
|
||||
#define POWMAN_READ_TIME_LOWER_BITS _u(0xffffffff)
|
||||
#define POWMAN_READ_TIME_LOWER_RESET _u(0x00000000)
|
||||
|
|
@ -1312,8 +1317,7 @@
|
|||
#define POWMAN_READ_TIME_LOWER_ACCESS "RO"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_ALARM_TIME_63TO48
|
||||
// Description : None
|
||||
// This field must only be written when POWMAN_ALARM_ENAB=0
|
||||
// Description : This field must only be written when POWMAN_ALARM_ENAB=0
|
||||
#define POWMAN_ALARM_TIME_63TO48_OFFSET _u(0x00000078)
|
||||
#define POWMAN_ALARM_TIME_63TO48_BITS _u(0x0000ffff)
|
||||
#define POWMAN_ALARM_TIME_63TO48_RESET _u(0x00000000)
|
||||
|
|
@ -1322,8 +1326,7 @@
|
|||
#define POWMAN_ALARM_TIME_63TO48_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_ALARM_TIME_47TO32
|
||||
// Description : None
|
||||
// This field must only be written when POWMAN_ALARM_ENAB=0
|
||||
// Description : This field must only be written when POWMAN_ALARM_ENAB=0
|
||||
#define POWMAN_ALARM_TIME_47TO32_OFFSET _u(0x0000007c)
|
||||
#define POWMAN_ALARM_TIME_47TO32_BITS _u(0x0000ffff)
|
||||
#define POWMAN_ALARM_TIME_47TO32_RESET _u(0x00000000)
|
||||
|
|
@ -1332,8 +1335,7 @@
|
|||
#define POWMAN_ALARM_TIME_47TO32_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_ALARM_TIME_31TO16
|
||||
// Description : None
|
||||
// This field must only be written when POWMAN_ALARM_ENAB=0
|
||||
// Description : This field must only be written when POWMAN_ALARM_ENAB=0
|
||||
#define POWMAN_ALARM_TIME_31TO16_OFFSET _u(0x00000080)
|
||||
#define POWMAN_ALARM_TIME_31TO16_BITS _u(0x0000ffff)
|
||||
#define POWMAN_ALARM_TIME_31TO16_RESET _u(0x00000000)
|
||||
|
|
@ -1342,8 +1344,7 @@
|
|||
#define POWMAN_ALARM_TIME_31TO16_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : POWMAN_ALARM_TIME_15TO0
|
||||
// Description : None
|
||||
// This field must only be written when POWMAN_ALARM_ENAB=0
|
||||
// Description : This field must only be written when POWMAN_ALARM_ENAB=0
|
||||
#define POWMAN_ALARM_TIME_15TO0_OFFSET _u(0x00000084)
|
||||
#define POWMAN_ALARM_TIME_15TO0_BITS _u(0x0000ffff)
|
||||
#define POWMAN_ALARM_TIME_15TO0_RESET _u(0x00000000)
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@
|
|||
// Field : ROSC_CTRL_FREQ_RANGE
|
||||
// Description : Controls the number of delay stages in the ROSC ring
|
||||
// LOW uses stages 0 to 7
|
||||
// MEDIUM uses stages 2 to 7
|
||||
// HIGH uses stages 4 to 7
|
||||
// TOOHIGH uses stages 6 to 7 and should not be used because its
|
||||
// MEDIUM uses stages 0 to 5
|
||||
// HIGH uses stages 0 to 3
|
||||
// TOOHIGH uses stages 0 to 1 and should not be used because its
|
||||
// frequency exceeds design specifications
|
||||
// The clock output will not glitch when changing the range up one
|
||||
// step at a time
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
// DS1_RANDOM=1
|
||||
#define ROSC_FREQA_OFFSET _u(0x00000004)
|
||||
#define ROSC_FREQA_BITS _u(0xffff77ff)
|
||||
#define ROSC_FREQA_RESET _u(0x00000000)
|
||||
#define ROSC_FREQA_RESET _u(0x00000088)
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : ROSC_FREQA_PASSWD
|
||||
// Description : Set to 0x9696 to apply the settings
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
// Field : ROSC_FREQA_DS1_RANDOM
|
||||
// Description : Randomises the stage 1 drive strength
|
||||
#define ROSC_FREQA_DS1_RANDOM_RESET _u(0x0)
|
||||
#define ROSC_FREQA_DS1_RANDOM_RESET _u(0x1)
|
||||
#define ROSC_FREQA_DS1_RANDOM_BITS _u(0x00000080)
|
||||
#define ROSC_FREQA_DS1_RANDOM_MSB _u(7)
|
||||
#define ROSC_FREQA_DS1_RANDOM_LSB _u(7)
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
// Field : ROSC_FREQA_DS0_RANDOM
|
||||
// Description : Randomises the stage 0 drive strength
|
||||
#define ROSC_FREQA_DS0_RANDOM_RESET _u(0x0)
|
||||
#define ROSC_FREQA_DS0_RANDOM_RESET _u(0x1)
|
||||
#define ROSC_FREQA_DS0_RANDOM_BITS _u(0x00000008)
|
||||
#define ROSC_FREQA_DS0_RANDOM_MSB _u(3)
|
||||
#define ROSC_FREQA_DS0_RANDOM_LSB _u(3)
|
||||
|
|
@ -206,7 +206,7 @@
|
|||
// On power-up this field is initialised to WAKE
|
||||
// An invalid write will also select WAKE
|
||||
// Warning: setup the irq before selecting dormant mode
|
||||
// 0x636f6d61 -> dormant
|
||||
// 0x636f6d61 -> DORMANT
|
||||
// 0x77616b65 -> WAKE
|
||||
#define ROSC_DORMANT_OFFSET _u(0x00000010)
|
||||
#define ROSC_DORMANT_BITS _u(0xffffffff)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
// In addition the following custom extensions are configured:
|
||||
// Xh3bm, Xh3power, Xh3irq, Xh3pmpm
|
||||
#define RVCSR_MISA_OFFSET _u(0x00000301)
|
||||
#define RVCSR_MISA_BITS _u(0xc0901107)
|
||||
#define RVCSR_MISA_BITS _u(0xc0b511bf)
|
||||
#define RVCSR_MISA_RESET _u(0x40901105)
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_MXL
|
||||
|
|
@ -106,6 +106,14 @@
|
|||
#define RVCSR_MISA_X_LSB _u(23)
|
||||
#define RVCSR_MISA_X_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_V
|
||||
// Description : Vector extension (not implemented).
|
||||
#define RVCSR_MISA_V_RESET _u(0x0)
|
||||
#define RVCSR_MISA_V_BITS _u(0x00200000)
|
||||
#define RVCSR_MISA_V_MSB _u(21)
|
||||
#define RVCSR_MISA_V_LSB _u(21)
|
||||
#define RVCSR_MISA_V_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_U
|
||||
// Description : Value of 1 indicates U-mode is implemented.
|
||||
#define RVCSR_MISA_U_RESET _u(0x1)
|
||||
|
|
@ -114,6 +122,22 @@
|
|||
#define RVCSR_MISA_U_LSB _u(20)
|
||||
#define RVCSR_MISA_U_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_S
|
||||
// Description : Supervisor extension (not implemented).
|
||||
#define RVCSR_MISA_S_RESET _u(0x0)
|
||||
#define RVCSR_MISA_S_BITS _u(0x00040000)
|
||||
#define RVCSR_MISA_S_MSB _u(18)
|
||||
#define RVCSR_MISA_S_LSB _u(18)
|
||||
#define RVCSR_MISA_S_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_Q
|
||||
// Description : Quad-precision floating point extension (not implemented).
|
||||
#define RVCSR_MISA_Q_RESET _u(0x0)
|
||||
#define RVCSR_MISA_Q_BITS _u(0x00010000)
|
||||
#define RVCSR_MISA_Q_MSB _u(16)
|
||||
#define RVCSR_MISA_Q_LSB _u(16)
|
||||
#define RVCSR_MISA_Q_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_M
|
||||
// Description : Value of 1 indicates the M extension (integer multiply/divide)
|
||||
// is implemented.
|
||||
|
|
@ -132,6 +156,39 @@
|
|||
#define RVCSR_MISA_I_LSB _u(8)
|
||||
#define RVCSR_MISA_I_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_H
|
||||
// Description : Hypervisor extension (not implemented, I agree it would be
|
||||
// pretty cool on a microcontroller through).
|
||||
#define RVCSR_MISA_H_RESET _u(0x0)
|
||||
#define RVCSR_MISA_H_BITS _u(0x00000080)
|
||||
#define RVCSR_MISA_H_MSB _u(7)
|
||||
#define RVCSR_MISA_H_LSB _u(7)
|
||||
#define RVCSR_MISA_H_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_F
|
||||
// Description : Single-precision floating point extension (not implemented).
|
||||
#define RVCSR_MISA_F_RESET _u(0x0)
|
||||
#define RVCSR_MISA_F_BITS _u(0x00000020)
|
||||
#define RVCSR_MISA_F_MSB _u(5)
|
||||
#define RVCSR_MISA_F_LSB _u(5)
|
||||
#define RVCSR_MISA_F_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_E
|
||||
// Description : RV32E/64E base ISA (not implemented).
|
||||
#define RVCSR_MISA_E_RESET _u(0x0)
|
||||
#define RVCSR_MISA_E_BITS _u(0x00000010)
|
||||
#define RVCSR_MISA_E_MSB _u(4)
|
||||
#define RVCSR_MISA_E_LSB _u(4)
|
||||
#define RVCSR_MISA_E_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_D
|
||||
// Description : Double-precision floating point extension (not implemented).
|
||||
#define RVCSR_MISA_D_RESET _u(0x0)
|
||||
#define RVCSR_MISA_D_BITS _u(0x00000008)
|
||||
#define RVCSR_MISA_D_MSB _u(3)
|
||||
#define RVCSR_MISA_D_LSB _u(3)
|
||||
#define RVCSR_MISA_D_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MISA_C
|
||||
// Description : Value of 1 indicates the C extension (compressed instructions)
|
||||
// is implemented.
|
||||
|
|
@ -207,7 +264,7 @@
|
|||
// Description : Timer interrupt enable. The processor transfers to the timer
|
||||
// interrupt vector when `mie.mtie`, `mip.mtip` and `mstatus.mie`
|
||||
// are all 1, unless a software or external interrupt request is
|
||||
// also valid at this time.
|
||||
// also both pending and enabled at this time.
|
||||
#define RVCSR_MIE_MTIE_RESET _u(0x0)
|
||||
#define RVCSR_MIE_MTIE_BITS _u(0x00000080)
|
||||
#define RVCSR_MIE_MTIE_MSB _u(7)
|
||||
|
|
@ -216,9 +273,9 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MIE_MSIE
|
||||
// Description : Software interrupt enable. The processor transfers to the
|
||||
// software interrupt vector `mie.msie`, `mip.msip` and
|
||||
// software interrupt vector when `mie.msie`, `mip.msip` and
|
||||
// `mstatus.mie` are all 1, unless an external interrupt request
|
||||
// is also valid at this time.
|
||||
// is also both pending and enabled at this time.
|
||||
#define RVCSR_MIE_MSIE_RESET _u(0x0)
|
||||
#define RVCSR_MIE_MSIE_BITS _u(0x00000008)
|
||||
#define RVCSR_MIE_MSIE_MSB _u(3)
|
||||
|
|
@ -336,7 +393,7 @@
|
|||
#define RVCSR_MENVCFGH_RESET _u(0x00000000)
|
||||
#define RVCSR_MENVCFGH_MSB _u(31)
|
||||
#define RVCSR_MENVCFGH_LSB _u(0)
|
||||
#define RVCSR_MENVCFGH_ACCESS "RW"
|
||||
#define RVCSR_MENVCFGH_ACCESS "-"
|
||||
// =============================================================================
|
||||
// Register : RVCSR_MCOUNTINHIBIT
|
||||
// Description : Count inhibit register for `mcycle`/`minstret`
|
||||
|
|
@ -732,7 +789,7 @@
|
|||
// Description : Timer interrupt pending. The processor transfers to the timer
|
||||
// interrupt vector when `mie.mtie`, `mip.mtip` and `mstatus.mie`
|
||||
// are all 1, unless a software or external interrupt request is
|
||||
// also valid at this time.
|
||||
// also both pending and enabled at this time.
|
||||
#define RVCSR_MIP_MTIP_RESET _u(0x0)
|
||||
#define RVCSR_MIP_MTIP_BITS _u(0x00000080)
|
||||
#define RVCSR_MIP_MTIP_MSB _u(7)
|
||||
|
|
@ -741,9 +798,9 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MIP_MSIP
|
||||
// Description : Software interrupt pending. The processor transfers to the
|
||||
// software interrupt vector `mie.msie`, `mip.msip` and
|
||||
// software interrupt vector when `mie.msie`, `mip.msip` and
|
||||
// `mstatus.mie` are all 1, unless an external interrupt request
|
||||
// is also valid at this time.
|
||||
// is also both pending and enabled at this time.
|
||||
#define RVCSR_MIP_MSIP_RESET _u(0x0)
|
||||
#define RVCSR_MIP_MSIP_BITS _u(0x00000008)
|
||||
#define RVCSR_MIP_MSIP_MSB _u(3)
|
||||
|
|
@ -3099,14 +3156,18 @@
|
|||
#define RVCSR_MVENDORID_RESET _u(0x00000000)
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MVENDORID_BANK
|
||||
#define RVCSR_MVENDORID_BANK_RESET "-"
|
||||
// Description : Value of 9 indicates 9 continuation codes, which is JEP106 bank
|
||||
// 10.
|
||||
#define RVCSR_MVENDORID_BANK_RESET _u(0x0000009)
|
||||
#define RVCSR_MVENDORID_BANK_BITS _u(0xffffff80)
|
||||
#define RVCSR_MVENDORID_BANK_MSB _u(31)
|
||||
#define RVCSR_MVENDORID_BANK_LSB _u(7)
|
||||
#define RVCSR_MVENDORID_BANK_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : RVCSR_MVENDORID_OFFSET
|
||||
#define RVCSR_MVENDORID_OFFSET_RESET "-"
|
||||
// Description : ID 0x13 in bank 10 is the JEP106 ID for Raspberry Pi Ltd, the
|
||||
// vendor of RP2350.
|
||||
#define RVCSR_MVENDORID_OFFSET_RESET _u(0x13)
|
||||
#define RVCSR_MVENDORID_OFFSET_BITS _u(0x0000007f)
|
||||
#define RVCSR_MVENDORID_OFFSET_MSB _u(6)
|
||||
#define RVCSR_MVENDORID_OFFSET_LSB _u(0)
|
||||
|
|
@ -3122,10 +3183,11 @@
|
|||
#define RVCSR_MARCHID_ACCESS "RO"
|
||||
// =============================================================================
|
||||
// Register : RVCSR_MIMPID
|
||||
// Description : Implementation ID
|
||||
// Description : Implementation ID. On RP2350 this reads as 0x86fc4e3f, which is
|
||||
// release v1.0-rc1 of Hazard3.
|
||||
#define RVCSR_MIMPID_OFFSET _u(0x00000f13)
|
||||
#define RVCSR_MIMPID_BITS _u(0xffffffff)
|
||||
#define RVCSR_MIMPID_RESET "-"
|
||||
#define RVCSR_MIMPID_RESET _u(0x86fc4e3f)
|
||||
#define RVCSR_MIMPID_MSB _u(31)
|
||||
#define RVCSR_MIMPID_LSB _u(0)
|
||||
#define RVCSR_MIMPID_ACCESS "RO"
|
||||
|
|
|
|||
|
|
@ -255,7 +255,10 @@
|
|||
// =============================================================================
|
||||
// Register : SYSCFG_AUXCTRL
|
||||
// Description : Auxiliary system control register
|
||||
// * Bits 7:2: Reserved
|
||||
// * Bits 7:3: Reserved
|
||||
//
|
||||
// * Bit 2: Set to mask OTP power analogue power supply detection
|
||||
// from resetting OTP controller and PSM
|
||||
//
|
||||
// * Bit 1: When clear, the LPOSC output is XORed into the TRNG
|
||||
// ROSC output as an additional, uncorrelated entropy source. When
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@
|
|||
#define TICKS_PROC0_CTRL_ENABLE_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_PROC0_CYCLES
|
||||
// Description : None
|
||||
// Total number of clk_tick cycles before the next tick.
|
||||
// Description : Total number of clk_tick cycles before the next tick.
|
||||
#define TICKS_PROC0_CYCLES_OFFSET _u(0x00000004)
|
||||
#define TICKS_PROC0_CYCLES_BITS _u(0x000001ff)
|
||||
#define TICKS_PROC0_CYCLES_RESET _u(0x00000000)
|
||||
|
|
@ -46,8 +45,7 @@
|
|||
#define TICKS_PROC0_CYCLES_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_PROC0_COUNT
|
||||
// Description : None
|
||||
// Count down timer: the remaining number clk_tick cycles before
|
||||
// Description : Count down timer: the remaining number clk_tick cycles before
|
||||
// the next tick is generated.
|
||||
#define TICKS_PROC0_COUNT_OFFSET _u(0x00000008)
|
||||
#define TICKS_PROC0_COUNT_BITS _u(0x000001ff)
|
||||
|
|
@ -79,8 +77,7 @@
|
|||
#define TICKS_PROC1_CTRL_ENABLE_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_PROC1_CYCLES
|
||||
// Description : None
|
||||
// Total number of clk_tick cycles before the next tick.
|
||||
// Description : Total number of clk_tick cycles before the next tick.
|
||||
#define TICKS_PROC1_CYCLES_OFFSET _u(0x00000010)
|
||||
#define TICKS_PROC1_CYCLES_BITS _u(0x000001ff)
|
||||
#define TICKS_PROC1_CYCLES_RESET _u(0x00000000)
|
||||
|
|
@ -89,8 +86,7 @@
|
|||
#define TICKS_PROC1_CYCLES_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_PROC1_COUNT
|
||||
// Description : None
|
||||
// Count down timer: the remaining number clk_tick cycles before
|
||||
// Description : Count down timer: the remaining number clk_tick cycles before
|
||||
// the next tick is generated.
|
||||
#define TICKS_PROC1_COUNT_OFFSET _u(0x00000014)
|
||||
#define TICKS_PROC1_COUNT_BITS _u(0x000001ff)
|
||||
|
|
@ -122,8 +118,7 @@
|
|||
#define TICKS_TIMER0_CTRL_ENABLE_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_TIMER0_CYCLES
|
||||
// Description : None
|
||||
// Total number of clk_tick cycles before the next tick.
|
||||
// Description : Total number of clk_tick cycles before the next tick.
|
||||
#define TICKS_TIMER0_CYCLES_OFFSET _u(0x0000001c)
|
||||
#define TICKS_TIMER0_CYCLES_BITS _u(0x000001ff)
|
||||
#define TICKS_TIMER0_CYCLES_RESET _u(0x00000000)
|
||||
|
|
@ -132,8 +127,7 @@
|
|||
#define TICKS_TIMER0_CYCLES_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_TIMER0_COUNT
|
||||
// Description : None
|
||||
// Count down timer: the remaining number clk_tick cycles before
|
||||
// Description : Count down timer: the remaining number clk_tick cycles before
|
||||
// the next tick is generated.
|
||||
#define TICKS_TIMER0_COUNT_OFFSET _u(0x00000020)
|
||||
#define TICKS_TIMER0_COUNT_BITS _u(0x000001ff)
|
||||
|
|
@ -165,8 +159,7 @@
|
|||
#define TICKS_TIMER1_CTRL_ENABLE_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_TIMER1_CYCLES
|
||||
// Description : None
|
||||
// Total number of clk_tick cycles before the next tick.
|
||||
// Description : Total number of clk_tick cycles before the next tick.
|
||||
#define TICKS_TIMER1_CYCLES_OFFSET _u(0x00000028)
|
||||
#define TICKS_TIMER1_CYCLES_BITS _u(0x000001ff)
|
||||
#define TICKS_TIMER1_CYCLES_RESET _u(0x00000000)
|
||||
|
|
@ -175,8 +168,7 @@
|
|||
#define TICKS_TIMER1_CYCLES_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_TIMER1_COUNT
|
||||
// Description : None
|
||||
// Count down timer: the remaining number clk_tick cycles before
|
||||
// Description : Count down timer: the remaining number clk_tick cycles before
|
||||
// the next tick is generated.
|
||||
#define TICKS_TIMER1_COUNT_OFFSET _u(0x0000002c)
|
||||
#define TICKS_TIMER1_COUNT_BITS _u(0x000001ff)
|
||||
|
|
@ -208,8 +200,7 @@
|
|||
#define TICKS_WATCHDOG_CTRL_ENABLE_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_WATCHDOG_CYCLES
|
||||
// Description : None
|
||||
// Total number of clk_tick cycles before the next tick.
|
||||
// Description : Total number of clk_tick cycles before the next tick.
|
||||
#define TICKS_WATCHDOG_CYCLES_OFFSET _u(0x00000034)
|
||||
#define TICKS_WATCHDOG_CYCLES_BITS _u(0x000001ff)
|
||||
#define TICKS_WATCHDOG_CYCLES_RESET _u(0x00000000)
|
||||
|
|
@ -218,8 +209,7 @@
|
|||
#define TICKS_WATCHDOG_CYCLES_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_WATCHDOG_COUNT
|
||||
// Description : None
|
||||
// Count down timer: the remaining number clk_tick cycles before
|
||||
// Description : Count down timer: the remaining number clk_tick cycles before
|
||||
// the next tick is generated.
|
||||
#define TICKS_WATCHDOG_COUNT_OFFSET _u(0x00000038)
|
||||
#define TICKS_WATCHDOG_COUNT_BITS _u(0x000001ff)
|
||||
|
|
@ -251,8 +241,7 @@
|
|||
#define TICKS_RISCV_CTRL_ENABLE_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_RISCV_CYCLES
|
||||
// Description : None
|
||||
// Total number of clk_tick cycles before the next tick.
|
||||
// Description : Total number of clk_tick cycles before the next tick.
|
||||
#define TICKS_RISCV_CYCLES_OFFSET _u(0x00000040)
|
||||
#define TICKS_RISCV_CYCLES_BITS _u(0x000001ff)
|
||||
#define TICKS_RISCV_CYCLES_RESET _u(0x00000000)
|
||||
|
|
@ -261,8 +250,7 @@
|
|||
#define TICKS_RISCV_CYCLES_ACCESS "RW"
|
||||
// =============================================================================
|
||||
// Register : TICKS_RISCV_COUNT
|
||||
// Description : None
|
||||
// Count down timer: the remaining number clk_tick cycles before
|
||||
// Description : Count down timer: the remaining number clk_tick cycles before
|
||||
// the next tick is generated.
|
||||
#define TICKS_RISCV_COUNT_OFFSET _u(0x00000044)
|
||||
#define TICKS_RISCV_COUNT_BITS _u(0x000001ff)
|
||||
|
|
|
|||
|
|
@ -1082,13 +1082,14 @@
|
|||
#define USB_SIE_STATUS_SPEED_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : USB_SIE_STATUS_SUSPENDED
|
||||
// Description : Bus in suspended state. Valid for device. Device will go into
|
||||
// suspend if neither Keep Alive / SOF frames are enabled.
|
||||
// Description : Bus in suspended state. Valid for device and host. Host and
|
||||
// device will go into suspend if neither Keep Alive / SOF frames
|
||||
// are enabled.
|
||||
#define USB_SIE_STATUS_SUSPENDED_RESET _u(0x0)
|
||||
#define USB_SIE_STATUS_SUSPENDED_BITS _u(0x00000010)
|
||||
#define USB_SIE_STATUS_SUSPENDED_MSB _u(4)
|
||||
#define USB_SIE_STATUS_SUSPENDED_LSB _u(4)
|
||||
#define USB_SIE_STATUS_SUSPENDED_ACCESS "WC"
|
||||
#define USB_SIE_STATUS_SUSPENDED_ACCESS "RO"
|
||||
// -----------------------------------------------------------------------------
|
||||
// Field : USB_SIE_STATUS_LINE_STATE
|
||||
// Description : USB bus line state
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
// BITMASK [BITRANGE] FIELDNAME (RESETVALUE) DESCRIPTION
|
||||
|
||||
/** \brief Bus fabric performance counters on RP2350 (used as typedef \ref bus_ctrl_perf_counter_t)
|
||||
* \ingroup hardware_busctrl
|
||||
*/
|
||||
typedef enum bus_ctrl_perf_counter_rp2350 {
|
||||
arbiter_rom_perf_event_access = 19,
|
||||
|
|
|
|||
|
|
@ -137,14 +137,14 @@ typedef struct {
|
|||
|
||||
_REG_(POWMAN_STATE_OFFSET) // POWMAN_STATE
|
||||
// This register controls the power state of the 4 power domains
|
||||
// 0x00002000 [13] CHANGING (0)
|
||||
// 0x00001000 [12] WAITING (0)
|
||||
// 0x00000800 [11] BAD_HW_REQ (0) Bad hardware initiated state request
|
||||
// 0x00000400 [10] BAD_SW_REQ (0) Bad software initiated state request
|
||||
// 0x00000200 [9] PWRUP_WHILE_WAITING (0) Request ignored because of a pending pwrup request
|
||||
// 0x00000100 [8] REQ_IGNORED (0)
|
||||
// 0x000000f0 [7:4] REQ (0x0)
|
||||
// 0x0000000f [3:0] CURRENT (0xf)
|
||||
// 0x00002000 [13] CHANGING (0) Indicates a power state change is in progress
|
||||
// 0x00001000 [12] WAITING (0) Indicates the power manager has received a state change...
|
||||
// 0x00000800 [11] BAD_HW_REQ (0) Invalid hardware initiated state request, power up...
|
||||
// 0x00000400 [10] BAD_SW_REQ (0) Invalid software initiated state request ignored
|
||||
// 0x00000200 [9] PWRUP_WHILE_WAITING (0) Indicates that a power state change request was ignored...
|
||||
// 0x00000100 [8] REQ_IGNORED (0) Indicates that a software state change request was...
|
||||
// 0x000000f0 [7:4] REQ (0x0) This is written by software or hardware to request a new...
|
||||
// 0x0000000f [3:0] CURRENT (0xf) Indicates the current power state
|
||||
io_rw_32 state;
|
||||
|
||||
_REG_(POWMAN_POW_FASTDIV_OFFSET) // POWMAN_POW_FASTDIV
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ typedef struct {
|
|||
// 0xffff0000 [31:16] PASSWD (0x0000) Set to 0x9696 to apply the settings +
|
||||
// 0x00007000 [14:12] DS3 (0x0) Stage 3 drive strength
|
||||
// 0x00000700 [10:8] DS2 (0x0) Stage 2 drive strength
|
||||
// 0x00000080 [7] DS1_RANDOM (0) Randomises the stage 1 drive strength
|
||||
// 0x00000080 [7] DS1_RANDOM (1) Randomises the stage 1 drive strength
|
||||
// 0x00000070 [6:4] DS1 (0x0) Stage 1 drive strength
|
||||
// 0x00000008 [3] DS0_RANDOM (0) Randomises the stage 0 drive strength
|
||||
// 0x00000008 [3] DS0_RANDOM (1) Randomises the stage 0 drive strength
|
||||
// 0x00000007 [2:0] DS0 (0x0) Stage 0 drive strength
|
||||
io_rw_32 freqa;
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ typedef struct {
|
|||
|
||||
_REG_(SYSCFG_AUXCTRL_OFFSET) // SYSCFG_AUXCTRL
|
||||
// Auxiliary system control register
|
||||
// 0x000000ff [7:0] AUXCTRL (0x00) * Bits 7:2: Reserved
|
||||
// 0x000000ff [7:0] AUXCTRL (0x00) * Bits 7:3: Reserved
|
||||
io_rw_32 auxctrl;
|
||||
} syscfg_hw_t;
|
||||
|
||||
|
|
|
|||
|
|
@ -483,11 +483,22 @@ def git_version():
|
|||
logging.debug("Got git version: %s" % (repr(ver),))
|
||||
return ver
|
||||
|
||||
# Obtain version info from "klippy/.version" file
|
||||
def file_version():
|
||||
if not os.path.exists('klippy/.version'):
|
||||
logging.debug("No 'klippy/.version' file/directory found")
|
||||
return ""
|
||||
ver = check_output("cat klippy/.version").strip()
|
||||
logging.debug("Got klippy version: %s" % (repr(ver),))
|
||||
return ver
|
||||
|
||||
def build_version(extra, cleanbuild):
|
||||
version = git_version()
|
||||
if not version:
|
||||
cleanbuild = False
|
||||
version = "?"
|
||||
version = file_version()
|
||||
if not version:
|
||||
version = "?"
|
||||
elif 'dirty' in version:
|
||||
cleanbuild = False
|
||||
if not cleanbuild:
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ echo -e "\n\n=============== Install python3 virtualenv\n\n"
|
|||
cd ${MAIN_DIR}
|
||||
virtualenv -p python3 ${BUILD_DIR}/python-env
|
||||
${BUILD_DIR}/python-env/bin/pip install -r ${MAIN_DIR}/scripts/klippy-requirements.txt
|
||||
${BUILD_DIR}/python-env/bin/pip install -r ${MAIN_DIR}/scripts/tests-requirements.txt
|
||||
|
||||
|
||||
######################################################################
|
||||
|
|
@ -71,3 +72,4 @@ echo -e "\n\n=============== Install python2 virtualenv\n\n"
|
|||
cd ${MAIN_DIR}
|
||||
virtualenv -p python2 ${BUILD_DIR}/python2-env
|
||||
${BUILD_DIR}/python2-env/bin/pip install -r ${MAIN_DIR}/scripts/klippy-requirements.txt
|
||||
${BUILD_DIR}/python2-env/bin/pip install -r ${MAIN_DIR}/scripts/tests-requirements.txt
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import util
|
|||
import reactor
|
||||
import serialhdl
|
||||
import clocksync
|
||||
import mcu
|
||||
|
||||
###########################################################
|
||||
#
|
||||
|
|
@ -143,11 +142,38 @@ class SPIFlashError(Exception):
|
|||
class MCUConfigError(SPIFlashError):
|
||||
pass
|
||||
|
||||
# Wrapper around query commands
|
||||
class CommandQueryWrapper:
|
||||
def __init__(self, serial, msgformat, respformat, oid=None):
|
||||
self._serial = serial
|
||||
self._cmd = serial.get_msgparser().lookup_command(msgformat)
|
||||
serial.get_msgparser().lookup_command(respformat)
|
||||
self._response = respformat.split()[0]
|
||||
self._oid = oid
|
||||
self._cmd_queue = serial.get_default_command_queue()
|
||||
def send(self, data=(), minclock=0, reqclock=0, retry=True):
|
||||
cmds = [self._cmd.encode(data)]
|
||||
xh = serialhdl.SerialRetryCommand(self._serial, self._response,
|
||||
self._oid)
|
||||
reqclock = max(minclock, reqclock)
|
||||
return xh.get_response(cmds, self._cmd_queue, minclock, reqclock, retry)
|
||||
|
||||
# Wrapper around command sending
|
||||
class CommandWrapper:
|
||||
def __init__(self, serial, msgformat):
|
||||
self._serial = serial
|
||||
msgparser = serial.get_msgparser()
|
||||
self._cmd = msgparser.lookup_command(msgformat)
|
||||
self._cmd_queue = serial.get_default_command_queue()
|
||||
def send(self, data=(), minclock=0, reqclock=0):
|
||||
cmd = self._cmd.encode(data)
|
||||
self._serial.raw_send(cmd, minclock, reqclock, self._cmd_queue)
|
||||
|
||||
class SPIDirect:
|
||||
def __init__(self, ser):
|
||||
self.oid = SPI_OID
|
||||
self._spi_send_cmd = mcu.CommandWrapper(ser, SPI_SEND_CMD)
|
||||
self._spi_transfer_cmd = mcu.CommandQueryWrapper(
|
||||
self._spi_send_cmd = CommandWrapper(ser, SPI_SEND_CMD)
|
||||
self._spi_transfer_cmd = CommandQueryWrapper(
|
||||
ser, SPI_XFER_CMD, SPI_XFER_RESPONSE, self.oid)
|
||||
|
||||
def spi_send(self, data):
|
||||
|
|
@ -159,18 +185,18 @@ class SPIDirect:
|
|||
class SDIODirect:
|
||||
def __init__(self, ser):
|
||||
self.oid = SDIO_OID
|
||||
self._sdio_send_cmd = mcu.CommandQueryWrapper(
|
||||
self._sdio_send_cmd = CommandQueryWrapper(
|
||||
ser, SDIO_SEND_CMD, SDIO_SEND_CMD_RESPONSE, self.oid)
|
||||
self._sdio_read_data = mcu.CommandQueryWrapper(
|
||||
self._sdio_read_data = CommandQueryWrapper(
|
||||
ser, SDIO_READ_DATA, SDIO_READ_DATA_RESPONSE, self.oid)
|
||||
self._sdio_write_data = mcu.CommandQueryWrapper(
|
||||
self._sdio_write_data = CommandQueryWrapper(
|
||||
ser, SDIO_WRITE_DATA, SDIO_WRITE_DATA_RESPONSE, self.oid)
|
||||
self._sdio_read_data_buffer = mcu.CommandQueryWrapper(
|
||||
self._sdio_read_data_buffer = CommandQueryWrapper(
|
||||
ser, SDIO_READ_DATA_BUFFER, SDIO_READ_DATA_BUFFER_RESPONSE,
|
||||
self.oid)
|
||||
self._sdio_write_data_buffer = mcu.CommandWrapper(ser,
|
||||
self._sdio_write_data_buffer = CommandWrapper(ser,
|
||||
SDIO_WRITE_DATA_BUFFER)
|
||||
self._sdio_set_speed = mcu.CommandWrapper(ser, SDIO_SET_SPEED)
|
||||
self._sdio_set_speed = CommandWrapper(ser, SDIO_SET_SPEED)
|
||||
|
||||
def sdio_send_cmd(self, cmd, argument, wait):
|
||||
return self._sdio_send_cmd.send([self.oid, cmd, argument, wait])
|
||||
|
|
@ -1254,7 +1280,7 @@ class MCUConnection:
|
|||
# Iterate through backwards compatible response strings
|
||||
for response in GET_CFG_RESPONSES:
|
||||
try:
|
||||
get_cfg_cmd = mcu.CommandQueryWrapper(
|
||||
get_cfg_cmd = CommandQueryWrapper(
|
||||
self._serial, GET_CFG_CMD, response)
|
||||
break
|
||||
except Exception as err:
|
||||
|
|
|
|||
7
scripts/tests-requirements.txt
Normal file
7
scripts/tests-requirements.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# This file describes the Python virtualenv package requirements for
|
||||
# the Klipper regression test cases. This is in addition to the
|
||||
# package requirements listed in the klippy-requirements.txt file.
|
||||
# Typically the packages listed here are installed via the command:
|
||||
# pip install -r tests-requirements.txt
|
||||
scipy==1.2.3 ; python_version < '3.0'
|
||||
scipy==1.15.3 ; python_version >= '3.0'
|
||||
|
|
@ -181,13 +181,13 @@ config NEED_SENSOR_BULK
|
|||
depends on WANT_ADXL345 || WANT_LIS2DW || WANT_MPU9250 || WANT_ICM20948 \
|
||||
|| WANT_HX71X || WANT_ADS1220 || WANT_LDC1612 || WANT_SENSOR_ANGLE
|
||||
default y
|
||||
config WANT_LOAD_CELL_PROBE
|
||||
config WANT_TRIGGER_ANALOG
|
||||
bool
|
||||
depends on WANT_HX71X || WANT_ADS1220
|
||||
depends on WANT_HX71X || WANT_ADS1220 || WANT_LDC1612
|
||||
default y
|
||||
config NEED_SOS_FILTER
|
||||
bool
|
||||
depends on WANT_LOAD_CELL_PROBE
|
||||
depends on WANT_TRIGGER_ANALOG
|
||||
default y
|
||||
menu "Optional features (to reduce code size)"
|
||||
depends on HAVE_LIMITED_CODE_SIZE
|
||||
|
|
|
|||
|
|
@ -29,4 +29,4 @@ src-$(CONFIG_WANT_LDC1612) += sensor_ldc1612.c
|
|||
src-$(CONFIG_WANT_SENSOR_ANGLE) += sensor_angle.c
|
||||
src-$(CONFIG_NEED_SENSOR_BULK) += sensor_bulk.c
|
||||
src-$(CONFIG_NEED_SOS_FILTER) += sos_filter.c
|
||||
src-$(CONFIG_WANT_LOAD_CELL_PROBE) += load_cell_probe.c
|
||||
src-$(CONFIG_WANT_TRIGGER_ANALOG) += trigger_analog.c
|
||||
|
|
|
|||
|
|
@ -25,27 +25,30 @@ static uint8_t __aligned(4) acmin[USB_CDC_EP_ACM_SIZE];
|
|||
static uint8_t __aligned(4) bulkout[USB_CDC_EP_BULK_OUT_SIZE];
|
||||
static uint8_t __aligned(4) bulkin[USB_CDC_EP_BULK_IN_SIZE];
|
||||
|
||||
// Convert 64, 32, 16, 8 sized buffer to 3, 2, 1, 0 for PCKSIZE.SIZE register
|
||||
#define BSIZE(bufname) (__builtin_ctz(sizeof(bufname)) - 3)
|
||||
|
||||
static UsbDeviceDescriptor usb_desc[] = {
|
||||
[0] = { {
|
||||
{
|
||||
.ADDR.reg = (uint32_t)ep0out,
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(ep0out) >> 4),
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(BSIZE(ep0out)),
|
||||
}, {
|
||||
.ADDR.reg = (uint32_t)ep0in,
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(ep0in) >> 4),
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(BSIZE(ep0in)),
|
||||
},
|
||||
} },
|
||||
[USB_CDC_EP_ACM] = { {
|
||||
{
|
||||
}, {
|
||||
.ADDR.reg = (uint32_t)acmin,
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(acmin) >> 4),
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(BSIZE(acmin)),
|
||||
},
|
||||
} },
|
||||
[USB_CDC_EP_BULK_OUT] = { {
|
||||
{
|
||||
.ADDR.reg = (uint32_t)bulkout,
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(bulkout) >> 4),
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(BSIZE(bulkout)),
|
||||
}, {
|
||||
},
|
||||
} },
|
||||
|
|
@ -53,7 +56,7 @@ static UsbDeviceDescriptor usb_desc[] = {
|
|||
{
|
||||
}, {
|
||||
.ADDR.reg = (uint32_t)bulkin,
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(bulkin) >> 4),
|
||||
.PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(BSIZE(bulkin)),
|
||||
},
|
||||
} },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ config AVR_SELECT
|
|||
select HAVE_GPIO_I2C
|
||||
select HAVE_GPIO_HARD_PWM
|
||||
select HAVE_STRICT_TIMING
|
||||
select HAVE_LIMITED_CODE_SIZE if MACH_atmega168 || MACH_atmega328 || MACH_atmega328p || MACH_atmega32u4
|
||||
select HAVE_LIMITED_CODE_SIZE if MACH_atmega168 || MACH_atmega328 || MACH_atmega328p || MACH_atmega32u4 || MACH_lgt8f328p
|
||||
|
||||
config BOARD_DIRECTORY
|
||||
string
|
||||
|
|
@ -39,6 +39,8 @@ choice
|
|||
bool "atmega328"
|
||||
config MACH_atmega168
|
||||
bool "atmega168"
|
||||
config MACH_lgt8f328p
|
||||
bool "lgt8f328p"
|
||||
endchoice
|
||||
|
||||
config MCU
|
||||
|
|
@ -53,6 +55,12 @@ config MCU
|
|||
default "atmega32u4" if MACH_atmega32u4
|
||||
default "atmega1280" if MACH_atmega1280
|
||||
default "atmega2560" if MACH_atmega2560
|
||||
default "lgt8f328p" if MACH_lgt8f328p
|
||||
|
||||
config AVR_BUILD_MCU
|
||||
string
|
||||
default MCU if !MACH_lgt8f328p
|
||||
default "atmega328p" if MACH_lgt8f328p
|
||||
|
||||
config AVRDUDE_PROTOCOL
|
||||
string
|
||||
|
|
@ -62,6 +70,9 @@ config AVRDUDE_PROTOCOL
|
|||
|
||||
choice
|
||||
prompt "Processor speed" if LOW_LEVEL_OPTIONS
|
||||
config AVR_FREQ_32000000
|
||||
bool "32Mhz"
|
||||
depends on MACH_lgt8f328p
|
||||
config AVR_FREQ_16000000
|
||||
bool "16Mhz"
|
||||
config AVR_FREQ_20000000
|
||||
|
|
@ -75,11 +86,12 @@ config CLOCK_FREQ
|
|||
int
|
||||
default 8000000 if AVR_FREQ_8000000
|
||||
default 20000000 if AVR_FREQ_20000000
|
||||
default 32000000 if AVR_FREQ_32000000
|
||||
default 16000000
|
||||
|
||||
config CLEAR_PRESCALER
|
||||
bool "Manually clear the CPU prescaler field at startup" if LOW_LEVEL_OPTIONS
|
||||
depends on MACH_at90usb1286 || MACH_at90usb646 || MACH_atmega32u4
|
||||
depends on MACH_at90usb1286 || MACH_at90usb646 || MACH_atmega32u4 || MACH_lgt8f328p
|
||||
default y
|
||||
help
|
||||
Some AVR chips ship with a "clock prescaler" that causes the
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ CROSS_PREFIX=avr-
|
|||
dirs-y += src/avr src/generic
|
||||
|
||||
CFLAGS-$(CONFIG_HAVE_LIMITED_CODE_SIZE) += -Os
|
||||
CFLAGS += $(CFLAGS-y) -mmcu=$(CONFIG_MCU)
|
||||
CFLAGS += $(CFLAGS-y) -mmcu=$(CONFIG_AVR_BUILD_MCU)
|
||||
|
||||
# Add avr source files
|
||||
src-y += avr/main.c avr/timer.c
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ static const uint8_t adc_pins[] PROGMEM = {
|
|||
GPIO('F', 4), GPIO('F', 5), GPIO('F', 6), GPIO('F', 7),
|
||||
GPIO('K', 0), GPIO('K', 1), GPIO('K', 2), GPIO('K', 3),
|
||||
GPIO('K', 4), GPIO('K', 5), GPIO('K', 6), GPIO('K', 7),
|
||||
#elif CONFIG_MACH_lgt8f328p
|
||||
GPIO('C', 0), GPIO('C', 1), GPIO('C', 2), GPIO('C', 3),
|
||||
GPIO('C', 4), GPIO('C', 5), GPIO('E', 1), GPIO('E', 3),
|
||||
GPIO('C', 7), GPIO('F', 0), GPIO('E', 6), GPIO('E', 7),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -41,7 +45,11 @@ DECL_ENUMERATION_RANGE("pin", "PE2", GPIO('E', 2), 2);
|
|||
enum { ADMUX_DEFAULT = 0x40 };
|
||||
enum { ADC_ENABLE = (1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADEN)|(1<<ADIF) };
|
||||
|
||||
#if CONFIG_MACH_lgt8f328p
|
||||
DECL_CONSTANT("ADC_MAX", 4095);
|
||||
#else
|
||||
DECL_CONSTANT("ADC_MAX", 1023);
|
||||
#endif
|
||||
|
||||
struct gpio_adc
|
||||
gpio_adc_setup(uint8_t pin)
|
||||
|
|
@ -59,10 +67,27 @@ gpio_adc_setup(uint8_t pin)
|
|||
ADCSRA = ADC_ENABLE;
|
||||
|
||||
// Disable digital input for this pin
|
||||
#ifdef DIDR2
|
||||
#if defined(DIDR2)
|
||||
if (chan >= 8)
|
||||
DIDR2 |= 1 << (chan & 0x07);
|
||||
else
|
||||
#elif CONFIG_MACH_lgt8f328p
|
||||
if (chan >= 8)
|
||||
switch (chan) {
|
||||
case 8:
|
||||
DIDR1 |= (1 << 2);
|
||||
break;
|
||||
case 9:
|
||||
DIDR1 |= (1 << 3);
|
||||
break;
|
||||
case 10:
|
||||
DIDR1 |= (1 << 6);
|
||||
break;
|
||||
case 11:
|
||||
DIDR1 |= (1 << 7);
|
||||
break;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
DIDR0 |= 1 << chan;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ DECL_ENUMERATION_RANGE("pin", "PC0", GPIO('C', 0), 8);
|
|||
DECL_ENUMERATION_RANGE("pin", "PD0", GPIO('D', 0), 8);
|
||||
#if CONFIG_MACH_atmega328p
|
||||
DECL_ENUMERATION_RANGE("pin", "PE0", GPIO('E', 0), 8);
|
||||
#elif CONFIG_MACH_lgt8f328p
|
||||
DECL_ENUMERATION_RANGE("pin", "PE0", GPIO('E', 0), 8);
|
||||
DECL_ENUMERATION_RANGE("pin", "PF0", GPIO('F', 0), 8);
|
||||
#endif
|
||||
#ifdef PINE
|
||||
DECL_ENUMERATION_RANGE("pin", "PE0", GPIO('E', 0), 8);
|
||||
|
|
@ -42,6 +45,9 @@ volatile uint8_t * const digital_regs[] PROGMEM = {
|
|||
&PINB, &PINC, &PIND,
|
||||
#if CONFIG_MACH_atmega328p
|
||||
&_SFR_IO8(0x0C), // PINE on atmega328pb
|
||||
#elif CONFIG_MACH_lgt8f328p
|
||||
&_SFR_IO8(0x0C), // lgt8f328p have PINE and PINF
|
||||
&_SFR_IO8(0x12)
|
||||
#endif
|
||||
#ifdef PINE
|
||||
&PINE, &PINF,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ struct gpio_pwm_info {
|
|||
enum { GP_8BIT=1, GP_AFMT=2 };
|
||||
|
||||
static const struct gpio_pwm_info pwm_regs[] PROGMEM = {
|
||||
#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
|
||||
#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 \
|
||||
|| CONFIG_MACH_atmega328p || CONFIG_MACH_lgt8f328p
|
||||
{ GPIO('D', 6), &OCR0A, &TCCR0A, &TCCR0B, 1<<COM0A1, GP_8BIT },
|
||||
{ GPIO('D', 5), &OCR0B, &TCCR0A, &TCCR0B, 1<<COM0B1, GP_8BIT },
|
||||
{ GPIO('B', 1), &OCR1A, &TCCR1A, &TCCR1B, 1<<COM1A1, 0 },
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@
|
|||
|
||||
DECL_ENUMERATION("i2c_bus", "twi", 0);
|
||||
|
||||
#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
|
||||
#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 \
|
||||
|| CONFIG_MACH_atmega328p || CONFIG_MACH_lgt8f328p
|
||||
static const uint8_t SCL = GPIO('C', 5), SDA = GPIO('C', 4);
|
||||
DECL_CONSTANT_STR("BUS_PINS_twi", "PC5,PC4");
|
||||
#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
DECL_ENUMERATION("spi_bus", "spi", 0);
|
||||
|
||||
#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
|
||||
#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 \
|
||||
|| CONFIG_MACH_atmega328p || CONFIG_MACH_lgt8f328p
|
||||
static const uint8_t MISO = GPIO('B', 4), MOSI = GPIO('B', 3);
|
||||
static const uint8_t SCK = GPIO('B', 5), SS = GPIO('B', 2);
|
||||
DECL_CONSTANT_STR("BUS_PINS_spi", "PB4,PB3,PB5");
|
||||
|
|
|
|||
|
|
@ -154,7 +154,6 @@ static struct timer wrap_timer = {
|
|||
.waketime = 0x8000,
|
||||
};
|
||||
|
||||
#define TIMER_IDLE_REPEAT_TICKS 8000
|
||||
#define TIMER_REPEAT_TICKS 3000
|
||||
|
||||
#define TIMER_MIN_ENTRY_TICKS 44
|
||||
|
|
@ -202,7 +201,7 @@ ISR(TIMER1_COMPA_vect)
|
|||
next = now + TIMER_DEFER_REPEAT_TICKS;
|
||||
goto done;
|
||||
}
|
||||
timer_repeat_set(now + TIMER_IDLE_REPEAT_TICKS);
|
||||
timer_repeat_set(now + TIMER_REPEAT_TICKS);
|
||||
timer_set(now);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,10 +112,9 @@ timer_init(void)
|
|||
DECL_INIT(timer_init);
|
||||
|
||||
static uint32_t timer_repeat_until;
|
||||
#define TIMER_IDLE_REPEAT_TICKS timer_from_us(500)
|
||||
#define TIMER_REPEAT_TICKS timer_from_us(100)
|
||||
|
||||
#define TIMER_MIN_TRY_TICKS timer_from_us(2)
|
||||
#define TIMER_MIN_TRY_TICKS 90
|
||||
#define TIMER_DEFER_REPEAT_TICKS timer_from_us(5)
|
||||
|
||||
// Invoke timers
|
||||
|
|
@ -141,7 +140,7 @@ timer_dispatch_many(void)
|
|||
timer_repeat_until = now + TIMER_REPEAT_TICKS;
|
||||
return TIMER_DEFER_REPEAT_TICKS;
|
||||
}
|
||||
timer_repeat_until = tru = now + TIMER_IDLE_REPEAT_TICKS;
|
||||
timer_repeat_until = tru = now + TIMER_REPEAT_TICKS;
|
||||
}
|
||||
|
||||
// Next timer in the past or near future - wait for it to be ready
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ timer_is_before(uint32_t time1, uint32_t time2)
|
|||
}
|
||||
|
||||
static uint32_t timer_repeat_until;
|
||||
#define TIMER_IDLE_REPEAT_TICKS timer_from_us(500)
|
||||
#define TIMER_REPEAT_TICKS timer_from_us(100)
|
||||
|
||||
#define TIMER_MIN_TRY_TICKS timer_from_us(2)
|
||||
|
|
@ -59,7 +58,7 @@ timer_dispatch_many(void)
|
|||
timer_repeat_until = now + TIMER_REPEAT_TICKS;
|
||||
return now + TIMER_DEFER_REPEAT_TICKS;
|
||||
}
|
||||
timer_repeat_until = tru = now + TIMER_IDLE_REPEAT_TICKS;
|
||||
timer_repeat_until = tru = now + TIMER_REPEAT_TICKS;
|
||||
}
|
||||
|
||||
// Next timer in the past or near future - wait for it to be ready
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue