2 Commits

Author SHA1 Message Date
Kevin O'Connor
c1feb47dbd heaters: Add "heaters/set_target_temperature" API endpoint
Add a mechanism for api clients to asynchronously set a target
temperature.  That is, a mechanism to set the temperature without
needing to wait for G-Code commands to complete.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-08-05 01:12:19 -04:00
Kevin O'Connor
36b5595290 heaters: Add 'temperature_wait' status to heaters object
Report if g-code processing is delayed waiting for a heater to reach a
temperature, along with the sensor that is being checked.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2024-08-05 00:43:41 -04:00
415 changed files with 9974 additions and 151280 deletions

View File

@@ -21,7 +21,7 @@ jobs:
run: ./scripts/ci-build.sh 2>&1
- name: Upload micro-controller data dictionaries
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: data-dict
path: ci_build/dict

View File

@@ -11,14 +11,16 @@ jobs:
steps:
- uses: actions/stale@v8
with:
stale-pr-message: |
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: |
Hello,
It looks like there hasn't been any recent updates on this
github ticket. We prefer to only list tickets as "open" if
they are actively being worked on. Feel free to provide an
update on this ticket. Otherwise the ticket will be
automatically closed in a few days.
Klipper github issue. If you created this issue and no
longer consider it open, then please login to github and
close the issue. Otherwise, if there is no further activity
on this thread then it will be automatically closed in a few
days.
Best regards,
@@ -27,10 +29,10 @@ jobs:
PS: I'm just an automated script, not a human being.
exempt-issue-labels: 'enhancement,bug'
days-before-stale: 60
days-before-stale: 35
days-before-close: 7
days-before-issue-stale: -1
days-before-issue-close: -1
days-before-pr-stale: -1
days-before-pr-close: -1
# Close tickets marked with "not on github" label
close_not_on_github:
if: github.repository == 'Klipper3d/klipper'
@@ -328,7 +330,7 @@ jobs:
}
# Lock closed issues after 6 months of inactivity and PRs after 1 year.
lock:
name: Lock Closed Tickets
name: Lock Closed Issues
if: github.repository == 'Klipper3d/klipper'
runs-on: ubuntu-latest
steps:

View File

@@ -4,14 +4,15 @@ Welcome to the Klipper project!
https://www.klipper3d.org/
The Klipper firmware controls 3d-Printers. It combines the power of a
general purpose computer with one or more micro-controllers. See the
Klipper is a 3d-Printer firmware. It combines the power of a general
purpose computer with one or more micro-controllers. See the
[features document](https://www.klipper3d.org/Features.html) for more
information on why you should use the Klipper software.
information on why you should use Klipper.
Start by [installing Klipper software](https://www.klipper3d.org/Installation.html).
To begin using Klipper start by
[installing](https://www.klipper3d.org/Installation.html) it.
Klipper software is Free Software. See the [license](COPYING) or read
the [documentation](https://www.klipper3d.org/Overview.html). We
depend on the generous support from our
Klipper is Free Software. See the [license](COPYING) or read the
[documentation](https://www.klipper3d.org/Overview.html). We depend on
the generous support from our
[sponsors](https://www.klipper3d.org/Sponsors.html).

View File

@@ -95,4 +95,4 @@ max_z_accel: 100
aliases:
EXP1_1=PC6,EXP1_3=PB10,EXP1_5=PB14,EXP1_7=PB12,EXP1_9=<GND>,
EXP1_2=PB2,EXP1_4=PB11,EXP1_6=PB13,EXP1_8=PB15,EXP1_10=<5V>,
PROBE_IN=PB0,PROBE_OUT=PB1,FIL_RUNOUT=PA4
PROBE_IN=PB0,PROBE_OUT=PB1,FIL_RUNOUT=PC6

View File

@@ -1,232 +0,0 @@
# This file contains common pin mappings for the Mellow Fly-E3-v2.
# To use this config, the firmware should be compiled for the
# STM32F407 with a "32KiB bootloader".
# The "make flash" command does not work on the Fly-E3-v2. Instead,
# after running "make", copy the generated "out/klipper.bin" file to a
# file named "firmware.bin" or "klipper.bin" on an SD card and then restart the Fly-E3-v2
# with that SD card.
# See docs/Config_Reference.md for a description of parameters.
[mcu]
serial: /dev/serial/by-id/usb-Klipper_stm32f407xx_27004A001851323333353137-if00
[stepper_x]
step_pin: PE5
dir_pin: PC0
enable_pin: !PC1
microsteps: 16
rotation_distance: 30
full_steps_per_rotation: 200
endstop_pin: PE7 #X-STOP
position_endstop: 0
position_max: 200
homing_speed: 50
second_homing_speed: 10
homing_retract_dist: 5.0
homing_positive_dir: false
step_pulse_duration: 0.000004
[stepper_y]
step_pin: PE4
dir_pin: !PC13
enable_pin: !PC14
microsteps: 16
rotation_distance: 30
full_steps_per_rotation: 200
endstop_pin: PE8 #Y-STOP
position_endstop: 0
position_max: 200
homing_speed: 50
second_homing_speed: 10
homing_retract_dist: 5.0
homing_positive_dir: false
step_pulse_duration: 0.000004
[stepper_z]
step_pin: PE1
dir_pin: !PB7
enable_pin: !PE3
microsteps: 16
rotation_distance: 30
full_steps_per_rotation: 200
endstop_pin: PE9 #Z-STOP
position_min: 0
position_endstop: 0
position_max: 200
homing_speed: 5
second_homing_speed: 3
homing_retract_dist: 5.0
homing_positive_dir: false
step_pulse_duration: 0.000004
[extruder]
step_pin: PE2
dir_pin: PD5
enable_pin: !PD6
microsteps: 16
rotation_distance: 33.500
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PC6 #E0
########################################
# Extruder 100K thermistor configuration
########################################
sensor_type: ATC Semitec 104GT-2
sensor_pin: PC4 #T0 TEMP
control: pid
pid_Kp: 22.2
pid_Ki: 1.08
pid_Kd: 114
min_temp: 0
max_temp: 275
########################################
# Extruder MAX31865 PT100 2 wire config
########################################
# sensor_type: MAX31865
# sensor_pin: PD15 #PT-100
# spi_speed: 4000000
# spi_software_sclk_pin: PD12
# spi_software_mosi_pin: PD11
# spi_software_miso_pin: PD13
# rtd_nominal_r: 100
# rtd_reference_r: 430
# rtd_num_of_wires: 2
# rtd_use_50Hz_filter: True
min_temp: 0
max_temp: 300
#[extruder1]
#step_pin: PE0
#dir_pin: PD1
#enable_pin: !PD3
#microsteps: 16
#heater_pin: PC7 #E1
#sensor_pin: PC5 #T1 TEMP
########################################
# TMC2209 configuration
########################################
[tmc2209 stepper_x]
uart_pin: PC15
interpolate: False
run_current: 0.3
sense_resistor: 0.110
stealthchop_threshold: 999999
[tmc2209 stepper_y]
uart_pin: PB6
interpolate: False
run_current: 0.3
sense_resistor: 0.110
stealthchop_threshold: 999999
[tmc2209 stepper_z]
uart_pin: PD7
interpolate: False
run_current: 0.4
sense_resistor: 0.110
stealthchop_threshold: 999999
[tmc2209 extruder]
uart_pin: PD4
interpolate: False
run_current: 0.27
sense_resistor: 0.075
stealthchop_threshold: 999999
#[tmc2209 extruder1]
#uart_pin: PD0
#interpolate: False
#run_current: 0.27
#sense_resistor: 0.075
#stealthchop_threshold: 999999
#######################################
# Heated Bed
#######################################
[heater_bed]
heater_pin: PB0 #BED
sensor_type: Generic 3950
sensor_pin: PB1 #B-TEMP
max_power: 1.0
min_temp: 0
max_temp: 120
control: pid
pid_kp: 58.437
pid_ki: 2.347
pid_kd: 363.769
#######################################
# LIGHTING
#######################################
#[led Toolhead]
#white_pin: PA2 #FAN2
#cycle_time: 0.010
#initial_white: 0
#######################################
# COOLING
#######################################
[heater_fan hotend_fan]
pin: PA1 #FAN1
max_power: 1.0
kick_start_time: 0.5
heater: extruder
heater_temp: 50
fan_speed: 1.0
[controller_fan controller_fan]
pin: PA0 #FAN0
max_power: 1.0
kick_start_time: 0.5
heater: extruder
stepper: stepper_x, stepper_y, stepper_z
fan_speed: 1.0
idle_timeout: 60
[fan]
pin: PA3 #FAN3
max_power: 1.0
off_below: 0.2
[temperature_sensor Mellow_Fly_E3_V2]
sensor_type: temperature_mcu
min_temp: 5
max_temp: 80
[printer]
kinematics: cartesian
max_velocity: 300
max_accel: 3000
max_z_velocity: 50
max_z_accel: 100
########################################
# EXP1 / EXP2 (display) pins
########################################
[board_pins]
aliases:
EXP1_1=PD10, EXP1_3=PA8, EXP1_5=PE15, EXP1_7=PA14, EXP1_9=<GND>,
EXP1_2=PA9, EXP1_4=PA10, EXP1_6=PE14, EXP1_8=PA13, EXP1_10=<5V>,
# EXP2 header
EXP2_1=PA6, EXP2_3=PB11, EXP2_5=PB10, EXP2_7=PE13, EXP2_9=<GND>,
EXP2_2=PA5, EXP2_4=PA4, EXP2_6=PA7, EXP2_8=<RST>, EXP2_10=<NC>,
# See the sample-lcd.cfg file for definitions of common LCD displays.
#######################################
# BL-Touch
#######################################
#[bltouch]
#sensor_pin: PC2
#control_pin: PE6
#z_offset: 0

View File

@@ -1,256 +0,0 @@
# This file contains common pin mappings for the Geeetech GT2560 v4.0 and v4.1b
# boards. These boards use a firmware compiled for the AVR atmega2560.
# For default Geeetech A10/A20 (1 extruder),
# A10M/A20M (mixing 2 in 1 out),
# A10T/A20T (mixing 3 in 1 out) printers
# Installation: https://www.klipper3d.org/Installation.html
# Always read for first start: https://www.klipper3d.org/Config_checks.html
[mcu]
# Might need to be changed: https://www.klipper3d.org/Installation.html
serial: /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
[printer]
kinematics: cartesian
max_velocity: 200
max_accel: 1500
max_z_velocity: 20
max_z_accel: 500
# # uncomment for BLTouch/3DTouch
# [bltouch]
# sensor_pin: PC7 # there is an external pull up so no need in ^
# control_pin: PB5
# speed: 3.0
# samples: 2
# x_offset: -42.0
# y_offset: -1.0
# z_offset: 1.0 # during calibration this line is commented out and new record added at the end of file
[safe_z_home]
home_xy_position: 100, 100 # Change coordinates to the center of your print bed
speed: 50
z_hop: 10 # Move up 10mm
z_hop_speed: 5
[stepper_x]
enable_pin: !PC2
dir_pin: !PG2
step_pin: PC0
microsteps: 16
rotation_distance: 40
endstop_pin: !PA2 # there are external pull ups
position_endstop: 0
position_max: 220 # for A10/M/T / change to 250 for A20/M/T
homing_speed: 40
[stepper_y]
enable_pin: !PA7
dir_pin: !PC4
step_pin: PC6
microsteps: 16
rotation_distance: 40
endstop_pin: !PA6 # there are external pull ups
position_endstop: 0
position_max: 220 # for A10/M/T / change to 250 for A20/M/T
homing_speed: 40
[stepper_z]
enable_pin: !PA5
dir_pin: PA1
step_pin: PA3
microsteps: 16
rotation_distance: 8
#endstop_pin: probe:z_virtual_endstop # uncomment for BLTouch/3DTouch
endstop_pin: !PC7 # comment for BLTouch/3DTouch
position_endstop: 0 # comment for BLTouch/3DTouch
position_max: 230 # for A10/M/T / change to 250 for A20/M/T
position_min: -5
homing_speed: 20
[extruder]
enable_pin: !PB6
dir_pin: PL5
step_pin: PL3
microsteps: 16
rotation_distance: 8 # Needs to be optimized: https://www.klipper3d.org/Rotation_Distance.html#calibrating-rotation_distance-on-extruders
nozzle_diameter: 0.4
filament_diameter: 1.750
heater_pin: PB4
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PK3
min_temp: 0
max_temp: 250
max_extrude_only_distance: 200.0
# Parameters for stock hotend on A10M
# Please recalibrate according to https://www.klipper3d.org/Config_checks.html#calibrate-pid-settings
control: pid
pid_kp: 54.722
pid_ki: 4.800
pid_kd: 155.958
[extruder_stepper extruder_1]
extruder:
enable_pin: !PL1
dir_pin: PL2
step_pin: PL0
microsteps: 16
rotation_distance: 8 # Needs to be optimized: https://www.klipper3d.org/Rotation_Distance.html#calibrating-rotation_distance-on-extruders
[extruder_stepper extruder_2]
extruder:
enable_pin: !PG0
dir_pin: PL4
step_pin: PL6
microsteps: 16
rotation_distance: 8 # Needs to be optimized: https://www.klipper3d.org/Rotation_Distance.html#calibrating-rotation_distance-on-extruders
[heater_bed]
heater_pin: PG5
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PK2
min_temp: 0
max_temp: 120
# Parameters for `SuperPlate` on A10M
# Please recalibrate according to https://www.klipper3d.org/Config_checks.html#calibrate-pid-settings
control: pid
pid_kp: 70.936
pid_ki: 1.785
pid_kd: 704.924
[fan]
pin: PH6
cycle_time: 0.150
kick_start_time: 0.300
# # for GT2560V4.0 with 20pin flat cable toward the display
# [display]
# lcd_type: hd44780
# hd44780_protocol_init: True
# rs_pin: PD1
# e_pin: PH0
# d4_pin: PH1
# d5_pin: PD0
# d6_pin: PE3
# d7_pin: PC1
# encoder_pins: ^PG1, ^PL7
# click_pin: ^!PD2
# for GT2560V4.1B with 12pin flat cable toward the display YHCB2004-06 ver3.0
# the aip31068_spi driver was added to Klipper on 2024-12-02, commit aecb29d2
[display]
lcd_type: aip31068_spi
latch_pin: PE3
spi_software_sclk_pin: PD0
spi_software_mosi_pin: PC1
spi_software_miso_pin: PH7 # any unused pin
encoder_pins: ^PH0, ^PH1
click_pin: ^!PD2
[filament_switch_sensor sensor_e0]
switch_pin: !PK4
[filament_switch_sensor sensor_e1]
switch_pin: !PK5
[filament_switch_sensor sensor_e2]
# switch_pin: !PE2 # for GT2560V4.0
switch_pin: !PF0 # for GT2560V4.1B
# to enable M118 echo command
[respond]
# Specific macros for mixing colors.
# Add in slicer new filament color and in filament start G-Code add desired mixing factor:
# M163 S0 P50 ; set extruder 0 to 50%
# M163 S1 P40 ; set extruder 1 to 40%
# M163 S2 P10 ; set extruder 2 to 10%
# M164 ; commit the mix factors
[gcode_macro M163]
description: M163 [P<factor>] [S<index>] Set a single mix factor (in proportion to the sum total of all mix factors). The mix must be committed to a virtual tool by M164 before it takes effect.
gcode:
{% if 'P' in params %}
{% set s = params.S|default(0)| int %}
{% if s == 0 %}
SET_GCODE_VARIABLE MACRO=M164 VARIABLE=e0_parts VALUE={params.P|default(0)|float}
M118 Set Mixing factor for extruder 0 to {params.P|default(0)|float}
{% elif s == 1 %}
SET_GCODE_VARIABLE MACRO=M164 VARIABLE=e1_parts VALUE={params.P|default(0)|float}
M118 Set Mixing factor for extruder 1 to {params.P|default(0)|float}
{% elif s == 2 %}
SET_GCODE_VARIABLE MACRO=M164 VARIABLE=e2_parts VALUE={params.P|default(0)|float}
M118 Set Mixing factor for extruder 2 to {params.P|default(0)|float}
{% endif %}
{% else %}
M118 No Mixing factor set, missing value for P
{% endif %}
M118 {e0_parts} {e1_parts} {e2_parts}
[gcode_macro M164]
description: Applies the set mixing factors to the extruders
# default values:
variable_e0_parts : 100
variable_e1_parts : 0
variable_e2_parts : 0
gcode:
# normalize the parts to sum of 1
{% set e0 = e0_parts / (e0_parts + e1_parts + e2_parts) | float %}
{% set e1 = e1_parts / (e0_parts + e1_parts + e2_parts) | float %}
{% set e2 = e2_parts / (e0_parts + e1_parts + e2_parts) | float %}
M118 scaled rot-dist_e0 { printer.configfile.settings.extruder.rotation_distance / (e0 + 0.000001) | float }
M118 scaled rot-dist_e1 { printer.configfile.settings['extruder_stepper extruder_1'].rotation_distance / (e1 + 0.000001) | float }
M118 scaled rot-dist_e2 { printer.configfile.settings['extruder_stepper extruder_2'].rotation_distance / (e2 + 0.000001) |float }
# activate stepper percentages
SYNC_EXTRUDER_MOTION EXTRUDER=extruder MOTION_QUEUE=extruder
SYNC_EXTRUDER_MOTION EXTRUDER=extruder_1 MOTION_QUEUE=extruder
SYNC_EXTRUDER_MOTION EXTRUDER=extruder_2 MOTION_QUEUE=extruder
SET_EXTRUDER_ROTATION_DISTANCE EXTRUDER=extruder DISTANCE={ printer.configfile.settings.extruder.rotation_distance / (e0+0.000001)|float }
SET_EXTRUDER_ROTATION_DISTANCE EXTRUDER=extruder_1 DISTANCE={ printer.configfile.settings['extruder_stepper extruder_1'].rotation_distance / (e1+0.000001)|float }
SET_EXTRUDER_ROTATION_DISTANCE EXTRUDER=extruder_2 DISTANCE={ printer.configfile.settings['extruder_stepper extruder_2'].rotation_distance / (e2+0.000001)|float }
M118 Mixing factors {e0} {e1} {e2} are activated
# In PrusaSlicer:
# - you can add as many extruders as mixing ratios you want
# - in Printer Settings -> Custom G-code -> Tool change G-code add:
# TOOL_CHANGE EXTRUDER={next_extruder}
# - in this config file add:
# [gcode_macro TOOL_CHANGE]
# description: Tool change macro with mix ratio setup for 11 extruders
# variable_extruder: 0
# gcode:
# {% set extruder = params.EXTRUDER|default(0)| int %}
# {% if extruder == 0 %}
# M163 S0 P100
# M163 S1 P0
# M163 S2 P0
# M164
# M118 Switching to Extruder 0
# {% elif extruder == 1 %}
# M163 S0 P90
# M163 S1 P10
# M163 S2P0
# M164
# M118 Switching to Extruder 1
# {% elif extruder == 2 %}
# # and so on ...
# {% else %}
# M118 Unknown extruder number: {extruder}
# {% endif %}
# In OrcaSlicer:
# you can add as many filaments as mixing ratios you want
# in Material settings -> Advanced -> Filament start G-code add desired mixing ratio:
# ; filament start gcode
# M163 S0 P100 ; set extruder 0
# M163 S1 P0 ; set extruder 1
# M163 S2 P0 ; set extruder 2
# M164 ; commit the mix factors
# For gradient over Z axis:
# In `Printer -> Custom G-code -> After layer change G-code` add:
# M163 S0 P{ layer_num * 100 / total_layer_count } ; Gradient 0-100
# M163 S1 P{(total_layer_count-layer_num) * 100 / total_layer_count} ; Gradient 100-0
# M164 ; commit the mix factors

View File

@@ -77,14 +77,5 @@ heater_temp: 50.0
pin: toolboard:PA9
z_offset: 20
[samd_sercom sercom_i2c]
sercom: sercom1
tx_pin: toolboard:PA16
clk_pin: toolboard:PA17
[lis3dh]
i2c_mcu: toolboard
i2c_bus: sercom1
[mcu toolboard]
canbus_uuid: 4b194673554e

View File

@@ -282,6 +282,22 @@ window" interface. Parsing content from the G-Code terminal output is
discouraged. Use the "objects/subscribe" endpoint to obtain updates on
Klipper's state.
### heaters/set_target_temperature
This endpoint is used to asynchronously set the target temperature for
a heater. For example:
`{"id": 123, "method": "heaters/set_target_temperature", "params":
{"heater":"heater_generic my_heater", "target": 100.3}}`
This endpoint is similar to the `SET_HEATER_TEMPERATURE` G-Code
command, but the target temperature takes effect immediately. It does
not wait for pending G-Code commands to complete.
If this endpoint is issued for a heater while a `WAIT_TEMPERATURE`
command (or `M109`, `M190`) is pending for that heater, then the
requested target temperature will be set and the `WAIT_TEMPERATURE`
command will exit with an error.
### motion_report/dump_stepper
This endpoint is used to subscribe to Klipper's internal stepper
@@ -375,10 +391,9 @@ A request may look like:
`{"id": 123, "method":"hx71x/dump_hx71x",
"params": {"sensor": "load_cell", "response_template": {}}}`
and might return:
`{"id": 123,"result":{"header":["time","counts","value"]}}`
`{"id": 123,"result":{"header":["time","counts"]}}`
and might later produce asynchronous messages such as:
`{"params":{"data":[[3292.432935, 562534, 0.067059278],
[3292.4394937, 5625322, 0.670590639]]}}`
`{"params":{"data":[[3292.432935, 562534], [3292.4394937, 5625322]]}}`
### ads1220/dump_ads1220
@@ -391,10 +406,9 @@ A request may look like:
`{"id": 123, "method":"ads1220/dump_ads1220",
"params": {"sensor": "load_cell", "response_template": {}}}`
and might return:
`{"id": 123,"result":{"header":["time","counts","value"]}}`
`{"id": 123,"result":{"header":["time","counts"]}}`
and might later produce asynchronous messages such as:
`{"params":{"data":[[3292.432935, 562534, 0.067059278],
[3292.4394937, 5625322, 0.670590639]]}}`
`{"params":{"data":[[3292.432935, 562534], [3292.4394937, 5625322]]}}`
### pause_resume/cancel

View File

@@ -24,51 +24,19 @@ try to probe the bed without attaching the probe if you use it.
> **Tip:** Make sure the [probe X and Y offsets](Config_Reference.md#probe) are
> correctly set as they greatly influence calibration.
### Basic Usage: X-Axis Calibration
1. After setting up the ```[axis_twist_compensation]``` module, run:
```
AXIS_TWIST_COMPENSATION_CALIBRATE
```
This command will calibrate the X-axis by default.
- The calibration wizard will prompt you to measure the probe Z offset at
several points along the bed.
- By default, the calibration uses 3 points, but you can specify a different
number with the option:
``
SAMPLE_COUNT=<value>
``
2. **Adjust Your Z Offset:**
After completing the calibration, be sure to [adjust your Z offset]
(Probe_Calibrate.md#calibrating-probe-z-offset).
3. **Perform Bed Leveling Operations:**
Use probe-based operations as needed, such as:
- [Screws Tilt Adjust](G-Codes.md#screws_tilt_adjust)
- [Z Tilt Adjust](G-Codes.md#z_tilt_adjust)
4. **Finalize the Setup:**
- Home all axes, and perform a [Bed Mesh](Bed_Mesh.md) if necessary.
- Run a test print, followed by any
[fine-tuning](Axis_Twist_Compensation.md#fine-tuning)
if needed.
### For Y-Axis Calibration
The calibration process for the Y-axis is similar to the X-axis. To calibrate
the Y-axis, use:
```
AXIS_TWIST_COMPENSATION_CALIBRATE AXIS=Y
```
This will guide you through the same measuring process as for the X-axis.
### Automatic Calibration for Both Axes
To perform automatic calibration for both the X and Y axes without manual
intervention, use:
```
AXIS_TWIST_COMPENSATION_CALIBRATE AUTO=True
```
In this mode, the calibration process will run for both axes automatically.
1. After setting up the [axis_twist_compensation] module,
perform `AXIS_TWIST_COMPENSATION_CALIBRATE`
* The calibration wizard will prompt you to measure the probe Z offset at a few
points along the bed
* The calibration defaults to 3 points but you can use the option
`SAMPLE_COUNT=` to use a different number.
2. [Adjust your Z offset](Probe_Calibrate.md#calibrating-probe-z-offset)
3. Perform automatic/probe-based bed tramming operations, such as
[Screws Tilt Adjust](G-Codes.md#screws_tilt_adjust),
[Z Tilt Adjust](G-Codes.md#z_tilt_adjust) etc
4. Home all axis, then perform a [Bed Mesh](Bed_Mesh.md) if required
5. Perform a test print, followed by any
[fine-tuning](Axis_Twist_Compensation.md#fine-tuning) as desired
> **Tip:** Bed temperature and nozzle temperature and size do not seem to have
> an influence to the calibration process.

View File

@@ -269,7 +269,7 @@ printers use an endstop for homing the Z axis and a probe for calibrating the
mesh. In this configuration it is possible offset the mesh so that the (X, Y)
`reference position` applies zero adjustment. The `reference postion` should
be the location on the bed where a
[Z_ENDSTOP_CALIBRATE](./Manual_Level.md#calibrating-a-z-endstop)
[Z_ENDSTOP_CALIBRATE](./Manual_Level#calibrating-a-z-endstop)
paper test is performed. The bed_mesh module provides the
`zero_reference_position` option for specifying this coordinate:

View File

@@ -354,26 +354,6 @@ micro-controller.
| 1 stepper (200Mhz) | 39 |
| 3 stepper (200Mhz) | 181 |
### SAME70 step rate benchmark
The following configuration sequence is used on the SAME70:
```
allocate_oids count=3
config_stepper oid=0 step_pin=PC18 dir_pin=PB5 invert_step=-1 step_pulse_ticks=0
config_stepper oid=1 step_pin=PC16 dir_pin=PD10 invert_step=-1 step_pulse_ticks=0
config_stepper oid=2 step_pin=PC28 dir_pin=PA4 invert_step=-1 step_pulse_ticks=0
finalize_config crc=0
```
The test was last run on commit `34e9ea55` with gcc version
`arm-none-eabi-gcc (NixOS 10.3-2021.10) 10.3.1` on a SAME70Q20B
micro-controller.
| same70 | ticks |
| -------------------- | ----- |
| 1 stepper | 45 |
| 3 stepper | 190 |
### AR100 step rate benchmark ###
The following configuration sequence is used on AR100 CPU (Allwinner A64):
@@ -386,7 +366,7 @@ finalize_config crc=0
```
The test was last run on commit `b7978d37` with gcc version
The test was last run on commit `08d037c6` with gcc version
`or1k-linux-musl-gcc (GCC) 9.2.0` on an Allwinner A64-H
micro-controller.
@@ -395,9 +375,9 @@ micro-controller.
| 1 stepper | 85 |
| 3 stepper | 359 |
### RPxxxx step rate benchmark
### RP2040 step rate benchmark
The following configuration sequence is used on the RP2040 and RP2350:
The following configuration sequence is used on the RP2040:
```
allocate_oids count=3
@@ -407,26 +387,15 @@ config_stepper oid=2 step_pin=gpio27 dir_pin=gpio5 invert_step=-1 step_pulse_tic
finalize_config crc=0
```
The test was last run on commit `f6718291` with gcc version
`arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0` on Raspberry Pi
Pico and Pico 2 boards.
The test was last run on commit `59314d99` with gcc version
`arm-none-eabi-gcc (Fedora 10.2.0-4.fc34) 10.2.0` on a Raspberry Pi
Pico board.
| rp2040 (*) | ticks |
| rp2040 | ticks |
| -------------------- | ----- |
| 1 stepper | 5 |
| 3 stepper | 22 |
| rp2350 | ticks |
| -------------------- | ----- |
| 1 stepper | 36 |
| 3 stepper | 169 |
(*) Note that the reported rp2040 ticks are relative to a 12Mhz
scheduling timer and do not correspond to its 125Mhz internal ARM
processing rate. It is expected that 5 scheduling ticks corresponds to
~47 ARM core cycles and 22 scheduling ticks corresponds to ~224 ARM
core cycles.
### Linux MCU step rate benchmark
The following configuration sequence is used on a Raspberry Pi:
@@ -487,8 +456,7 @@ hub.
| sam4s8c (USB) | 650K | 8d4a5c16 | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
| samd51 (USB) | 864K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
| stm32f446 (USB) | 870K | 01d2183f | arm-none-eabi-gcc (Fedora 7.4.0-1.fc30) 7.4.0 |
| rp2040 (USB) | 885K | f6718291 | arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0 |
| rp2350 (USB) | 885K | f6718291 | arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0 |
| rp2040 (USB) | 873K | c5667193 | arm-none-eabi-gcc (Fedora 10.2.0-4.fc34) 10.2.0 |
## Host Benchmarks

View File

@@ -359,10 +359,10 @@ Useful steps:
be efficient as it is typically only called during homing and
probing operations.
5. Other methods. Implement the `check_move()`, `get_status()`,
`get_steppers()`, `home()`, `clear_homing_state()`, and `set_position()`
methods. These functions are typically used to provide kinematic
specific checks. However, at the start of development one can use
boiler-plate code here.
`get_steppers()`, `home()`, and `set_position()` methods. These
functions are typically used to provide kinematic specific checks.
However, at the start of development one can use boiler-plate code
here.
6. Implement test cases. Create a g-code file with a series of moves
that can test important cases for the given kinematics. Follow the
[debugging documentation](Debugging.md) to convert this g-code file

View File

@@ -8,37 +8,6 @@ All dates in this document are approximate.
## Changes
20241203: The resonance test has been changed to include slow sweeping
moves. This change requires that testing point(s) have some clearance
in X/Y plane (+/- 30 mm from the test point should suffice when using
the default settings). The new test should generally produce more
accurate and reliable test results. However, if required, the previous
test behavior can be restored by adding options `sweeping_period: 0` and
`accel_per_hz: 75` to the `[resonance_tester]` config section.
20241201: In some cases Klipper may have ignored leading characters or
spaces in a traditional G-Code command. For example, "99M123" may have
been interpreted as "M123" and "M 321" may have been interpreted as
"M321". Klipper will now report these cases with an "Unknown command"
warning.
20241112: Option `CHIPS=<chip_name>` in `TEST_RESONANCES` and
`SHAPER_CALIBRATE` requires specifying the full name(s) of the accel
chip(s). For example, `adxl345 rpi` instead of short name - `rpi`.
20240912: `SET_PIN`, `SET_SERVO`, `SET_FAN_SPEED`, `M106`, and `M107`
commands are now collated. Previously, if many updates to the same
object were issued faster than the minimum scheduling time (typically
100ms) then actual updates could be queued far into the future. Now if
many updates are issued in rapid succession then it is possible that
only the latest request will be applied. If the previous behavior is
requried then consider adding explicit `G4` delay commands between
updates.
20240912: Support for `maximum_mcu_duration` and `static_value`
parameters in `[output_pin]` config sections have been removed. These
options have been deprecated since 20240123.
20240415: The `on_error_gcode` parameter in the `[virtual_sdcard]`
config section now has a default. If this parameter is not specified
it now defaults to `TURN_OFF_HEATERS`. If the previous behavior is

View File

@@ -1675,9 +1675,8 @@ Support for LIS2DW accelerometers.
```
[lis2dw]
#cs_pin:
# The SPI enable pin for the sensor. This parameter must be provided
# if using SPI.
cs_pin:
# The SPI enable pin for the sensor. This parameter must be provided.
#spi_speed: 5000000
# The SPI speed (in hz) to use when communicating with the chip.
# The default is 5000000.
@@ -1687,46 +1686,6 @@ Support for LIS2DW accelerometers.
#spi_software_miso_pin:
# See the "common SPI settings" section for a description of the
# above parameters.
#i2c_address:
# Default is 25 (0x19). If SA0 is high, it would be 24 (0x18) instead.
#i2c_mcu:
#i2c_bus:
#i2c_software_scl_pin:
#i2c_software_sda_pin:
#i2c_speed: 400000
# See the "common I2C settings" section for a description of the
# above parameters. The default "i2c_speed" is 400000.
#axes_map: x, y, z
# See the "adxl345" section for information on this parameter.
```
### [lis3dh]
Support for LIS3DH accelerometers.
```
[lis3dh]
#cs_pin:
# The SPI enable pin for the sensor. This parameter must be provided
# if using SPI.
#spi_speed: 5000000
# The SPI speed (in hz) to use when communicating with the chip.
# The default is 5000000.
#spi_bus:
#spi_software_sclk_pin:
#spi_software_mosi_pin:
#spi_software_miso_pin:
# See the "common SPI settings" section for a description of the
# above parameters.
#i2c_address:
# Default is 25 (0x19). If SA0 is high, it would be 24 (0x18) instead.
#i2c_mcu:
#i2c_bus:
#i2c_software_scl_pin:
#i2c_software_sda_pin:
#i2c_speed: 400000
# See the "common I2C settings" section for a description of the
# above parameters. The default "i2c_speed" is 400000.
#axes_map: x, y, z
# See the "adxl345" section for information on this parameter.
```
@@ -1790,14 +1749,11 @@ section of the measuring resonances guide for more information on
# auto-calibration (with 'SHAPER_CALIBRATE' command). By default no
# maximum smoothing is specified. Refer to Measuring_Resonances guide
# for more details on using this feature.
#move_speed: 50
# The speed (in mm/s) to move the toolhead to and between test points
# during the calibration. The default is 50.
#min_freq: 5
# Minimum frequency to test for resonances. The default is 5 Hz.
#max_freq: 133.33
# Maximum frequency to test for resonances. The default is 133.33 Hz.
#accel_per_hz: 60
#accel_per_hz: 75
# This parameter is used to determine which acceleration to use to
# test a specific frequency: accel = accel_per_hz * freq. Higher the
# value, the higher is the energy of the oscillations. Can be set to
@@ -1811,13 +1767,6 @@ section of the measuring resonances guide for more information on
# hz_per_sec. Small values make the test slow, and the large values
# will decrease the precision of the test. The default value is 1.0
# (Hz/sec == sec^-2).
#sweeping_accel: 400
# An acceleration of slow sweeping moves. The default is 400 mm/sec^2.
#sweeping_period: 1.2
# A period of slow sweeping moves. Setting this parameter to 0
# disables slow sweeping moves. Avoid setting it to a too small
# non-zero value in order to not poison the measurements.
# The default is 1.2 sec which is a good all-round choice.
```
## Config file helpers
@@ -2093,9 +2042,9 @@ sensor_type: ldc1612
### [axis_twist_compensation]
A tool to compensate for inaccurate probe readings due to twist in X or Y
gantry. See the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md)
for more detailed information regarding symptoms, configuration and setup.
A tool to compensate for inaccurate probe readings due to twist in X gantry. See
the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md) for more
detailed information regarding symptoms, configuration and setup.
```
[axis_twist_compensation]
@@ -2108,33 +2057,16 @@ for more detailed information regarding symptoms, configuration and setup.
calibrate_start_x: 20
# Defines the minimum X coordinate of the calibration
# This should be the X coordinate that positions the nozzle at the starting
# calibration position.
# calibration position. This parameter must be provided.
calibrate_end_x: 200
# Defines the maximum X coordinate of the calibration
# This should be the X coordinate that positions the nozzle at the ending
# calibration position.
# calibration position. This parameter must be provided.
calibrate_y: 112.5
# Defines the Y coordinate of the calibration
# This should be the Y coordinate that positions the nozzle during the
# calibration process. This parameter is recommended to
# calibration process. This parameter must be provided and is recommended to
# be near the center of the bed
# For Y-axis twist compensation, specify the following parameters:
calibrate_start_y: ...
# Defines the minimum Y coordinate of the calibration
# This should be the Y coordinate that positions the nozzle at the starting
# calibration position for the Y axis. This parameter must be provided if
# compensating for Y axis twist.
calibrate_end_y: ...
# Defines the maximum Y coordinate of the calibration
# This should be the Y coordinate that positions the nozzle at the ending
# calibration position for the Y axis. This parameter must be provided if
# compensating for Y axis twist.
calibrate_x: ...
# Defines the X coordinate of the calibration for Y axis twist compensation
# This should be the X coordinate that positions the nozzle during the
# calibration process for Y axis twist compensation. This parameter must be
# provided and is recommended to be near the center of the bed.
```
## Additional stepper motors and extruders
@@ -2526,10 +2458,6 @@ postfix for both sections.
# "calibration_extruder_temp" option is set. Its recommended to heat
# the extruder some distance from the bed to minimize its impact on
# the probe coil temperature. The default is 50.
#max_validation_temp: 60.
# The maximum temperature used to validate the calibration. It is
# recommended to set this to a value between 100 and 120 for enclosed
# printers. The default is 60.
```
## Temperature sensors
@@ -3846,7 +3774,6 @@ run_current:
#driver_SEIMIN: 0
#driver_SFILT: 0
#driver_SG4_ANGLE_OFFSET: 1
#driver_SLOPE_CONTROL: 0
# Set the given register during the configuration of the TMC2240
# chip. This may be used to set custom motor parameters. The
# defaults for each parameter are next to the parameter name in the
@@ -4150,16 +4077,15 @@ Support for a display attached to the micro-controller.
[display]
lcd_type:
# The type of LCD chip in use. This may be "hd44780", "hd44780_spi",
# "aip31068_spi", "st7920", "emulated_st7920", "uc1701", "ssd1306", or
# "sh1106".
# "st7920", "emulated_st7920", "uc1701", "ssd1306", or "sh1106".
# See the display sections below for information on each type and
# additional parameters they provide. This parameter must be
# provided.
#display_group:
# The name of the display_data group to show on the display. This
# controls the content of the screen (see the "display_data" section
# for more information). The default is _default_20x4 for hd44780 or
# aip31068_spi displays and _default_16x4 for other displays.
# for more information). The default is _default_20x4 for hd44780
# displays and _default_16x4 for other displays.
#menu_timeout:
# Timeout for menu. Being inactive this amount of seconds will
# trigger menu exit or return to root menu when having autorun
@@ -4285,31 +4211,6 @@ spi_software_miso_pin:
...
```
#### aip31068_spi display
Information on configuring an aip31068_spi display - a very similar to hd44780_spi
a 20x04 (20 symbols by 4 lines) display with slightly different internal
protocol.
```
[display]
lcd_type: aip31068_spi
latch_pin:
spi_software_sclk_pin:
spi_software_mosi_pin:
spi_software_miso_pin:
# The pins connected to the shift register controlling the display.
# The spi_software_miso_pin needs to be set to an unused pin of the
# printer mainboard as the shift register does not have a MISO pin,
# but the software spi implementation requires this pin to be
# configured.
#line_length:
# Set the number of characters per line for an hd44780 type lcd.
# Possible values are 20 (default) and 16. The number of lines is
# fixed to 4.
...
```
#### st7920 display
Information on configuring st7920 displays (which is used in
@@ -4756,7 +4657,7 @@ sensor_type:
# This must be one of the supported sensor types, see below.
```
#### HX711
#### XH711
This is a 24 bit low sample rate chip using "bit-bang" communications. It is
suitable for filament scales.
```
@@ -4824,30 +4725,13 @@ data_ready_pin:
#gain: 128
# Valid gain values are 128, 64, 32, 16, 8, 4, 2, 1
# The default is 128
#pga_bypass: False
# Disable the internal Programmable Gain Amplifier. If
# True the PGA will be disabled for gains 1, 2, and 4. The PGA is always
# enabled for gain settings 8 to 128, regardless of the pga_bypass setting.
# If AVSS is used as an input pga_bypass is forced to True.
# The default is False.
#sample_rate: 660
# This chip supports two ranges of sample rates, Normal and Turbo. In turbo
# mode the chip's internal clock runs twice as fast and the SPI communication
# mode the chips c internal clock runs twice as fast and the SPI communication
# speed is also doubled.
# Normal sample rates: 20, 45, 90, 175, 330, 600, 1000
# Turbo sample rates: 40, 90, 180, 350, 660, 1200, 2000
# The default is 660
#input_mux:
# Input multiplexer configuration, select a pair of pins to use. The first pin
# is the positive, AINP, and the second pin is the negative, AINN. Valid
# values are: 'AIN0_AIN1', 'AIN0_AIN2', 'AIN0_AIN3', 'AIN1_AIN2', 'AIN1_AIN3',
# 'AIN2_AIN3', 'AIN1_AIN0', 'AIN3_AIN2', 'AIN0_AVSS', 'AIN1_AVSS', 'AIN2_AVSS'
# and 'AIN3_AVSS'. If AVSS is used the PGA is bypassed and the pga_bypass
# setting will be forced to True.
# The default is AIN0_AIN1.
#vref:
# The selected voltage reference. Valid values are: 'internal', 'REF0', 'REF1'
# and 'analog_supply'. Default is 'internal'.
```
## Board specific hardware support
@@ -4936,50 +4820,6 @@ vssa_pin:
# noise. The default is 2 seconds.
```
### [ads1x1x]
ADS1013, ADS1014, ADS1015, ADS1113, ADS1114 and ADS1115 are I2C based Analog to
Digital Converters that can be used for temperature sensors. They provide 4
analog input pins either as single line or as differential input.
Note: Use caution if using this sensor to control heaters. The heater min_temp
and max_temp are only verified in the host and only if the host is running and
operating normally. (ADC inputs directly connected to the micro-controller
verify min_temp and max_temp within the micro-controller and do not require a
working connection to the host.)
```
[ads1x1x my_ads1x1x]
chip: ADS1115
#pga: 4.096V
# Default value is 4.096V. The maximum voltage range used for the input. This
# scales all values read from the ADC. Options are: 6.144V, 4.096V, 2.048V,
# 1.024V, 0.512V, 0.256V
#adc_voltage: 3.3
# The suppy voltage for the device. This allows additional software scaling
# for all values read from the ADC.
i2c_mcu: host
i2c_bus: i2c.1
#address_pin: GND
# Default value is GND. There can be up to four addressed devices depending
# upon wiring of the device. Check the datasheet for details. The i2c_address
# can be specified directly instead of using the address_pin.
```
The chip provides pins that can be used on other sensors.
```
sensor_type: ...
# Can be any thermistor or adc_temperature.
sensor_pin: my_ads1x1x:AIN0
# A combination of the name of the ads1x1x chip and the pin. Possible
# pin values are AIN0, AIN1, AIN2 and AIN3 for single ended lines and
# DIFF01, DIFF03, DIFF13 and DIFF23 for differential between their
# correspoding lines. For example
# DIFF03 measures the differential between line 0 and 3. Only specific
# combinations for the differentials are allowed.
```
### [replicape]
Replicape support - see the [beaglebone guide](Beaglebone.md) and the
@@ -5085,9 +4925,8 @@ serial:
### [angle]
Magnetic hall angle sensor support for reading stepper motor angle
shaft measurements using a1333, as5047d, mt6816, mt6826s,
or tle5012b SPI chips.
The measurements are available via the [API Server](API_Server.md) and
shaft measurements using a1333, as5047d, or tle5012b SPI chips. The
measurements are available via the [API Server](API_Server.md) and
[motion analysis tool](Debugging.md#motion-analysis-and-data-logging).
See the [G-Code reference](G-Codes.md#angle) for available commands.
@@ -5095,7 +4934,7 @@ See the [G-Code reference](G-Codes.md#angle) for available commands.
[angle my_angle_sensor]
sensor_type:
# The type of the magnetic hall sensor chip. Available choices are
# "a1333", "as5047d", "mt6816", "mt6826s", and "tle5012b". This parameter must be
# "a1333", "as5047d", and "tle5012b". This parameter must be
# specified.
#sample_period: 0.000400
# The query period (in seconds) to use during measurements. The
@@ -5158,9 +4997,8 @@ Most Klipper micro-controller implementations only support an
micro-controller supports a 400000 speed (*fast mode*, 400kbit/s), but it must be
[set in the operating system](RPi_microcontroller.md#optional-enabling-i2c)
and the `i2c_speed` parameter is otherwise ignored. The Klipper
"RP2040" micro-controller and ATmega AVR family and some STM32
(F0, G0, G4, L4, F7, H7) support a rate of 400000 via the `i2c_speed` parameter.
All other Klipper micro-controllers use a
"RP2040" micro-controller and ATmega AVR family support a rate of 400000
via the `i2c_speed` parameter. All other Klipper micro-controllers use a
100000 rate and ignore the `i2c_speed` parameter.
```

View File

@@ -132,10 +132,3 @@ There are several
you have questions on the code then you can also ask in the
[Klipper Discourse Forum](#discourse-forum) or on the
[Klipper Discord Chat](#discord-chat).
## Professional Services
![](img/klipper-logo-small.png)
Custom software development, software support, and solutions:
[https://ko-fi.com/koconnor](https://ko-fi.com/koconnor)

View File

@@ -78,9 +78,7 @@ for further details on how to configure a `temperature_probe`. It is
advised to configure the `calibration_position`,
`calibration_extruder_temp`, `extruder_heating_z`, and
`calibration_bed_temp` options, as doing so will automate some of the
steps outlined below. If the printer to be calibrated is enclosed, it
is strongly recommended to set the `max_validation_temp` option to a value
between 100 and 120.
steps outlined below.
Eddy probe manufacturers may offer a stock drift calibration that can be
manually added to `drift_calibration` option of the `[probe_eddy_current]`

View File

@@ -190,8 +190,6 @@ represent total number of steps per second on the micro-controller.
| AR100 | 3529K | 2507K |
| STM32F407 | 3652K | 2459K |
| STM32F446 | 3913K | 2634K |
| RP2350 | 4167K | 2663K |
| SAME70 | 6667K | 4737K |
| STM32H743 | 9091K | 6061K |
If unsure of the micro-controller on a particular board, find the

View File

@@ -127,14 +127,6 @@ use this tool the Python "numpy" package must be installed (see the
[measuring resonance document](Measuring_Resonances.md#software-installation)
for more information).
#### ANGLE_CHIP_CALIBRATE
`ANGLE_CHIP_CALIBRATE CHIP=<chip_name>`: Perform internal sensor calibration,
if implemented (MT6826S/MT6835).
- **MT68XX**: The motor should be disconnected
from any printer carriage before performing calibration.
After calibration, the sensor should be reset by disconnecting the power.
#### ANGLE_DEBUG_READ
`ANGLE_DEBUG_READ CHIP=<config_name> REG=<register>`: Queries sensor
register "register" (e.g. 44 or 0x2C). Can be useful for debugging
@@ -154,19 +146,9 @@ The following commands are available when the
section](Config_Reference.md#axis_twist_compensation) is enabled.
#### AXIS_TWIST_COMPENSATION_CALIBRATE
`AXIS_TWIST_COMPENSATION_CALIBRATE [AXIS=<X|Y>] [AUTO=<True|False>]
[SAMPLE_COUNT=<value>]`
Calibrates axis twist compensation by specifying the target axis or
enabling automatic calibration.
- **AXIS:** Define the axis (`X` or `Y`) for which the twist compensation
will be calibrated. If not specified, the axis defaults to `'X'`.
- **AUTO:** Enables automatic calibration mode. When `AUTO=True`, the
calibration will run for both the X and Y axes. In this mode, `AXIS`
cannot be specified. If both `AXIS` and `AUTO` are provided, an error
will be raised.
`AXIS_TWIST_COMPENSATION_CALIBRATE [SAMPLE_COUNT=<value>]`: Initiates the X
twist calibration wizard. `SAMPLE_COUNT` specifies the number of points along
the X axis to calibrate at and defaults to 3.
### [bed_mesh]
@@ -494,20 +476,6 @@ enabled.
`SET_FAN_SPEED FAN=config_name SPEED=<speed>` This command sets the
speed of a fan. "speed" must be between 0.0 and 1.0.
`SET_FAN_SPEED PIN=config_name TEMPLATE=<template_name>
[<param_x>=<literal>]`: If `TEMPLATE` is specified then it assigns a
[display_template](Config_Reference.md#display_template) to the given
fan. For example, if one defined a `[display_template
my_fan_template]` config section then one could assign
`TEMPLATE=my_fan_template` here. The display_template should produce a
string containing a floating point number with the desired value. The
template will be continuously evaluated and the fan will be
automatically set to the resulting speed. One may set display_template
parameters to use during template evaluation (parameters will be
parsed as Python literals). If TEMPLATE is an empty string then this
command will clear any previous template assigned to the pin (one can
then use `SET_FAN_SPEED` commands to manage the values directly).
### [filament_switch_sensor]
The following command is available when a
@@ -585,18 +553,15 @@ state; issue a G28 afterwards to reset the kinematics. This command is
intended for low-level diagnostics and debugging.
#### SET_KINEMATIC_POSITION
`SET_KINEMATIC_POSITION [X=<value>] [Y=<value>] [Z=<value>]
[CLEAR=<[X][Y][Z]>]`: Force the low-level kinematic code to believe the
toolhead is at the given cartesian position. This is a diagnostic and
debugging command; use SET_GCODE_OFFSET and/or G92 for regular axis
transformations. If an axis is not specified then it will default to the
position that the head was last commanded to. Setting an incorrect or
invalid position may lead to internal software errors. Use the CLEAR
parameter to forget the homing state for the given axes. Note that CLEAR
will not override the previous functionality; if an axis is not specified
to CLEAR it will have its kinematic position set as per above. This
command may invalidate future boundary checks; issue a G28 afterwards to
reset the kinematics.
`SET_KINEMATIC_POSITION [X=<value>] [Y=<value>] [Z=<value>]`: Force
the low-level kinematic code to believe the toolhead is at the given
cartesian position. This is a diagnostic and debugging command; use
SET_GCODE_OFFSET and/or G92 for regular axis transformations. If an
axis is not specified then it will default to the position that the
head was last commanded to. Setting an incorrect or invalid position
may lead to internal software errors. This command may invalidate
future boundary checks; issue a G28 afterwards to reset the
kinematics.
### [gcode]
@@ -892,20 +857,6 @@ output `VALUE`. VALUE should be 0 or 1 for "digital" output pins. For
PWM pins, set to a value between 0.0 and 1.0, or between 0.0 and
`scale` if a scale is configured in the output_pin config section.
`SET_PIN PIN=config_name TEMPLATE=<template_name> [<param_x>=<literal>]`:
If `TEMPLATE` is specified then it assigns a
[display_template](Config_Reference.md#display_template) to the given
pin. For example, if one defined a `[display_template
my_pin_template]` config section then one could assign
`TEMPLATE=my_pin_template` here. The display_template should produce a
string containing a floating point number with the desired value. The
template will be continuously evaluated and the pin will be
automatically set to the resulting value. One may set display_template
parameters to use during template evaluation (parameters will be
parsed as Python literals). If TEMPLATE is an empty string then this
command will clear any previous template assigned to the pin (one can
then use `SET_PIN` commands to manage the values directly).
### [palette2]
The following commands are available when the
@@ -1070,21 +1021,6 @@ CYCLE_TIME parameter is not stored between SET_PIN commands (any
SET_PIN command without an explicit CYCLE_TIME parameter will use the
`cycle_time` specified in the pwm_cycle_time config section).
### [quad_gantry_level]
The following commands are available when the
[quad_gantry_level config section](Config_Reference.md#quad_gantry_level)
is enabled.
#### QUAD_GANTRY_LEVEL
`QUAD_GANTRY_LEVEL [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.
### [query_adc]
The query_adc module is automatically loaded.
@@ -1120,19 +1056,20 @@ is enabled (also see the
all enabled accelerometer chips.
#### TEST_RESONANCES
`TEST_RESONANCES AXIS=<axis> [OUTPUT=<resonances,raw_data>]
`TEST_RESONANCES AXIS=<axis> OUTPUT=<resonances,raw_data>
[NAME=<name>] [FREQ_START=<min_freq>] [FREQ_END=<max_freq>]
[ACCEL_PER_HZ=<accel_per_hz>] [HZ_PER_SEC=<hz_per_sec>] [CHIPS=<chip_name>]
[POINT=x,y,z] [INPUT_SHAPING=<0:1>]`: Runs the resonance
[HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
[POINT=x,y,z] [INPUT_SHAPING=[<0:1>]]`: Runs the resonance
test in all configured probe points for the requested "axis" and
measures the acceleration using the accelerometer chips configured for
the respective axis. "axis" can either be X or Y, or specify an
arbitrary direction as `AXIS=dx,dy`, where dx and dy are floating
point numbers defining a direction vector (e.g. `AXIS=X`, `AXIS=Y`, or
`AXIS=1,-1` to define a diagonal direction). Note that `AXIS=dx,dy`
and `AXIS=-dx,-dy` is equivalent. `chip_name` can be one or
more configured accel chips, delimited with comma, for example
`CHIPS="adxl345, adxl345 rpi"`. If POINT is specified it will override the point(s)
and `AXIS=-dx,-dy` is equivalent. `adxl345_chip_name` can be one or
more configured adxl345 chip,delimited with comma, for example
`CHIPS="adxl345, adxl345 rpi"`. Note that `adxl345` can be omitted from
named adxl345 chips. If POINT is specified it will override the point(s)
configured in `[resonance_tester]`. If `INPUT_SHAPING=0` or not set(default),
disables input shaping for the resonance testing, because
it is not valid to run the resonance testing with the input shaper
@@ -1149,9 +1086,8 @@ frequency response is calculated (across all probe points) and written into
#### SHAPER_CALIBRATE
`SHAPER_CALIBRATE [AXIS=<axis>] [NAME=<name>] [FREQ_START=<min_freq>]
[FREQ_END=<max_freq>] [ACCEL_PER_HZ=<accel_per_hz>][HZ_PER_SEC=<hz_per_sec>]
[CHIPS=<chip_name>] [MAX_SMOOTHING=<max_smoothing>] [INPUT_SHAPING=<0:1>]`:
Similarly to `TEST_RESONANCES`, runs
[FREQ_END=<max_freq>] [HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
[MAX_SMOOTHING=<max_smoothing>]`: Similarly to `TEST_RESONANCES`, runs
the resonance test as configured, and tries to find the optimal
parameters for the input shaper for the requested axis (or both X and
Y axes if `AXIS` parameter is unset). If `MAX_SMOOTHING` is unset, its
@@ -1474,13 +1410,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>]
[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.
`Z_TILT_ADJUST [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 `HORIZONTAL_MOVE_Z`
value overrides the `horizontal_move_z` option specified in the config file.
### [temperature_probe]

View File

@@ -1,20 +1,15 @@
# Installation
These instructions assume the software will run on a linux based host
running a Klipper compatible front end. It is recommended that a
SBC(Small Board Computer) such as a Raspberry Pi or Debian based Linux
device be used as the host machine (see the
These instructions assume the software will run on a Raspberry Pi
computer in conjunction with OctoPrint. It is recommended that a
Raspberry Pi 2 (or later) be used as the host machine (see the
[FAQ](FAQ.md#can-i-run-klipper-on-something-other-than-a-raspberry-pi-3)
for other options).
For the purposes of these instructions host relates to the Linux device and
mcu relates to the printboard. SBC relates to the term Small Board Computer
such as the Raspberry Pi.
for other machines).
## Obtain a Klipper Configuration File
Most Klipper settings are determined by a "printer configuration file"
printer.cfg, that will be stored on the host. An appropriate configuration
that will be stored on the Raspberry Pi. An appropriate configuration
file can often be found by looking in the Klipper
[config directory](../config/) for a file starting with a "printer-"
prefix that corresponds to the target printer. The Klipper
@@ -40,51 +35,38 @@ printer configuration file, then start with the closest example
[config file](../config/) and use the Klipper
[config reference](Config_Reference.md) for further information.
## Interacting with Klipper
## Prepping an OS image
Klipper is a 3d printer firmware, so it needs some way for the user to
interact with it.
Start by installing [OctoPi](https://github.com/guysoft/OctoPi) on the
Raspberry Pi computer. Use OctoPi v0.17.0 or later - see the
[OctoPi releases](https://github.com/guysoft/OctoPi/releases) for
release information. One should verify that OctoPi boots and that the
OctoPrint web server works. After connecting to the OctoPrint web
page, follow the prompt to upgrade OctoPrint to v1.4.2 or later.
Currently the best choices are front ends that retrieve information through
the [Moonraker web API](https://moonraker.readthedocs.io/) and there is also
the option to use [Octoprint](https://octoprint.org/) to control Klipper.
After installing OctoPi and upgrading OctoPrint, it will be necessary
to ssh into the target machine to run a handful of system commands. If
using a Linux or MacOS desktop, then the "ssh" software should already
be installed on the desktop. There are free ssh clients available for
other desktops (eg,
[PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/)). Use the
ssh utility to connect to the Raspberry Pi (`ssh pi@octopi` -- password
is "raspberry") and run the following commands:
The choice is up to the user on what to use, but the underlying Klipper is the
same in all cases. We encourage users to research the options available and
make an informed decision.
```
git clone https://github.com/Klipper3d/klipper
./klipper/scripts/install-octopi.sh
```
## Obtaining an OS image for SBC's
There are many ways to obtain an OS image for Klipper for SBC use, most depend on
what front end you wish to use. Some manafactures of these SBC boards also provide
their own Klipper-centric images.
The two main Moonraker based front ends are [Fluidd](https://docs.fluidd.xyz/)
and [Mainsail](https://docs.mainsail.xyz/), the latter of which has a premade install
image ["MainsailOS"](http://docs.mainsailOS.xyz), this has the option for Raspberry Pi
and some OrangePi varianta.
Fluidd can be installed via KIAUH(Klipper Install And Update Helper), which
is explained below and is a 3rd party installer for all things Klipper.
OctoPrint can be installed via the popular OctoPi image or via KIAUH, this
process is explained in [OctoPrint.md](OctoPrint.md)
## Installing via KIAUH
Normally you would start with a base image for your SBC, RPiOS Lite for example,
or in the case of a x86 Linux device, Ubuntu Server. Please note that Desktop
variants are not recommended due to certain helper programs that can stop some
Klipper functions working and even mask access to some print boards.
KIAUH can be used to install Klipper and its associated programs on a variety
of Linux based systems that run a form of Debian. More information can be found
at https://github.com/dw-0/kiauh
The above will download Klipper, install some system dependencies,
setup Klipper to run at system startup, and start the Klipper host
software. It will require an internet connection and it may take a few
minutes to complete.
## Building and flashing the micro-controller
To compile the micro-controller code, start by running these commands
on your host device:
on the Raspberry Pi:
```
cd ~/klipper/
@@ -126,21 +108,10 @@ It should report something similar to the following:
It's common for each printer to have its own unique serial port name.
This unique name will be used when flashing the micro-controller. It's
possible there may be multiple lines in the above output - if so,
choose the line corresponding to the micro-controller. If many
items are listed and the choice is ambiguous, unplug the board and
run the command again, the missing item will be your print board(see the
choose the line corresponding to the micro-controller (see the
[FAQ](FAQ.md#wheres-my-serial-port) for more information).
For common micro-controllers with STM32 or clone chips, LPC chips and
others it is usual that these need an initial Klipper flash via SD card.
When flashing with this method, it is important to make sure that the
print board is not connected with USB to the host, due to some boards
being able to feed power back to the board and stopping a flash from
occuring.
For common micro-controllers using Atmega chips, for example the 2560,
the code can be flashed with something
For common micro-controllers, the code can be flashed with something
similar to:
```
@@ -152,38 +123,53 @@ sudo service klipper start
Be sure to update the FLASH_DEVICE with the printer's unique serial
port name.
For common micro-controllers using RP2040 chips, the code can be flashed
with something similar to:
When flashing for the first time, make sure that OctoPrint is not
connected directly to the printer (from the OctoPrint web page, under
the "Connection" section, click "Disconnect").
```
sudo service klipper stop
make flash FLASH_DEVICE=first
sudo service klipper start
```
## Configuring OctoPrint to use Klipper
It is important to note that RP2040 chips may need to be put into Boot mode
before this operation.
The OctoPrint web server needs to be configured to communicate with
the Klipper host software. Using a web browser, login to the OctoPrint
web page and then configure the following items:
Navigate to the Settings tab (the wrench icon at the top of the
page). Under "Serial Connection" in "Additional serial ports" add
`/tmp/printer`. Then click "Save".
Enter the Settings tab again and under "Serial Connection" change the
"Serial Port" setting to `/tmp/printer`.
In the Settings tab, navigate to the "Behavior" sub-tab and select the
"Cancel any ongoing prints but stay connected to the printer"
option. Click "Save".
From the main page, under the "Connection" section (at the top left of
the page) make sure the "Serial Port" is set to `/tmp/printer` and
click "Connect". (If `/tmp/printer` is not an available selection then
try reloading the page.)
Once connected, navigate to the "Terminal" tab and type "status"
(without the quotes) into the command entry box and click "Send". The
terminal window will likely report there is an error opening the
config file - that means OctoPrint is successfully communicating with
Klipper. Proceed to the next section.
## Configuring Klipper
The next step is to copy the
[printer configuration file](#obtain-a-klipper-configuration-file) to
the host.
the Raspberry Pi.
Arguably the easiest way to set the Klipper configuration file is using the
built in editors in Mainsail or Fluidd. These will allow the user to open
the configuration examples and save them to be printer.cfg.
Another option is to use a desktop editor that supports editing files
over the "scp" and/or "sftp" protocols. There are freely available tools
that support this (eg, Notepad++, WinSCP, and Cyberduck).
Load the printer config file in the editor and then save it as a file
named "printer.cfg" in the home directory of the pi user
(ie, /home/pi/printer.cfg).
Arguably the easiest way to set the Klipper configuration file is to
use a desktop editor that supports editing files over the "scp" and/or
"sftp" protocols. There are freely available tools that support this
(eg, Notepad++, WinSCP, and Cyberduck). Load the printer config file
in the editor and then save it as a file named `printer.cfg` in the
home directory of the pi user (ie, `/home/pi/printer.cfg`).
Alternatively, one can also copy and edit the file directly on the
host via ssh. That may look something like the following (be
Raspberry Pi via ssh. That may look something like the following (be
sure to update the command to use the appropriate printer config
filename):
@@ -215,7 +201,7 @@ serial: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0
```
After creating and editing the file it will be necessary to issue a
"restart" command in the command console to load the config. A
"restart" command in the OctoPrint web terminal to load the config. A
"status" command will report the printer is ready if the Klipper
config file is successfully read and the micro-controller is
successfully found and configured.
@@ -225,10 +211,10 @@ Klipper to report a configuration error. If an error occurs, make any
necessary corrections to the printer config file and issue "restart"
until "status" reports the printer is ready.
Klipper reports error messages via the command console and via pop up in
Fluidd and Mainsail. The "status" command can be used to re-report error
messages. A log is available and usually located in ~/printer_data/logs
this is named klippy.log
Klipper reports error messages via the OctoPrint terminal tab. The
"status" command can be used to re-report error messages. The default
Klipper startup script also places a log in **/tmp/klippy.log** which
provides more detailed information.
After Klipper reports that the printer is ready, proceed to the
[config check document](Config_checks.md) to perform some basic checks

View File

@@ -1,26 +1,24 @@
# Measuring Resonances
Klipper has built-in support for the ADXL345, MPU-9250, LIS2DW and LIS3DH compatible
Klipper has built-in support for the ADXL345, MPU-9250 and LIS2DW compatible
accelerometers which can be used to measure resonance frequencies of the printer
for different axes, and auto-tune [input shapers](Resonance_Compensation.md) to
compensate for resonances. Note that using accelerometers requires some
soldering and crimping. The ADXL345 can be connected to the SPI interface
soldering and crimping. The ADXL345/LIS2DW can be connected to the SPI interface
of a Raspberry Pi or MCU board (it needs to be reasonably fast). The MPU family can
be connected to the I2C interface of a Raspberry Pi directly, or to an I2C
interface of an MCU board that supports 400kbit/s *fast mode* in Klipper. The
LIS2DW and LIS3DH can be connected to either SPI or I2C with the same considerations
as above.
interface of an MCU board that supports 400kbit/s *fast mode* in Klipper.
When sourcing accelerometers, be aware that there are a variety of different PCB
board designs and different clones of them. If it is going to be connected to a
5V printer MCU ensure it has a voltage regulator and level shifters.
For ADXL345s, make sure that the board supports SPI mode (a small number of
For ADXL345s/LIS2DWs, make sure that the board supports SPI mode (a small number of
boards appear to be hard-configured for I2C by pulling SDO to GND).
For MPU-9250/MPU-9255/MPU-6515/MPU-6050/MPU-6500s and LIS2DW/LIS3DH there are also
a variety of board designs and clones with different I2C pull-up resistors which
will need supplementing.
For MPU-9250/MPU-9255/MPU-6515/MPU-6050/MPU-6500s there are also a variety of
board designs and clones with different I2C pull-up resistors which will need
supplementing.
## MCUs with Klipper I2C *fast-mode* Support
@@ -29,7 +27,6 @@ will need supplementing.
| Raspberry Pi | 3B+, Pico | 3A, 3A+, 3B, 4 |
| AVR ATmega | ATmega328p | ATmega32u4, ATmega128, ATmega168, ATmega328, ATmega644p, ATmega1280, ATmega1284, ATmega2560 |
| AVR AT90 | - | AT90usb646, AT90usb1286 |
| SAMD | SAMC21G18 | SAMC21G18, SAMD21G18, SAMD21E18, SAMD21J18, SAMD21E15, SAMD51G19, SAMD51J19, SAMD51N19, SAMD51P20, SAME51J19, SAME51N19, SAME54P20 |
## Installation instructions
@@ -215,20 +212,12 @@ sudo apt install python3-numpy python3-matplotlib libatlas-base-dev libopenblas-
Next, in order to install NumPy in the Klipper environment, run the command:
```
~/klippy-env/bin/pip install -v "numpy<1.26"
~/klippy-env/bin/pip install -v numpy
```
Note that, depending on the performance of the CPU, it may take *a lot*
of time, up to 10-20 minutes. Be patient and wait for the completion of
the installation. On some occasions, if the board has too little RAM
the installation may fail and you will need to enable swap. Also note
the forced version, due to newer versions of NumPY having requirements
that may not be satisfied in some klipper python environments.
Once installed please check that no errors show from the command:
```
~/klippy-env/bin/python -c 'import numpy;'
```
The correct output should simply be a new line.
the installation may fail and you will need to enable swap.
#### Configure ADXL345 With RPi
@@ -316,7 +305,7 @@ you'll also want to modify your `printer.cfg` file to include this:
Restart Klipper via the `RESTART` command.
#### Configure LIS2DW series over SPI
#### Configure LIS2DW series
```
[mcu lis]
@@ -694,24 +683,6 @@ If you are doing a shaper re-calibration and the reported smoothing for the
suggested shaper configuration is almost the same as what you got during the
previous calibration, this step can be skipped.
### Unreliable measurements of resonance frequencies
Sometimes the resonance measurements can produce bogus results, leading to
the incorrect suggestions for the input shapers. This can be caused by a
variety of reasons, including running fans on the toolhead, incorrect
position or non-rigid mounting of the accelerometer, or mechanical problems
such as loose belts or binding or bumpy axis. Keep in mind that all fans
should be disabled for resonance testing, especially the noisy ones, and
that the accelerometer should be rigidly mounted on the corresponding
moving part (e.g. on the bed itself for the bed slinger, or on the extruder
of the printer itself and not the carriage, and some people get better
results by mounting the accelerometer on the nozzle itself). As for
mechanical problems, the user should inspect if there is any fault that
can be fixed with a moving axis (e.g. linear guide rails cleaned up and
lubricated and V-slot wheels tension adjusted correctly). If none of that
helps, a user may try the other shapers from the produced list besides the
one recommended by default.
### Testing custom axes
`TEST_RESONANCES` command supports custom axes. While this is not really

View File

@@ -1,79 +0,0 @@
# OctoPrint for Klipper
Klipper has a few options for its front ends, Octoprint was the first
and original front end for Klipper. This document will give
a brief overview of installing with this option.
## Install with OctoPi
Start by installing [OctoPi](https://github.com/guysoft/OctoPi) on the
Raspberry Pi computer. Use OctoPi v0.17.0 or later - see the
[OctoPi releases](https://github.com/guysoft/OctoPi/releases) for
release information.
One should verify that OctoPi boots and that the
OctoPrint web server works. After connecting to the OctoPrint web
page, follow the prompt to upgrade OctoPrint if needed.
After installing OctoPi and upgrading OctoPrint, it will be necessary
to ssh into the target machine to run a handful of system commands.
Start by running these commands on your host device:
__If you do not have git installed, please do so with:__
```
sudo apt install git
```
then proceed:
```
cd ~
git clone https://github.com/Klipper3d/klipper
./klipper/scripts/install-octopi.sh
```
The above will download Klipper, install the needed system dependencies,
setup Klipper to run at system startup, and start the Klipper host
software. It will require an internet connection and it may take a few
minutes to complete.
## Installing with KIAUH
KIAUH can be used to install OctoPrint on a variety of Linux based systems
that run a form of Debian. More information can be found
at https://github.com/dw-0/kiauh
## Configuring OctoPrint to use Klipper
The OctoPrint web server needs to be configured to communicate with the Klipper
host software. Using a web browser, login to the OctoPrint web page and then
configure the following items:
Navigate to the Settings tab (the wrench icon at the top of the page).
Under "Serial Connection" in "Additional serial ports" add:
```
~/printer_data/comms/klippy.serial
```
Then click "Save".
_In some older setups this address may be `/tmp/printer`_
Enter the Settings tab again and under "Serial Connection" change the "Serial Port"
setting to the one added above.
In the Settings tab, navigate to the "Behavior" sub-tab and select the
"Cancel any ongoing prints but stay connected to the printer" option. Click "Save".
From the main page, under the "Connection" section (at the top left of the page)
make sure the "Serial Port" is set to the new additional one added
and click "Connect". (If it is not in the available selection then
try reloading the page.)
Once connected, navigate to the "Terminal" tab and type "status" (without the quotes)
into the command entry box and click "Send". The terminal window will likely report
there is an error opening the config file - that means OctoPrint is successfully
communicating with Klipper.
Please proceed to [Installation.md](Installation.md) and the
_Building and flashing the micro-controller_ section

View File

@@ -17,7 +17,6 @@ communication with the Klipper developers.
## Installation and Configuration
- [Installation](Installation.md): Guide to installing Klipper.
- [Octoprint](OctoPrint.md): Guide to installing Octoprint with Klipper.
- [Config Reference](Config_Reference.md): Description of config
parameters.
- [Rotation Distance](Rotation_Distance.md): Calculating the

View File

@@ -17,6 +17,7 @@ serve the 3D printing community better. Follow them on
## Sponsors
[<img src="./img/sponsors/obico-light-horizontal.png" width="200" style="margin:25px" />](https://obico.io/klipper.html?source=klipper_sponsor)
[<img src="./img/sponsors/peopoly-logo.png" width="200" style="margin:25px" />](https://peopoly.net)
## Klipper Developers

View File

@@ -256,6 +256,11 @@ object is available if any heater is defined):
e.g. `["tmc2240 stepper_x"]`. While a temperature sensor is always
available to read, a temperature monitor may not be available and
will return null in such case.
- `temperature_wait`: Indicates if G-Code processing is stalled
waiting for a requested temperature (typically via
`TEMPERATURE_WAIT`, `M109`, or `M190` commands). The value will
contain the name of the sensor that is causing the stall or `None`
if no wait is in progress.
## idle_timeout

View File

@@ -88,9 +88,7 @@ nav:
- Config_Changes.md
- Contact.md
- Installation and Configuration:
- Installation:
- Installation.md
- OctoPrint.md
- Installation.md
- Configuration Reference:
- Config_Reference.md
- Rotation_Distance.md

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -6,16 +6,13 @@ title: Welcome
![](img/klipper-logo.png){ .center-image }
The Klipper firmware controls 3d-Printers. It combines the power of a
general purpose computer with one or more micro-controllers. See the
[features document](https://www.klipper3d.org/Features.html) for more
information on why you should use the Klipper software.
Klipper is a 3d-Printer firmware. It combines the power of a general
purpose computer with one or more micro-controllers. See the
[features](Features.md) document for more information on why you
should use Klipper.
Start by [installing Klipper software](https://www.klipper3d.org/Installation.html).
To begin using Klipper start by [installing](Installation.md) it.
Klipper software is Free Software. Read the
[documentation](https://www.klipper3d.org/Overview.html), see the
[license](COPYING), or
[download](https://github.com/Klipper3d/Klipper) the software. We
depend on the generous support from our
[sponsors](https://www.klipper3d.org/Sponsors.html).
Klipper is Free Software. Read the [documentation](Overview.md) or
view [the Klipper code on github](https://github.com/Klipper3d/klipper).
We depend on the generous support from our [sponsors](Sponsors.md).

View File

@@ -1,17 +1,12 @@
# Code for reading and writing the Klipper config file
#
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import sys, os, glob, re, time, logging, configparser, io
error = configparser.Error
######################################################################
# Config section parsing helper
######################################################################
class sentinel:
pass
@@ -139,13 +134,30 @@ class ConfigWrapper:
pconfig = self.printer.lookup_object("configfile")
pconfig.deprecate(self.section, option, value, msg)
AUTOSAVE_HEADER = """
#*# <---------------------- SAVE_CONFIG ---------------------->
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
#*#
"""
######################################################################
# Config file parsing (with include file support)
######################################################################
class ConfigFileReader:
def read_config_file(self, filename):
class PrinterConfig:
def __init__(self, printer):
self.printer = printer
self.autosave = None
self.deprecated = {}
self.runtime_warnings = []
self.deprecate_warnings = []
self.status_raw_config = {}
self.status_save_pending = {}
self.status_settings = {}
self.status_warnings = []
self.save_config_pending = False
gcode = self.printer.lookup_object('gcode')
gcode.register_command("SAVE_CONFIG", self.cmd_SAVE_CONFIG,
desc=self.cmd_SAVE_CONFIG_help)
def get_printer(self):
return self.printer
def _read_config_file(self, filename):
try:
f = open(filename, 'r')
data = f.read()
@@ -155,102 +167,6 @@ class ConfigFileReader:
logging.exception(msg)
raise error(msg)
return data.replace('\r\n', '\n')
def build_config_string(self, fileconfig):
sfile = io.StringIO()
fileconfig.write(sfile)
return sfile.getvalue().strip()
def append_fileconfig(self, fileconfig, data, filename):
if not data:
return
# Strip trailing comments
lines = data.split('\n')
for i, line in enumerate(lines):
pos = line.find('#')
if pos >= 0:
lines[i] = line[:pos]
sbuffer = io.StringIO('\n'.join(lines))
if sys.version_info.major >= 3:
fileconfig.read_file(sbuffer, filename)
else:
fileconfig.readfp(sbuffer, filename)
def _create_fileconfig(self):
if sys.version_info.major >= 3:
fileconfig = configparser.RawConfigParser(
strict=False, inline_comment_prefixes=(';', '#'))
else:
fileconfig = configparser.RawConfigParser()
return fileconfig
def build_fileconfig(self, data, filename):
fileconfig = self._create_fileconfig()
self.append_fileconfig(fileconfig, data, filename)
return fileconfig
def _resolve_include(self, source_filename, include_spec, fileconfig,
visited):
dirname = os.path.dirname(source_filename)
include_spec = include_spec.strip()
include_glob = os.path.join(dirname, include_spec)
include_filenames = glob.glob(include_glob)
if not include_filenames and not glob.has_magic(include_glob):
# Empty set is OK if wildcard but not for direct file reference
raise error("Include file '%s' does not exist" % (include_glob,))
include_filenames.sort()
for include_filename in include_filenames:
include_data = self.read_config_file(include_filename)
self._parse_config(include_data, include_filename, fileconfig,
visited)
return include_filenames
def _parse_config(self, data, filename, fileconfig, visited):
path = os.path.abspath(filename)
if path in visited:
raise error("Recursive include of config file '%s'" % (filename))
visited.add(path)
lines = data.split('\n')
# Buffer lines between includes and parse as a unit so that overrides
# in includes apply linearly as they do within a single file
buf = []
for line in lines:
# Strip trailing comment
pos = line.find('#')
if pos >= 0:
line = line[:pos]
# Process include or buffer line
mo = configparser.RawConfigParser.SECTCRE.match(line)
header = mo and mo.group('header')
if header and header.startswith('include '):
self.append_fileconfig(fileconfig, '\n'.join(buf), filename)
del buf[:]
include_spec = header[8:].strip()
self._resolve_include(filename, include_spec, fileconfig,
visited)
else:
buf.append(line)
self.append_fileconfig(fileconfig, '\n'.join(buf), filename)
visited.remove(path)
def build_fileconfig_with_includes(self, data, filename):
fileconfig = self._create_fileconfig()
self._parse_config(data, filename, fileconfig, set())
return fileconfig
######################################################################
# Config auto save helper
######################################################################
AUTOSAVE_HEADER = """
#*# <---------------------- SAVE_CONFIG ---------------------->
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
#*#
"""
class ConfigAutoSave:
def __init__(self, printer):
self.printer = printer
self.fileconfig = None
self.status_save_pending = {}
self.save_config_pending = False
gcode = self.printer.lookup_object('gcode')
gcode.register_command("SAVE_CONFIG", self.cmd_SAVE_CONFIG,
desc=self.cmd_SAVE_CONFIG_help)
def _find_autosave_data(self, data):
regular_data = data
autosave_data = ""
@@ -259,7 +175,7 @@ class ConfigAutoSave:
regular_data = data[:pos]
autosave_data = data[pos + len(AUTOSAVE_HEADER):].strip()
# Check for errors and strip line prefixes
if "\n#*# " in regular_data or autosave_data.find(AUTOSAVE_HEADER) >= 0:
if "\n#*# " in regular_data:
logging.warning("Can't read autosave from config file"
" - autosave state corrupted")
return data, ""
@@ -276,7 +192,7 @@ class ConfigAutoSave:
return regular_data, "\n".join(out)
comment_r = re.compile('[#;].*$')
value_r = re.compile('[^A-Za-z0-9_].*$')
def _strip_duplicates(self, data, fileconfig):
def _strip_duplicates(self, data, config):
# Comment out fields in 'data' that are defined in 'config'
lines = data.split('\n')
section = None
@@ -294,31 +210,152 @@ class ConfigAutoSave:
section = pruned_line[1:-1].strip()
continue
field = self.value_r.sub('', pruned_line)
if fileconfig.has_option(section, field):
if config.fileconfig.has_option(section, field):
is_dup_field = True
lines[lineno] = '#' + lines[lineno]
return "\n".join(lines)
def load_main_config(self):
def _parse_config_buffer(self, buffer, filename, fileconfig):
if not buffer:
return
data = '\n'.join(buffer)
del buffer[:]
sbuffer = io.StringIO(data)
if sys.version_info.major >= 3:
fileconfig.read_file(sbuffer, filename)
else:
fileconfig.readfp(sbuffer, filename)
def _resolve_include(self, source_filename, include_spec, fileconfig,
visited):
dirname = os.path.dirname(source_filename)
include_spec = include_spec.strip()
include_glob = os.path.join(dirname, include_spec)
include_filenames = glob.glob(include_glob)
if not include_filenames and not glob.has_magic(include_glob):
# Empty set is OK if wildcard but not for direct file reference
raise error("Include file '%s' does not exist" % (include_glob,))
include_filenames.sort()
for include_filename in include_filenames:
include_data = self._read_config_file(include_filename)
self._parse_config(include_data, include_filename, fileconfig,
visited)
return include_filenames
def _parse_config(self, data, filename, fileconfig, visited):
path = os.path.abspath(filename)
if path in visited:
raise error("Recursive include of config file '%s'" % (filename))
visited.add(path)
lines = data.split('\n')
# Buffer lines between includes and parse as a unit so that overrides
# in includes apply linearly as they do within a single file
buffer = []
for line in lines:
# Strip trailing comment
pos = line.find('#')
if pos >= 0:
line = line[:pos]
# Process include or buffer line
mo = configparser.RawConfigParser.SECTCRE.match(line)
header = mo and mo.group('header')
if header and header.startswith('include '):
self._parse_config_buffer(buffer, filename, fileconfig)
include_spec = header[8:].strip()
self._resolve_include(filename, include_spec, fileconfig,
visited)
else:
buffer.append(line)
self._parse_config_buffer(buffer, filename, fileconfig)
visited.remove(path)
def _build_config_wrapper(self, data, filename):
if sys.version_info.major >= 3:
fileconfig = configparser.RawConfigParser(
strict=False, inline_comment_prefixes=(';', '#'))
else:
fileconfig = configparser.RawConfigParser()
self._parse_config(data, filename, fileconfig, set())
return ConfigWrapper(self.printer, fileconfig, {}, 'printer')
def _build_config_string(self, config):
sfile = io.StringIO()
config.fileconfig.write(sfile)
return sfile.getvalue().strip()
def read_config(self, filename):
return self._build_config_wrapper(self._read_config_file(filename),
filename)
def read_main_config(self):
filename = self.printer.get_start_args()['config_file']
cfgrdr = ConfigFileReader()
data = cfgrdr.read_config_file(filename)
data = self._read_config_file(filename)
regular_data, autosave_data = self._find_autosave_data(data)
regular_fileconfig = cfgrdr.build_fileconfig_with_includes(
regular_data, filename)
autosave_data = self._strip_duplicates(autosave_data,
regular_fileconfig)
self.fileconfig = cfgrdr.build_fileconfig(autosave_data, filename)
cfgrdr.append_fileconfig(regular_fileconfig,
autosave_data, '*AUTOSAVE*')
return regular_fileconfig, self.fileconfig
regular_config = self._build_config_wrapper(regular_data, filename)
autosave_data = self._strip_duplicates(autosave_data, regular_config)
self.autosave = self._build_config_wrapper(autosave_data, filename)
cfg = self._build_config_wrapper(regular_data + autosave_data, filename)
return cfg
def check_unused_options(self, config):
fileconfig = config.fileconfig
objects = dict(self.printer.lookup_objects())
# Determine all the fields that have been accessed
access_tracking = dict(config.access_tracking)
for section in self.autosave.fileconfig.sections():
for option in self.autosave.fileconfig.options(section):
access_tracking[(section.lower(), option.lower())] = 1
# Validate that there are no undefined parameters in the config file
valid_sections = { s: 1 for s, o in access_tracking }
for section_name in fileconfig.sections():
section = section_name.lower()
if section not in valid_sections and section not in objects:
raise error("Section '%s' is not a valid config section"
% (section,))
for option in fileconfig.options(section_name):
option = option.lower()
if (section, option) not in access_tracking:
raise error("Option '%s' is not valid in section '%s'"
% (option, section))
# Setup get_status()
self._build_status(config)
def log_config(self, config):
lines = ["===== Config file =====",
self._build_config_string(config),
"======================="]
self.printer.set_rollover_info("config", "\n".join(lines))
# Status reporting
def runtime_warning(self, msg):
logging.warning(msg)
res = {'type': 'runtime_warning', 'message': msg}
self.runtime_warnings.append(res)
self.status_warnings = self.runtime_warnings + self.deprecate_warnings
def deprecate(self, section, option, value=None, msg=None):
self.deprecated[(section, option, value)] = msg
def _build_status(self, config):
self.status_raw_config.clear()
for section in config.get_prefix_sections(''):
self.status_raw_config[section.get_name()] = section_status = {}
for option in section.get_prefix_options(''):
section_status[option] = section.get(option, note_valid=False)
self.status_settings = {}
for (section, option), value in config.access_tracking.items():
self.status_settings.setdefault(section, {})[option] = value
self.deprecate_warnings = []
for (section, option, value), msg in self.deprecated.items():
if value is None:
res = {'type': 'deprecated_option'}
else:
res = {'type': 'deprecated_value', 'value': value}
res['message'] = msg
res['section'] = section
res['option'] = option
self.deprecate_warnings.append(res)
self.status_warnings = self.runtime_warnings + self.deprecate_warnings
def get_status(self, eventtime):
return {'save_config_pending': self.save_config_pending,
return {'config': self.status_raw_config,
'settings': self.status_settings,
'warnings': self.status_warnings,
'save_config_pending': self.save_config_pending,
'save_config_pending_items': self.status_save_pending}
# Autosave functions
def set(self, section, option, value):
if not self.fileconfig.has_section(section):
self.fileconfig.add_section(section)
if not self.autosave.fileconfig.has_section(section):
self.autosave.fileconfig.add_section(section)
svalue = str(value)
self.fileconfig.set(section, option, svalue)
self.autosave.fileconfig.set(section, option, svalue)
pending = dict(self.status_save_pending)
if not section in pending or pending[section] is None:
pending[section] = {}
@@ -329,8 +366,8 @@ class ConfigAutoSave:
self.save_config_pending = True
logging.info("save_config: set [%s] %s = %s", section, option, svalue)
def remove_section(self, section):
if self.fileconfig.has_section(section):
self.fileconfig.remove_section(section)
if self.autosave.fileconfig.has_section(section):
self.autosave.fileconfig.remove_section(section)
pending = dict(self.status_save_pending)
pending[section] = None
self.status_save_pending = pending
@@ -341,20 +378,21 @@ class ConfigAutoSave:
del pending[section]
self.status_save_pending = pending
self.save_config_pending = True
def _disallow_include_conflicts(self, regular_fileconfig):
for section in self.fileconfig.sections():
for option in self.fileconfig.options(section):
if regular_fileconfig.has_option(section, option):
def _disallow_include_conflicts(self, regular_data, cfgname, gcode):
config = self._build_config_wrapper(regular_data, cfgname)
for section in self.autosave.fileconfig.sections():
for option in self.autosave.fileconfig.options(section):
if config.fileconfig.has_option(section, option):
msg = ("SAVE_CONFIG section '%s' option '%s' conflicts "
"with included value" % (section, option))
raise self.printer.command_error(msg)
raise gcode.error(msg)
cmd_SAVE_CONFIG_help = "Overwrite config file and restart"
def cmd_SAVE_CONFIG(self, gcmd):
if not self.fileconfig.sections():
if not self.autosave.fileconfig.sections():
return
gcode = self.printer.lookup_object('gcode')
# Create string containing autosave data
cfgrdr = ConfigFileReader()
autosave_data = cfgrdr.build_config_string(self.fileconfig)
autosave_data = self._build_config_string(self.autosave)
lines = [('#*# ' + l).strip()
for l in autosave_data.split('\n')]
lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip())
@@ -363,27 +401,16 @@ class ConfigAutoSave:
# Read in and validate current config file
cfgname = self.printer.get_start_args()['config_file']
try:
data = cfgrdr.read_config_file(cfgname)
except error as e:
msg = "Unable to read existing config on SAVE_CONFIG"
logging.exception(msg)
raise gcmd.error(msg)
regular_data, old_autosave_data = self._find_autosave_data(data)
regular_data = self._strip_duplicates(regular_data, self.fileconfig)
data = regular_data.rstrip() + autosave_data
new_regular_data, new_autosave_data = self._find_autosave_data(data)
if not new_autosave_data:
raise gcmd.error(
"Existing config autosave is corrupted."
" Can't complete SAVE_CONFIG")
try:
regular_fileconfig = cfgrdr.build_fileconfig_with_includes(
new_regular_data, cfgname)
data = self._read_config_file(cfgname)
regular_data, old_autosave_data = self._find_autosave_data(data)
config = self._build_config_wrapper(regular_data, cfgname)
except error as e:
msg = "Unable to parse existing config on SAVE_CONFIG"
logging.exception(msg)
raise gcmd.error(msg)
self._disallow_include_conflicts(regular_fileconfig)
raise gcode.error(msg)
regular_data = self._strip_duplicates(regular_data, self.autosave)
self._disallow_include_conflicts(regular_data, cfgname, gcode)
data = regular_data.rstrip() + autosave_data
# Determine filenames
datestr = time.strftime("-%Y%m%d_%H%M%S")
backup_name = cfgname + datestr
@@ -403,135 +430,6 @@ class ConfigAutoSave:
except:
msg = "Unable to write config file during SAVE_CONFIG"
logging.exception(msg)
raise gcmd.error(msg)
raise gcode.error(msg)
# Request a restart
gcode = self.printer.lookup_object('gcode')
gcode.request_restart('restart')
######################################################################
# Config validation (check for undefined options)
######################################################################
class ConfigValidate:
def __init__(self, printer):
self.printer = printer
self.status_settings = {}
self.access_tracking = {}
self.autosave_options = {}
def start_access_tracking(self, autosave_fileconfig):
# Note autosave options for use during undefined options check
self.autosave_options = {}
for section in autosave_fileconfig.sections():
for option in autosave_fileconfig.options(section):
self.autosave_options[(section.lower(), option.lower())] = 1
self.access_tracking = {}
return self.access_tracking
def check_unused(self, fileconfig):
# Don't warn on fields set in autosave segment
access_tracking = dict(self.access_tracking)
access_tracking.update(self.autosave_options)
# Note locally used sections
valid_sections = { s: 1 for s, o in self.printer.lookup_objects() }
valid_sections.update({ s: 1 for s, o in access_tracking })
# Validate that there are no undefined parameters in the config file
for section_name in fileconfig.sections():
section = section_name.lower()
if section not in valid_sections:
raise error("Section '%s' is not a valid config section"
% (section,))
for option in fileconfig.options(section_name):
option = option.lower()
if (section, option) not in access_tracking:
raise error("Option '%s' is not valid in section '%s'"
% (option, section))
# Setup get_status()
self._build_status_settings()
# Clear tracking state
self.access_tracking.clear()
self.autosave_options.clear()
def _build_status_settings(self):
self.status_settings = {}
for (section, option), value in self.access_tracking.items():
self.status_settings.setdefault(section, {})[option] = value
def get_status(self, eventtime):
return {'settings': self.status_settings}
######################################################################
# Main printer config tracking
######################################################################
class PrinterConfig:
def __init__(self, printer):
self.printer = printer
self.autosave = ConfigAutoSave(printer)
self.validate = ConfigValidate(printer)
self.deprecated = {}
self.runtime_warnings = []
self.deprecate_warnings = []
self.status_raw_config = {}
self.status_warnings = []
def get_printer(self):
return self.printer
def read_config(self, filename):
cfgrdr = ConfigFileReader()
data = cfgrdr.read_config_file(filename)
fileconfig = cfgrdr.build_fileconfig(data, filename)
return ConfigWrapper(self.printer, fileconfig, {}, 'printer')
def read_main_config(self):
fileconfig, autosave_fileconfig = self.autosave.load_main_config()
access_tracking = self.validate.start_access_tracking(
autosave_fileconfig)
config = ConfigWrapper(self.printer, fileconfig,
access_tracking, 'printer')
self._build_status_config(config)
return config
def log_config(self, config):
cfgrdr = ConfigFileReader()
lines = ["===== Config file =====",
cfgrdr.build_config_string(config.fileconfig),
"======================="]
self.printer.set_rollover_info("config", "\n".join(lines))
def check_unused_options(self, config):
self.validate.check_unused(config.fileconfig)
# Deprecation warnings
def runtime_warning(self, msg):
logging.warning(msg)
res = {'type': 'runtime_warning', 'message': msg}
self.runtime_warnings.append(res)
self.status_warnings = self.runtime_warnings + self.deprecate_warnings
def deprecate(self, section, option, value=None, msg=None):
key = (section, option, value)
if key in self.deprecated and self.deprecated[key] == msg:
return
self.deprecated[key] = msg
self.deprecate_warnings = []
for (section, option, value), msg in self.deprecated.items():
if value is None:
res = {'type': 'deprecated_option'}
else:
res = {'type': 'deprecated_value', 'value': value}
res['message'] = msg
res['section'] = section
res['option'] = option
self.deprecate_warnings.append(res)
self.status_warnings = self.runtime_warnings + self.deprecate_warnings
# Status reporting
def _build_status_config(self, config):
self.status_raw_config = {}
for section in config.get_prefix_sections(''):
self.status_raw_config[section.get_name()] = section_status = {}
for option in section.get_prefix_options(''):
section_status[option] = section.get(option, note_valid=False)
def get_status(self, eventtime):
status = {'config': self.status_raw_config,
'warnings': self.status_warnings}
status.update(self.autosave.get_status(eventtime))
status.update(self.validate.get_status(eventtime))
return status
# Autosave functions
def set(self, section, option, value):
self.autosave.set(section, option, value)
def remove_section(self, section):
self.autosave.remove_section(section)

View File

@@ -24,7 +24,7 @@ def hexify(byte_array):
return "[%s]" % (", ".join([hex(b) for b in byte_array]))
class ADS1220:
class ADS1220():
def __init__(self, config):
self.printer = printer = config.get_printer()
self.name = config.get_name().split()[-1]
@@ -42,35 +42,8 @@ class ADS1220:
'660': 660, '1200': 1200, '2000': 2000}
self.sps_options = self.sps_normal.copy()
self.sps_options.update(self.sps_turbo)
self.sps = config.getchoice('sample_rate', self.sps_options,
default='660')
self.sps = config.getchoice('sps', self.sps_options, default='660')
self.is_turbo = str(self.sps) in self.sps_turbo
# Input multiplexer: AINP and AINN
mux_options = {'AIN0_AIN1': 0b0000, 'AIN0_AIN2': 0b0001,
'AIN0_AIN3': 0b0010, 'AIN1_AIN2': 0b0011,
'AIN1_AIN3': 0b0100, 'AIN2_AIN3': 0b0101,
'AIN1_AIN0': 0b0110, 'AIN3_AIN2': 0b0111,
'AIN0_AVSS': 0b1000, 'AIN1_AVSS': 0b1001,
'AIN2_AVSS': 0b1010, 'AIN3_AVSS': 0b1011}
self.mux = config.getchoice('input_mux', mux_options,
default='AIN0_AIN1')
# PGA Bypass
self.pga_bypass = config.getboolean('pga_bypass', default=False)
# bypass PGA when AVSS is the negative input
force_pga_bypass = self.mux >= 0b1000
self.pga_bypass = force_pga_bypass or self.pga_bypass
# Voltage Reference
self.vref_options = {'internal': 0b0, 'REF0': 0b01, 'REF1': 0b10,
'analog_supply': 0b11}
self.vref = config.getchoice('vref', self.vref_options,
default='internal')
# check for conflict between REF1 and AIN0/AIN3
mux_conflict = [0b0000, 0b0001, 0b0010, 0b0100, 0b0101, 0b0110, 0b0111,
0b1000, 0b1011]
if self.vref == 0b10 and self.mux in mux_conflict:
raise config.error("ADS1220 config error: AIN0/REFP1 and AIN3/REFN1"
" cant be used as a voltage reference and"
" an input at the same time")
# SPI Setup
spi_speed = 512000 if self.is_turbo else 256000
self.spi = bus.MCU_SPI_from_config(config, 1, default_speed=spi_speed)
@@ -96,9 +69,9 @@ class ADS1220:
self.printer, self._process_batch, self._start_measurements,
self._finish_measurements, UPDATE_INTERVAL)
# publish raw samples to the socket
hdr = {'header': ('time', 'counts', 'value')}
self.batch_bulk.add_mux_endpoint("ads1220/dump_ads1220", "sensor",
self.name, hdr)
self.name,
{'header': ('time', 'counts')})
# Command Configuration
mcu.add_config_cmd(
"config_ads1220 oid=%d spi_oid=%d data_ready_pin=%s"
@@ -184,10 +157,8 @@ class ADS1220:
mode = 0x2 if self.is_turbo else 0x0 # turbo mode
sps_list = self.sps_turbo if self.is_turbo else self.sps_normal
data_rate = list(sps_list.keys()).index(str(self.sps))
reg_values = [(self.mux << 4) | (self.gain << 1) | int(self.pga_bypass),
(data_rate << 5) | (mode << 3) | (continuous << 2),
(self.vref << 6),
0x0]
reg_values = [(self.gain << 1),
(data_rate << 5) | (mode << 3) | (continuous << 2)]
self.write_reg(0x0, reg_values)
# start measurements immediately
self.send_command(START_SYNC_CMD)
@@ -206,7 +177,7 @@ class ADS1220:
write_command.extend(register_bytes)
self.spi.spi_send(write_command)
stored_val = self.read_reg(reg, len(register_bytes))
if bytearray(register_bytes) != stored_val:
if register_bytes != stored_val:
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)" % (

View File

@@ -1,393 +0,0 @@
# Support for I2C based ADS1013, ADS1014, ADS1015, ADS1113, ADS1114 and ADS1115
#
# Copyright (C) 2024 Konstantin Koch <korsarnek@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
import pins
from . import bus
# Supported chip types
ADS1X1X_CHIP_TYPE = {
'ADS1013': 3,
'ADS1014': 4,
'ADS1015': 5,
'ADS1113': 13,
'ADS1114': 14,
'ADS1115': 15
}
def isADS101X(chip):
return (chip == ADS1X1X_CHIP_TYPE['ADS1013'] \
or chip == ADS1X1X_CHIP_TYPE['ADS1014'] \
or chip == ADS1X1X_CHIP_TYPE['ADS1015'])
def isADS111X(chip):
return (chip == ADS1X1X_CHIP_TYPE['ADS1113'] \
or chip == ADS1X1X_CHIP_TYPE['ADS1114'] \
or chip == ADS1X1X_CHIP_TYPE['ADS1115'])
# Address is defined by how the address pin is wired
ADS1X1X_CHIP_ADDR = {
'GND': 0x48,
'VCC': 0x49,
'SDA': 0x4a,
'SCL': 0x4b
}
# Chip "pointer" registers
ADS1X1X_REG_POINTER_MASK = 0x03
ADS1X1X_REG_POINTER = {
'CONVERSION': 0x00,
'CONFIG': 0x01,
'LO_THRESH': 0x02,
'HI_THRESH': 0x03
}
# Config register masks
ADS1X1X_REG_CONFIG = {
'OS_MASK': 0x8000,
'MULTIPLEXER_MASK': 0x7000,
'PGA_MASK': 0x0E00,
'MODE_MASK': 0x0100,
'DATA_RATE_MASK': 0x00E0,
'COMPARATOR_MODE_MASK': 0x0010,
'COMPARATOR_POLARITY_MASK': 0x0008,
# Determines if ALERT/RDY pin latches once asserted
'COMPARATOR_LATCHING_MASK': 0x0004,
'COMPARATOR_QUEUE_MASK': 0x0003
}
#
# The following enums are to be used with the configuration functions.
#
ADS1X1X_OS = {
'OS_IDLE': 0x8000, # Device is not performing a conversion
'OS_SINGLE': 0x8000 # Single-conversion
}
ADS1X1X_MUX = {
'DIFF01': 0x0000, # Differential P = AIN0, N = AIN1 0
'DIFF03': 0x1000, # Differential P = AIN0, N = AIN3 4096
'DIFF13': 0x2000, # Differential P = AIN1, N = AIN3 8192
'DIFF23': 0x3000, # Differential P = AIN2, N = AIN3 12288
'AIN0': 0x4000, # Single-ended (ADS1015: AIN0 16384)
'AIN1': 0x5000, # Single-ended (ADS1015: AIN1 20480)
'AIN2': 0x6000, # Single-ended (ADS1015: AIN2 24576)
'AIN3': 0x7000 # Single-ended (ADS1015: AIN3 28672)
}
ADS1X1X_PGA = {
'6.144V': 0x0000, # +/-6.144V range = Gain 2/3
'4.096V': 0x0200, # +/-4.096V range = Gain 1
'2.048V': 0x0400, # +/-2.048V range = Gain 2
'1.024V': 0x0600, # +/-1.024V range = Gain 4
'0.512V': 0x0800, # +/-0.512V range = Gain 8
'0.256V': 0x0A00 # +/-0.256V range = Gain 16
}
ADS1X1X_PGA_VALUE = {
0x0000: 6.144,
0x0200: 4.096,
0x0400: 2.048,
0x0600: 1.024,
0x0800: 0.512,
0x0A00: 0.256,
}
ADS111X_RESOLUTION = 32767.0
ADS111X_PGA_SCALAR = {
0x0000: 6.144 / ADS111X_RESOLUTION, # +/-6.144V range = Gain 2/3
0x0200: 4.096 / ADS111X_RESOLUTION, # +/-4.096V range = Gain 1
0x0400: 2.048 / ADS111X_RESOLUTION, # +/-2.048V range = Gain 2
0x0600: 1.024 / ADS111X_RESOLUTION, # +/-1.024V range = Gain 4
0x0800: 0.512 / ADS111X_RESOLUTION, # +/-0.512V range = Gain 8
0x0A00: 0.256 / ADS111X_RESOLUTION # +/-0.256V range = Gain 16
}
ADS101X_RESOLUTION = 2047.0
ADS101X_PGA_SCALAR = {
0x0000: 6.144 / ADS101X_RESOLUTION, # +/-6.144V range = Gain 2/3
0x0200: 4.096 / ADS101X_RESOLUTION, # +/-4.096V range = Gain 1
0x0400: 2.048 / ADS101X_RESOLUTION, # +/-2.048V range = Gain 2
0x0600: 1.024 / ADS101X_RESOLUTION, # +/-1.024V range = Gain 4
0x0800: 0.512 / ADS101X_RESOLUTION, # +/-0.512V range = Gain 8
0x0A00: 0.256 / ADS101X_RESOLUTION # +/-0.256V range = Gain 16
}
ADS1X1X_MODE = {
'continuous': 0x0000, # Continuous conversion mode
'single': 0x0100 # Power-down single-shot mode
}
# Lesser samples per second means it takes and averages more samples before
# returning a result.
ADS101X_SAMPLES_PER_SECOND = {
'128': 0x0000, # 128 samples per second
'250': 0x0020, # 250 samples per second
'490': 0x0040, # 490 samples per second
'920': 0x0060, # 920 samples per second
'1600': 0x0080, # 1600 samples per second
'2400': 0x00a0, # 2400 samples per second
'3300': 0x00c0, # 3300 samples per second
}
ADS111X_SAMPLES_PER_SECOND = {
'8': 0x0000, # 8 samples per second
'16': 0x0020, # 16 samples per second
'32': 0x0040, # 32 samples per second
'64': 0x0060, # 64 samples per second
'128': 0x0080, # 128 samples per second
'250': 0x00a0, # 250 samples per second
'475': 0x00c0, # 475 samples per second
'860': 0x00e0 # 860 samples per second
}
ADS1X1X_COMPARATOR_MODE = {
'TRADITIONAL': 0x0000, # Traditional comparator with hysteresis
'WINDOW': 0x0010 # Window comparator
}
ADS1X1X_COMPARATOR_POLARITY = {
'ACTIVE_LO': 0x0000, # ALERT/RDY pin is low when active
'ACTIVE_HI': 0x0008 # ALERT/RDY pin is high when active
}
ADS1X1X_COMPARATOR_LATCHING = {
'NON_LATCHING': 0x0000, # Non-latching comparator
'LATCHING': 0x0004 # Latching comparator
}
ADS1X1X_COMPARATOR_QUEUE = {
'QUEUE_1': 0x0000, # Assert ALERT/RDY after one conversions
'QUEUE_2': 0x0001, # Assert ALERT/RDY after two conversions
'QUEUE_4': 0x0002, # Assert ALERT/RDY after four conversions
'QUEUE_NONE': 0x0003 # Disable the comparator and put ALERT/RDY
# in high state
}
ADS1X1_OPERATIONS = {
'SET_MUX': 0,
'READ_CONVERSION': 1
}
class ADS1X1X_chip:
def __init__(self, config):
self._printer = config.get_printer()
self._reactor = self._printer.get_reactor()
self.name = config.get_name().split()[-1]
self.chip = config.getchoice('chip', ADS1X1X_CHIP_TYPE)
address = ADS1X1X_CHIP_ADDR['GND']
# If none is specified, i2c_address can be used for a specific address
if config.get('address_pin', None) is not None:
address = config.getchoice('address_pin', ADS1X1X_CHIP_ADDR)
self._ppins = self._printer.lookup_object("pins")
self._ppins.register_chip(self.name, self)
self.pga = config.getchoice('pga', ADS1X1X_PGA, '4.096V')
self.adc_voltage = config.getfloat('adc_voltage', above=0., default=3.3)
# Comparators are not implemented, they would only be useful if the
# alert pin is used, which we haven't made configurable.
# But that wouldn't be useful for a normal temperature sensor anyway.
self.comp_mode = ADS1X1X_COMPARATOR_MODE['TRADITIONAL']
self.comp_polarity = ADS1X1X_COMPARATOR_POLARITY['ACTIVE_LO']
self.comp_latching = ADS1X1X_COMPARATOR_LATCHING['NON_LATCHING']
self.comp_queue = ADS1X1X_COMPARATOR_QUEUE['QUEUE_NONE']
self._i2c = bus.MCU_I2C_from_config(config, address)
self.mcu = self._i2c.get_mcu()
self._printer.add_object("ads1x1x " + self.name, self)
self._printer.register_event_handler("klippy:connect", \
self._handle_connect)
self._pins = {}
self._mutex = self._reactor.mutex()
def setup_pin(self, pin_type, pin_params):
pin = pin_params['pin']
if pin_type == 'adc':
if (pin not in ADS1X1X_MUX):
raise pins.error('ADS1x1x pin %s is not valid' % \
pin_params['pin'])
config = 0
config |= (ADS1X1X_OS['OS_SINGLE'] & \
ADS1X1X_REG_CONFIG['OS_MASK'])
config |= (ADS1X1X_MUX[pin_params['pin']] & \
ADS1X1X_REG_CONFIG['MULTIPLEXER_MASK'])
config |= (self.pga & ADS1X1X_REG_CONFIG['PGA_MASK'])
# Have to use single mode, because in continuous, it never reaches
# idle state, which we use to determine if the sampling is done.
config |= (ADS1X1X_MODE['single'] & \
ADS1X1X_REG_CONFIG['MODE_MASK'])
# lowest sample rate per default, until report time has been set in
# setup_adc_sample
config |= (self.comp_mode \
& ADS1X1X_REG_CONFIG['COMPARATOR_MODE_MASK'])
config |= (self.comp_polarity \
& ADS1X1X_REG_CONFIG['COMPARATOR_POLARITY_MASK'])
config |= (self.comp_latching \
& ADS1X1X_REG_CONFIG['COMPARATOR_LATCHING_MASK'])
config |= (self.comp_queue \
& ADS1X1X_REG_CONFIG['COMPARATOR_QUEUE_MASK'])
pin_obj = ADS1X1X_pin(self, config)
if pin in self._pins:
raise pins.error(
'pin %s for chip %s is used multiple times' \
% (pin, self.name))
self._pins[pin] = pin_obj
return pin_obj
raise pins.error('Wrong pin or incompatible type: %s with type %s! ' % (
pin, pin_type))
def _handle_connect(self):
try:
# Init all devices on bus for this kind of device
self._i2c.i2c_write([0x06, 0x00, 0x00])
except Exception:
logging.exception("ADS1X1X: error while resetting device")
def is_ready(self):
config = self._read_register(ADS1X1X_REG_POINTER['CONFIG'])
return bool((config & ADS1X1X_REG_CONFIG['OS_MASK']) == \
ADS1X1X_OS['OS_IDLE'])
def calculate_sample_rate(self):
pin_count = len(self._pins)
lowest_report_time = 1
for pin in self._pins.values():
lowest_report_time = min(lowest_report_time, pin.report_time)
sample_rate = 1 / lowest_report_time * pin_count
samples_per_second = ADS111X_SAMPLES_PER_SECOND
if isADS101X(self.chip):
samples_per_second = ADS101X_SAMPLES_PER_SECOND
# make sure the samples list is sorted correctly by number.
samples_per_second = sorted(samples_per_second.items(), \
key=lambda t: int(t[0]))
for rate, bits in samples_per_second:
rate_number = int(rate)
if sample_rate <= rate_number:
return (rate_number, bits)
logging.warning(
"ADS1X1X: requested sample rate %s is higher than supported by %s."\
% (sample_rate, self.name))
return (rate_number, bits)
def handle_report_time_update(self):
(sample_rate, sample_rate_bits) = self.calculate_sample_rate()
for pin in self._pins.values():
pin.config = (pin.config & ~ADS1X1X_REG_CONFIG['DATA_RATE_MASK']) \
| (sample_rate_bits & ADS1X1X_REG_CONFIG['DATA_RATE_MASK'])
self.delay = 1 / float(sample_rate)
def sample(self, pin):
with self._mutex:
try:
self._write_register(ADS1X1X_REG_POINTER['CONFIG'], pin.config)
self._reactor.pause(self._reactor.monotonic() + self.delay)
start_time = self._reactor.monotonic()
while not self.is_ready():
self._reactor.pause(self._reactor.monotonic() + 0.001)
# if we waited twice the expected time, mark this an error
if start_time + self.delay < self._reactor.monotonic():
logging.warning("ADS1X1X: timeout during sampling")
return None
return self._read_register(ADS1X1X_REG_POINTER['CONVERSION'])
except Exception as e:
logging.exception("ADS1X1X: error while sampling: %s" % str(e))
return None
def _read_register(self, reg):
# read a single register
params = self._i2c.i2c_read([reg], 2)
buff = bytearray(params['response'])
return (buff[0]<<8 | buff[1])
def _write_register(self, reg, data):
data = [
(reg & 0xFF), # Control register
((data>>8) & 0xFF), # High byte
(data & 0xFF), # Lo byte
]
self._i2c.i2c_write(data)
class ADS1X1X_pin:
def __init__(self, chip, config):
self.mcu = chip.mcu
self.chip = chip
self.config = config
self.invalid_count = 0
self.chip._printer.register_event_handler("klippy:connect", \
self._handle_connect)
def _handle_connect(self):
self._reactor = self.chip._printer.get_reactor()
self._sample_timer = \
self._reactor.register_timer(self._process_sample, \
self._reactor.NOW)
def _process_sample(self, eventtime):
sample = self.chip.sample(self)
if sample is not None:
# The sample is encoded in the top 12 or full 16 bits
# Value's meaning is defined by ADS1X1X_REG_CONFIG['PGA_MASK']
if isADS101X(self.chip.chip):
sample >>= 4
target_value = sample / ADS101X_RESOLUTION
else:
target_value = sample / ADS111X_RESOLUTION
# Thermistors expect a value between 0 and 1 to work. If we use a
# PGA with 4.096V but supply only 3.3V, the reference voltage for
# voltage divider is only 3.3V, not 4.096V. So we remap the range
# from what the PGA allows as range to end up between 0 and 1 for
# the thermistor logic to work as expected.
target_value = target_value * (ADS1X1X_PGA_VALUE[self.chip.pga] / \
self.chip.adc_voltage)
if target_value > self.maxval or target_value < self.minval:
self.invalid_count = self.invalid_count + 1
logging.warning("ADS1X1X: temperature outside range")
self.check_invalid()
else:
self.invalid_count = 0
# Publish result
measured_time = self._reactor.monotonic()
self.callback(self.chip.mcu.estimated_print_time(measured_time),
target_value)
else:
self.invalid_count = self.invalid_count + 1
self.check_invalid()
return eventtime + self.report_time
def check_invalid(self):
if self.invalid_count > self.range_check_count:
self.chip._printer.invoke_shutdown(
"ADS1X1X temperature check failed")
def get_mcu(self):
return self.mcu
def setup_adc_callback(self, report_time, callback):
self.report_time = report_time
self.callback = callback
self.chip.handle_report_time_update()
def setup_adc_sample(self, sample_time, sample_count,
minval=0., maxval=1., range_check_count=0):
self.minval = minval
self.maxval = maxval
self.range_check_count = range_check_count
def load_config_prefix(config):
return ADS1X1X_chip(config)

View File

@@ -411,196 +411,6 @@ class HelperTLE5012B:
parser=lambda x: int(x, 0))
self._write_reg(reg, val)
class HelperMT6816:
SPI_MODE = 3
SPI_SPEED = 10000000
def __init__(self, config, spi, oid):
self.printer = config.get_printer()
self.spi = spi
self.oid = oid
self.mcu = spi.get_mcu()
self.mcu.register_config_callback(self._build_config)
self.spi_angle_transfer_cmd = None
self.is_tcode_absolute = False
self.last_temperature = None
name = config.get_name().split()[-1]
gcode = self.printer.lookup_object("gcode")
gcode.register_mux_command("ANGLE_DEBUG_READ", "CHIP", name,
self.cmd_ANGLE_DEBUG_READ,
desc=self.cmd_ANGLE_DEBUG_READ_help)
def _build_config(self):
cmdqueue = self.spi.get_command_queue()
self.spi_angle_transfer_cmd = self.mcu.lookup_query_command(
"spi_angle_transfer oid=%c data=%*s",
"spi_angle_transfer_response oid=%c clock=%u response=%*s",
oid=self.oid, cq=cmdqueue)
def _send_spi(self, msg):
return self.spi.spi_transfer(msg)
def get_static_delay(self):
return .000001
def _read_reg(self, reg):
msg = [reg, 0, 0]
params = self._send_spi(msg)
resp = bytearray(params['response'])
val = (resp[1] << 8) | resp[2]
return val
def start(self):
pass
cmd_ANGLE_DEBUG_READ_help = "Query low-level angle sensor register"
def cmd_ANGLE_DEBUG_READ(self, gcmd):
reg = 0x83
val = self._read_reg(reg)
gcmd.respond_info("ANGLE REG[0x%02x] = 0x%04x" % (reg, val))
angle = val >> 2
parity = bin(val >> 1).count("1") % 2
gcmd.respond_info("Angle %i ~ %.2f" % (angle, angle * 360 / (1 << 14)))
gcmd.respond_info("No Mag: %i" % (val >> 1 & 0x1))
gcmd.respond_info("Parity: %i == %i" % (parity, val & 0x1))
class HelperMT6826S:
SPI_MODE = 3
SPI_SPEED = 10000000
def __init__(self, config, spi, oid):
self.printer = config.get_printer()
self.stepper_name = config.get('stepper', None)
self.spi = spi
self.oid = oid
self.mcu = spi.get_mcu()
self.mcu.register_config_callback(self._build_config)
self.spi_angle_transfer_cmd = None
self.is_tcode_absolute = False
self.last_temperature = None
name = config.get_name().split()[-1]
gcode = self.printer.lookup_object("gcode")
gcode.register_mux_command("ANGLE_DEBUG_READ", "CHIP", name,
self.cmd_ANGLE_DEBUG_READ,
desc=self.cmd_ANGLE_DEBUG_READ_help)
gcode.register_mux_command("ANGLE_CHIP_CALIBRATE", "CHIP", name,
self.cmd_ANGLE_CHIP_CALIBRATE,
desc=self.cmd_ANGLE_CHIP_CALIBRATE_help)
self.status_map = {
0: "No Calibration",
1: "Running Calibration",
2: "Calibration Failed",
3: "Calibration Successful"
}
def _build_config(self):
cmdqueue = self.spi.get_command_queue()
self.spi_angle_transfer_cmd = self.mcu.lookup_query_command(
"spi_angle_transfer oid=%c data=%*s",
"spi_angle_transfer_response oid=%c clock=%u response=%*s",
oid=self.oid, cq=cmdqueue)
def _send_spi(self, msg):
params = self.spi.spi_transfer(msg)
return params
def get_static_delay(self):
return .00001
def _read_reg(self, reg):
reg = 0x3000 | reg
msg = [reg >> 8, reg & 0xff, 0]
params = self._send_spi(msg)
resp = bytearray(params['response'])
return resp[2]
def _write_reg(self, reg, data):
reg = 0x6000 | reg
msg = [reg >> 8, reg & 0xff, data]
self._send_spi(msg)
def crc8(self, data):
polynomial = 0x07
crc = 0x00
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x80:
crc = (crc << 1) ^ polynomial
else:
crc <<= 1
crc &= 0xFF
return crc
def _read_angle(self, reg):
reg = 0x3000 | reg
msg = [reg >> 8, reg & 0xff, 0, 0, 0, 0]
params = self._send_spi(msg)
resp = bytearray(params['response'])
angle = (resp[2] << 7) | (resp[3] >> 1)
status = resp[4]
crc_computed = self.crc8([resp[2], resp[3], resp[4]])
crc = resp[5]
return angle, status, crc, crc_computed
def start(self):
val = self._read_reg(0x00d)
# Set histeresis to 0.003 degree
self._write_reg(0x00d, (val & 0xf8) | 0x5)
def get_microsteps(self):
configfile = self.printer.lookup_object('configfile')
sconfig = configfile.get_status(None)['settings']
stconfig = sconfig.get(self.stepper_name, {})
microsteps = stconfig['microsteps']
full_steps = stconfig['full_steps_per_rotation']
return microsteps, full_steps
cmd_ANGLE_CHIP_CALIBRATE_help = "Run MT6826s calibration sequence"
def cmd_ANGLE_CHIP_CALIBRATE(self, gcmd):
fmove = self.printer.lookup_object('force_move')
mcu_stepper = fmove.lookup_stepper(self.stepper_name)
if self.stepper_name is None:
gcmd.respond_info("stepper not defined")
return
gcmd.respond_info("MT6826S Run calibration sequence")
gcmd.respond_info("Motor will do 18+ rotations -" +
" ensure pulley is disconnected")
req_freq = self._read_reg(0x00e) >> 4 & 0x7
# Minimal calibration speed
rpm = (3200 >> req_freq) + 1
rps = rpm / 60
move = fmove.manual_move
# Move stepper several turns (to allow internal sensor calibration)
microsteps, full_steps = self.get_microsteps()
step_dist = mcu_stepper.get_step_dist()
full_step_dist = step_dist * microsteps
rotation_dist = full_steps * full_step_dist
move(mcu_stepper, 2 * rotation_dist, rps * rotation_dist)
self._write_reg(0x155, 0x5e)
move(mcu_stepper, 20 * rotation_dist, rps * rotation_dist)
val = self._read_reg(0x113)
code = val >> 6
gcmd.respond_info("Status: %s" % (self.status_map[code]))
while code == 1:
move(mcu_stepper, 5 * rotation_dist, rps * rotation_dist)
val = self._read_reg(0x113)
code = val >> 6
gcmd.respond_info("Status: %s" % (self.status_map[code]))
if code == 2:
gcmd.respond_info("Calibration failed")
if code == 3:
gcmd.respond_info("Calibration success, please poweroff sensor")
cmd_ANGLE_DEBUG_READ_help = "Query low-level angle sensor register"
def cmd_ANGLE_DEBUG_READ(self, gcmd):
reg = gcmd.get("REG", minval=0, maxval=0x155,
parser=lambda x: int(x, 0))
if reg == 0x003:
angle, status, crc1, crc2 = self._read_angle(reg)
gcmd.respond_info("ANGLE REG[0x003] = 0x%02x" %
(angle >> 7))
gcmd.respond_info("ANGLE REG[0x004] = 0x%02x" %
((angle << 1) & 0xff))
gcmd.respond_info("Angle %i ~ %.2f" % (angle,
angle * 360 / (1 << 15)))
gcmd.respond_info("Weak Mag: %i" % (status >> 1 & 0x1))
gcmd.respond_info("Under Voltage: %i" % (status >> 2 & 0x1))
gcmd.respond_info("CRC: 0x%02x == 0x%02x" % (crc1, crc2))
elif reg == 0x00e:
val = self._read_reg(reg)
gcmd.respond_info("GPIO_DS = %i" % (val >> 7))
gcmd.respond_info("AUTOCAL_FREQ = %i" % (val >> 4 & 0x7))
elif reg == 0x113:
val = self._read_reg(reg)
gcmd.respond_info("Status: %s" % (self.cal_status[val >> 6]))
else:
val = self._read_reg(reg)
gcmd.respond_info("REG[0x%04x] = 0x%02x" % (reg, val))
BYTES_PER_SAMPLE = 3
SAMPLES_PER_BLOCK = bulk_sensor.MAX_BULK_MSG_SIZE // BYTES_PER_SAMPLE
@@ -617,11 +427,8 @@ class Angle:
self.start_clock = self.time_shift = self.sample_ticks = 0
self.last_sequence = self.last_angle = 0
# Sensor type
sensors = { "a1333": HelperA1333,
"as5047d": HelperAS5047D,
"tle5012b": HelperTLE5012B,
"mt6816": HelperMT6816,
"mt6826s": HelperMT6826S }
sensors = { "a1333": HelperA1333, "as5047d": HelperAS5047D,
"tle5012b": HelperTLE5012B }
sensor_type = config.getchoice('sensor_type', {s: s for s in sensors})
sensor_class = sensors[sensor_type]
self.spi = bus.MCU_SPI_from_config(config, sensor_class.SPI_MODE,

View File

@@ -23,27 +23,18 @@ class AxisTwistCompensation:
self.horizontal_move_z = config.getfloat('horizontal_move_z',
DEFAULT_HORIZONTAL_MOVE_Z)
self.speed = config.getfloat('speed', DEFAULT_SPEED)
self.calibrate_start_x = config.getfloat('calibrate_start_x',
default=None)
self.calibrate_end_x = config.getfloat('calibrate_end_x', default=None)
self.calibrate_y = config.getfloat('calibrate_y', default=None)
self.calibrate_start_x = config.getfloat('calibrate_start_x')
self.calibrate_end_x = config.getfloat('calibrate_end_x')
self.calibrate_y = config.getfloat('calibrate_y')
self.z_compensations = config.getlists('z_compensations',
default=[], parser=float)
self.compensation_start_x = config.getfloat('compensation_start_x',
default=None)
self.compensation_end_x = config.getfloat('compensation_end_x',
self.compensation_end_x = config.getfloat('compensation_start_y',
default=None)
self.calibrate_start_y = config.getfloat('calibrate_start_y',
default=None)
self.calibrate_end_y = config.getfloat('calibrate_end_y', default=None)
self.calibrate_x = config.getfloat('calibrate_x', default=None)
self.compensation_start_y = config.getfloat('compensation_start_y',
default=None)
self.compensation_end_y = config.getfloat('compensation_end_y',
default=None)
self.zy_compensations = config.getlists('zy_compensations',
default=[], parser=float)
self.m = None
self.b = None
# setup calibrater
self.calibrater = Calibrater(self, config)
@@ -52,46 +43,28 @@ class AxisTwistCompensation:
self._update_z_compensation_value)
def _update_z_compensation_value(self, pos):
if self.z_compensations:
pos[2] += self._get_interpolated_z_compensation(
pos[0], 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,
self.compensation_start_y,
self.compensation_end_y
)
def _get_interpolated_z_compensation(
self, coord, z_compensations,
comp_start,
comp_end
):
if not self.z_compensations:
return
x_coord = pos[0]
z_compensations = self.z_compensations
sample_count = len(z_compensations)
spacing = ((comp_end - comp_start)
spacing = ((self.calibrate_end_x - self.calibrate_start_x)
/ (sample_count - 1))
interpolate_t = (coord - comp_start) / spacing
interpolate_t = (x_coord - self.calibrate_start_x) / spacing
interpolate_i = int(math.floor(interpolate_t))
interpolate_i = bed_mesh.constrain(interpolate_i, 0, sample_count - 2)
interpolate_t -= interpolate_i
interpolated_z_compensation = bed_mesh.lerp(
interpolate_t, z_compensations[interpolate_i],
z_compensations[interpolate_i + 1])
return interpolated_z_compensation
pos[2] += interpolated_z_compensation
def clear_compensations(self):
self.z_compensations = []
self.m = None
self.b = None
def clear_compensations(self, axis=None):
if axis is None:
self.z_compensations = []
self.zy_compensations = []
elif axis == 'X':
self.z_compensations = []
elif axis == 'Y':
self.zy_compensations = []
class Calibrater:
def __init__(self, compensation, config):
@@ -107,14 +80,10 @@ class Calibrater:
self._handle_connect)
self.speed = compensation.speed
self.horizontal_move_z = compensation.horizontal_move_z
self.x_start_point = (compensation.calibrate_start_x,
self.start_point = (compensation.calibrate_start_x,
compensation.calibrate_y)
self.x_end_point = (compensation.calibrate_end_x,
self.end_point = (compensation.calibrate_end_x,
compensation.calibrate_y)
self.y_start_point = (compensation.calibrate_x,
compensation.calibrate_start_y)
self.y_end_point = (compensation.calibrate_x,
compensation.calibrate_end_y)
self.results = None
self.current_point_index = None
self.gcmd = None
@@ -150,88 +119,20 @@ class Calibrater:
def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd):
self.gcmd = gcmd
sample_count = gcmd.get_int('SAMPLE_COUNT', DEFAULT_SAMPLE_COUNT)
axis = gcmd.get('AXIS', None)
auto = gcmd.get('AUTO', False)
if axis is not None and auto:
raise self.gcmd.error(
"Cannot use both 'AXIS' and 'AUTO' at the same time."
)
if auto:
self._start_autocalibration(sample_count)
return
if axis is None and not auto:
axis = 'X'
# check for valid sample_count
if sample_count < 2:
if sample_count is None or sample_count < 2:
raise self.gcmd.error(
"SAMPLE_COUNT to probe must be at least 2")
# calculate the points to put the probe at, returned as a list of tuples
nozzle_points = []
if axis == 'X':
self.compensation.clear_compensations('X')
if not all([
self.x_start_point[0],
self.x_end_point[0],
self.x_start_point[1]
]):
raise self.gcmd.error(
"""AXIS_TWIST_COMPENSATION for X axis requires
calibrate_start_x, calibrate_end_x and calibrate_y
to be defined
"""
)
start_point = self.x_start_point
end_point = self.x_end_point
x_axis_range = end_point[0] - start_point[0]
interval_dist = x_axis_range / (sample_count - 1)
for i in range(sample_count):
x = start_point[0] + i * interval_dist
y = start_point[1]
nozzle_points.append((x, y))
elif axis == 'Y':
self.compensation.clear_compensations('Y')
if not all([
self.y_start_point[0],
self.y_end_point[0],
self.y_start_point[1]
]):
raise self.gcmd.error(
"""AXIS_TWIST_COMPENSATION for Y axis requires
calibrate_start_y, calibrate_end_y and calibrate_x
to be defined
"""
)
start_point = self.y_start_point
end_point = self.y_end_point
y_axis_range = end_point[1] - start_point[1]
interval_dist = y_axis_range / (sample_count - 1)
for i in range(sample_count):
x = start_point[0]
y = start_point[1] + i * interval_dist
nozzle_points.append((x, y))
else:
raise self.gcmd.error(
"AXIS_TWIST_COMPENSATION_CALIBRATE: "
"Invalid axis.")
# clear the current config
self.compensation.clear_compensations()
# calculate some values
x_range = self.end_point[0] - self.start_point[0]
interval_dist = x_range / (sample_count - 1)
nozzle_points = self._calculate_nozzle_points(sample_count,
interval_dist)
probe_points = self._calculate_probe_points(
nozzle_points, self.probe_x_offset, self.probe_y_offset)
@@ -241,155 +142,16 @@ class Calibrater:
# begin calibration
self.current_point_index = 0
self.results = []
self.current_axis = axis
self._calibration(probe_points, nozzle_points, interval_dist)
def _calculate_corrections(self, coordinates):
# Extracting x, y, and z values from coordinates
x_coords = [coord[0] for coord in coordinates]
y_coords = [coord[1] for coord in coordinates]
z_coords = [coord[2] for coord in coordinates]
# Calculate the desired point (average of all corner points in z)
# For a general case, we should extract the unique
# combinations of corner points
z_corners = [z_coords[i] for i, coord in enumerate(coordinates)
if (coord[0] in [x_coords[0], x_coords[-1]])
and (coord[1] in [y_coords[0], y_coords[-1]])]
z_desired = sum(z_corners) / len(z_corners)
# Calculate average deformation per axis
unique_x_coords = sorted(set(x_coords))
unique_y_coords = sorted(set(y_coords))
avg_z_x = []
for x in unique_x_coords:
indices = [i for i, coord in enumerate(coordinates)
if coord[0] == x]
avg_z = sum(z_coords[i] for i in indices) / len(indices)
avg_z_x.append(avg_z)
avg_z_y = []
for y in unique_y_coords:
indices = [i for i, coord in enumerate(coordinates)
if coord[1] == y]
avg_z = sum(z_coords[i] for i in indices) / len(indices)
avg_z_y.append(avg_z)
# Calculate corrections to reach the desired point
x_corrections = [z_desired - avg for avg in avg_z_x]
y_corrections = [z_desired - avg for avg in avg_z_y]
return x_corrections, y_corrections
def _start_autocalibration(self, sample_count):
if not all([
self.x_start_point[0],
self.x_end_point[0],
self.y_start_point[0],
self.y_end_point[0]
]):
raise self.gcmd.error(
"""AXIS_TWIST_COMPENSATION_AUTOCALIBRATE requires
calibrate_start_x, calibrate_end_x, calibrate_start_y
and calibrate_end_y to be defined
"""
)
# check for valid sample_count
if sample_count is None or sample_count < 2:
raise self.gcmd.error(
"SAMPLE_COUNT to probe must be at least 2")
# verify no other manual probe is in progress
manual_probe.verify_no_manual_probe(self.printer)
# clear the current config
self.compensation.clear_compensations()
min_x = self.x_start_point[0]
max_x = self.x_end_point[0]
min_y = self.y_start_point[1]
max_y = self.y_end_point[1]
# calculate x positions
interval_x = (max_x - min_x) / (sample_count - 1)
xps = [min_x + interval_x * i for i in range(sample_count)]
# Calculate points array
interval_y = (max_y - min_y) / (sample_count - 1)
flip = False
points = []
def _calculate_nozzle_points(self, sample_count, interval_dist):
# calculate the points to put the probe at, returned as a list of tuples
nozzle_points = []
for i in range(sample_count):
for j in range(sample_count):
if(not flip):
idx = j
else:
idx = sample_count -1 - j
points.append([xps[i], min_y + interval_y * idx ])
flip = not flip
# calculate the points to put the nozzle at, and probe
probe_points = []
for i in range(len(points)):
x = points[i][0] - self.probe_x_offset
y = points[i][1] - self.probe_y_offset
probe_points.append([x, y, self._auto_calibration((x,y))[2]])
# calculate corrections
x_corr, y_corr = self._calculate_corrections(probe_points)
x_corr_str = ', '.join(["{:.6f}".format(x)
for x in x_corr])
y_corr_str = ', '.join(["{:.6f}".format(x)
for x in y_corr])
# finalize
configfile = self.printer.lookup_object('configfile')
configfile.set(self.configname, 'z_compensations', x_corr_str)
configfile.set(self.configname, 'compensation_start_x',
self.x_start_point[0])
configfile.set(self.configname, 'compensation_end_x',
self.x_end_point[0])
configfile.set(self.configname, 'zy_compensations', y_corr_str)
configfile.set(self.configname, 'compensation_start_y',
self.y_start_point[1])
configfile.set(self.configname, 'compensation_end_y',
self.y_end_point[1])
self.gcode.respond_info(
"AXIS_TWIST_COMPENSATION state has been saved "
"for the current session. The SAVE_CONFIG command will "
"update the printer config file and restart the printer.")
# output result
self.gcmd.respond_info(
"AXIS_TWIST_COMPENSATION_AUTOCALIBRATE: Calibration complete: ")
self.gcmd.respond_info("\n".join(map(str, [x_corr, y_corr])), log=False)
def _auto_calibration(self, probe_point):
# horizontal_move_z (to prevent probe trigger or hitting bed)
self._move_helper((None, None, self.horizontal_move_z))
# move to point to probe
self._move_helper((probe_point[0],
probe_point[1], None))
# probe the point
pos = probe.run_single_probe(self.probe, self.gcmd)
# horizontal_move_z (to prevent probe trigger or hitting bed)
self._move_helper((None, None, self.horizontal_move_z))
return pos
x = self.start_point[0] + i * interval_dist
y = self.start_point[1]
nozzle_points.append((x, y))
return nozzle_points
def _calculate_probe_points(self, nozzle_points,
probe_x_offset, probe_y_offset):
@@ -476,31 +238,14 @@ class Calibrater:
configfile = self.printer.lookup_object('configfile')
values_as_str = ', '.join(["{:.6f}".format(x)
for x in self.results])
if(self.current_axis == 'X'):
configfile.set(self.configname, 'z_compensations', values_as_str)
configfile.set(self.configname, 'compensation_start_x',
self.x_start_point[0])
configfile.set(self.configname, 'compensation_end_x',
self.x_end_point[0])
self.compensation.z_compensations = self.results
self.compensation.compensation_start_x = self.x_start_point[0]
self.compensation.compensation_end_x = self.x_end_point[0]
elif(self.current_axis == 'Y'):
configfile.set(self.configname, 'zy_compensations', values_as_str)
configfile.set(self.configname, 'compensation_start_y',
self.y_start_point[1])
configfile.set(self.configname, 'compensation_end_y',
self.y_end_point[1])
self.compensation.zy_compensations = self.results
self.compensation.compensation_start_y = self.y_start_point[1]
self.compensation.compensation_end_y = self.y_end_point[1]
configfile.set(self.configname, 'z_compensations', values_as_str)
configfile.set(self.configname, 'compensation_start_x',
self.start_point[0])
configfile.set(self.configname, 'compensation_end_x',
self.end_point[0])
self.compensation.z_compensations = self.results
self.compensation.compensation_start_x = self.start_point[0]
self.compensation.compensation_end_x = self.end_point[0]
self.gcode.respond_info(
"AXIS_TWIST_COMPENSATION state has been saved "
"for the current session. The SAVE_CONFIG command will "

View File

@@ -83,7 +83,6 @@ BMP180_REGS = {
STATUS_MEASURING = 1 << 3
STATUS_IM_UPDATE = 1
MODE = 1
MODE_PERIODIC = 3
RUN_GAS = 1 << 4
NB_CONV_0 = 0
EAS_NEW_DATA = 1 << 7
@@ -144,7 +143,6 @@ class BME280:
pow(2, self.os_temp - 1), pow(2, self.os_hum - 1),
pow(2, self.os_pres - 1)))
logging.info("BMxx80: IIR: %dx" % (pow(2, self.iir_filter) - 1))
self.iir_filter = self.iir_filter & 0x07
self.temp = self.pressure = self.humidity = self.gas = self.t_fine = 0.
self.min_temp = self.max_temp = self.range_switching_error = 0.
@@ -157,7 +155,6 @@ class BME280:
return
self.printer.register_event_handler("klippy:connect",
self.handle_connect)
self.last_gas_time = 0
def handle_connect(self):
self._init_bmxx80()
@@ -284,7 +281,7 @@ class BME280:
self.chip_type, self.i2c.i2c_address))
# Reset chip
self.write_register('RESET', [RESET_CHIP_VALUE], wait=True)
self.write_register('RESET', [RESET_CHIP_VALUE])
self.reactor.pause(self.reactor.monotonic() + .5)
# Make sure non-volatile memory has been copied to registers
@@ -296,15 +293,15 @@ class BME280:
status = self.read_register('STATUS', 1)[0]
if self.chip_type == 'BME680':
self.max_sample_time = \
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
+ ((2.3 * self.os_hum) + .575)) / 1000
self.max_sample_time = 0.5
self.sample_timer = self.reactor.register_timer(self._sample_bme680)
self.chip_registers = BME680_REGS
elif self.chip_type == 'BMP180':
self.max_sample_time = (1.25 + ((2.3 * self.os_pres) + .575)) / 1000
self.sample_timer = self.reactor.register_timer(self._sample_bmp180)
self.chip_registers = BMP180_REGS
elif self.chip_type == 'BMP388':
self.max_sample_time = 0.5
self.chip_registers = BMP388_REGS
self.write_register(
"PWR_CTRL",
@@ -321,18 +318,15 @@ class BME280:
self.write_register("INT_CTRL", [BMP388_REG_VAL_DRDY_EN])
self.sample_timer = self.reactor.register_timer(self._sample_bmp388)
elif self.chip_type == 'BME280':
else:
self.max_sample_time = \
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
+ ((2.3 * self.os_hum) + .575)) / 1000
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
self.chip_registers = BME280_REGS
else:
self.max_sample_time = \
(1.25 + (2.3 * self.os_temp)
+ ((2.3 * self.os_pres) + .575)) / 1000
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
self.chip_registers = BME280_REGS
if self.chip_type in ('BME680', 'BME280'):
self.write_register('CONFIG', (self.iir_filter & 0x07) << 2)
# Read out and calculate the trimming parameters
if self.chip_type == 'BMP180':
@@ -353,64 +347,21 @@ class BME280:
elif self.chip_type == 'BMP388':
self.dig = read_calibration_data_bmp388(cal_1)
if self.chip_type in ('BME280', 'BMP280'):
max_standby_time = REPORT_TIME - self.max_sample_time
# 0.5 ms
t_sb = 0
if self.chip_type == 'BME280':
if max_standby_time > 1:
t_sb = 5
elif max_standby_time > 0.5:
t_sb = 4
elif max_standby_time > 0.25:
t_sb = 3
elif max_standby_time > 0.125:
t_sb = 2
elif max_standby_time > 0.0625:
t_sb = 1
elif max_standby_time > 0.020:
t_sb = 7
elif max_standby_time > 0.010:
t_sb = 6
else:
if max_standby_time > 4:
t_sb = 7
elif max_standby_time > 2:
t_sb = 6
elif max_standby_time > 1:
t_sb = 5
elif max_standby_time > 0.5:
t_sb = 4
elif max_standby_time > 0.25:
t_sb = 3
elif max_standby_time > 0.125:
t_sb = 2
elif max_standby_time > 0.0625:
t_sb = 1
cfg = t_sb << 5 | self.iir_filter << 2
self.write_register('CONFIG', cfg)
if self.chip_type == 'BME280':
self.write_register('CTRL_HUM', self.os_hum)
# Enter normal (periodic) mode
meas = self.os_temp << 5 | self.os_pres << 2 | MODE_PERIODIC
self.write_register('CTRL_MEAS', meas, wait=True)
if self.chip_type == 'BME680':
self.write_register('CONFIG', self.iir_filter << 2)
# Should be set once and reused on every mode register write
self.write_register('CTRL_HUM', self.os_hum & 0x07)
gas_wait_0 = self._calc_gas_heater_duration(self.gas_heat_duration)
self.write_register('GAS_WAIT_0', [gas_wait_0])
res_heat_0 = self._calc_gas_heater_resistance(self.gas_heat_temp)
self.write_register('RES_HEAT_0', [res_heat_0])
# Set initial heater current to reach Gas heater target on start
self.write_register('IDAC_HEAT_0', 96)
def _sample_bme280(self, eventtime):
# In normal mode data shadowing is performed
# So reading can be done while measurements are in process
# Enter forced mode
if self.chip_type == 'BME280':
self.write_register('CTRL_HUM', self.os_hum)
meas = self.os_temp << 5 | self.os_pres << 2 | MODE
self.write_register('CTRL_MEAS', meas)
try:
# wait until results are ready
status = self.read_register('STATUS', 1)[0]
while status & STATUS_MEASURING:
self.reactor.pause(
self.reactor.monotonic() + self.max_sample_time)
status = self.read_register('STATUS', 1)[0]
if self.chip_type == 'BME280':
data = self.read_register('PRESSURE_MSB', 8)
elif self.chip_type == 'BMP280':
@@ -511,40 +462,36 @@ class BME280:
return comp_press
def _sample_bme680(self, eventtime):
def data_ready(stat, run_gas):
self.write_register('CTRL_HUM', self.os_hum & 0x07)
meas = self.os_temp << 5 | self.os_pres << 2
self.write_register('CTRL_MEAS', [meas])
gas_wait_0 = self._calculate_gas_heater_duration(self.gas_heat_duration)
self.write_register('GAS_WAIT_0', [gas_wait_0])
res_heat_0 = self._calculate_gas_heater_resistance(self.gas_heat_temp)
self.write_register('RES_HEAT_0', [res_heat_0])
gas_config = RUN_GAS | NB_CONV_0
self.write_register('CTRL_GAS_1', [gas_config])
def data_ready(stat):
new_data = (stat & EAS_NEW_DATA)
gas_done = not (stat & GAS_DONE)
meas_done = not (stat & MEASURE_DONE)
if not run_gas:
gas_done = True
return new_data and gas_done and meas_done
run_gas = False
# Check VOC once a while
if self.reactor.monotonic() - self.last_gas_time > 3:
gas_config = RUN_GAS | NB_CONV_0
self.write_register('CTRL_GAS_1', [gas_config])
run_gas = True
# Enter forced mode
meas = self.os_temp << 5 | self.os_pres << 2 | MODE
self.write_register('CTRL_MEAS', meas, wait=True)
max_sample_time = self.max_sample_time
if run_gas:
max_sample_time += self.gas_heat_duration / 1000
self.reactor.pause(self.reactor.monotonic() + max_sample_time)
meas = meas | MODE
self.write_register('CTRL_MEAS', meas)
try:
# wait until results are ready
status = self.read_register('EAS_STATUS_0', 1)[0]
while not data_ready(status, run_gas):
while not data_ready(status):
self.reactor.pause(
self.reactor.monotonic() + self.max_sample_time)
status = self.read_register('EAS_STATUS_0', 1)[0]
data = self.read_register('PRESSURE_MSB', 8)
gas_data = [0, 0]
if run_gas:
gas_data = self.read_register('GAS_R_MSB', 2)
gas_data = self.read_register('GAS_R_MSB', 2)
except Exception:
logging.exception("BME680: Error reading data")
self.temp = self.pressure = self.humidity = self.gas = .0
@@ -568,10 +515,6 @@ class BME280:
gas_raw = (gas_data[0] << 2) | ((gas_data[1] & 0xC0) >> 6)
gas_range = (gas_data[1] & 0x0F)
self.gas = self._compensate_gas(gas_raw, gas_range)
# Disable gas measurement on success
gas_config = NB_CONV_0
self.write_register('CTRL_GAS_1', [gas_config])
self.last_gas_time = self.reactor.monotonic()
if self.temp < self.min_temp or self.temp > self.max_temp:
self.printer.invoke_shutdown(
@@ -700,7 +643,7 @@ class BME280:
gas_raw - 512. + var1)
return gas
def _calc_gas_heater_resistance(self, target_temp):
def _calculate_gas_heater_resistance(self, target_temp):
amb_temp = self.temp
heater_data = self.read_register('RES_HEAT_VAL', 3)
res_heat_val = get_signed_byte(heater_data[0])
@@ -715,7 +658,7 @@ class BME280:
* (1. / (1. + (res_heat_val * 0.002)))) - 25))
return int(res_heat)
def _calc_gas_heater_duration(self, duration_ms):
def _calculate_gas_heater_duration(self, duration_ms):
if duration_ms >= 4032:
duration_reg = 0xff
else:
@@ -776,15 +719,12 @@ class BME280:
params = self.i2c.i2c_read(regs, read_len)
return bytearray(params['response'])
def write_register(self, reg_name, data, wait = False):
def write_register(self, reg_name, data):
if type(data) is not list:
data = [data]
reg = self.chip_registers[reg_name]
data.insert(0, reg)
if not wait:
self.i2c.i2c_write(data)
else:
self.i2c.i2c_write_wait_ack(data)
self.i2c.i2c_write(data)
def get_status(self, eventtime):
data = {

View File

@@ -160,7 +160,7 @@ class MCU_I2C:
% (self.oid, speed, addr))
self.cmd_queue = self.mcu.alloc_command_queue()
self.mcu.register_config_callback(self.build_config)
self.i2c_write_cmd = self.i2c_read_cmd = None
self.i2c_write_cmd = self.i2c_read_cmd = self.i2c_modify_bits_cmd = None
def get_oid(self):
return self.oid
def get_mcu(self):
@@ -180,6 +180,9 @@ class MCU_I2C:
"i2c_read oid=%c reg=%*s read_len=%u",
"i2c_read_response oid=%c response=%*s", oid=self.oid,
cq=self.cmd_queue)
self.i2c_modify_bits_cmd = self.mcu.lookup_command(
"i2c_modify_bits oid=%c reg=%*s clear_set_bits=%*s",
cq=self.cmd_queue)
def i2c_write(self, data, minclock=0, reqclock=0):
if self.i2c_write_cmd is None:
# Send setup message via mcu initialization
@@ -194,6 +197,19 @@ class MCU_I2C:
minclock=minclock, reqclock=reqclock)
def i2c_read(self, write, read_len):
return self.i2c_read_cmd.send([self.oid, write, read_len])
def i2c_modify_bits(self, reg, clear_bits, set_bits,
minclock=0, reqclock=0):
clearset = clear_bits + set_bits
if self.i2c_modify_bits_cmd is None:
# Send setup message via mcu initialization
reg_msg = "".join(["%02x" % (x,) for x in reg])
clearset_msg = "".join(["%02x" % (x,) for x in clearset])
self.mcu.add_config_cmd(
"i2c_modify_bits oid=%d reg=%s clear_set_bits=%s" % (
self.oid, reg_msg, clearset_msg), is_init=True)
return
self.i2c_modify_bits_cmd.send([self.oid, reg, clearset],
minclock=minclock, reqclock=reqclock)
def MCU_I2C_from_config(config, default_addr=None, default_speed=100000):
# Load bus parameters

View File

@@ -62,7 +62,9 @@ class ControllerFan:
self.last_on += 1
if speed != self.last_speed:
self.last_speed = speed
self.fan.set_speed(speed)
curtime = self.printer.get_reactor().monotonic()
print_time = self.fan.get_mcu().estimated_print_time(curtime)
self.fan.set_speed(print_time + PIN_MIN_TIME, speed)
return eventtime + 1.
def load_config_prefix(config):

View File

@@ -1,209 +0,0 @@
# Support for YHCB2004 (20x4 text) LCD displays based on AiP31068 controller
#
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2018 Eric Callahan <arksine.code@gmail.com>
# Copyright (C) 2021 Marc-Andre Denis <marcadenis@msn.com>
# Copyright (C) 2024 Alexander Bazarov <oss@bazarov.dev>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
# This file is a modified version of hd44780_spi.py, introducing slightly
# different protocol as implemented in Marlin FW (based on
# https://github.com/red-scorp/LiquidCrystal_AIP31068 ).
# In addition, a hack is used to send 8 commands, each 9 bits, at once,
# allowing the transmission of a full 9 bytes.
# This helps avoid modifying the SW_SPI driver to handle non-8-bit data.
from .. import bus
LINE_LENGTH_DEFAULT=20
LINE_LENGTH_OPTIONS={16:16, 20:20}
TextGlyphs = { 'right_arrow': b'\x7e' }
# Each command is 9 bits long:
# 1 bit for RS (Register Select) - 0 for command, 1 for data
# 8 bits for the command/data
# Command is a bitwise OR of CMND(=opcode) and flg_CMND(=parameters) multiplied
# by 1 or 0 as En/Dis flag.
# cmd = CMND | flg_CMND.param0*0 | flg_CMND.param1*1
# or just by OR with enabled flags:
# cmd = CMND | flg_CMND.param1
class CMND:
CLR = 1 # Clear display
HOME = 2 # Return home
ENTERY_MODE = 2**2 # Entry mode set
DISPLAY = 2**3 # Display on/off control
SHIFT = 2**4 # Cursor or display shift
FUNCTION = 2**5 # Function set
CGRAM = 2**6 # Character Generator RAM
DDRAM = 2**7 # Display Data RAM
WRITE_RAM = 2**8 # Write to RAM
# Define flags for all commands:
class flg_ENTERY_MODE:
INC = 2**1 # Increment
SHIFT = 2**0 # Shift display
class flg_DISPLAY:
ON = 2**2 # Display ON
CURSOR = 2**1 # Cursor ON
BLINK = 2**0 # Blink ON
class flg_SHIFT:
WHOLE_DISPLAY = 2**3 # Shift whole display
RIGHT = 2**2 # Shift right
class flg_FUNCTION:
TWO_LINES = 2**3 # 2-line display mode
FIVE_BY_ELEVEN = 2**2 # 5x11 dot character font
class flg_CGRAM:
MASK = 0b00111111 # CGRAM address mask
class flg_DDRAM:
MASK = 0b01111111 # DDRAM address mask
class flg_WRITE_RAM:
MASK = 0b11111111 # Write RAM mask
DISPLAY_INIT_CMNDS= [
# CMND.CLR - no need as framebuffer will rewrite all
CMND.HOME, # move cursor to home (0x00)
CMND.ENTERY_MODE | flg_ENTERY_MODE.INC, # increment cursor and no shift
CMND.DISPLAY | flg_DISPLAY.ON, # keep cursor and blinking off
CMND.SHIFT | flg_SHIFT.RIGHT, # shift right cursor only
CMND.FUNCTION | flg_FUNCTION.TWO_LINES, # 2-line display mode, 5x8 dots
]
class aip31068_spi:
def __init__(self, config):
self.printer = config.get_printer()
# spi config
self.spi = bus.MCU_SPI_from_config(
config, 0x00, pin_option="latch_pin") # (config, mode, cs_name)
self.mcu = self.spi.get_mcu()
self.icons = {}
self.line_length = config.getchoice('line_length', LINE_LENGTH_OPTIONS,
LINE_LENGTH_DEFAULT)
# each controller's line is 2 lines on the display and hence twice
# line length
self.text_framebuffers = [bytearray(b' '*2*self.line_length),
bytearray(b' '*2*self.line_length)]
self.glyph_framebuffer = bytearray(64)
# all_framebuffers - list of tuples per buffer type.
# Each tuple contains:
# 1. the updated framebuffer
# 2. a copy of the old framebuffer == data on the display
# 3. the command to send to write to this buffer
# Then flush() will compare new data with data on the display
# and send only the differences to the display controller
# and update the old framebuffer with the new data
# (immutable tuple is allowed to store mutable bytearray)
self.all_framebuffers = [
# Text framebuffers
(self.text_framebuffers[0], bytearray(b'~'*2*self.line_length),
CMND.DDRAM | (flg_DDRAM.MASK & 0x00) ),
(self.text_framebuffers[1], bytearray(b'~'*2*self.line_length),
CMND.DDRAM | (flg_DDRAM.MASK & 0x40) ),
# Glyph framebuffer
(self.glyph_framebuffer, bytearray(b'~'*64),
CMND.CGRAM | (flg_CGRAM.MASK & 0x00) ) ]
@staticmethod
def encode(data, width = 9):
encoded_bytes = []
accumulator = 0 # To accumulate bits
acc_bits = 0 # Count of bits in the accumulator
for num in data:
# check that num will fit in width bits
if num >= (1 << width):
raise ValueError("Number {} does not fit in {} bits".
format(num, width))
# Shift the current number into the accumulator from the right
accumulator = (accumulator << width) | num
acc_bits += width # Update the count of bits in the accumulator
# While we have at least 8 bits, form a byte and append it
while acc_bits >= 8:
acc_bits -= 8 # Decrease bit count by 8
# Extract the 8 most significant bits to form a byte
byte = (accumulator >> acc_bits) & 0xFF
# Remove msb 8 bits from the accumulator
accumulator &= (1 << acc_bits) - 1
encoded_bytes.append(byte)
# Handle any remaining bits by padding them on the right to byte
if acc_bits > 0:
last_byte = accumulator << (8 - acc_bits)
encoded_bytes.append(last_byte)
return encoded_bytes
def send(self, data, minclock=0):
# different commands have different processing time
# to avoid timing violation pad with some fast command, e.g. ENTRY_MODE
# that has execution time of 39us (for comparison CLR is 1.53ms)
pad = CMND.ENTERY_MODE | flg_ENTERY_MODE.INC
for i in range(0, len(data), 8):
# Take a slice of 8 numbers
group = data[i:i+8]
# Pad the group if it has fewer than 8 elements
if len(group) < 8:
group.extend([pad] * (8 - len(group)))
self.spi.spi_send(self.encode(group), minclock)
def flush(self):
# Find all differences in the framebuffers and send them to the chip
for new_data, old_data, fb_cmnd in self.all_framebuffers:
if new_data == old_data:
continue
# Find the position of all changed bytes in this framebuffer
diffs = [[i, 1] for i, (n, o) in enumerate(zip(new_data, old_data))
if n != o]
# Batch together changes that are close to each other
for i in range(len(diffs)-2, -1, -1):
pos, count = diffs[i]
nextpos, nextcount = diffs[i+1]
if pos + 4 >= nextpos and nextcount < 16:
diffs[i][1] = nextcount + (nextpos - pos)
del diffs[i+1]
# Transmit changes
for pos, count in diffs:
chip_pos = pos
self.send([fb_cmnd + chip_pos])
self.send([CMND.WRITE_RAM | byte for byte in
new_data[pos:pos+count]])
old_data[:] = new_data
def init(self):
curtime = self.printer.get_reactor().monotonic()
print_time = self.mcu.estimated_print_time(curtime)
for i, cmds in enumerate(DISPLAY_INIT_CMNDS):
minclock = self.mcu.print_time_to_clock(print_time + i * .100)
self.send([cmds], minclock=minclock)
self.flush()
def write_text(self, x, y, data):
if x + len(data) > self.line_length:
data = data[:self.line_length - min(x, self.line_length)]
pos = x + ((y & 0x02) >> 1) * self.line_length
self.text_framebuffers[y & 1][pos:pos+len(data)] = data
def set_glyphs(self, glyphs):
for glyph_name, glyph_data in glyphs.items():
data = glyph_data.get('icon5x8')
if data is not None:
self.icons[glyph_name] = data
def write_glyph(self, x, y, glyph_name):
data = self.icons.get(glyph_name)
if data is not None:
slot, bits = data
self.write_text(x, y, [slot])
self.glyph_framebuffer[slot * 8:(slot + 1) * 8] = bits
return 1
char = TextGlyphs.get(glyph_name)
if char is not None:
# Draw character
self.write_text(x, y, char)
return 1
return 0
def write_graphics(self, x, y, data):
pass # this display supports only hardcoded or 8 user defined glyphs
def clear(self):
spaces = b' ' * 2*self.line_length
self.text_framebuffers[0][:] = spaces
self.text_framebuffers[1][:] = spaces
def get_dimensions(self):
return (self.line_length, 4)

View File

@@ -6,7 +6,7 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, os, ast
from . import aip31068_spi, hd44780, hd44780_spi, st7920, uc1701, menu
from . import hd44780, hd44780_spi, st7920, uc1701, menu
# Normal time between each screen redraw
REDRAW_TIME = 0.500
@@ -17,8 +17,7 @@ LCD_chips = {
'st7920': st7920.ST7920, 'emulated_st7920': st7920.EmulatedST7920,
'hd44780': hd44780.HD44780, 'uc1701': uc1701.UC1701,
'ssd1306': uc1701.SSD1306, 'sh1106': uc1701.SH1106,
'hd44780_spi': hd44780_spi.hd44780_spi,
'aip31068_spi':aip31068_spi.aip31068_spi
'hd44780_spi': hd44780_spi.hd44780_spi
}
# Storage of [display_template my_template] config sections

View File

@@ -1,9 +1,9 @@
# Support for "dotstar" leds
#
# Copyright (C) 2019-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2019-2022 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import bus, led
from . import bus
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@@ -22,8 +22,9 @@ class PrinterDotstar:
self.spi = bus.MCU_SPI(mcu, None, None, 0, 500000, sw_spi_pins)
# Initialize color data
self.chain_count = config.getint('chain_count', 1, minval=1)
self.led_helper = led.LEDHelper(config, self.update_leds,
self.chain_count)
pled = printer.load_object(config, "led")
self.led_helper = pled.setup_helper(config, self.update_leds,
self.chain_count)
self.prev_data = None
# Register commands
printer.register_event_handler("klippy:connect", self.handle_connect)

View File

@@ -1,14 +1,17 @@
# Printer cooling fan
#
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2020 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import pulse_counter, output_pin
from . import pulse_counter
FAN_MIN_TIME = 0.100
class Fan:
def __init__(self, config, default_shutdown_speed=0.):
self.printer = config.get_printer()
self.last_fan_value = self.last_req_value = 0.
self.last_fan_value = 0.
self.last_fan_time = 0.
# Read config
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
self.kick_start_time = config.getfloat('kick_start_time', 0.1,
@@ -33,10 +36,6 @@ class Fan:
self.enable_pin = ppins.setup_pin('digital_out', enable_pin)
self.enable_pin.setup_max_duration(0.)
# Create gcode request queue
self.gcrq = output_pin.GCodeRequestQueue(config, self.mcu_fan.get_mcu(),
self._apply_speed)
# Setup tachometer
self.tachometer = FanTachometer(config)
@@ -46,37 +45,37 @@ class Fan:
def get_mcu(self):
return self.mcu_fan.get_mcu()
def _apply_speed(self, print_time, value):
def set_speed(self, print_time, value):
if value < self.off_below:
value = 0.
value = max(0., min(self.max_power, value * self.max_power))
if value == self.last_fan_value:
return "discard", 0.
return
print_time = max(self.last_fan_time + FAN_MIN_TIME, print_time)
if self.enable_pin:
if value > 0 and self.last_fan_value == 0:
self.enable_pin.set_digital(print_time, 1)
elif value == 0 and self.last_fan_value > 0:
self.enable_pin.set_digital(print_time, 0)
if (value and self.kick_start_time
if (value and value < self.max_power and self.kick_start_time
and (not self.last_fan_value or value - self.last_fan_value > .5)):
# Run fan at full speed for specified kick_start_time
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
self.last_fan_value = self.last_req_value = value
print_time += self.kick_start_time
self.mcu_fan.set_pwm(print_time, value)
def set_speed(self, value, print_time=None):
self.gcrq.send_async_request(value, print_time)
self.last_fan_time = print_time
self.last_fan_value = value
def set_speed_from_command(self, value):
self.gcrq.queue_gcode_request(value)
toolhead = self.printer.lookup_object('toolhead')
toolhead.register_lookahead_callback((lambda pt:
self.set_speed(pt, value)))
def _handle_request_restart(self, print_time):
self.set_speed(0., print_time)
self.set_speed(print_time, 0.)
def get_status(self, eventtime):
tachometer_status = self.tachometer.get_status(eventtime)
return {
'speed': self.last_req_value,
'speed': self.last_fan_value,
'rpm': tachometer_status['rpm'],
}

View File

@@ -1,10 +1,9 @@
# Support fans that are controlled by gcode
#
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2020 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
from . import fan, output_pin
from . import fan
class PrinterFanGeneric:
cmd_SET_FAN_SPEED_help = "Sets the speed of a fan"
@@ -13,9 +12,6 @@ class PrinterFanGeneric:
self.fan = fan.Fan(config, default_shutdown_speed=0.)
self.fan_name = config.get_name().split()[-1]
# Template handling
self.template_eval = output_pin.lookup_template_eval(config)
gcode = self.printer.lookup_object("gcode")
gcode.register_mux_command("SET_FAN_SPEED", "FAN",
self.fan_name,
@@ -24,21 +20,8 @@ class PrinterFanGeneric:
def get_status(self, eventtime):
return self.fan.get_status(eventtime)
def _template_update(self, text):
try:
value = float(text)
except ValueError as e:
logging.exception("fan_generic template render error")
self.fan.set_speed(value)
def cmd_SET_FAN_SPEED(self, gcmd):
speed = gcmd.get_float('SPEED', None, 0.)
template = gcmd.get('TEMPLATE', None)
if (speed is None) == (template is None):
raise gcmd.error("SET_FAN_SPEED must specify SPEED or TEMPLATE")
# Check for template setting
if template is not None:
self.template_eval.set_template(gcmd, self._template_update)
return
speed = gcmd.get_float('SPEED', 0.)
self.fan.set_speed_from_command(speed)
def load_config_prefix(config):

View File

@@ -131,12 +131,8 @@ class ForceMove:
x = gcmd.get_float('X', curpos[0])
y = gcmd.get_float('Y', curpos[1])
z = gcmd.get_float('Z', curpos[2])
clear = gcmd.get('CLEAR', '').lower()
clear_axes = "".join([a for a in "xyz" if a in clear])
logging.info("SET_KINEMATIC_POSITION pos=%.3f,%.3f,%.3f clear=%s",
x, y, z, clear_axes)
toolhead.set_position([x, y, z, curpos[3]], homing_axes="xyz")
toolhead.get_kinematics().clear_homing_state(clear_axes)
logging.info("SET_KINEMATIC_POSITION pos=%.3f,%.3f,%.3f", x, y, z)
toolhead.set_position([x, y, z, curpos[3]], homing_axes=(0, 1, 2))
def load_config(config):
return ForceMove(config)

View File

@@ -39,6 +39,8 @@ class ArcSupport:
self.gcode.register_command("G18", self.cmd_G18)
self.gcode.register_command("G19", self.cmd_G19)
self.Coord = self.gcode.Coord
# backwards compatibility, prior implementation only supported XY
self.plane = ARC_PLANE_X_Y
@@ -62,36 +64,52 @@ class ArcSupport:
if not gcodestatus['absolute_coordinates']:
raise gcmd.error("G2/G3 does not support relative move mode")
currentPos = gcodestatus['gcode_position']
absolut_extrude = gcodestatus['absolute_extrude']
# Parse parameters
asTarget = [gcmd.get_float("X", currentPos[0]),
gcmd.get_float("Y", currentPos[1]),
gcmd.get_float("Z", currentPos[2])]
asTarget = self.Coord(x=gcmd.get_float("X", currentPos[0]),
y=gcmd.get_float("Y", currentPos[1]),
z=gcmd.get_float("Z", currentPos[2]),
e=None)
if gcmd.get_float("R", None) is not None:
raise gcmd.error("G2/G3 does not support R moves")
# determine the plane coordinates and the helical axis
I = gcmd.get_float('I', 0.)
J = gcmd.get_float('J', 0.)
asPlanar = (I, J)
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('IJ') ]
axes = (X_AXIS, Y_AXIS, Z_AXIS)
if self.plane == ARC_PLANE_X_Z:
K = gcmd.get_float('K', 0.)
asPlanar = (I, K)
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('IK') ]
axes = (X_AXIS, Z_AXIS, Y_AXIS)
elif self.plane == ARC_PLANE_Y_Z:
K = gcmd.get_float('K', 0.)
asPlanar = (J, K)
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('JK') ]
axes = (Y_AXIS, Z_AXIS, X_AXIS)
if not (asPlanar[0] or asPlanar[1]):
raise gcmd.error("G2/G3 requires IJ, IK or JK parameters")
# Build linear coordinates to move
self.planArc(currentPos, asTarget, asPlanar, clockwise,
gcmd, absolut_extrude, *axes)
asE = gcmd.get_float("E", None)
asF = gcmd.get_float("F", None)
# Build list of linear coordinates to move
coords = self.planArc(currentPos, asTarget, asPlanar,
clockwise, *axes)
e_per_move = e_base = 0.
if asE is not None:
if gcodestatus['absolute_extrude']:
e_base = currentPos[3]
e_per_move = (asE - e_base) / len(coords)
# Convert coords into G1 commands
for coord in coords:
g1_params = {'X': coord[0], 'Y': coord[1], 'Z': coord[2]}
if e_per_move:
g1_params['E'] = e_base + e_per_move
if gcodestatus['absolute_extrude']:
e_base += e_per_move
if asF is not None:
g1_params['F'] = asF
g1_gcmd = self.gcode.create_gcode_command("G1", "G1", g1_params)
self.gcode_move.cmd_G1(g1_gcmd)
# function planArc() originates from marlin plan_arc()
# https://github.com/MarlinFirmware/Marlin
@@ -102,7 +120,6 @@ class ArcSupport:
#
# alpha and beta axes are the current plane, helical axis is linear travel
def planArc(self, currentPos, targetPos, offset, clockwise,
gcmd, absolut_extrude,
alpha_axis, beta_axis, helical_axis):
# todo: sometimes produces full circles
@@ -142,42 +159,23 @@ class ArcSupport:
# Generate coordinates
theta_per_segment = angular_travel / segments
linear_per_segment = linear_travel / segments
asE = gcmd.get_float("E", None)
asF = gcmd.get_float("F", None)
e_per_move = e_base = 0.
if asE is not None:
if absolut_extrude:
e_base = currentPos[3]
e_per_move = (asE - e_base) / segments
for i in range(1, int(segments) + 1):
coords = []
for i in range(1, int(segments)):
dist_Helical = i * linear_per_segment
c_theta = i * theta_per_segment
cos_Ti = math.cos(c_theta)
sin_Ti = math.sin(c_theta)
cos_Ti = math.cos(i * theta_per_segment)
sin_Ti = math.sin(i * theta_per_segment)
r_P = -offset[0] * cos_Ti + offset[1] * sin_Ti
r_Q = -offset[0] * sin_Ti - offset[1] * cos_Ti
c = [None, None, None]
# Coord doesn't support index assignment, create list
c = [None, None, None, None]
c[alpha_axis] = center_P + r_P
c[beta_axis] = center_Q + r_Q
c[helical_axis] = currentPos[helical_axis] + dist_Helical
coords.append(self.Coord(*c))
if i == segments:
c = targetPos
# Convert coords into G1 commands
g1_params = {'X': c[0], 'Y': c[1], 'Z': c[2]}
if e_per_move:
g1_params['E'] = e_base + e_per_move
if absolut_extrude:
e_base += e_per_move
if asF is not None:
g1_params['F'] = asF
g1_gcmd = self.gcode.create_gcode_command("G1", "G1", g1_params)
self.gcode_move.cmd_G1(g1_gcmd)
coords.append(targetPos)
return coords
def load_config(config):
return ArcSupport(config)

View File

@@ -33,7 +33,9 @@ class PrinterHeaterFan:
speed = self.fan_speed
if speed != self.last_speed:
self.last_speed = speed
self.fan.set_speed(speed)
curtime = self.printer.get_reactor().monotonic()
print_time = self.fan.get_mcu().estimated_print_time(curtime)
self.fan.set_speed(print_time + PIN_MIN_TIME, speed)
return eventtime + 1.
def load_config_prefix(config):

View File

@@ -1,6 +1,6 @@
# Tracking of PWM controlled heaters and their temperature control
#
# Copyright (C) 2016-2020 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import os, logging, threading
@@ -14,7 +14,6 @@ KELVIN_TO_CELSIUS = -273.15
MAX_HEAT_TIME = 5.0
AMBIENT_TEMP = 25.
PID_PARAM_BASE = 255.
MAX_MAINTHREAD_TIME = 5.0
class Heater:
def __init__(self, config, sensor):
@@ -38,7 +37,8 @@ class Heater:
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
self.smooth_time = config.getfloat('smooth_time', 1., above=0.)
self.inv_smooth_time = 1. / self.smooth_time
self.verify_mainthread_time = -999.
self.is_shutdown = False
self.set_temp_count = 0
self.lock = threading.Lock()
self.last_temp = self.smoothed_temp = self.target_temp = 0.
self.last_temp_time = 0.
@@ -64,10 +64,13 @@ class Heater:
gcode.register_mux_command("SET_HEATER_TEMPERATURE", "HEATER",
short_name, self.cmd_SET_HEATER_TEMPERATURE,
desc=self.cmd_SET_HEATER_TEMPERATURE_help)
wh = self.printer.lookup_object('webhooks')
wh.register_mux_endpoint("heaters/set_target_temperature", "heater",
self.name, self._api_set_target_temperature)
self.printer.register_event_handler("klippy:shutdown",
self._handle_shutdown)
def set_pwm(self, read_time, value):
if self.target_temp <= 0. or read_time > self.verify_mainthread_time:
if self.target_temp <= 0. or self.is_shutdown:
value = 0.
if ((read_time < self.next_pwm_time or not self.last_pwm_value)
and abs(value - self.last_pwm_value) < 0.05):
@@ -92,7 +95,7 @@ class Heater:
self.can_extrude = (self.smoothed_temp >= self.min_extrude_temp)
#logging.debug("temp: %.3f %f = %f", read_time, temp)
def _handle_shutdown(self):
self.verify_mainthread_time = -999.
self.is_shutdown = True
# External commands
def get_name(self):
return self.name
@@ -107,6 +110,7 @@ class Heater:
raise self.printer.command_error(
"Requested temperature (%.1f) out of range (%.1f:%.1f)"
% (degrees, self.min_temp, self.max_temp))
self.set_temp_count += 1
with self.lock:
self.target_temp = degrees
def get_temp(self, eventtime):
@@ -130,9 +134,6 @@ class Heater:
target_temp = max(self.min_temp, min(self.max_temp, target_temp))
self.target_temp = target_temp
def stats(self, eventtime):
est_print_time = self.mcu_pwm.get_mcu().estimated_print_time(eventtime)
if not self.printer.is_shutdown():
self.verify_mainthread_time = est_print_time + MAX_MAINTHREAD_TIME
with self.lock:
target_temp = self.target_temp
last_temp = self.last_temp
@@ -147,11 +148,17 @@ class Heater:
last_pwm_value = self.last_pwm_value
return {'temperature': round(smoothed_temp, 2), 'target': target_temp,
'power': last_pwm_value}
def get_set_temp_count(self):
return self.set_temp_count
cmd_SET_HEATER_TEMPERATURE_help = "Sets a heater temperature"
def cmd_SET_HEATER_TEMPERATURE(self, gcmd):
temp = gcmd.get_float('TARGET', 0.)
pheaters = self.printer.lookup_object('heaters')
pheaters.set_temperature(self, temp)
def _api_set_target_temperature(self, web_request):
temp = web_request.get_float('target')
pheaters = self.printer.lookup_object('heaters')
pheaters.set_temperature(self, temp)
######################################################################
@@ -243,6 +250,7 @@ class PrinterHeaters:
self.available_heaters = []
self.available_sensors = []
self.available_monitors = []
self.in_temperature_wait = None
self.has_started = self.have_load_sensors = False
self.printer.register_event_handler("klippy:ready", self._handle_ready)
self.printer.register_event_handler("gcode:request_restart",
@@ -263,8 +271,7 @@ class PrinterHeaters:
try:
dconfig = pconfig.read_config(filename)
except Exception:
logging.exception("Unable to load temperature_sensors.cfg")
raise config.error("Cannot load config '%s'" % (filename,))
raise config.config_error("Cannot load config '%s'" % (filename,))
for c in dconfig.get_prefix_sections(''):
self.printer.load_object(dconfig, c.get_name())
def add_sensor_factory(self, sensor_type, sensor_factory):
@@ -310,7 +317,8 @@ class PrinterHeaters:
def get_status(self, eventtime):
return {'available_heaters': self.available_heaters,
'available_sensors': self.available_sensors,
'available_monitors': self.available_monitors}
'available_monitors': self.available_monitors,
'temperature_wait': self.in_temperature_wait}
def turn_off_all_heaters(self, print_time=0.):
for heater in self.heaters.values():
heater.set_temp(0.)
@@ -341,14 +349,23 @@ class PrinterHeaters:
# Helper to wait on heater.check_busy() and report M105 temperatures
if self.printer.get_start_args().get('debugoutput') is not None:
return
full_name = heater.get_name()
set_temp_count = heater.get_set_temp_count()
toolhead = self.printer.lookup_object("toolhead")
gcode = self.printer.lookup_object("gcode")
reactor = self.printer.get_reactor()
eventtime = reactor.monotonic()
while not self.printer.is_shutdown() and heater.check_busy(eventtime):
self.in_temperature_wait = full_name
print_time = toolhead.get_last_move_time()
gcode.respond_raw(self._get_temp(eventtime))
eventtime = reactor.pause(eventtime + 1.)
if heater.get_set_temp_count() != set_temp_count:
self.in_temperature_wait = None
raise self.printer.command_error(
"Heater '%s' target temperature changed during wait"
% (full_name,))
self.in_temperature_wait = None
def set_temperature(self, heater, temp, wait=False):
toolhead = self.printer.lookup_object('toolhead')
toolhead.register_lookahead_callback((lambda pt: None))
@@ -367,8 +384,12 @@ class PrinterHeaters:
"Error on 'TEMPERATURE_WAIT': missing MINIMUM or MAXIMUM.")
if self.printer.get_start_args().get('debugoutput') is not None:
return
full_name = sensor_name
set_temp_count = None
if sensor_name in self.heaters:
sensor = self.heaters[sensor_name]
full_name = sensor.get_name()
set_temp_count = sensor.get_set_temp_count()
else:
sensor = self.printer.lookup_object(sensor_name)
toolhead = self.printer.lookup_object("toolhead")
@@ -377,10 +398,18 @@ class PrinterHeaters:
while not self.printer.is_shutdown():
temp, target = sensor.get_temp(eventtime)
if temp >= min_temp and temp <= max_temp:
return
break
self.in_temperature_wait = full_name
print_time = toolhead.get_last_move_time()
gcmd.respond_raw(self._get_temp(eventtime))
eventtime = reactor.pause(eventtime + 1.)
if (set_temp_count is not None
and sensor.get_set_temp_count() != set_temp_count):
self.in_temperature_wait = None
raise self.printer.command_error(
"Heater '%s' target temperature changed during wait"
% (full_name,))
self.in_temperature_wait = None
def load_config(config):
return PrinterHeaters(config)

View File

@@ -1,6 +1,6 @@
# Helper code for implementing homing operations
#
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, math
@@ -29,17 +29,10 @@ class StepperPosition:
self.endstop_name = endstop_name
self.stepper_name = stepper.get_name()
self.start_pos = stepper.get_mcu_position()
self.start_cmd_pos = stepper.mcu_to_commanded_position(self.start_pos)
self.halt_pos = self.trig_pos = None
def note_home_end(self, trigger_time):
self.halt_pos = self.stepper.get_mcu_position()
self.trig_pos = self.stepper.get_past_mcu_position(trigger_time)
def verify_no_probe_skew(self, haltpos):
new_start_pos = self.stepper.get_mcu_position(self.start_cmd_pos)
if new_start_pos != self.start_pos:
logging.warning(
"Stepper '%s' position skew after probe: pos %d now %d",
self.stepper.get_name(), self.start_pos, new_start_pos)
# Implementation of homing/probing moves
class HomingMove:
@@ -128,9 +121,6 @@ class HomingMove:
haltpos = trigpos = self.calc_toolhead_pos(kin_spos, trig_steps)
if trig_steps != halt_steps:
haltpos = self.calc_toolhead_pos(kin_spos, halt_steps)
self.toolhead.set_position(haltpos)
for sp in self.stepper_positions:
sp.verify_no_probe_skew(haltpos)
else:
haltpos = trigpos = movepos
over_steps = {sp.stepper_name: sp.halt_pos - sp.trig_pos
@@ -140,7 +130,7 @@ class HomingMove:
halt_kin_spos = {s.get_name(): s.get_commanded_position()
for s in kin.get_steppers()}
haltpos = self.calc_toolhead_pos(halt_kin_spos, over_steps)
self.toolhead.set_position(haltpos)
self.toolhead.set_position(haltpos)
# Signal homing/probing move complete
try:
self.printer.send_event("homing:homing_move_end", self)
@@ -187,8 +177,7 @@ class Homing:
# Notify of upcoming homing operation
self.printer.send_event("homing:home_rails_begin", self, rails)
# Alter kinematics class to think printer is at forcepos
force_axes = [axis for axis in range(3) if forcepos[axis] is not None]
homing_axes = "".join(["xyz"[i] for i in force_axes])
homing_axes = [axis for axis in range(3) if forcepos[axis] is not None]
startpos = self._fill_coord(forcepos)
homepos = self._fill_coord(movepos)
self.toolhead.set_position(startpos, homing_axes=homing_axes)
@@ -232,7 +221,7 @@ class Homing:
+ self.adjust_pos.get(s.get_name(), 0.))
for s in kin.get_steppers()}
newpos = kin.calc_position(kin_spos)
for axis in force_axes:
for axis in homing_axes:
homepos[axis] = newpos[axis]
self.toolhead.set_position(homepos)

View File

@@ -46,11 +46,11 @@ class HomingOverride:
# Calculate forced position (if configured)
toolhead = self.printer.lookup_object('toolhead')
pos = toolhead.get_position()
homing_axes = ""
homing_axes = []
for axis, loc in enumerate(self.start_pos):
if loc is not None:
pos[axis] = loc
homing_axes += "xyz"[axis]
homing_axes.append(axis)
toolhead.set_position(pos, homing_axes=homing_axes)
# Perform homing
context = self.template.create_template_context()

View File

@@ -14,7 +14,7 @@ SAMPLE_ERROR_DESYNC = -0x80000000
SAMPLE_ERROR_LONG_READ = 0x40000000
# Implementation of HX711 and HX717
class HX71xBase:
class HX71xBase():
def __init__(self, config, sensor_type,
sample_rate_options, default_sample_rate,
gain_options, default_gain):
@@ -53,8 +53,8 @@ class HX71xBase:
self._finish_measurements, UPDATE_INTERVAL)
# publish raw samples to the socket
dump_path = "%s/dump_%s" % (sensor_type, sensor_type)
hdr = {'header': ('time', 'counts', 'value')}
self.batch_bulk.add_mux_endpoint(dump_path, "sensor", self.name, hdr)
self.batch_bulk.add_mux_endpoint(dump_path, "sensor", self.name,
{'header': ('time', 'counts')})
# Command Configuration
self.query_hx71x_cmd = None
mcu.add_config_cmd(
@@ -145,21 +145,23 @@ class HX71xBase:
'overflows': self.ffreader.get_last_overflows()}
def HX711(config):
return HX71xBase(config, "hx711",
# HX711 sps options
{80: 80, 10: 10}, 80,
# HX711 gain/channel options
{'A-128': 1, 'B-32': 2, 'A-64': 3}, 'A-128')
class HX711(HX71xBase):
def __init__(self, config):
super(HX711, self).__init__(config, "hx711",
# HX711 sps options
{80: 80, 10: 10}, 80,
# HX711 gain/channel options
{'A-128': 1, 'B-32': 2, 'A-64': 3}, 'A-128')
def HX717(config):
return HX71xBase(config, "hx717",
# HX717 sps options
{320: 320, 80: 80, 20: 20, 10: 10}, 320,
# HX717 gain/channel options
{'A-128': 1, 'B-64': 2, 'A-64': 3,
'B-8': 4}, 'A-128')
class HX717(HX71xBase):
def __init__(self, config):
super(HX717, self).__init__(config, "hx717",
# HX717 sps options
{320: 320, 80: 80, 20: 20, 10: 10}, 320,
# HX717 gain/channel options
{'A-128': 1, 'B-64': 2, 'A-64': 3,
'B-8': 4}, 'A-128')
HX71X_SENSOR_TYPES = {

View File

@@ -1,10 +1,13 @@
# Support for PWM driven LEDs
#
# Copyright (C) 2019-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2019-2022 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
from . import output_pin
import logging, ast
from .display import display
# Time between each led template update
RENDER_TIME = 0.500
# Helper code for common LED initialization and control
class LEDHelper:
@@ -19,22 +22,14 @@ class LEDHelper:
blue = config.getfloat('initial_BLUE', 0., minval=0., maxval=1.)
white = config.getfloat('initial_WHITE', 0., minval=0., maxval=1.)
self.led_state = [(red, green, blue, white)] * led_count
# Support setting an led template
self.template_eval = output_pin.lookup_template_eval(config)
self.tcallbacks = [(lambda text, s=self, index=i:
s._template_update(index, text))
for i in range(led_count)]
# Register commands
name = config.get_name().split()[-1]
gcode = self.printer.lookup_object('gcode')
gcode.register_mux_command("SET_LED", "LED", name, self.cmd_SET_LED,
desc=self.cmd_SET_LED_help)
gcode.register_mux_command("SET_LED_TEMPLATE", "LED", name,
self.cmd_SET_LED_TEMPLATE,
desc=self.cmd_SET_LED_TEMPLATE_help)
def get_status(self, eventtime=None):
return {'color_data': self.led_state}
def _set_color(self, index, color):
def get_led_count(self):
return self.led_count
def set_color(self, index, color):
if index is None:
new_led_state = [color] * self.led_count
if self.led_state == new_led_state:
@@ -46,17 +41,7 @@ class LEDHelper:
new_led_state[index - 1] = color
self.led_state = new_led_state
self.need_transmit = True
def _template_update(self, index, text):
try:
parts = [max(0., min(1., float(f)))
for f in text.split(',', 4)]
except ValueError as e:
logging.exception("led template render error")
parts = []
if len(parts) < 4:
parts += [0.] * (4 - len(parts))
self._set_color(index, tuple(parts))
def _check_transmit(self, print_time=None):
def check_transmit(self, print_time):
if not self.need_transmit:
return
self.need_transmit = False
@@ -77,9 +62,9 @@ class LEDHelper:
color = (red, green, blue, white)
# Update and transmit data
def lookahead_bgfunc(print_time):
self._set_color(index, color)
self.set_color(index, color)
if transmit:
self._check_transmit(print_time)
self.check_transmit(print_time)
if sync:
#Sync LED Update with print time and send
toolhead = self.printer.lookup_object('toolhead')
@@ -87,15 +72,112 @@ class LEDHelper:
else:
#Send update now (so as not to wake toolhead and reset idle_timeout)
lookahead_bgfunc(None)
def get_status(self, eventtime=None):
return {'color_data': self.led_state}
# Main LED tracking code
class PrinterLED:
def __init__(self, config):
self.printer = config.get_printer()
self.led_helpers = {}
self.active_templates = {}
self.render_timer = None
# Load templates
dtemplates = display.lookup_display_templates(config)
self.templates = dtemplates.get_display_templates()
gcode_macro = self.printer.lookup_object("gcode_macro")
self.create_template_context = gcode_macro.create_template_context
# Register handlers
gcode = self.printer.lookup_object('gcode')
gcode.register_command("SET_LED_TEMPLATE", self.cmd_SET_LED_TEMPLATE,
desc=self.cmd_SET_LED_TEMPLATE_help)
def setup_helper(self, config, update_func, led_count=1):
led_helper = LEDHelper(config, update_func, led_count)
name = config.get_name().split()[-1]
self.led_helpers[name] = led_helper
return led_helper
def _activate_timer(self):
if self.render_timer is not None or not self.active_templates:
return
reactor = self.printer.get_reactor()
self.render_timer = reactor.register_timer(self._render, reactor.NOW)
def _activate_template(self, led_helper, index, template, lparams):
key = (led_helper, index)
if template is not None:
uid = (template,) + tuple(sorted(lparams.items()))
self.active_templates[key] = (uid, template, lparams)
return
if key in self.active_templates:
del self.active_templates[key]
def _render(self, eventtime):
if not self.active_templates:
# Nothing to do - unregister timer
reactor = self.printer.get_reactor()
reactor.register_timer(self.render_timer)
self.render_timer = None
return reactor.NEVER
# Setup gcode_macro template context
context = self.create_template_context(eventtime)
def render(name, **kwargs):
return self.templates[name].render(context, **kwargs)
context['render'] = render
# Render all templates
need_transmit = {}
rendered = {}
template_info = self.active_templates.items()
for (led_helper, index), (uid, template, lparams) in template_info:
color = rendered.get(uid)
if color is None:
try:
text = template.render(context, **lparams)
parts = [max(0., min(1., float(f)))
for f in text.split(',', 4)]
except Exception as e:
logging.exception("led template render error")
parts = []
if len(parts) < 4:
parts += [0.] * (4 - len(parts))
rendered[uid] = color = tuple(parts)
need_transmit[led_helper] = 1
led_helper.set_color(index, color)
context.clear() # Remove circular references for better gc
# Transmit pending changes
for led_helper in need_transmit.keys():
led_helper.check_transmit(None)
return eventtime + RENDER_TIME
cmd_SET_LED_TEMPLATE_help = "Assign a display_template to an LED"
def cmd_SET_LED_TEMPLATE(self, gcmd):
index = gcmd.get_int("INDEX", None, minval=1, maxval=self.led_count)
set_template = self.template_eval.set_template
led_name = gcmd.get("LED")
led_helper = self.led_helpers.get(led_name)
if led_helper is None:
raise gcmd.error("Unknown LED '%s'" % (led_name,))
led_count = led_helper.get_led_count()
index = gcmd.get_int("INDEX", None, minval=1, maxval=led_count)
template = None
lparams = {}
tpl_name = gcmd.get("TEMPLATE")
if tpl_name:
template = self.templates.get(tpl_name)
if template is None:
raise gcmd.error("Unknown display_template '%s'" % (tpl_name,))
tparams = template.get_params()
for p, v in gcmd.get_command_parameters().items():
if not p.startswith("PARAM_"):
continue
p = p.lower()
if p not in tparams:
raise gcmd.error("Invalid display_template parameter: %s"
% (p,))
try:
lparams[p] = ast.literal_eval(v)
except ValueError as e:
raise gcmd.error("Unable to parse '%s' as a literal" % (v,))
if index is not None:
set_template(gcmd, self.tcallbacks[index-1], self._check_transmit)
self._activate_template(led_helper, index, template, lparams)
else:
for i in range(self.led_count):
set_template(gcmd, self.tcallbacks[i], self._check_transmit)
for i in range(led_count):
self._activate_template(led_helper, i+1, template, lparams)
self._activate_timer()
PIN_MIN_TIME = 0.100
MAX_SCHEDULE_TIME = 5.0
@@ -123,7 +205,8 @@ class PrinterPWMLED:
% (config.get_name(),))
self.last_print_time = 0.
# Initialize color data
self.led_helper = LEDHelper(config, self.update_leds, 1)
pled = printer.load_object(config, "led")
self.led_helper = pled.setup_helper(config, self.update_leds, 1)
self.prev_color = color = self.led_helper.get_status()['color_data'][0]
for idx, mcu_pin in self.pins:
mcu_pin.setup_start_value(color[idx], 0.)
@@ -142,5 +225,8 @@ class PrinterPWMLED:
def get_status(self, eventtime=None):
return self.led_helper.get_status(eventtime)
def load_config(config):
return PrinterLED(config)
def load_config_prefix(config):
return PrinterPWMLED(config)

View File

@@ -12,8 +12,6 @@ REG_LIS2DW_WHO_AM_I_ADDR = 0x0F
REG_LIS2DW_CTRL_REG1_ADDR = 0x20
REG_LIS2DW_CTRL_REG2_ADDR = 0x21
REG_LIS2DW_CTRL_REG3_ADDR = 0x22
REG_LIS2DW_CTRL_REG4_ADDR = 0x23
REG_LIS2DW_CTRL_REG5_ADDR = 0x24
REG_LIS2DW_CTRL_REG6_ADDR = 0x25
REG_LIS2DW_STATUS_REG_ADDR = 0x27
REG_LIS2DW_OUT_XL_ADDR = 0x28
@@ -28,57 +26,26 @@ REG_MOD_READ = 0x80
# REG_MOD_MULTI = 0x40
LIS2DW_DEV_ID = 0x44
LIS3DH_DEV_ID = 0x33
LIS_I2C_ADDR = 0x19
# Right shift for left justified registers.
FREEFALL_ACCEL = 9.80665
LIS2DW_SCALE = FREEFALL_ACCEL * 1.952 / 4
LIS3DH_SCALE = FREEFALL_ACCEL * 3.906 / 16
SCALE = FREEFALL_ACCEL * 1.952 / 4
BATCH_UPDATES = 0.100
# "Enums" that should be compatible with all python versions
LIS2DW_TYPE = 'LIS2DW'
LIS3DH_TYPE = 'LIS3DH'
SPI_SERIAL_TYPE = 'spi'
I2C_SERIAL_TYPE = 'i2c'
# Printer class that controls LIS2DW chip
class LIS2DW:
def __init__(self, config, lis_type):
def __init__(self, config):
self.printer = config.get_printer()
adxl345.AccelCommandHelper(config, self)
self.lis_type = lis_type
if self.lis_type == LIS2DW_TYPE:
self.axes_map = adxl345.read_axes_map(config, LIS2DW_SCALE,
LIS2DW_SCALE, LIS2DW_SCALE)
self.data_rate = 1600
else:
self.axes_map = adxl345.read_axes_map(config, LIS3DH_SCALE,
LIS3DH_SCALE, LIS3DH_SCALE)
self.data_rate = 1344
# Check for spi or i2c
if config.get('cs_pin', None) is not None:
self.bus_type = SPI_SERIAL_TYPE
else:
self.bus_type = I2C_SERIAL_TYPE
self.axes_map = adxl345.read_axes_map(config, SCALE, SCALE, SCALE)
self.data_rate = 1600
# Setup mcu sensor_lis2dw bulk query code
if self.bus_type == SPI_SERIAL_TYPE:
self.bus = bus.MCU_SPI_from_config(config,
3, default_speed=5000000)
else:
self.bus = bus.MCU_I2C_from_config(config,
default_addr=LIS_I2C_ADDR, default_speed=400000)
self.mcu = mcu = self.bus.get_mcu()
self.spi = bus.MCU_SPI_from_config(config, 3, default_speed=5000000)
self.mcu = mcu = self.spi.get_mcu()
self.oid = oid = mcu.create_oid()
self.query_lis2dw_cmd = None
mcu.add_config_cmd("config_lis2dw oid=%d bus_oid=%d bus_oid_type=%s "
"lis_chip_type=%s" % (oid, self.bus.get_oid(),
self.bus_type, self.lis_type))
mcu.add_config_cmd("config_lis2dw oid=%d spi_oid=%d"
% (oid, self.spi.get_oid()))
mcu.add_config_cmd("query_lis2dw oid=%d rest_ticks=0"
% (oid,), on_restart=True)
mcu.register_config_callback(self._build_config)
@@ -96,23 +63,17 @@ class LIS2DW:
self.name, {'header': hdr})
def _build_config(self):
cmdqueue = self.bus.get_command_queue()
cmdqueue = self.spi.get_command_queue()
self.query_lis2dw_cmd = self.mcu.lookup_command(
"query_lis2dw oid=%c rest_ticks=%u", cq=cmdqueue)
self.ffreader.setup_query_command("query_lis2dw_status oid=%c",
oid=self.oid, cq=cmdqueue)
def read_reg(self, reg):
if self.bus_type == SPI_SERIAL_TYPE:
params = self.bus.spi_transfer([reg | REG_MOD_READ, 0x00])
response = bytearray(params['response'])
return response[1]
params = self.bus.i2c_read([reg], 1)
return bytearray(params['response'])[0]
params = self.spi.spi_transfer([reg | REG_MOD_READ, 0x00])
response = bytearray(params['response'])
return response[1]
def set_reg(self, reg, val, minclock=0):
if self.bus_type == SPI_SERIAL_TYPE:
self.bus.spi_send([reg, val & 0xFF], minclock=minclock)
else:
self.bus.i2c_write([reg, val & 0xFF], minclock=minclock)
self.spi.spi_send([reg, val & 0xFF], minclock=minclock)
stored_val = self.read_reg(reg)
if stored_val != val:
raise self.printer.command_error(
@@ -141,48 +102,26 @@ class LIS2DW:
# noise or wrong signal as a correctly initialized device
dev_id = self.read_reg(REG_LIS2DW_WHO_AM_I_ADDR)
logging.info("lis2dw_dev_id: %x", dev_id)
if self.lis_type == LIS2DW_TYPE:
if dev_id != LIS2DW_DEV_ID:
raise self.printer.command_error(
"Invalid lis2dw id (got %x vs %x).\n"
"This is generally indicative of connection problems\n"
"(e.g. faulty wiring) or a faulty lis2dw chip."
% (dev_id, LIS2DW_DEV_ID))
# Setup chip in requested query rate
# ODR/2, +-16g, low-pass filter, Low-noise abled
self.set_reg(REG_LIS2DW_CTRL_REG6_ADDR, 0x34)
# Continuous mode: If the FIFO is full
# the new sample overwrites the older sample.
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0xC0)
# High-Performance / Low-Power mode 1600/200 Hz
# High-Performance Mode (14-bit resolution)
self.set_reg(REG_LIS2DW_CTRL_REG1_ADDR, 0x94)
else:
if dev_id != LIS3DH_DEV_ID:
raise self.printer.command_error(
"Invalid lis3dh id (got %x vs %x).\n"
"This is generally indicative of connection problems\n"
"(e.g. faulty wiring) or a faulty lis3dh chip."
% (dev_id, LIS3DH_DEV_ID))
# High Resolution / Low Power mode 1344/5376 Hz
# High Resolution mode (12-bit resolution)
# Enable X Y Z axes
self.set_reg(REG_LIS2DW_CTRL_REG1_ADDR, 0x97)
# Disable all filtering
self.set_reg(REG_LIS2DW_CTRL_REG2_ADDR, 0)
# Set +-8g, High Resolution mode
self.set_reg(REG_LIS2DW_CTRL_REG4_ADDR, 0x28)
# Enable FIFO
self.set_reg(REG_LIS2DW_CTRL_REG5_ADDR, 0x40)
# Stream mode
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0x80)
if dev_id != LIS2DW_DEV_ID:
raise self.printer.command_error(
"Invalid lis2dw id (got %x vs %x).\n"
"This is generally indicative of connection problems\n"
"(e.g. faulty wiring) or a faulty lis2dw chip."
% (dev_id, LIS2DW_DEV_ID))
# Setup chip in requested query rate
# ODR/2, +-16g, low-pass filter, Low-noise abled
self.set_reg(REG_LIS2DW_CTRL_REG6_ADDR, 0x34)
# Continuous mode: If the FIFO is full
# the new sample overwrites the older sample.
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0xC0)
# High-Performance / Low-Power mode 1600/200 Hz
# High-Performance Mode (14-bit resolution)
self.set_reg(REG_LIS2DW_CTRL_REG1_ADDR, 0x94)
# Start bulk reading
rest_ticks = self.mcu.seconds_to_clock(4. / self.data_rate)
self.query_lis2dw_cmd.send([self.oid, rest_ticks])
if self.lis_type == LIS2DW_TYPE:
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0xC0)
else:
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0x80)
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0xC0)
logging.info("LIS2DW starting '%s' measurements", self.name)
# Initialize clock tracking
self.ffreader.note_start()
@@ -203,7 +142,7 @@ class LIS2DW:
'overflows': self.ffreader.get_last_overflows()}
def load_config(config):
return LIS2DW(config, LIS2DW_TYPE)
return LIS2DW(config)
def load_config_prefix(config):
return LIS2DW(config, LIS2DW_TYPE)
return LIS2DW(config)

View File

@@ -1,12 +0,0 @@
# Support for reading acceleration data from an LIS3DH chip
#
# Copyright (C) 2024 Luke Vuksta <wulfstawulfsta@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import lis2dw
def load_config(config):
return lis2dw.LIS2DW(config, lis2dw.LIS3DH_TYPE)
def load_config_prefix(config):
return lis2dw.LIS2DW(config, lis2dw.LIS3DH_TYPE)

View File

@@ -109,7 +109,7 @@ class ManualStepper:
self.sync_print_time()
def get_position(self):
return [self.rail.get_commanded_position(), 0., 0., 0.]
def set_position(self, newpos, homing_axes=""):
def set_position(self, newpos, homing_axes=()):
self.do_set_position(newpos[0])
def get_last_move_time(self):
self.sync_print_time()

View File

@@ -4,7 +4,6 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
from . import led
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@@ -41,7 +40,9 @@ class PrinterNeoPixel:
if len(self.color_map) > MAX_MCU_SIZE:
raise config.error("neopixel chain too long")
# Initialize color data
self.led_helper = led.LEDHelper(config, self.update_leds, chain_count)
pled = printer.load_object(config, "led")
self.led_helper = pled.setup_helper(config, self.update_leds,
chain_count)
self.color_data = bytearray(len(self.color_map))
self.update_color_data(self.led_helper.get_status()['color_data'])
self.old_color_data = bytearray([d ^ 1 for d in self.color_data])

View File

@@ -3,180 +3,9 @@
# Copyright (C) 2017-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, ast
from .display import display
######################################################################
# G-Code request queuing helper
######################################################################
PIN_MIN_TIME = 0.100
# Helper code to queue g-code requests
class GCodeRequestQueue:
def __init__(self, config, mcu, callback):
self.printer = printer = config.get_printer()
self.mcu = mcu
self.callback = callback
self.rqueue = []
self.next_min_flush_time = 0.
self.toolhead = None
mcu.register_flush_callback(self._flush_notification)
printer.register_event_handler("klippy:connect", self._handle_connect)
def _handle_connect(self):
self.toolhead = self.printer.lookup_object('toolhead')
def _flush_notification(self, print_time, clock):
rqueue = self.rqueue
while rqueue:
next_time = max(rqueue[0][0], self.next_min_flush_time)
if next_time > print_time:
return
# Skip requests that have been overridden with a following request
pos = 0
while pos + 1 < len(rqueue) and rqueue[pos + 1][0] <= next_time:
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
if action == "discard":
del rqueue[:pos+1]
continue
if action == "delay":
pos -= 1
del rqueue[:pos+1]
self.next_min_flush_time = next_time + max(min_wait, PIN_MIN_TIME)
# Ensure following queue items are flushed
self.toolhead.note_mcu_movequeue_activity(self.next_min_flush_time)
def _queue_request(self, print_time, value):
self.rqueue.append((print_time, value))
self.toolhead.note_mcu_movequeue_activity(print_time)
def queue_gcode_request(self, value):
self.toolhead.register_lookahead_callback(
(lambda pt: self._queue_request(pt, value)))
def send_async_request(self, value, print_time=None):
if print_time is None:
systime = self.printer.get_reactor().monotonic()
print_time = self.mcu.estimated_print_time(systime + PIN_MIN_TIME)
while 1:
next_time = max(print_time, self.next_min_flush_time)
# Invoke callback for the request
action, min_wait = "normal", 0.
ret = self.callback(next_time, value)
if ret is not None:
# Handle special cases
action, min_wait = ret
if action == "discard":
break
self.next_min_flush_time = next_time + max(min_wait, PIN_MIN_TIME)
if action != "delay":
break
######################################################################
# Template evaluation helper
######################################################################
# Time between each template update
RENDER_TIME = 0.500
# Main template evaluation code
class PrinterTemplateEvaluator:
def __init__(self, config):
self.printer = config.get_printer()
self.active_templates = {}
self.render_timer = None
# Load templates
dtemplates = display.lookup_display_templates(config)
self.templates = dtemplates.get_display_templates()
gcode_macro = self.printer.load_object(config, "gcode_macro")
self.create_template_context = gcode_macro.create_template_context
def _activate_timer(self):
if self.render_timer is not None or not self.active_templates:
return
reactor = self.printer.get_reactor()
self.render_timer = reactor.register_timer(self._render, reactor.NOW)
def _activate_template(self, callback, template, lparams, flush_callback):
if template is not None:
uid = (template,) + tuple(sorted(lparams.items()))
self.active_templates[callback] = (
uid, template, lparams, flush_callback)
return
if callback in self.active_templates:
del self.active_templates[callback]
def _render(self, eventtime):
if not self.active_templates:
# Nothing to do - unregister timer
reactor = self.printer.get_reactor()
reactor.unregister_timer(self.render_timer)
self.render_timer = None
return reactor.NEVER
# Setup gcode_macro template context
context = self.create_template_context(eventtime)
def render(name, **kwargs):
return self.templates[name].render(context, **kwargs)
context['render'] = render
# Render all templates
flush_callbacks = {}
rendered = {}
template_info = self.active_templates.items()
for callback, (uid, template, lparams, flush_callback) in template_info:
text = rendered.get(uid)
if text is None:
try:
text = template.render(context, **lparams)
except Exception as e:
logging.exception("display template render error")
text = ""
rendered[uid] = text
if flush_callback is not None:
flush_callbacks[flush_callback] = 1
callback(text)
context.clear() # Remove circular references for better gc
# Invoke optional flush callbacks
for flush_callback in flush_callbacks.keys():
flush_callback()
return eventtime + RENDER_TIME
def set_template(self, gcmd, callback, flush_callback=None):
template = None
lparams = {}
tpl_name = gcmd.get("TEMPLATE")
if tpl_name:
template = self.templates.get(tpl_name)
if template is None:
raise gcmd.error("Unknown display_template '%s'" % (tpl_name,))
tparams = template.get_params()
for p, v in gcmd.get_command_parameters().items():
if not p.startswith("PARAM_"):
continue
p = p.lower()
if p not in tparams:
raise gcmd.error("Invalid display_template parameter: %s"
% (p,))
try:
lparams[p] = ast.literal_eval(v)
except ValueError as e:
raise gcmd.error("Unable to parse '%s' as a literal" % (v,))
self._activate_template(callback, template, lparams, flush_callback)
self._activate_timer()
def lookup_template_eval(config):
printer = config.get_printer()
te = printer.lookup_object("template_evaluator", None)
if te is None:
te = PrinterTemplateEvaluator(config)
printer.add_object("template_evaluator", te)
return te
######################################################################
# Main output pin handling
######################################################################
RESEND_HOST_TIME = 0.300 + PIN_MIN_TIME
MAX_SCHEDULE_TIME = 5.0
class PrinterOutputPin:
@@ -195,18 +24,30 @@ class PrinterOutputPin:
else:
self.mcu_pin = ppins.setup_pin('digital_out', config.get('pin'))
self.scale = 1.
self.mcu_pin.setup_max_duration(0.)
self.last_print_time = 0.
# Support mcu checking for maximum duration
self.reactor = self.printer.get_reactor()
self.resend_timer = None
self.resend_interval = 0.
max_mcu_duration = config.getfloat('maximum_mcu_duration', 0.,
minval=0.500,
maxval=MAX_SCHEDULE_TIME)
self.mcu_pin.setup_max_duration(max_mcu_duration)
if max_mcu_duration:
config.deprecate('maximum_mcu_duration')
self.resend_interval = max_mcu_duration - RESEND_HOST_TIME
# Determine start and shutdown values
self.last_value = config.getfloat(
'value', 0., minval=0., maxval=self.scale) / self.scale
self.shutdown_value = config.getfloat(
'shutdown_value', 0., minval=0., maxval=self.scale) / self.scale
static_value = config.getfloat('static_value', None,
minval=0., maxval=self.scale)
if static_value is not None:
config.deprecate('static_value')
self.last_value = self.shutdown_value = static_value / self.scale
else:
self.last_value = config.getfloat(
'value', 0., minval=0., maxval=self.scale) / self.scale
self.shutdown_value = config.getfloat(
'shutdown_value', 0., minval=0., maxval=self.scale) / self.scale
self.mcu_pin.setup_start_value(self.last_value, self.shutdown_value)
# Create gcode request queue
self.gcrq = GCodeRequestQueue(config, self.mcu_pin.get_mcu(),
self._set_pin)
# Template handling
self.template_eval = lookup_template_eval(config)
# Register commands
pin_name = config.get_name().split()[1]
gcode = self.printer.lookup_object('gcode')
@@ -215,36 +56,45 @@ class PrinterOutputPin:
desc=self.cmd_SET_PIN_help)
def get_status(self, eventtime):
return {'value': self.last_value}
def _set_pin(self, print_time, value):
if value == self.last_value:
return "discard", 0.
self.last_value = value
def _set_pin(self, print_time, value, is_resend=False):
if value == self.last_value and not is_resend:
return
print_time = max(print_time, self.last_print_time + PIN_MIN_TIME)
if self.is_pwm:
self.mcu_pin.set_pwm(print_time, value)
else:
self.mcu_pin.set_digital(print_time, value)
def _template_update(self, text):
try:
value = float(text)
except ValueError as e:
logging.exception("output_pin template render error")
self.gcrq.send_async_request(value)
self.last_value = value
self.last_print_time = print_time
if self.resend_interval and self.resend_timer is None:
self.resend_timer = self.reactor.register_timer(
self._resend_current_val, self.reactor.NOW)
cmd_SET_PIN_help = "Set the value of an output pin"
def cmd_SET_PIN(self, gcmd):
value = gcmd.get_float('VALUE', None, minval=0., maxval=self.scale)
template = gcmd.get('TEMPLATE', None)
if (value is None) == (template is None):
raise gcmd.error("SET_PIN command must specify VALUE or TEMPLATE")
# Check for template setting
if template is not None:
self.template_eval.set_template(gcmd, self._template_update)
return
# Read requested value
value = gcmd.get_float('VALUE', minval=0., maxval=self.scale)
value /= self.scale
if not self.is_pwm and value not in [0., 1.]:
raise gcmd.error("Invalid pin value")
# Queue requested value
self.gcrq.queue_gcode_request(value)
# Obtain print_time and apply requested settings
toolhead = self.printer.lookup_object('toolhead')
toolhead.register_lookahead_callback(
lambda print_time: self._set_pin(print_time, value))
def _resend_current_val(self, eventtime):
if self.last_value == self.shutdown_value:
self.reactor.unregister_timer(self.resend_timer)
self.resend_timer = None
return self.reactor.NEVER
systime = self.reactor.monotonic()
print_time = self.mcu_pin.get_mcu().estimated_print_time(systime)
time_diff = (self.last_print_time + self.resend_interval) - print_time
if time_diff > 0.:
# Reschedule for resend time
return systime + time_diff
self._set_pin(print_time + PIN_MIN_TIME, self.last_value, True)
return systime + self.resend_interval
def load_config_prefix(config):
return PrinterOutputPin(config)

View File

@@ -4,7 +4,7 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
from . import bus, led
from . import bus
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@@ -16,7 +16,8 @@ class PCA9533:
def __init__(self, config):
self.printer = config.get_printer()
self.i2c = bus.MCU_I2C_from_config(config, default_addr=98)
self.led_helper = led.LEDHelper(config, self.update_leds, 1)
pled = self.printer.load_object(config, "led")
self.led_helper = pled.setup_helper(config, self.update_leds, 1)
self.i2c.i2c_write([PCA9533_PWM0, 85])
self.i2c.i2c_write([PCA9533_PWM1, 170])
self.update_leds(self.led_helper.get_status()['color_data'], None)

View File

@@ -3,7 +3,7 @@
# Copyright (C) 2022 Ricardo Alcantara <ricardo@vulcanolabs.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import bus, led, mcp4018
from . import bus, mcp4018
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@@ -34,7 +34,8 @@ class PCA9632:
raise config.error("Invalid color_order '%s'" % (color_order,))
self.color_map = ["RGBW".index(c) for c in color_order]
self.prev_regs = {}
self.led_helper = led.LEDHelper(config, self.update_leds, 1)
pled = printer.load_object(config, "led")
self.led_helper = pled.setup_helper(config, self.update_leds, 1)
printer.register_event_handler("klippy:connect", self.handle_connect)
def reg_write(self, reg, val, minclock=0):
if self.prev_regs.get(reg) == val:

View File

@@ -45,96 +45,40 @@ def _parse_axis(gcmd, raw_axis):
"Unable to parse axis direction '%s'" % (raw_axis,))
return TestAxis(vib_dir=(dir_x, dir_y))
class VibrationPulseTestGenerator:
class VibrationPulseTest:
def __init__(self, config):
self.printer = config.get_printer()
self.gcode = self.printer.lookup_object('gcode')
self.min_freq = config.getfloat('min_freq', 5., minval=1.)
self.max_freq = config.getfloat('max_freq', 135.,
# Defaults are such that max_freq * accel_per_hz == 10000 (max_accel)
self.max_freq = config.getfloat('max_freq', 10000. / 75.,
minval=self.min_freq, maxval=300.)
self.accel_per_hz = config.getfloat('accel_per_hz', 60., above=0.)
self.accel_per_hz = config.getfloat('accel_per_hz', 75., above=0.)
self.hz_per_sec = config.getfloat('hz_per_sec', 1.,
minval=0.1, maxval=2.)
self.probe_points = config.getlists('probe_points', seps=(',', '\n'),
parser=float, count=3)
def get_start_test_points(self):
return self.probe_points
def prepare_test(self, gcmd):
self.freq_start = gcmd.get_float("FREQ_START", self.min_freq, minval=1.)
self.freq_end = gcmd.get_float("FREQ_END", self.max_freq,
minval=self.freq_start, maxval=300.)
self.test_accel_per_hz = gcmd.get_float("ACCEL_PER_HZ",
self.accel_per_hz, above=0.)
self.test_hz_per_sec = gcmd.get_float("HZ_PER_SEC", self.hz_per_sec,
above=0., maxval=2.)
def gen_test(self):
freq = self.freq_start
res = []
sign = 1.
time = 0.
while freq <= self.freq_end + 0.000001:
t_seg = .25 / freq
accel = self.test_accel_per_hz * freq
time += t_seg
res.append((time, sign * accel, freq))
time += t_seg
res.append((time, -sign * accel, freq))
freq += 2. * t_seg * self.test_hz_per_sec
sign = -sign
return res
def get_max_freq(self):
return self.freq_end
class SweepingVibrationsTestGenerator:
def __init__(self, config):
self.vibration_generator = VibrationPulseTestGenerator(config)
self.sweeping_accel = config.getfloat('sweeping_accel', 400., above=0.)
self.sweeping_period = config.getfloat('sweeping_period', 1.2,
minval=0.)
def prepare_test(self, gcmd):
self.vibration_generator.prepare_test(gcmd)
self.test_sweeping_accel = gcmd.get_float(
"SWEEPING_ACCEL", self.sweeping_accel, above=0.)
self.test_sweeping_period = gcmd.get_float(
"SWEEPING_PERIOD", self.sweeping_period, minval=0.)
def gen_test(self):
test_seq = self.vibration_generator.gen_test()
accel_fraction = math.sqrt(2.0) * 0.125
if self.test_sweeping_period:
t_rem = self.test_sweeping_period * accel_fraction
sweeping_accel = self.test_sweeping_accel
else:
t_rem = float('inf')
sweeping_accel = 0.
res = []
last_t = 0.
sig = 1.
accel_fraction += 0.25
for next_t, accel, freq in test_seq:
t_seg = next_t - last_t
while t_rem <= t_seg:
last_t += t_rem
res.append((last_t, accel + sweeping_accel * sig, freq))
t_seg -= t_rem
t_rem = self.test_sweeping_period * accel_fraction
accel_fraction = 0.5
sig = -sig
t_rem -= t_seg
res.append((next_t, accel + sweeping_accel * sig, freq))
last_t = next_t
return res
def get_max_freq(self):
return self.vibration_generator.get_max_freq()
class ResonanceTestExecutor:
def __init__(self, config):
self.printer = config.get_printer()
self.gcode = self.printer.lookup_object('gcode')
def run_test(self, test_seq, axis, gcmd):
reactor = self.printer.get_reactor()
self.hz_per_sec = gcmd.get_float("HZ_PER_SEC", self.hz_per_sec,
above=0., maxval=2.)
def run_test(self, axis, gcmd):
toolhead = self.printer.lookup_object('toolhead')
X, Y, Z, E = toolhead.get_position()
sign = 1.
freq = self.freq_start
# Override maximum acceleration and acceleration to
# deceleration based on the maximum test frequency
systime = reactor.monotonic()
systime = self.printer.get_reactor().monotonic()
toolhead_info = toolhead.get_status(systime)
old_max_accel = toolhead_info['max_accel']
old_minimum_cruise_ratio = toolhead_info['minimum_cruise_ratio']
max_accel = max([abs(a) for _, a, _ in test_seq])
max_accel = self.freq_end * self.accel_per_hz
self.gcode.run_script_from_command(
"SET_VELOCITY_LIMIT ACCEL=%.3f MINIMUM_CRUISE_RATIO=0"
% (max_accel,))
@@ -144,46 +88,24 @@ class ResonanceTestExecutor:
gcmd.respond_info("Disabled [input_shaper] for resonance testing")
else:
input_shaper = None
last_v = last_t = last_accel = last_freq = 0.
for next_t, accel, freq in test_seq:
t_seg = next_t - last_t
gcmd.respond_info("Testing frequency %.0f Hz" % (freq,))
while freq <= self.freq_end + 0.000001:
t_seg = .25 / freq
accel = self.accel_per_hz * freq
max_v = accel * t_seg
toolhead.cmd_M204(self.gcode.create_gcode_command(
"M204", "M204", {"S": abs(accel)}))
v = last_v + accel * t_seg
abs_v = abs(v)
if abs_v < 0.000001:
v = abs_v = 0.
abs_last_v = abs(last_v)
v2 = v * v
last_v2 = last_v * last_v
half_inv_accel = .5 / accel
d = (v2 - last_v2) * half_inv_accel
dX, dY = axis.get_point(d)
nX = X + dX
nY = Y + dY
toolhead.limit_next_junction_speed(abs_last_v)
if v * last_v < 0:
# The move first goes to a complete stop, then changes direction
d_decel = -last_v2 * half_inv_accel
decel_X, decel_Y = axis.get_point(d_decel)
toolhead.move([X + decel_X, Y + decel_Y, Z, E], abs_last_v)
toolhead.move([nX, nY, Z, E], abs_v)
else:
toolhead.move([nX, nY, Z, E], max(abs_v, abs_last_v))
if math.floor(freq) > math.floor(last_freq):
"M204", "M204", {"S": accel}))
L = .5 * accel * t_seg**2
dX, dY = axis.get_point(L)
nX = X + sign * dX
nY = Y + sign * dY
toolhead.move([nX, nY, Z, E], max_v)
toolhead.move([X, Y, Z, E], max_v)
sign = -sign
old_freq = freq
freq += 2. * t_seg * self.hz_per_sec
if math.floor(freq) > math.floor(old_freq):
gcmd.respond_info("Testing frequency %.0f Hz" % (freq,))
reactor.pause(reactor.monotonic() + 0.01)
X, Y = nX, nY
last_t = next_t
last_v = v
last_accel = accel
last_freq = freq
if last_v:
d_decel = -.5 * last_v2 / old_max_accel
decel_X, decel_Y = axis.get_point(d_decel)
toolhead.cmd_M204(self.gcode.create_gcode_command(
"M204", "M204", {"S": old_max_accel}))
toolhead.move([X + decel_X, Y + decel_Y, Z, E], abs(last_v))
# Restore the original acceleration values
self.gcode.run_script_from_command(
"SET_VELOCITY_LIMIT ACCEL=%.3f MINIMUM_CRUISE_RATIO=%.3f"
@@ -192,13 +114,14 @@ class ResonanceTestExecutor:
if input_shaper is not None:
input_shaper.enable_shaping()
gcmd.respond_info("Re-enabled [input_shaper]")
def get_max_freq(self):
return self.freq_end
class ResonanceTester:
def __init__(self, config):
self.printer = config.get_printer()
self.move_speed = config.getfloat('move_speed', 50., above=0.)
self.generator = SweepingVibrationsTestGenerator(config)
self.executor = ResonanceTestExecutor(config)
self.test = VibrationPulseTest(config)
if not config.get('accel_chip_x', None):
self.accel_chip_names = [('xy', config.get('accel_chip').strip())]
else:
@@ -208,8 +131,6 @@ class ResonanceTester:
if self.accel_chip_names[0][1] == self.accel_chip_names[1][1]:
self.accel_chip_names = [('xy', self.accel_chip_names[0][1])]
self.max_smoothing = config.getfloat('max_smoothing', None, minval=0.05)
self.probe_points = config.getlists('probe_points', seps=(',', '\n'),
parser=float, count=3)
self.gcode = self.printer.lookup_object('gcode')
self.gcode.register_command("MEASURE_AXES_NOISE",
@@ -233,9 +154,12 @@ class ResonanceTester:
toolhead = self.printer.lookup_object('toolhead')
calibration_data = {axis: None for axis in axes}
self.generator.prepare_test(gcmd)
self.test.prepare_test(gcmd)
test_points = [test_point] if test_point else self.probe_points
if test_point is not None:
test_points = [test_point]
else:
test_points = self.test.get_start_test_points()
for point in test_points:
toolhead.manual_move(point, self.move_speed)
@@ -260,8 +184,7 @@ class ResonanceTester:
raw_values.append((axis, aclient, chip.name))
# Generate moves
test_seq = self.generator.gen_test()
self.executor.run_test(test_seq, axis, gcmd)
self.test.run_test(axis, gcmd)
for chip_axis, aclient, chip_name in raw_values:
aclient.finish_measurements()
if raw_name_suffix is not None:
@@ -289,11 +212,15 @@ class ResonanceTester:
def _parse_chips(self, accel_chips):
parsed_chips = []
for chip_name in accel_chips.split(','):
chip = self.printer.lookup_object(chip_name.strip())
if "adxl345" in chip_name:
chip_lookup_name = chip_name.strip()
else:
chip_lookup_name = "adxl345 " + chip_name.strip();
chip = self.printer.lookup_object(chip_lookup_name)
parsed_chips.append(chip)
return parsed_chips
def _get_max_calibration_freq(self):
return 1.5 * self.generator.get_max_freq()
return 1.5 * self.test.get_max_freq()
cmd_TEST_RESONANCES_help = ("Runs the resonance test for a specifed axis")
def cmd_TEST_RESONANCES(self, gcmd):
# Parse parameters

View File

@@ -37,10 +37,11 @@ class SafeZHoming:
if 'z' not in kin_status['homed_axes']:
# Always perform the z_hop if the Z axis is not homed
pos[2] = 0
toolhead.set_position(pos, homing_axes="z")
toolhead.set_position(pos, homing_axes=[2])
toolhead.manual_move([None, None, self.z_hop],
self.z_hop_speed)
toolhead.get_kinematics().clear_homing_state("z")
if hasattr(toolhead.get_kinematics(), "note_z_not_homed"):
toolhead.get_kinematics().note_z_not_homed()
elif pos[2] < self.z_hop:
# If the Z axis is homed, and below z_hop, lift it to z_hop
toolhead.manual_move([None, None, self.z_hop],

View File

@@ -12,7 +12,7 @@ class ScrewsTiltAdjust:
self.config = config
self.printer = config.get_printer()
self.screws = []
self.results = {}
self.results = []
self.max_diff = None
self.max_diff_error = False
# Read config

View File

@@ -1,11 +1,11 @@
# Support for servos
#
# Copyright (C) 2017-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2017-2020 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import output_pin
SERVO_SIGNAL_PERIOD = 0.020
PIN_MIN_TIME = 0.100
class PrinterServo:
def __init__(self, config):
@@ -18,7 +18,7 @@ class PrinterServo:
self.max_angle = config.getfloat('maximum_servo_angle', 180.)
self.angle_to_width = (self.max_width - self.min_width) / self.max_angle
self.width_to_value = 1. / SERVO_SIGNAL_PERIOD
self.last_value = 0.
self.last_value = self.last_value_time = 0.
initial_pwm = 0.
iangle = config.getfloat('initial_angle', None, minval=0., maxval=360.)
if iangle is not None:
@@ -33,9 +33,6 @@ class PrinterServo:
self.mcu_servo.setup_max_duration(0.)
self.mcu_servo.setup_cycle_time(SERVO_SIGNAL_PERIOD)
self.mcu_servo.setup_start_value(initial_pwm, 0.)
# Create gcode request queue
self.gcrq = output_pin.GCodeRequestQueue(
config, self.mcu_servo.get_mcu(), self._set_pwm)
# Register commands
servo_name = config.get_name().split()[1]
gcode = self.printer.lookup_object('gcode')
@@ -46,9 +43,11 @@ class PrinterServo:
return {'value': self.last_value}
def _set_pwm(self, print_time, value):
if value == self.last_value:
return "discard", 0.
self.last_value = value
return
print_time = max(print_time, self.last_value_time + PIN_MIN_TIME)
self.mcu_servo.set_pwm(print_time, value)
self.last_value = value
self.last_value_time = print_time
def _get_pwm_from_angle(self, angle):
angle = max(0., min(self.max_angle, angle))
width = self.min_width + angle * self.angle_to_width
@@ -65,7 +64,9 @@ class PrinterServo:
else:
angle = gcmd.get_float('ANGLE')
value = self._get_pwm_from_angle(angle)
self.gcrq.queue_gcode_request(value)
toolhead = self.printer.lookup_object('toolhead')
toolhead.register_lookahead_callback((lambda pt:
self._set_pwm(pt, value)))
def load_config_prefix(config):
return PrinterServo(config)

View File

@@ -48,9 +48,7 @@ class CalibrationData:
# Avoid division by zero errors
psd /= self.freq_bins + .1
# Remove low-frequency noise
low_freqs = self.freq_bins < 2. * MIN_FREQ
psd[low_freqs] *= self.numpy.exp(
-(2. * MIN_FREQ / (self.freq_bins[low_freqs] + .1))**2 + 1.)
psd[self.freq_bins < MIN_FREQ] = 0.
def get_psd(self, axis='all'):
return self._psd_map[axis]

View File

@@ -94,7 +94,6 @@ class PrinterStepperEnable:
print_time = toolhead.get_last_move_time()
for el in self.enable_lines.values():
el.motor_disable(print_time)
toolhead.get_kinematics().clear_homing_state("xyz")
self.printer.send_event("stepper_enable:motor_off", print_time)
toolhead.dwell(DISABLE_STALL_TIME)
def motor_debug_enable(self, stepper, enable):

View File

@@ -38,17 +38,19 @@ class SX1509(object):
REG_INPUT_DISABLE : 0, REG_ANALOG_DRIVER_ENABLE : 0}
self.reg_i_on_dict = {reg : 0 for reg in REG_I_ON}
def _build_config(self):
# Reset the chip, Default RegClock/RegMisc 0x0
# Reset the chip
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
self._oid, REG_RESET, 0x12))
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
self._oid, REG_RESET, 0x34))
# Enable Oscillator
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
self._oid, REG_CLOCK, (1 << 6)))
self._mcu.add_config_cmd("i2c_modify_bits oid=%d reg=%02x"
" clear_set_bits=%02x%02x" % (
self._oid, REG_CLOCK, 0, (1 << 6)))
# Setup Clock Divider
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
self._oid, REG_MISC, (1 << 4)))
self._mcu.add_config_cmd("i2c_modify_bits oid=%d reg=%02x"
" clear_set_bits=%02x%02x" % (
self._oid, REG_MISC, 0, (1 << 4)))
# Transfer all regs with their initial cached state
for _reg, _data in self.reg_dict.items():
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%04x" % (

View File

@@ -46,7 +46,7 @@ class TemperatureFan:
self.cmd_SET_TEMPERATURE_FAN_TARGET,
desc=self.cmd_SET_TEMPERATURE_FAN_TARGET_help)
def set_tf_speed(self, read_time, value):
def set_speed(self, read_time, value):
if value <= 0.:
value = 0.
elif value < self.min_speed:
@@ -60,7 +60,7 @@ class TemperatureFan:
speed_time = read_time + self.speed_delay
self.next_speed_time = speed_time + 0.75 * MAX_FAN_TIME
self.last_speed_value = value
self.fan.set_speed(value, speed_time)
self.fan.set_speed(speed_time, value)
def temperature_callback(self, read_time, temp):
self.last_temp = temp
self.control.temperature_callback(read_time, temp)
@@ -128,10 +128,10 @@ class ControlBangBang:
and temp <= target_temp-self.max_delta):
self.heating = True
if self.heating:
self.temperature_fan.set_tf_speed(read_time, 0.)
self.temperature_fan.set_speed(read_time, 0.)
else:
self.temperature_fan.set_tf_speed(
read_time, self.temperature_fan.get_max_speed())
self.temperature_fan.set_speed(read_time,
self.temperature_fan.get_max_speed())
######################################################################
# Proportional Integral Derivative (PID) control algo
@@ -171,7 +171,7 @@ class ControlPID:
# Calculate output
co = self.Kp*temp_err + self.Ki*temp_integ - self.Kd*temp_deriv
bounded_co = max(0., min(self.temperature_fan.get_max_speed(), co))
self.temperature_fan.set_tf_speed(
self.temperature_fan.set_speed(
read_time, max(self.temperature_fan.get_min_speed(),
self.temperature_fan.get_max_speed() - bounded_co))
# Store state for next measurement

View File

@@ -66,7 +66,7 @@ class PrinterTemperatureMCU:
self.mcu_type = mcu.get_constants().get("MCU", "")
# Run MCU specific configuration
cfg_funcs = [
('rp2', self.config_rp2040),
('rp2040', self.config_rp2040),
('sam3', self.config_sam3), ('sam4', self.config_sam4),
('same70', self.config_same70), ('samd21', self.config_samd21),
('samd51', self.config_samd51), ('same5', self.config_samd51),

View File

@@ -490,7 +490,6 @@ class EddyDriftCompensation:
self.cal_temp = config.getfloat("calibration_temp", 0.)
self.drift_calibration = None
self.calibration_samples = None
self.max_valid_temp = config.getfloat("max_validation_temp", 60.)
self.dc_min_temp = config.getfloat("drift_calibration_min_temp", 0.)
dc = config.getlists(
"drift_calibration", None, seps=(',', '\n'), parser=float
@@ -504,8 +503,7 @@ class EddyDriftCompensation:
)
self.drift_calibration = [Polynomial2d(*coefs) for coefs in dc]
cal = self.drift_calibration
start_temp, end_temp = self.dc_min_temp, self.max_valid_temp
self._check_calibration(cal, start_temp, end_temp, config.error)
self._check_calibration(cal, self.dc_min_temp, config.error)
low_poly = self.drift_calibration[-1]
self.min_freq = min([low_poly(temp) for temp in range(121)])
cal_str = "\n".join([repr(p) for p in cal])
@@ -640,15 +638,13 @@ class EddyDriftCompensation:
"calbration error, not enough samples"
)
min_temp, _ = cal_samples[0][0]
max_temp, _ = cal_samples[-1][0]
polynomials = []
for i, coords in enumerate(cal_samples):
height = .05 + i * .5
poly = Polynomial2d.fit(coords)
polynomials.append(poly)
logging.info("Polynomial at Z=%.2f: %s" % (height, repr(poly)))
end_vld_temp = max(self.max_valid_temp, max_temp)
self._check_calibration(polynomials, min_temp, end_vld_temp)
self._check_calibration(polynomials, min_temp)
coef_cfg = "\n" + "\n".join([str(p) for p in polynomials])
configfile = self.printer.lookup_object('configfile')
configfile.set(self.name, "drift_calibration", coef_cfg)
@@ -660,11 +656,10 @@ class EddyDriftCompensation:
% (self.name, len(polynomials))
)
def _check_calibration(self, calibration, start_temp, end_temp, error=None):
def _check_calibration(self, calibration, start_temp, error=None):
error = error or self.printer.command_error
start = int(start_temp)
end = int(end_temp) + 1
for temp in range(start, end, 1):
for temp in range(start, 121, 1):
last_freq = calibration[0](temp)
for i, poly in enumerate(calibration[1:]):
next_freq = poly(temp)

View File

@@ -348,7 +348,7 @@ class TMC2240:
if config.get("uart_pin", None) is not None:
# use UART for communication
self.mcu_tmc = tmc_uart.MCU_TMC_uart(config, Registers, self.fields,
7, TMC_FREQUENCY)
3, TMC_FREQUENCY)
else:
# Use SPI bus for communication
self.mcu_tmc = tmc2130.MCU_TMC_SPI(config, Registers, self.fields,
@@ -408,8 +408,6 @@ class TMC2240:
set_config_field(config, "tpowerdown", 10)
# SG4_THRS
set_config_field(config, "sg4_angle_offset", 1)
# DRV_CONF
set_config_field(config, "slope_control", 0)
def load_config_prefix(config):
return TMC2240(config)

View File

@@ -108,7 +108,7 @@ class RetryHelper:
return self.increasing > 1
def check_retry(self, z_positions):
if self.max_retries == 0:
return "done"
return
error = round(max(z_positions) - min(z_positions),6)
self.gcode.respond_info(
"Retries: %d/%d %s: %0.6f tolerance: %0.6f" % (

View File

@@ -1,6 +1,6 @@
# Parse gcode commands
#
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import os, re, logging, collections, shlex
@@ -28,18 +28,19 @@ class GCodeCommand:
return self._params
def get_raw_command_parameters(self):
command = self._command
origline = self._commandline
param_start = len(command)
param_end = len(origline)
if origline[:param_start].upper() != command:
# Skip any gcode line-number and ignore any trailing checksum
param_start += origline.upper().find(command)
end = origline.rfind('*')
if end >= 0 and origline[end+1:].isdigit():
param_end = end
if origline[param_start:param_start+1].isspace():
param_start += 1
return origline[param_start:param_end]
if command.startswith("M117 ") or command.startswith("M118 "):
command = command[:4]
rawparams = self._commandline
urawparams = rawparams.upper()
if not urawparams.startswith(command):
rawparams = rawparams[urawparams.find(command):]
end = rawparams.rfind('*')
if end >= 0:
rawparams = rawparams[:end]
rawparams = rawparams[len(command):]
if rawparams.startswith(' '):
rawparams = rawparams[1:]
return rawparams
def ack(self, msg=None):
if not self._need_ack:
return False
@@ -132,10 +133,6 @@ class GCodeDispatch:
raise self.printer.config_error(
"gcode command %s already registered" % (cmd,))
if not self.is_traditional_gcode(cmd):
if (cmd.upper() != cmd or not cmd.replace('_', 'A').isalnum()
or cmd[0].isdigit() or cmd[1:2].isdigit()):
raise self.printer.config_error(
"Can't register '%s' as it is an invalid name" % (cmd,))
origfunc = func
func = lambda params: origfunc(self._get_extended_params(params))
self.ready_gcode_handlers[cmd] = func
@@ -187,7 +184,7 @@ class GCodeDispatch:
self._build_status_commands()
self._respond_state("Ready")
# Parse input into commands
args_r = re.compile('([A-Z_]+|[A-Z*])')
args_r = re.compile('([A-Z_]+|[A-Z*/])')
def _process_commands(self, commands, need_ack=True):
for line in commands:
# Ignore comments and leading/trailing spaces
@@ -197,14 +194,16 @@ class GCodeDispatch:
line = line[:cpos]
# Break line into parts and determine command
parts = self.args_r.split(line.upper())
if ''.join(parts[:2]) == 'N':
numparts = len(parts)
cmd = ""
if numparts >= 3 and parts[1] != 'N':
cmd = parts[1] + parts[2].strip()
elif numparts >= 5 and parts[1] == 'N':
# Skip line number at start of command
cmd = ''.join(parts[3:5]).strip()
else:
cmd = ''.join(parts[:3]).strip()
cmd = parts[3] + parts[4].strip()
# Build gcode "params" dictionary
params = { parts[i]: parts[i+1].strip()
for i in range(1, len(parts), 2) }
for i in range(1, numparts, 2) }
gcmd = GCodeCommand(self, cmd, origline, params, need_ack)
# Invoke handler for command
handler = self.gcode_handlers.get(cmd, self.cmd_default)
@@ -252,22 +251,26 @@ class GCodeDispatch:
def _respond_state(self, state):
self.respond_info("Klipper state: %s" % (state,), log=False)
# Parameter parsing helpers
extended_r = re.compile(
r'^\s*(?:N[0-9]+\s*)?'
r'(?P<cmd>[a-zA-Z_][a-zA-Z0-9_]+)(?:\s+|$)'
r'(?P<args>[^#*;]*?)'
r'\s*(?:[#*;].*)?$')
def _get_extended_params(self, gcmd):
rawparams = gcmd.get_raw_command_parameters()
# Extract args while allowing shell style quoting
s = shlex.shlex(rawparams, posix=True)
s.whitespace_split = True
s.commenters = '#;'
m = self.extended_r.match(gcmd.get_commandline())
if m is None:
raise self.error("Malformed command '%s'"
% (gcmd.get_commandline(),))
eargs = m.group('args')
try:
eparams = [earg.split('=', 1) for earg in s]
eparams = [earg.split('=', 1) for earg in shlex.split(eargs)]
eparams = { k.upper(): v for k, v in eparams }
gcmd._params.clear()
gcmd._params.update(eparams)
return gcmd
except ValueError as e:
raise self.error("Malformed command '%s'"
% (gcmd.get_commandline(),))
# Update gcmd with new parameters
gcmd._params.clear()
gcmd._params.update(eparams)
return gcmd
# G-Code special command handlers
def cmd_default(self, gcmd):
cmd = gcmd.get_command()
@@ -286,15 +289,12 @@ class GCodeDispatch:
if cmdline:
logging.debug(cmdline)
return
if ' ' in cmd:
if cmd.startswith("M117 ") or cmd.startswith("M118 "):
# Handle M117/M118 gcode with numeric and special characters
realcmd = cmd.split()[0]
if realcmd in ["M117", "M118", "M23"]:
handler = self.gcode_handlers.get(realcmd, None)
if handler is not None:
gcmd._command = realcmd
handler(gcmd)
return
handler = self.gcode_handlers.get(cmd[:4], None)
if handler is not None:
handler(gcmd)
return
elif cmd in ['M140', 'M104'] and not gcmd.get_float('S', 0.):
# Don't warn about requests to turn off heaters when not present
return

View File

@@ -40,6 +40,8 @@ class CartKinematics:
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
self.printer.register_event_handler("stepper_enable:motor_off",
self._motor_off)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
@@ -65,17 +67,15 @@ class CartKinematics:
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
for axis_name in homing_axes:
axis = "xyz".index(axis_name)
for axis in homing_axes:
if self.dc_module and axis == self.dc_module.axis:
rail = self.dc_module.get_primary_rail().get_rail()
else:
rail = self.rails[axis]
self.limits[axis] = rail.get_range()
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
def note_z_not_homed(self):
# Helper for Safe Z Home
self.limits[2] = (1.0, -1.0)
def home_axis(self, homing_state, axis, rail):
# Determine movement
position_min, position_max = rail.get_range()
@@ -96,6 +96,8 @@ class CartKinematics:
self.dc_module.home(homing_state)
else:
self.home_axis(homing_state, axis, self.rails[axis])
def _motor_off(self, print_time):
self.limits = [(1.0, -1.0)] * 3
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):

View File

@@ -21,6 +21,8 @@ class CoreXYKinematics:
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
config.get_printer().register_event_handler("stepper_enable:motor_off",
self._motor_off)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
@@ -39,12 +41,11 @@ class CoreXYKinematics:
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
if "xyz"[i] in homing_axes:
if i in homing_axes:
self.limits[i] = rail.get_range()
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
def note_z_not_homed(self):
# Helper for Safe Z Home
self.limits[2] = (1.0, -1.0)
def home(self, homing_state):
# Each axis is homed independently and in order
for axis in homing_state.get_axes():
@@ -61,6 +62,8 @@ class CoreXYKinematics:
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
def _motor_off(self, print_time):
self.limits = [(1.0, -1.0)] * 3
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):

View File

@@ -21,6 +21,8 @@ class CoreXZKinematics:
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
config.get_printer().register_event_handler("stepper_enable:motor_off",
self._motor_off)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
@@ -39,12 +41,11 @@ class CoreXZKinematics:
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
if "xyz"[i] in homing_axes:
if i in homing_axes:
self.limits[i] = rail.get_range()
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
def note_z_not_homed(self):
# Helper for Safe Z Home
self.limits[2] = (1.0, -1.0)
def home(self, homing_state):
# Each axis is homed independently and in order
for axis in homing_state.get_axes():
@@ -61,6 +62,8 @@ class CoreXZKinematics:
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
def _motor_off(self, print_time):
self.limits = [(1.0, -1.0)] * 3
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):

View File

@@ -23,6 +23,8 @@ class DeltaKinematics:
stepper_configs[2], need_position_minmax = False,
default_position_endstop=a_endstop)
self.rails = [rail_a, rail_b, rail_c]
config.get_printer().register_event_handler("stepper_enable:motor_off",
self._motor_off)
# Setup max velocity
self.max_velocity, self.max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
@@ -88,7 +90,7 @@ class DeltaKinematics:
math.sqrt(self.very_slow_xy2)))
self.axes_min = toolhead.Coord(-max_xy, -max_xy, self.min_z, 0.)
self.axes_max = toolhead.Coord(max_xy, max_xy, self.max_z, 0.)
self.set_position([0., 0., 0.], "")
self.set_position([0., 0., 0.], ())
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
def _actuator_to_cartesian(self, spos):
@@ -101,19 +103,17 @@ class DeltaKinematics:
for rail in self.rails:
rail.set_position(newpos)
self.limit_xy2 = -1.
if homing_axes == "xyz":
if tuple(homing_axes) == (0, 1, 2):
self.need_home = False
def clear_homing_state(self, clear_axes):
# Clearing homing state for each axis individually is not implemented
if clear_axes:
self.limit_xy2 = -1
self.need_home = True
def home(self, homing_state):
# All axes are homed simultaneously
homing_state.set_axes([0, 1, 2])
forcepos = list(self.home_position)
forcepos[2] = -1.5 * math.sqrt(max(self.arm2)-self.max_xy2)
homing_state.home_rails(self.rails, forcepos, self.home_position)
def _motor_off(self, print_time):
self.limit_xy2 = -1.
self.need_home = True
def check_move(self, move):
end_pos = move.end_pos
end_xy2 = end_pos[0]**2 + end_pos[1]**2

View File

@@ -41,6 +41,8 @@ class DeltesianKinematics:
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
config.get_printer().register_event_handler(
"stepper_enable:motor_off", self._motor_off)
self.limits = [(1.0, -1.0)] * 3
# X axis limits
min_angle = config.getfloat('min_angle', MIN_ANGLE,
@@ -87,7 +89,7 @@ class DeltesianKinematics:
self.axes_min = toolhead.Coord(*[l[0] for l in self.limits], e=0.)
self.axes_max = toolhead.Coord(*[l[1] for l in self.limits], e=0.)
self.homed_axis = [False] * 3
self.set_position([0., 0., 0.], "")
self.set_position([0., 0., 0.], ())
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
def _actuator_to_cartesian(self, sp):
@@ -113,13 +115,8 @@ class DeltesianKinematics:
def set_position(self, newpos, homing_axes):
for rail in self.rails:
rail.set_position(newpos)
for axis_name in homing_axes:
axis = "xyz".index(axis_name)
self.homed_axis[axis] = True
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.homed_axis[axis] = False
for n in homing_axes:
self.homed_axis[n] = True
def home(self, homing_state):
homing_axes = homing_state.get_axes()
home_xz = 0 in homing_axes or 2 in homing_axes
@@ -145,6 +142,8 @@ class DeltesianKinematics:
else:
forcepos[1] += 1.5 * (position_max - hi.position_endstop)
homing_state.home_rails([self.rails[2]], forcepos, homepos)
def _motor_off(self, print_time):
self.homed_axis = [False] * 3
def check_move(self, move):
limits = list(map(list, self.limits))
spos, epos = move.start_pos, move.end_pos

View File

@@ -42,6 +42,8 @@ class HybridCoreXYKinematics:
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
self.printer.register_event_handler("stepper_enable:motor_off",
self._motor_off)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
@@ -67,17 +69,15 @@ class HybridCoreXYKinematics:
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
for axis_name in homing_axes:
axis = "xyz".index(axis_name)
for axis in homing_axes:
if self.dc_module and axis == self.dc_module.axis:
rail = self.dc_module.get_primary_rail().get_rail()
else:
rail = self.rails[axis]
self.limits[axis] = rail.get_range()
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
def note_z_not_homed(self):
# Helper for Safe Z Home
self.limits[2] = (1.0, -1.0)
def home_axis(self, homing_state, axis, rail):
position_min, position_max = rail.get_range()
hi = rail.get_homing_info()
@@ -96,6 +96,8 @@ class HybridCoreXYKinematics:
self.dc_module.home(homing_state)
else:
self.home_axis(homing_state, axis, self.rails[axis])
def _motor_off(self, print_time):
self.limits = [(1.0, -1.0)] * 3
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):

View File

@@ -42,6 +42,8 @@ class HybridCoreXZKinematics:
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
self.printer.register_event_handler("stepper_enable:motor_off",
self._motor_off)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
@@ -67,17 +69,15 @@ class HybridCoreXZKinematics:
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
for axis_name in homing_axes:
axis = "xyz".index(axis_name)
for axis in homing_axes:
if self.dc_module and axis == self.dc_module.axis:
rail = self.dc_module.get_primary_rail().get_rail()
else:
rail = self.rails[axis]
self.limits[axis] = rail.get_range()
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
def note_z_not_homed(self):
# Helper for Safe Z Home
self.limits[2] = (1.0, -1.0)
def home_axis(self, homing_state, axis, rail):
position_min, position_max = rail.get_range()
hi = rail.get_homing_info()
@@ -96,6 +96,8 @@ class HybridCoreXZKinematics:
self.dc_module.home(homing_state)
else:
self.home_axis(homing_state, axis, self.rails[axis])
def _motor_off(self, print_time):
self.limits = [(1.0, -1.0)] * 3
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):

View File

@@ -13,8 +13,6 @@ class NoneKinematics:
return [0, 0, 0]
def set_position(self, newpos, homing_axes):
pass
def clear_homing_state(self, clear_axes):
pass
def home(self, homing_state):
pass
def check_move(self, move):

View File

@@ -22,6 +22,8 @@ class PolarKinematics:
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
config.get_printer().register_event_handler("stepper_enable:motor_off",
self._motor_off)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
@@ -45,16 +47,13 @@ class PolarKinematics:
def set_position(self, newpos, homing_axes):
for s in self.steppers:
s.set_position(newpos)
if "z" in homing_axes:
if 2 in homing_axes:
self.limit_z = self.rails[1].get_range()
if "x" in homing_axes and "y" in homing_axes:
if 0 in homing_axes and 1 in homing_axes:
self.limit_xy2 = self.rails[0].get_range()[1]**2
def clear_homing_state(self, clear_axes):
if "x" in clear_axes or "y" in clear_axes:
# X and Y cannot be cleared separately
self.limit_xy2 = -1.
if "z" in clear_axes:
self.limit_z = (1.0, -1.0)
def note_z_not_homed(self):
# Helper for Safe Z Home
self.limit_z = (1.0, -1.0)
def _home_axis(self, homing_state, axis, rail):
# Determine movement
position_min, position_max = rail.get_range()
@@ -86,6 +85,9 @@ class PolarKinematics:
self._home_axis(homing_state, 0, self.rails[0])
if home_z:
self._home_axis(homing_state, 2, self.rails[1])
def _motor_off(self, print_time):
self.limit_z = (1.0, -1.0)
self.limit_xy2 = -1.
def check_move(self, move):
end_pos = move.end_pos
xy2 = end_pos[0]**2 + end_pos[1]**2

View File

@@ -21,6 +21,8 @@ class RotaryDeltaKinematics:
stepper_configs[2], need_position_minmax=False,
default_position_endstop=a_endstop, units_in_radians=True)
self.rails = [rail_a, rail_b, rail_c]
config.get_printer().register_event_handler("stepper_enable:motor_off",
self._motor_off)
# Read config
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
@@ -74,7 +76,7 @@ class RotaryDeltaKinematics:
max_xy = math.sqrt(self.max_xy2)
self.axes_min = toolhead.Coord(-max_xy, -max_xy, self.min_z, 0.)
self.axes_max = toolhead.Coord(max_xy, max_xy, self.max_z, 0.)
self.set_position([0., 0., 0.], "")
self.set_position([0., 0., 0.], ())
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
def calc_position(self, stepper_positions):
@@ -84,13 +86,8 @@ class RotaryDeltaKinematics:
for rail in self.rails:
rail.set_position(newpos)
self.limit_xy2 = -1.
if homing_axes == "xyz":
if tuple(homing_axes) == (0, 1, 2):
self.need_home = False
def clear_homing_state(self, clear_axes):
# Clearing homing state for each axis individually is not implemented
if clear_axes:
self.limit_xy2 = -1
self.need_home = True
def home(self, homing_state):
# All axes are homed simultaneously
homing_state.set_axes([0, 1, 2])
@@ -99,6 +96,9 @@ class RotaryDeltaKinematics:
#forcepos[2] = self.calibration.actuator_to_cartesian(min_angles)[2]
forcepos[2] = -1.
homing_state.home_rails(self.rails, forcepos, self.home_position)
def _motor_off(self, print_time):
self.limit_xy2 = -1.
self.need_home = True
def check_move(self, move):
end_pos = move.end_pos
end_xy2 = end_pos[0]**2 + end_pos[1]**2

View File

@@ -26,7 +26,7 @@ class WinchKinematics:
acoords = list(zip(*self.anchors))
self.axes_min = toolhead.Coord(*[min(a) for a in acoords], e=0.)
self.axes_max = toolhead.Coord(*[max(a) for a in acoords], e=0.)
self.set_position([0., 0., 0.], "")
self.set_position([0., 0., 0.], ())
def get_steppers(self):
return list(self.steppers)
def calc_position(self, stepper_positions):
@@ -36,9 +36,6 @@ class WinchKinematics:
def set_position(self, newpos, homing_axes):
for s in self.steppers:
s.set_position(newpos)
def clear_homing_state(self, clear_axes):
# XXX - homing not implemented
pass
def home(self, homing_state):
# XXX - homing not implemented
homing_state.set_axes([0, 1, 2])

View File

@@ -214,7 +214,7 @@ class Printer:
logging.info("Reactor garbage collection: %s",
self.reactor.get_gc_stats())
self.send_event("klippy:notify_mcu_shutdown", msg, details)
def invoke_async_shutdown(self, msg, details={}):
def invoke_async_shutdown(self, msg, details):
self.reactor.register_async_callback(
(lambda e: self.invoke_shutdown(msg, details)))
def register_event_handler(self, event, callback):

View File

@@ -832,10 +832,9 @@ class MCU:
systime = self._reactor.monotonic()
get_clock = self._clocksync.get_clock
calc_freq = get_clock(systime + 1) - get_clock(systime)
freq_diff = abs(mcu_freq - calc_freq)
mcu_freq_mhz = int(mcu_freq / 1000000. + 0.5)
calc_freq_mhz = int(calc_freq / 1000000. + 0.5)
if freq_diff > mcu_freq*0.01 and mcu_freq_mhz != calc_freq_mhz:
if mcu_freq_mhz != calc_freq_mhz:
pconfig = self._printer.lookup_object('configfile')
msg = ("MCU '%s' configured for %dMhz but running at %dMhz!"
% (self._name, mcu_freq_mhz, calc_freq_mhz))

View File

@@ -324,7 +324,7 @@ class MessageParser:
def create_command(self, msg):
parts = msg.strip().split()
if not parts:
return []
return ""
msgname = parts[0]
mp = self.messages_by_name.get(msgname)
if mp is None:

View File

@@ -138,10 +138,8 @@ class MCU_stepper:
def get_commanded_position(self):
ffi_main, ffi_lib = chelper.get_ffi()
return ffi_lib.itersolve_get_commanded_pos(self._stepper_kinematics)
def get_mcu_position(self, cmd_pos=None):
if cmd_pos is None:
cmd_pos = self.get_commanded_position()
mcu_pos_dist = cmd_pos + self._mcu_position_offset
def get_mcu_position(self):
mcu_pos_dist = self.get_commanded_position() + self._mcu_position_offset
mcu_pos = mcu_pos_dist / self._step_dist
if mcu_pos >= 0.:
return int(mcu_pos + 0.5)
@@ -403,7 +401,7 @@ class PrinterRail:
changed_invert = pin_params['invert'] != endstop['invert']
changed_pullup = pin_params['pullup'] != endstop['pullup']
if changed_invert or changed_pullup:
raise error("Printer rail %s shared endstop pin %s "
raise error("Pinter rail %s shared endstop pin %s "
"must specify the same pullup/invert settings" % (
self.get_name(), pin_name))
mcu_endstop.add_stepper(stepper)

View File

@@ -47,7 +47,6 @@ class Move:
self.delta_v2 = 2.0 * move_d * self.accel
self.max_smoothed_v2 = 0.
self.smooth_delta_v2 = 2.0 * move_d * toolhead.max_accel_to_decel
self.next_junction_v2 = 999999999.9
def limit_speed(self, speed, accel):
speed2 = speed**2
if speed2 < self.max_cruise_v2:
@@ -56,8 +55,6 @@ class Move:
self.accel = min(self.accel, accel)
self.delta_v2 = 2.0 * self.move_d * self.accel
self.smooth_delta_v2 = min(self.smooth_delta_v2, self.delta_v2)
def limit_next_junction_speed(self, speed):
self.next_junction_v2 = min(self.next_junction_v2, speed**2)
def move_error(self, msg="Move out of range"):
ep = self.end_pos
m = "%s: %.3f %.3f %.3f [%.3f]" % (msg, ep[0], ep[1], ep[2], ep[3])
@@ -67,33 +64,32 @@ class Move:
return
# Allow extruder to calculate its maximum junction
extruder_v2 = self.toolhead.extruder.calc_junction(prev_move, self)
max_start_v2 = min(extruder_v2, self.max_cruise_v2,
prev_move.max_cruise_v2, prev_move.next_junction_v2,
prev_move.max_start_v2 + prev_move.delta_v2)
# Find max velocity using "approximated centripetal velocity"
axes_r = self.axes_r
prev_axes_r = prev_move.axes_r
junction_cos_theta = -(axes_r[0] * prev_axes_r[0]
+ axes_r[1] * prev_axes_r[1]
+ axes_r[2] * prev_axes_r[2])
sin_theta_d2 = math.sqrt(max(0.5*(1.0-junction_cos_theta), 0.))
cos_theta_d2 = math.sqrt(max(0.5*(1.0+junction_cos_theta), 0.))
one_minus_sin_theta_d2 = 1. - sin_theta_d2
if one_minus_sin_theta_d2 > 0. and cos_theta_d2 > 0.:
R_jd = sin_theta_d2 / one_minus_sin_theta_d2
move_jd_v2 = R_jd * self.junction_deviation * self.accel
pmove_jd_v2 = R_jd * prev_move.junction_deviation * prev_move.accel
# Approximated circle must contact moves no further than mid-move
# centripetal_v2 = .5 * self.move_d * self.accel * tan_theta_d2
quarter_tan_theta_d2 = .25 * sin_theta_d2 / cos_theta_d2
move_centripetal_v2 = self.delta_v2 * quarter_tan_theta_d2
pmove_centripetal_v2 = prev_move.delta_v2 * quarter_tan_theta_d2
max_start_v2 = min(max_start_v2, move_jd_v2, pmove_jd_v2,
move_centripetal_v2, pmove_centripetal_v2)
if junction_cos_theta > 0.999999:
return
junction_cos_theta = max(junction_cos_theta, -0.999999)
sin_theta_d2 = math.sqrt(0.5*(1.0-junction_cos_theta))
R_jd = sin_theta_d2 / (1. - sin_theta_d2)
# Approximated circle must contact moves no further away than mid-move
tan_theta_d2 = sin_theta_d2 / math.sqrt(0.5*(1.0+junction_cos_theta))
move_centripetal_v2 = .5 * self.move_d * tan_theta_d2 * self.accel
prev_move_centripetal_v2 = (.5 * prev_move.move_d * tan_theta_d2
* prev_move.accel)
# Apply limits
self.max_start_v2 = max_start_v2
self.max_start_v2 = min(
R_jd * self.junction_deviation * self.accel,
R_jd * prev_move.junction_deviation * prev_move.accel,
move_centripetal_v2, prev_move_centripetal_v2,
extruder_v2, self.max_cruise_v2, prev_move.max_cruise_v2,
prev_move.max_start_v2 + prev_move.delta_v2)
self.max_smoothed_v2 = min(
max_start_v2, prev_move.max_smoothed_v2 + prev_move.smooth_delta_v2)
self.max_start_v2
, prev_move.max_smoothed_v2 + prev_move.smooth_delta_v2)
def set_junction(self, start_v2, cruise_v2, end_v2):
# Determine accel, cruise, and decel portions of the move distance
half_inv_accel = .5 / self.accel
@@ -457,7 +453,7 @@ class ToolHead:
# Movement commands
def get_position(self):
return list(self.commanded_pos)
def set_position(self, newpos, homing_axes=""):
def set_position(self, newpos, homing_axes=()):
self.flush_step_generation()
ffi_main, ffi_lib = chelper.get_ffi()
ffi_lib.trapq_set_position(self.trapq, self.print_time,
@@ -465,10 +461,6 @@ class ToolHead:
self.commanded_pos[:] = newpos
self.kin.set_position(newpos, homing_axes)
self.printer.send_event("toolhead:set_position")
def limit_next_junction_speed(self, speed):
last_move = self.lookahead.get_last()
if last_move is not None:
last_move.limit_next_junction_speed(speed)
def move(self, newpos, speed):
move = Move(self, self.commanded_pos, newpos, speed)
if not move.move_d:

View File

@@ -105,23 +105,16 @@ The stm32h7 directory contains code from:
version v1.9.0 (ccb11556044540590ca6e45056e6b65cdca2deb2). Contents
taken from the Drivers/CMSIS/Device/ST/STM32H7xx/ directory.
The pico-sdk directory contains code from the pico sdk:
The rp2040 directory contains code from the pico sdk:
https://github.com/raspberrypi/pico-sdk.git
version 2.0.0 (efe2103f9b28458a1615ff096054479743ade236). It has been
version 1.2.0 (bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7). It has been
modified so that it can build outside of the pico sdk. See
pico-sdk.patch for the modifications.
The elf2uf2 directory contains code from the pico sdk:
https://github.com/raspberrypi/pico-sdk.git
version 1.2.0 (bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7). Contents
taken from the tools/elf2uf2/ directory.
rp2040.patch for the modifications.
The rp2040_flash directory contains a light-weight bootsel flash tool.
It uses C part of the the `picoboot_connection` directory found in:
https://github.com/raspberrypi/picotool.git
version 2.0.0 (8a9af99ab10b20b1c6afb30cd9384e562a6647f9). Note that
Makefile and main.c are locally developed files (the remaining files
are from the picotool repo).
version v1.1.0 (55fd880c3dc029b961fc1a0967a6cfdc0af02721).
The hub-ctrl directory contains code from:
https://github.com/codazoda/hub-ctrl.c/
@@ -174,7 +167,7 @@ used to upload firmware to devices flashed with the CanBoot bootloader.
The can2040 directory contains code from:
https://github.com/KevinOConnor/can2040
version v1.7.0 (90515f53ce89442f1bcc3033aae222e9eb77818c).
version v1.6.0 (af3d21e5d61b8408c63fbdfb0aceb21d69d91693)
The Huada HC32F460 directory contains code from:
https://www.hdsc.com.cn/Category83-1490

View File

@@ -1,13 +1,13 @@
// Software CANbus implementation for rp2040
//
// Copyright (C) 2022-2024 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2022,2023 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <stdint.h> // uint32_t
#include <string.h> // memset
#include "RP2040.h" // hw_set_bits
#include "can2040.h" // can2040_setup
#include "cmsis_gcc.h" // __DMB
#include "hardware/regs/dreq.h" // DREQ_PIO0_RX1
#include "hardware/structs/dma.h" // dma_hw
#include "hardware/structs/iobank0.h" // iobank0_hw
@@ -20,13 +20,6 @@
* rp2040 and low-level helper functions
****************************************************************/
// Determine if the target is an rp2350
#ifdef PICO_RP2350
#define IS_RP2350 1
#else
#define IS_RP2350 0
#endif
// Helper compiler definitions
#define barrier() __asm__ __volatile__("": : :"memory")
#define likely(x) __builtin_expect(!!(x), 1)
@@ -130,29 +123,19 @@ static const uint16_t can2040_program_instructions[] = {
#define SI_RX_DATA PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS
#define SI_TXPENDING PIO_IRQ0_INTE_SM1_BITS // Misc bit manually forced
// Return the gpio bank offset (on rp2350 chips)
static uint32_t
pio_gpiobase(struct can2040 *cd)
{
if (!IS_RP2350)
return 0;
return (cd->gpio_rx > 31 || cd->gpio_tx > 31) ? 16 : 0;
}
// Setup PIO "sync" state machine (state machine 0)
static void
pio_sync_setup(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
pio_sm_hw_t *sm = &pio_hw->sm[0];
uint32_t gpio_rx = (cd->gpio_rx - pio_gpiobase(cd)) & 0x1f;
struct pio_sm_hw *sm = &pio_hw->sm[0];
sm->execctrl = (
gpio_rx << PIO_SM0_EXECCTRL_JMP_PIN_LSB
cd->gpio_rx << PIO_SM0_EXECCTRL_JMP_PIN_LSB
| (can2040_offset_sync_end - 1) << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
| can2040_offset_sync_signal_start << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
sm->pinctrl = (
1 << PIO_SM0_PINCTRL_SET_COUNT_LSB
| gpio_rx << PIO_SM0_PINCTRL_SET_BASE_LSB);
| cd->gpio_rx << PIO_SM0_PINCTRL_SET_BASE_LSB);
sm->instr = 0xe080; // set pindirs, 0
sm->pinctrl = 0;
pio_hw->txf[0] = 9 + 6 * PIO_CLOCK_PER_BIT / 2;
@@ -165,12 +148,11 @@ static void
pio_rx_setup(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
pio_sm_hw_t *sm = &pio_hw->sm[1];
uint32_t gpio_rx = (cd->gpio_rx - pio_gpiobase(cd)) & 0x1f;
struct pio_sm_hw *sm = &pio_hw->sm[1];
sm->execctrl = (
(can2040_offset_shared_rx_end - 1) << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
| can2040_offset_shared_rx_read << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
sm->pinctrl = gpio_rx << PIO_SM0_PINCTRL_IN_BASE_LSB;
sm->pinctrl = cd->gpio_rx << PIO_SM0_PINCTRL_IN_BASE_LSB;
sm->shiftctrl = 0; // flush fifo on a restart
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS
| PIO_RX_WAKE_BITS << PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB
@@ -183,16 +165,15 @@ static void
pio_match_setup(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
pio_sm_hw_t *sm = &pio_hw->sm[2];
struct pio_sm_hw *sm = &pio_hw->sm[2];
sm->execctrl = (
(can2040_offset_match_end - 1) << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
| can2040_offset_shared_rx_read << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
uint32_t gpio_rx = (cd->gpio_rx - pio_gpiobase(cd)) & 0x1f;
sm->pinctrl = gpio_rx << PIO_SM0_PINCTRL_IN_BASE_LSB;
sm->pinctrl = cd->gpio_rx << PIO_SM0_PINCTRL_IN_BASE_LSB;
sm->shiftctrl = 0;
sm->instr = 0xe040; // set y, 0
sm->instr = 0xa0e2; // mov osr, y
sm->instr = 0xa02a; // mov x, !y
sm->instr = 0xa02a, // mov x, !y
sm->instr = can2040_offset_match_load_next; // jmp match_load_next
}
@@ -201,19 +182,17 @@ static void
pio_tx_setup(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
pio_sm_hw_t *sm = &pio_hw->sm[3];
uint32_t gpio_rx = (cd->gpio_rx - pio_gpiobase(cd)) & 0x1f;
uint32_t gpio_tx = (cd->gpio_tx - pio_gpiobase(cd)) & 0x1f;
struct pio_sm_hw *sm = &pio_hw->sm[3];
sm->execctrl = (
gpio_rx << PIO_SM0_EXECCTRL_JMP_PIN_LSB
cd->gpio_rx << PIO_SM0_EXECCTRL_JMP_PIN_LSB
| can2040_offset_tx_conflict << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
| can2040_offset_tx_conflict << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS
| PIO_SM0_SHIFTCTRL_AUTOPULL_BITS);
sm->pinctrl = (1 << PIO_SM0_PINCTRL_SET_COUNT_LSB
| 1 << PIO_SM0_PINCTRL_OUT_COUNT_LSB
| gpio_tx << PIO_SM0_PINCTRL_SET_BASE_LSB
| gpio_tx << PIO_SM0_PINCTRL_OUT_BASE_LSB);
| cd->gpio_tx << PIO_SM0_PINCTRL_SET_BASE_LSB
| cd->gpio_tx << PIO_SM0_PINCTRL_OUT_BASE_LSB);
sm->instr = 0xe001; // set pins, 1
sm->instr = 0xe081; // set pindirs, 1
}
@@ -276,7 +255,7 @@ pio_tx_reset(struct can2040 *cd)
| (0x08 << PIO_CTRL_SM_RESTART_LSB));
pio_hw->irq = (SI_MATCHED | SI_ACKDONE) >> 8; // clear PIO irq flags
// Clear tx fifo
pio_sm_hw_t *sm = &pio_hw->sm[3];
struct pio_sm_hw *sm = &pio_hw->sm[3];
sm->shiftctrl = 0;
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS
| PIO_SM0_SHIFTCTRL_AUTOPULL_BITS);
@@ -292,7 +271,7 @@ pio_tx_send(struct can2040 *cd, uint32_t *data, uint32_t count)
uint32_t i;
for (i=0; i<count; i++)
pio_hw->txf[3] = data[i];
pio_sm_hw_t *sm = &pio_hw->sm[3];
struct pio_sm_hw *sm = &pio_hw->sm[3];
sm->instr = 0xe001; // set pins, 1
sm->instr = 0x6021; // out x, 1
sm->instr = can2040_offset_tx_write_pin; // jmp tx_write_pin
@@ -308,7 +287,7 @@ pio_tx_inject_ack(struct can2040 *cd, uint32_t match_key)
pio_tx_reset(cd);
pio_hw->instr_mem[can2040_offset_tx_got_recessive] = 0xc023; // irq wait 3
pio_hw->txf[3] = 0x7fffffff;
pio_sm_hw_t *sm = &pio_hw->sm[3];
struct pio_sm_hw *sm = &pio_hw->sm[3];
sm->instr = 0xe001; // set pins, 1
sm->instr = 0x6021; // out x, 1
sm->instr = can2040_offset_tx_write_pin; // jmp tx_write_pin
@@ -403,10 +382,6 @@ pio_setup(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate)
{
// Configure pio0 clock
uint32_t rb = cd->pio_num ? RESETS_RESET_PIO1_BITS : RESETS_RESET_PIO0_BITS;
#if IS_RP2350
if (cd->pio_num == 2)
rb = RESETS_RESET_PIO2_BITS;
#endif
rp2040_clear_reset(rb);
// Setup and sync pio state machine clocks
@@ -416,16 +391,11 @@ pio_setup(struct can2040 *cd, uint32_t sys_clock, uint32_t bitrate)
for (i=0; i<4; i++)
pio_hw->sm[i].clkdiv = div << PIO_SM0_CLKDIV_FRAC_LSB;
// Configure gpiobase (on rp2350)
#if IS_RP2350
pio_hw->gpiobase = pio_gpiobase(cd);
#endif
// Configure state machines
pio_sm_setup(cd);
// Map Rx/Tx gpios
uint32_t pio_func = 6 + cd->pio_num;
uint32_t pio_func = cd->pio_num ? 7 : 6;
rp2040_gpio_peripheral(cd->gpio_rx, pio_func, 1);
rp2040_gpio_peripheral(cd->gpio_tx, pio_func, 0);
}
@@ -525,7 +495,7 @@ unstuf_restore_state(struct can2040_bitunstuffer *bu, uint32_t data)
// Pull bits from unstuffer (as specified in unstuf_set_count() )
static int
unstuf_pull_bits_rp2040(struct can2040_bitunstuffer *bu)
unstuf_pull_bits(struct can2040_bitunstuffer *bu)
{
uint32_t sb = bu->stuffed_bits, edges = sb ^ (sb >> 1);
uint32_t e2 = edges | (edges >> 1), e4 = e2 | (e2 >> 2), rm_bits = ~e4;
@@ -569,49 +539,6 @@ unstuf_pull_bits_rp2040(struct can2040_bitunstuffer *bu)
}
}
// Pull bits from unstuffer (optimized for rp2350)
static int
unstuf_pull_bits(struct can2040_bitunstuffer *bu)
{
if (!IS_RP2350)
return unstuf_pull_bits_rp2040(bu);
uint32_t sb = bu->stuffed_bits, edges = sb ^ (sb >> 1);
uint32_t e2 = edges | (edges >> 1), e4 = e2 | (e2 >> 2), rm_bits = ~e4;
uint32_t cs = bu->count_stuff, cu = bu->count_unstuff;
for (;;) {
if (!cs)
// Need more data
return 1;
uint32_t try_cnt = cs > cu ? cu : cs;
uint32_t try_mask = ((1 << try_cnt) - 1) << (cs + 1 - try_cnt);
uint32_t rm_masked_bits = rm_bits & try_mask;
if (likely(!rm_masked_bits)) {
// No stuff bits in try_cnt bits - copy into unstuffed_bits
bu->count_unstuff = cu = cu - try_cnt;
bu->count_stuff = cs = cs - try_cnt;
bu->unstuffed_bits |= ((sb >> cs) & ((1 << try_cnt) - 1)) << cu;
if (! cu)
// Extracted desired bits
return 0;
// Need more data
return 1;
}
// Copy any leading bits prior to stuff bit (may be zero)
uint32_t copy_cnt = cs - (31 - __builtin_clz(rm_masked_bits));
cs -= copy_cnt;
bu->count_unstuff = cu = cu - copy_cnt;
bu->unstuffed_bits |= ((sb >> cs) & ((1 << copy_cnt) - 1)) << cu;
// High bit is now a stuff bit - remove it
bu->count_stuff = cs = cs - 1;
if (unlikely(rm_bits & (1 << cs))) {
// Six consecutive bits - a bitstuff error
if (sb & (1 << cs))
return -1;
return -2;
}
}
}
// Return most recent raw (still stuffed) bits
static uint32_t
unstuf_get_raw(struct can2040_bitunstuffer *bu)
@@ -626,7 +553,7 @@ unstuf_get_raw(struct can2040_bitunstuffer *bu)
// Stuff 'num_bits' bits in '*pb' - upper bits must already be stuffed
static uint32_t
bitstuff_rp2040(uint32_t *pb, uint32_t num_bits)
bitstuff(uint32_t *pb, uint32_t num_bits)
{
uint32_t b = *pb, count = num_bits;
for (;;) {
@@ -663,34 +590,6 @@ done:
return count;
}
// Stuff 'num_bits' bits in '*pb' (optimized for rp2350)
static uint32_t
bitstuff(uint32_t *pb, uint32_t num_bits)
{
if (!IS_RP2350)
return bitstuff_rp2040(pb, num_bits);
uint32_t b = *pb, count = num_bits;
for (;;) {
uint32_t edges = b ^ (b >> 1);
uint32_t e2 = edges | (edges >> 1), e4 = e2 | (e2 >> 2), add_bits = ~e4;
uint32_t mask = (1 << num_bits) - 1, add_masked_bits = add_bits & mask;
if (!add_masked_bits)
// No more stuff bits needed
break;
// Insert a stuff bit
uint32_t stuff_pos = 1 + 31 - __builtin_clz(add_masked_bits);
uint32_t low_mask = (1 << stuff_pos) - 1, low = b & low_mask;
uint32_t high = (b & ~(low_mask >> 1)) << 1;
b = high ^ low ^ (1 << (stuff_pos - 1));
count += 1;
if (stuff_pos <= 4)
break;
num_bits = stuff_pos - 4;
}
*pb = b;
return count;
}
// State storage for building bit stuffed transmit messages
struct bitstuffer_s {
uint32_t prev_stuffed, bitpos, *buf;
@@ -1427,12 +1326,6 @@ can2040_setup(struct can2040 *cd, uint32_t pio_num)
memset(cd, 0, sizeof(*cd));
cd->pio_num = !!pio_num;
cd->pio_hw = cd->pio_num ? pio1_hw : pio0_hw;
#if IS_RP2350
if (pio_num == 2) {
cd->pio_num = pio_num;
cd->pio_hw = pio2_hw;
}
#endif
}
// API function to configure callback

File diff suppressed because it is too large Load Diff

View File

@@ -1,352 +0,0 @@
/******************************************************************************
* @file mpu_armv8.h
* @brief CMSIS MPU API for Armv8-M and Armv8.1-M MPU
* @version V5.1.2
* @date 10. February 2020
******************************************************************************/
/*
* Copyright (c) 2017-2020 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined ( __ICCARM__ )
#pragma system_include /* treat file as system include file for MISRA check */
#elif defined (__clang__)
#pragma clang system_header /* treat file as system include file */
#endif
#ifndef ARM_MPU_ARMV8_H
#define ARM_MPU_ARMV8_H
/** \brief Attribute for device memory (outer only) */
#define ARM_MPU_ATTR_DEVICE ( 0U )
/** \brief Attribute for non-cacheable, normal memory */
#define ARM_MPU_ATTR_NON_CACHEABLE ( 4U )
/** \brief Attribute for normal memory (outer and inner)
* \param NT Non-Transient: Set to 1 for non-transient data.
* \param WB Write-Back: Set to 1 to use write-back update policy.
* \param RA Read Allocation: Set to 1 to use cache allocation on read miss.
* \param WA Write Allocation: Set to 1 to use cache allocation on write miss.
*/
#define ARM_MPU_ATTR_MEMORY_(NT, WB, RA, WA) \
((((NT) & 1U) << 3U) | (((WB) & 1U) << 2U) | (((RA) & 1U) << 1U) | ((WA) & 1U))
/** \brief Device memory type non Gathering, non Re-ordering, non Early Write Acknowledgement */
#define ARM_MPU_ATTR_DEVICE_nGnRnE (0U)
/** \brief Device memory type non Gathering, non Re-ordering, Early Write Acknowledgement */
#define ARM_MPU_ATTR_DEVICE_nGnRE (1U)
/** \brief Device memory type non Gathering, Re-ordering, Early Write Acknowledgement */
#define ARM_MPU_ATTR_DEVICE_nGRE (2U)
/** \brief Device memory type Gathering, Re-ordering, Early Write Acknowledgement */
#define ARM_MPU_ATTR_DEVICE_GRE (3U)
/** \brief Memory Attribute
* \param O Outer memory attributes
* \param I O == ARM_MPU_ATTR_DEVICE: Device memory attributes, else: Inner memory attributes
*/
#define ARM_MPU_ATTR(O, I) ((((O) & 0xFU) << 4U) | ((((O) & 0xFU) != 0U) ? ((I) & 0xFU) : (((I) & 0x3U) << 2U)))
/** \brief Normal memory non-shareable */
#define ARM_MPU_SH_NON (0U)
/** \brief Normal memory outer shareable */
#define ARM_MPU_SH_OUTER (2U)
/** \brief Normal memory inner shareable */
#define ARM_MPU_SH_INNER (3U)
/** \brief Memory access permissions
* \param RO Read-Only: Set to 1 for read-only memory.
* \param NP Non-Privileged: Set to 1 for non-privileged memory.
*/
#define ARM_MPU_AP_(RO, NP) ((((RO) & 1U) << 1U) | ((NP) & 1U))
/** \brief Region Base Address Register value
* \param BASE The base address bits [31:5] of a memory region. The value is zero extended. Effective address gets 32 byte aligned.
* \param SH Defines the Shareability domain for this memory region.
* \param RO Read-Only: Set to 1 for a read-only memory region.
* \param NP Non-Privileged: Set to 1 for a non-privileged memory region.
* \oaram XN eXecute Never: Set to 1 for a non-executable memory region.
*/
#define ARM_MPU_RBAR(BASE, SH, RO, NP, XN) \
(((BASE) & MPU_RBAR_BASE_Msk) | \
(((SH) << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk) | \
((ARM_MPU_AP_(RO, NP) << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk) | \
(((XN) << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk))
/** \brief Region Limit Address Register value
* \param LIMIT The limit address bits [31:5] for this memory region. The value is one extended.
* \param IDX The attribute index to be associated with this memory region.
*/
#define ARM_MPU_RLAR(LIMIT, IDX) \
(((LIMIT) & MPU_RLAR_LIMIT_Msk) | \
(((IDX) << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | \
(MPU_RLAR_EN_Msk))
#if defined(MPU_RLAR_PXN_Pos)
/** \brief Region Limit Address Register with PXN value
* \param LIMIT The limit address bits [31:5] for this memory region. The value is one extended.
* \param PXN Privileged execute never. Defines whether code can be executed from this privileged region.
* \param IDX The attribute index to be associated with this memory region.
*/
#define ARM_MPU_RLAR_PXN(LIMIT, PXN, IDX) \
(((LIMIT) & MPU_RLAR_LIMIT_Msk) | \
(((PXN) << MPU_RLAR_PXN_Pos) & MPU_RLAR_PXN_Msk) | \
(((IDX) << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | \
(MPU_RLAR_EN_Msk))
#endif
/**
* Struct for a single MPU Region
*/
typedef struct {
uint32_t RBAR; /*!< Region Base Address Register value */
uint32_t RLAR; /*!< Region Limit Address Register value */
} ARM_MPU_Region_t;
/** Enable the MPU.
* \param MPU_Control Default access permissions for unconfigured regions.
*/
__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control)
{
__DMB();
MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk;
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
#endif
__DSB();
__ISB();
}
/** Disable the MPU.
*/
__STATIC_INLINE void ARM_MPU_Disable(void)
{
__DMB();
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
#endif
MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;
__DSB();
__ISB();
}
#ifdef MPU_NS
/** Enable the Non-secure MPU.
* \param MPU_Control Default access permissions for unconfigured regions.
*/
__STATIC_INLINE void ARM_MPU_Enable_NS(uint32_t MPU_Control)
{
__DMB();
MPU_NS->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk;
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
SCB_NS->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
#endif
__DSB();
__ISB();
}
/** Disable the Non-secure MPU.
*/
__STATIC_INLINE void ARM_MPU_Disable_NS(void)
{
__DMB();
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
SCB_NS->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
#endif
MPU_NS->CTRL &= ~MPU_CTRL_ENABLE_Msk;
__DSB();
__ISB();
}
#endif
/** Set the memory attribute encoding to the given MPU.
* \param mpu Pointer to the MPU to be configured.
* \param idx The attribute index to be set [0-7]
* \param attr The attribute value to be set.
*/
__STATIC_INLINE void ARM_MPU_SetMemAttrEx(MPU_Type* mpu, uint8_t idx, uint8_t attr)
{
const uint8_t reg = idx / 4U;
const uint32_t pos = ((idx % 4U) * 8U);
const uint32_t mask = 0xFFU << pos;
if (reg >= (sizeof(mpu->MAIR) / sizeof(mpu->MAIR[0]))) {
return; // invalid index
}
mpu->MAIR[reg] = ((mpu->MAIR[reg] & ~mask) | ((attr << pos) & mask));
}
/** Set the memory attribute encoding.
* \param idx The attribute index to be set [0-7]
* \param attr The attribute value to be set.
*/
__STATIC_INLINE void ARM_MPU_SetMemAttr(uint8_t idx, uint8_t attr)
{
ARM_MPU_SetMemAttrEx(MPU, idx, attr);
}
#ifdef MPU_NS
/** Set the memory attribute encoding to the Non-secure MPU.
* \param idx The attribute index to be set [0-7]
* \param attr The attribute value to be set.
*/
__STATIC_INLINE void ARM_MPU_SetMemAttr_NS(uint8_t idx, uint8_t attr)
{
ARM_MPU_SetMemAttrEx(MPU_NS, idx, attr);
}
#endif
/** Clear and disable the given MPU region of the given MPU.
* \param mpu Pointer to MPU to be used.
* \param rnr Region number to be cleared.
*/
__STATIC_INLINE void ARM_MPU_ClrRegionEx(MPU_Type* mpu, uint32_t rnr)
{
mpu->RNR = rnr;
mpu->RLAR = 0U;
}
/** Clear and disable the given MPU region.
* \param rnr Region number to be cleared.
*/
__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr)
{
ARM_MPU_ClrRegionEx(MPU, rnr);
}
#ifdef MPU_NS
/** Clear and disable the given Non-secure MPU region.
* \param rnr Region number to be cleared.
*/
__STATIC_INLINE void ARM_MPU_ClrRegion_NS(uint32_t rnr)
{
ARM_MPU_ClrRegionEx(MPU_NS, rnr);
}
#endif
/** Configure the given MPU region of the given MPU.
* \param mpu Pointer to MPU to be used.
* \param rnr Region number to be configured.
* \param rbar Value for RBAR register.
* \param rlar Value for RLAR register.
*/
__STATIC_INLINE void ARM_MPU_SetRegionEx(MPU_Type* mpu, uint32_t rnr, uint32_t rbar, uint32_t rlar)
{
mpu->RNR = rnr;
mpu->RBAR = rbar;
mpu->RLAR = rlar;
}
/** Configure the given MPU region.
* \param rnr Region number to be configured.
* \param rbar Value for RBAR register.
* \param rlar Value for RLAR register.
*/
__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rnr, uint32_t rbar, uint32_t rlar)
{
ARM_MPU_SetRegionEx(MPU, rnr, rbar, rlar);
}
#ifdef MPU_NS
/** Configure the given Non-secure MPU region.
* \param rnr Region number to be configured.
* \param rbar Value for RBAR register.
* \param rlar Value for RLAR register.
*/
__STATIC_INLINE void ARM_MPU_SetRegion_NS(uint32_t rnr, uint32_t rbar, uint32_t rlar)
{
ARM_MPU_SetRegionEx(MPU_NS, rnr, rbar, rlar);
}
#endif
/** Memcopy with strictly ordered memory access, e.g. for register targets.
* \param dst Destination data is copied to.
* \param src Source data is copied from.
* \param len Amount of data words to be copied.
*/
__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len)
{
uint32_t i;
for (i = 0U; i < len; ++i)
{
dst[i] = src[i];
}
}
/** Load the given number of MPU regions from a table to the given MPU.
* \param mpu Pointer to the MPU registers to be used.
* \param rnr First region number to be configured.
* \param table Pointer to the MPU configuration table.
* \param cnt Amount of regions to be configured.
*/
__STATIC_INLINE void ARM_MPU_LoadEx(MPU_Type* mpu, uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt)
{
const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U;
if (cnt == 1U) {
mpu->RNR = rnr;
ARM_MPU_OrderedMemcpy(&(mpu->RBAR), &(table->RBAR), rowWordSize);
} else {
uint32_t rnrBase = rnr & ~(MPU_TYPE_RALIASES-1U);
uint32_t rnrOffset = rnr % MPU_TYPE_RALIASES;
mpu->RNR = rnrBase;
while ((rnrOffset + cnt) > MPU_TYPE_RALIASES) {
uint32_t c = MPU_TYPE_RALIASES - rnrOffset;
ARM_MPU_OrderedMemcpy(&(mpu->RBAR)+(rnrOffset*2U), &(table->RBAR), c*rowWordSize);
table += c;
cnt -= c;
rnrOffset = 0U;
rnrBase += MPU_TYPE_RALIASES;
mpu->RNR = rnrBase;
}
ARM_MPU_OrderedMemcpy(&(mpu->RBAR)+(rnrOffset*2U), &(table->RBAR), cnt*rowWordSize);
}
}
/** Load the given number of MPU regions from a table.
* \param rnr First region number to be configured.
* \param table Pointer to the MPU configuration table.
* \param cnt Amount of regions to be configured.
*/
__STATIC_INLINE void ARM_MPU_Load(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt)
{
ARM_MPU_LoadEx(MPU, rnr, table, cnt);
}
#ifdef MPU_NS
/** Load the given number of MPU regions from a table to the Non-secure MPU.
* \param rnr First region number to be configured.
* \param table Pointer to the MPU configuration table.
* \param cnt Amount of regions to be configured.
*/
__STATIC_INLINE void ARM_MPU_Load_NS(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt)
{
ARM_MPU_LoadEx(MPU_NS, rnr, table, cnt);
}
#endif
#endif

View File

@@ -1,42 +0,0 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _BOOT_PICOBOOT_CONSTANTS_H
#define _BOOT_PICOBOOT_CONSTANTS_H
#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)
// 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_FLASH_UPDATE 0x4 // param0 = update_base
// values 8-15 are secure only
#define REBOOT2_FLAG_REBOOT_TYPE_PC_SP 0xd
#define REBOOT2_FLAG_REBOOT_TO_ARM 0x10
#define REBOOT2_FLAG_REBOOT_TO_RISCV 0x20
#define REBOOT2_FLAG_NO_RETURN_ON_SUCCESS 0x100
#define BOOTSEL_FLAG_DISABLE_MSD_INTERFACE 0x01
#define BOOTSEL_FLAG_DISABLE_PICOBOOT_INTERFACE 0x02
#define BOOTSEL_FLAG_GPIO_PIN_ACTIVE_LOW 0x10
#define BOOTSEL_FLAG_GPIO_PIN_SPECIFIED 0x20
#define PICOBOOT_GET_INFO_SYS 1
#define PICOBOOT_GET_INFO_PARTTION_TABLE 2
#define PICOBOOT_GET_INFO_UF2_TARGET_PARTITION 3
#define PICOBOOT_GET_INFO_UF2_STATUS 4
#define UF2_STATUS_IGNORED_FAMILY 0x01
#define UF2_STATUS_ABORT_EXCLUSIVELY_LOCKED 0x10
#define UF2_STATUS_ABORT_BAD_ADDRESS 0x20
#define UF2_STATUS_ABORT_WRITE_ERROR 0x40
#define UF2_STATUS_ABORT_REBOOT_FAILED 0x80
#endif

View File

@@ -1,31 +0,0 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _HARDWARE_PLATFORM_DEFS_H
#define _HARDWARE_PLATFORM_DEFS_H
#define NUM_CORES 2u
#define NUM_DMA_CHANNELS 12u
#define NUM_GENERIC_TIMERS 1u
#define NUM_ALARMS 4u
#define NUM_IRQS 32u
#define NUM_SPIN_LOCKS 32u
#define XOSC_HZ 12000000u
#define NUM_SPIN_LOCKS 32u
#define NUM_BANK0_GPIOS 30
#ifndef _u
#define _u(x) x ## u
#endif
#endif

View File

@@ -1,69 +0,0 @@
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
+++ b/lib/pico-sdk/hardware/address_mapped.h
@@ -7,7 +7,10 @@
#ifndef _HARDWARE_ADDRESS_MAPPED_H
#define _HARDWARE_ADDRESS_MAPPED_H
-#include "pico.h"
+//#include "pico.h"
+#define __force_inline inline
+#define static_assert(a,b)
+#define valid_params_if(a,b)
#include "hardware/regs/addressmap.h"
/** \file address_mapped.h
diff --git a/lib/pico-sdk/rp2040/cmsis_include/RP2040.h b/lib/pico-sdk/rp2040/cmsis_include/RP2040.h
index 8da431fae..be661392c 100644
--- a/lib/pico-sdk/rp2040/cmsis_include/RP2040.h
+++ b/lib/pico-sdk/rp2040/cmsis_include/RP2040.h
@@ -2572,6 +2572,7 @@ typedef struct { /*!< RTC Structure
* @{
*/
+#if 0
#define RESETS_BASE 0x4000C000UL
#define PSM_BASE 0x40010000UL
#define CLOCKS_BASE 0x40008000UL
@@ -2608,6 +2609,7 @@ typedef struct { /*!< RTC Structure
#define TBMAN_BASE 0x4006C000UL
#define VREG_AND_CHIP_RESET_BASE 0x40064000UL
#define RTC_BASE 0x4005C000UL
+#endif
/** @} */ /* End of group Device_Peripheral_peripheralAddr */
diff --git a/lib/pico-sdk/rp2040/pico/asm_helper.S b/lib/pico-sdk/rp2040/pico/asm_helper.S
index aff1fc9ae..59c67db19 100644
--- a/lib/pico-sdk/rp2040/pico/asm_helper.S
+++ b/lib/pico-sdk/rp2040/pico/asm_helper.S
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
-#include "pico.h"
+//#include "pico.h"
# note we don't do this by default in this file for backwards comaptibility with user code
# that may include this file, but not use unified syntax. Note that this macro does equivalent
diff --git a/lib/pico-sdk/rp2350/cmsis_include/RP2350.h b/lib/pico-sdk/rp2350/cmsis_include/RP2350.h
index 8ae014e04..94d0f178c 100644
--- a/lib/pico-sdk/rp2350/cmsis_include/RP2350.h
+++ b/lib/pico-sdk/rp2350/cmsis_include/RP2350.h
@@ -5933,6 +5933,7 @@ typedef struct { /*!< USB_DPRAM Structure
* @{
*/
+#if 0
#define RESETS_BASE 0x40020000UL
#define PSM_BASE 0x40018000UL
#define CLOCKS_BASE 0x40010000UL
@@ -5986,6 +5987,7 @@ typedef struct { /*!< USB_DPRAM Structure
#define OTP_DATA_RAW_BASE 0x40134000UL
#define TBMAN_BASE 0x40160000UL
#define USB_DPRAM_BASE 0x50100000UL
+#endif
/** @} */ /* End of group Device_Peripheral_peripheralAddr */

View File

@@ -1,342 +0,0 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* 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

View File

@@ -1,146 +0,0 @@
# Always include these libraries through //src/rp2_common:*!
# This ensures that you'll get the right headers for the MCU you're targeting.
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
load("@rules_python//python:defs.bzl", "py_binary")
load("//bazel/toolchain:objcopy.bzl", "objcopy_to_bin")
load("//bazel/util:multiple_choice_flag.bzl", "declare_flag_choices", "flag_choice")
load("//bazel/util:transition.bzl", "rp2040_bootloader_binary")
# There's a lot of implementation details in here that shouldn't be considered
# stable, so allowlist visibility to just the public-facing pieces.
package(default_visibility = ["//visibility:private"])
# Known choices for boot2:
BOOT2_CHOICES = [
"boot2_at25sf128a",
"boot2_generic_03h",
"boot2_is25lp080",
"boot2_usb_blinky",
"boot2_w25q080",
"boot2_w25x10cl",
"compile_time_choice",
]
BOOT2_CHOICE_FILES = [c + ".S" for c in BOOT2_CHOICES]
BOOT2_CHOICE_FILE_MAP = {c: [c + ".S"] for c in BOOT2_CHOICES}
BOOT2_CHOICE_DEFINE_MAP = {c: ['PICO_BUILD_BOOT_STAGE2_NAME=\\"{}\\"'.format(c)] for c in BOOT2_CHOICES}
# Define shouldn't be set for compile_time_choice.
BOOT2_CHOICE_DEFINE_MAP["compile_time_choice"] = []
cc_library(
name = "config",
hdrs = [
"asminclude/boot2_helpers/exit_from_boot2.S",
"asminclude/boot2_helpers/read_flash_sreg.S",
"asminclude/boot2_helpers/wait_ssi_ready.S",
"include/boot_stage2/config.h",
] + BOOT2_CHOICE_FILES,
defines = select(flag_choice(
"//bazel/config:PICO_DEFAULT_BOOT_STAGE2",
":__pkg__",
BOOT2_CHOICE_DEFINE_MAP,
)),
includes = [
"asminclude",
"include",
],
target_compatible_with = ["//bazel/constraint:rp2040"],
visibility = ["//visibility:public"],
)
# Creates a config_setting for each known boot2 option with the name:
# PICO_DEFAULT_BOOT_STAGE2_[choice]
declare_flag_choices(
"//bazel/config:PICO_DEFAULT_BOOT_STAGE2",
BOOT2_CHOICES,
)
filegroup(
name = "build_selected_boot2",
srcs = select(flag_choice(
"//bazel/config:PICO_DEFAULT_BOOT_STAGE2",
":__pkg__",
BOOT2_CHOICE_FILE_MAP,
)),
visibility = ["//src/rp2_common:__pkg__"],
)
cc_binary(
name = "boot_stage2_elf_actual",
srcs = ["//bazel/config:PICO_DEFAULT_BOOT_STAGE2_FILE"],
copts = ["-fPIC"],
# Incompatible with section garbage collection.
features = ["-gc_sections"],
linkopts = [
"-Wl,--no-gc-sections",
"-nostartfiles",
"-Wl,--entry=_stage2_boot",
"-T$(location boot_stage2.ld)",
],
# this does nothing if someone passes --custom_malloc, so the
# rp2040_bootloader_binary transition forcibly clobbers --custom_malloc.
malloc = "//bazel:empty_cc_lib",
tags = ["manual"], # Only build as an explicit dependency.
target_compatible_with = ["//bazel/constraint:rp2040"],
deps = [
"boot_stage2.ld",
":config",
"//src/common/pico_base_headers",
"//src/rp2_common:pico_platform_internal",
],
)
# Always build the bootloader with the bootloader-specific platform.
rp2040_bootloader_binary(
name = "boot_stage2_elf",
src = "boot_stage2_elf_actual",
)
objcopy_to_bin(
name = "boot_stage2_bin",
src = ":boot_stage2_elf",
out = "boot_stage2.bin",
target_compatible_with = ["//bazel/constraint:rp2040"],
)
# WORKAROUND: Python rules always require a .py extension.
copy_file(
name = "copy_tool_to_py",
src = "pad_checksum",
out = "pad_checksum_tool.py",
target_compatible_with = ["//bazel/constraint:host"],
)
py_binary(
name = "pad_checksum_tool",
srcs = ["pad_checksum_tool.py"],
target_compatible_with = ["//bazel/constraint:host"],
)
run_binary(
name = "boot_stage2_padded",
srcs = [":boot_stage2_bin"],
outs = ["boot_stage2.S"],
args = [
"-s 0xffffffff",
"$(location boot_stage2_bin)",
"$(location boot_stage2.S)",
],
target_compatible_with = ["//bazel/constraint:rp2040"],
tool = ":pad_checksum_tool",
)
cc_library(
name = "boot_stage2",
srcs = [":boot_stage2_padded"],
target_compatible_with = ["//bazel/constraint:rp2040"],
visibility = ["//src/rp2_common:__pkg__"],
# This isn't referenced as a symbol, so alwayslink is required to ensure
# it doesn't get dropped before the linker script can find it.
alwayslink = True,
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,119 +0,0 @@
/*
* Copyright (c) 2024 Raspberry Pi Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _HARDWARE_PLATFORM_DEFS_H
#define _HARDWARE_PLATFORM_DEFS_H
// This header is included from C and assembler - intended mostly for #defines; guard other stuff with #ifdef __ASSEMBLER__
#ifndef _u
#ifdef __ASSEMBLER__
#define _u(x) x
#else
#define _u(x) x ## u
#endif
#endif
#define NUM_CORES _u(2)
#define NUM_DMA_CHANNELS _u(12)
#define NUM_DMA_TIMERS _u(4)
#define NUM_DMA_IRQS _u(2)
#define NUM_IRQS _u(32)
#define NUM_USER_IRQS _u(6)
#define NUM_PIOS _u(2)
#define NUM_PIO_STATE_MACHINES _u(4)
#define NUM_PIO_IRQS _u(2)
#define NUM_PWM_SLICES _u(8)
#define NUM_PWM_IRQS _u(1)
#define NUM_SPIN_LOCKS _u(32)
#define NUM_UARTS _u(2)
#define NUM_I2CS _u(2)
#define NUM_SPIS _u(2)
#define NUM_GENERIC_TIMERS _u(1)
#define NUM_ALARMS _u(4)
#define ADC_BASE_PIN _u(26)
#define NUM_ADC_CHANNELS _u(5)
#define NUM_RESETS _u(24)
#define NUM_BANK0_GPIOS _u(30)
#define NUM_QSPI_GPIOS _u(6)
#define PIO_INSTRUCTION_COUNT _u(32)
#define USBCTRL_DPRAM_SIZE _u(4096)
#define HAS_SIO_DIVIDER 1
#define HAS_RP2040_RTC 1
// 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
// override the defaults which live in src/rp2_common/hardware_clocks/include/hardware/clocks.h
// Please see the comments there about calculating the new PLL setting values.
#ifndef XOSC_HZ
#ifdef XOSC_KHZ
#define XOSC_HZ ((XOSC_KHZ) * _u(1000))
#elif defined(XOSC_MHZ)
#define XOSC_HZ ((XOSC_MHZ) * _u(1000000))
#else
#define XOSC_HZ _u(12000000)
#endif
#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
#define SYS_CLK_HZ ((SYS_CLK_KHZ) * _u(1000))
#elif defined(SYS_CLK_MHZ)
#define SYS_CLK_HZ ((SYS_CLK_MHZ) * _u(1000000))
#else
#define SYS_CLK_HZ _u(125000000)
#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
#ifdef USB_CLK_KHZ
#define USB_CLK_HZ ((USB_CLK_KHZ) * _u(1000))
#elif defined(USB_CLK_MHZ)
#define USB_CLK_HZ ((USB_CLK_MHZ) * _u(1000000))
#else
#define USB_CLK_HZ _u(48000000)
#endif
#endif
// For backwards compatibility define XOSC_KHZ if the frequency is indeed an integer number of Khz.
#if defined(XOSC_HZ) && !defined(XOSC_KHZ) && (XOSC_HZ % 1000 == 0)
#define XOSC_KHZ (XOSC_HZ / 1000)
#endif
// For backwards compatibility define XOSC_MHZ if the frequency is indeed an integer number of Mhz.
#if defined(XOSC_KHZ) && !defined(XOSC_MHZ) && (XOSC_KHZ % 1000 == 0)
#define XOSC_MHZ (XOSC_KHZ / 1000)
#endif
// For backwards compatibility define SYS_CLK_KHZ if the frequency is indeed an integer number of Khz.
#if defined(SYS_CLK_HZ) && !defined(SYS_CLK_KHZ) && (SYS_CLK_HZ % 1000 == 0)
#define SYS_CLK_KHZ (SYS_CLK_HZ / 1000)
#endif
// For backwards compatibility define SYS_CLK_MHZ if the frequency is indeed an integer number of Mhz.
#if defined(SYS_CLK_KHZ) && !defined(SYS_CLK_MHZ) && (SYS_CLK_KHZ % 1000 == 0)
#define SYS_CLK_MHZ (SYS_CLK_KHZ / 1000)
#endif
// For backwards compatibility define USB_CLK_KHZ if the frequency is indeed an integer number of Khz.
#if defined(USB_CLK_HZ) && !defined(USB_CLK_KHZ) && (USB_CLK_HZ % 1000 == 0)
#define USB_CLK_KHZ (USB_CLK_HZ / 1000)
#endif
// For backwards compatibility define USB_CLK_MHZ if the frequency is indeed an integer number of Mhz.
#if defined(USB_CLK_KHZ) && !defined(USB_CLK_MHZ) && (USB_CLK_KHZ % 1000 == 0)
#define USB_CLK_MHZ (USB_CLK_KHZ / 1000)
#endif
#define FIRST_USER_IRQ (NUM_IRQS - NUM_USER_IRQS)
#define VTABLE_FIRST_IRQ 16
#endif

View File

@@ -1,117 +0,0 @@
// THIS HEADER FILE IS AUTOMATICALLY GENERATED -- DO NOT EDIT
/**
* Copyright (c) 2024 Raspberry Pi Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _DREQ_H
#define _DREQ_H
/**
* \file rp2040/dreq.h
*/
#ifdef __ASSEMBLER__
#define DREQ_PIO0_TX0 0
#define DREQ_PIO0_TX1 1
#define DREQ_PIO0_TX2 2
#define DREQ_PIO0_TX3 3
#define DREQ_PIO0_RX0 4
#define DREQ_PIO0_RX1 5
#define DREQ_PIO0_RX2 6
#define DREQ_PIO0_RX3 7
#define DREQ_PIO1_TX0 8
#define DREQ_PIO1_TX1 9
#define DREQ_PIO1_TX2 10
#define DREQ_PIO1_TX3 11
#define DREQ_PIO1_RX0 12
#define DREQ_PIO1_RX1 13
#define DREQ_PIO1_RX2 14
#define DREQ_PIO1_RX3 15
#define DREQ_SPI0_TX 16
#define DREQ_SPI0_RX 17
#define DREQ_SPI1_TX 18
#define DREQ_SPI1_RX 19
#define DREQ_UART0_TX 20
#define DREQ_UART0_RX 21
#define DREQ_UART1_TX 22
#define DREQ_UART1_RX 23
#define DREQ_PWM_WRAP0 24
#define DREQ_PWM_WRAP1 25
#define DREQ_PWM_WRAP2 26
#define DREQ_PWM_WRAP3 27
#define DREQ_PWM_WRAP4 28
#define DREQ_PWM_WRAP5 29
#define DREQ_PWM_WRAP6 30
#define DREQ_PWM_WRAP7 31
#define DREQ_I2C0_TX 32
#define DREQ_I2C0_RX 33
#define DREQ_I2C1_TX 34
#define DREQ_I2C1_RX 35
#define DREQ_ADC 36
#define DREQ_XIP_STREAM 37
#define DREQ_XIP_SSITX 38
#define DREQ_XIP_SSIRX 39
#define DREQ_DMA_TIMER0 59
#define DREQ_DMA_TIMER1 60
#define DREQ_DMA_TIMER2 61
#define DREQ_DMA_TIMER3 62
#define DREQ_FORCE 63
#else
/**
* \brief DREQ numbers for DMA pacing on RP2040 (used as typedef \ref dreq_num_t)
* \ingroup hardware_dma
*/
typedef enum dreq_num_rp2040 {
DREQ_PIO0_TX0 = 0, ///< Select PIO0's TX FIFO 0 as DREQ
DREQ_PIO0_TX1 = 1, ///< Select PIO0's TX FIFO 1 as DREQ
DREQ_PIO0_TX2 = 2, ///< Select PIO0's TX FIFO 2 as DREQ
DREQ_PIO0_TX3 = 3, ///< Select PIO0's TX FIFO 3 as DREQ
DREQ_PIO0_RX0 = 4, ///< Select PIO0's RX FIFO 0 as DREQ
DREQ_PIO0_RX1 = 5, ///< Select PIO0's RX FIFO 1 as DREQ
DREQ_PIO0_RX2 = 6, ///< Select PIO0's RX FIFO 2 as DREQ
DREQ_PIO0_RX3 = 7, ///< Select PIO0's RX FIFO 3 as DREQ
DREQ_PIO1_TX0 = 8, ///< Select PIO1's TX FIFO 0 as DREQ
DREQ_PIO1_TX1 = 9, ///< Select PIO1's TX FIFO 1 as DREQ
DREQ_PIO1_TX2 = 10, ///< Select PIO1's TX FIFO 2 as DREQ
DREQ_PIO1_TX3 = 11, ///< Select PIO1's TX FIFO 3 as DREQ
DREQ_PIO1_RX0 = 12, ///< Select PIO1's RX FIFO 0 as DREQ
DREQ_PIO1_RX1 = 13, ///< Select PIO1's RX FIFO 1 as DREQ
DREQ_PIO1_RX2 = 14, ///< Select PIO1's RX FIFO 2 as DREQ
DREQ_PIO1_RX3 = 15, ///< Select PIO1's RX FIFO 3 as DREQ
DREQ_SPI0_TX = 16, ///< Select SPI0's TX FIFO as DREQ
DREQ_SPI0_RX = 17, ///< Select SPI0's RX FIFO as DREQ
DREQ_SPI1_TX = 18, ///< Select SPI1's TX FIFO as DREQ
DREQ_SPI1_RX = 19, ///< Select SPI1's RX FIFO as DREQ
DREQ_UART0_TX = 20, ///< Select UART0's TX FIFO as DREQ
DREQ_UART0_RX = 21, ///< Select UART0's RX FIFO as DREQ
DREQ_UART1_TX = 22, ///< Select UART1's TX FIFO as DREQ
DREQ_UART1_RX = 23, ///< Select UART1's RX FIFO as DREQ
DREQ_PWM_WRAP0 = 24, ///< Select PWM Counter 0's Wrap Value as DREQ
DREQ_PWM_WRAP1 = 25, ///< Select PWM Counter 1's Wrap Value as DREQ
DREQ_PWM_WRAP2 = 26, ///< Select PWM Counter 2's Wrap Value as DREQ
DREQ_PWM_WRAP3 = 27, ///< Select PWM Counter 3's Wrap Value as DREQ
DREQ_PWM_WRAP4 = 28, ///< Select PWM Counter 4's Wrap Value as DREQ
DREQ_PWM_WRAP5 = 29, ///< Select PWM Counter 5's Wrap Value as DREQ
DREQ_PWM_WRAP6 = 30, ///< Select PWM Counter 6's Wrap Value as DREQ
DREQ_PWM_WRAP7 = 31, ///< Select PWM Counter 7's Wrap Value as DREQ
DREQ_I2C0_TX = 32, ///< Select I2C0's TX FIFO as DREQ
DREQ_I2C0_RX = 33, ///< Select I2C0's RX FIFO as DREQ
DREQ_I2C1_TX = 34, ///< Select I2C1's TX FIFO as DREQ
DREQ_I2C1_RX = 35, ///< Select I2C1's RX FIFO as DREQ
DREQ_ADC = 36, ///< Select the ADC as DREQ
DREQ_XIP_STREAM = 37, ///< Select the XIP Streaming FIFO as DREQ
DREQ_XIP_SSITX = 38, ///< Select the XIP SSI TX FIFO as DREQ
DREQ_XIP_SSIRX = 39, ///< Select the XIP SSI RX FIFO 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_TIMER3 = 62, ///< Select DMA_TIMER3 as DREQ
DREQ_FORCE = 63, ///< Select FORCE as DREQ
DREQ_COUNT
} dreq_num_t;
#endif
#endif // _DREQ_H

View File

@@ -1,106 +0,0 @@
// THIS HEADER FILE IS AUTOMATICALLY GENERATED -- DO NOT EDIT
/**
* Copyright (c) 2024 Raspberry Pi Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _INTCTRL_H
#define _INTCTRL_H
/**
* \file rp2040/intctrl.h
*/
#ifdef __ASSEMBLER__
#define TIMER_IRQ_0 0
#define TIMER_IRQ_1 1
#define TIMER_IRQ_2 2
#define TIMER_IRQ_3 3
#define PWM_IRQ_WRAP 4
#define USBCTRL_IRQ 5
#define XIP_IRQ 6
#define PIO0_IRQ_0 7
#define PIO0_IRQ_1 8
#define PIO1_IRQ_0 9
#define PIO1_IRQ_1 10
#define DMA_IRQ_0 11
#define DMA_IRQ_1 12
#define IO_IRQ_BANK0 13
#define IO_IRQ_QSPI 14
#define SIO_IRQ_PROC0 15
#define SIO_IRQ_PROC1 16
#define CLOCKS_IRQ 17
#define SPI0_IRQ 18
#define SPI1_IRQ 19
#define UART0_IRQ 20
#define UART1_IRQ 21
#define ADC_IRQ_FIFO 22
#define I2C0_IRQ 23
#define I2C1_IRQ 24
#define RTC_IRQ 25
#else
/**
* \brief Interrupt numbers on RP2040 (used as typedef \ref irq_num_t)
* \ingroup hardware_irq
*/
typedef enum irq_num_rp2040 {
TIMER_IRQ_0 = 0, ///< Select TIMER's IRQ 0 output
TIMER_IRQ_1 = 1, ///< Select TIMER's IRQ 1 output
TIMER_IRQ_2 = 2, ///< Select TIMER's IRQ 2 output
TIMER_IRQ_3 = 3, ///< Select TIMER's IRQ 3 output
PWM_IRQ_WRAP = 4, ///< Select PWM's IRQ_WRAP output
USBCTRL_IRQ = 5, ///< Select USBCTRL's IRQ output
XIP_IRQ = 6, ///< Select XIP's IRQ output
PIO0_IRQ_0 = 7, ///< Select PIO0's IRQ 0 output
PIO0_IRQ_1 = 8, ///< Select PIO0's IRQ 1 output
PIO1_IRQ_0 = 9, ///< Select PIO1's IRQ 0 output
PIO1_IRQ_1 = 10, ///< Select PIO1's IRQ 1 output
DMA_IRQ_0 = 11, ///< Select DMA's IRQ 0 output
DMA_IRQ_1 = 12, ///< Select DMA's IRQ 1 output
IO_IRQ_BANK0 = 13, ///< Select IO_BANK0's IRQ output
IO_IRQ_QSPI = 14, ///< Select IO_QSPI's IRQ output
SIO_IRQ_PROC0 = 15, ///< Select SIO_PROC0's IRQ output
SIO_IRQ_PROC1 = 16, ///< Select SIO_PROC1's IRQ output
CLOCKS_IRQ = 17, ///< Select CLOCKS's IRQ output
SPI0_IRQ = 18, ///< Select SPI0's IRQ output
SPI1_IRQ = 19, ///< Select SPI1's IRQ output
UART0_IRQ = 20, ///< Select UART0's IRQ output
UART1_IRQ = 21, ///< Select UART1's IRQ output
ADC_IRQ_FIFO = 22, ///< Select ADC's IRQ_FIFO output
I2C0_IRQ = 23, ///< Select I2C0's IRQ output
I2C1_IRQ = 24, ///< Select I2C1's IRQ output
RTC_IRQ = 25, ///< Select RTC's IRQ output
IRQ_COUNT
} irq_num_t;
#endif
#define isr_timer_0 isr_irq0
#define isr_timer_1 isr_irq1
#define isr_timer_2 isr_irq2
#define isr_timer_3 isr_irq3
#define isr_pwm_wrap isr_irq4
#define isr_usbctrl isr_irq5
#define isr_xip isr_irq6
#define isr_pio0_0 isr_irq7
#define isr_pio0_1 isr_irq8
#define isr_pio1_0 isr_irq9
#define isr_pio1_1 isr_irq10
#define isr_dma_0 isr_irq11
#define isr_dma_1 isr_irq12
#define isr_io_bank0 isr_irq13
#define isr_io_qspi isr_irq14
#define isr_sio_proc0 isr_irq15
#define isr_sio_proc1 isr_irq16
#define isr_clocks isr_irq17
#define isr_spi0 isr_irq18
#define isr_spi1 isr_irq19
#define isr_uart0 isr_irq20
#define isr_uart1 isr_irq21
#define isr_adc_fifo isr_irq22
#define isr_i2c0 isr_irq23
#define isr_i2c1 isr_irq24
#define isr_rtc isr_irq25
#endif // _INTCTRL_H

View File

@@ -1,523 +0,0 @@
// THIS HEADER FILE IS AUTOMATICALLY GENERATED -- DO NOT EDIT
/**
* Copyright (c) 2024 Raspberry Pi Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// =============================================================================
// Register block : SPI
// Version : 1
// Bus type : apb
// =============================================================================
#ifndef _HARDWARE_REGS_SPI_H
#define _HARDWARE_REGS_SPI_H
// =============================================================================
// Register : SPI_SSPCR0
// Description : Control register 0, SSPCR0 on page 3-4
#define SPI_SSPCR0_OFFSET _u(0x00000000)
#define SPI_SSPCR0_BITS _u(0x0000ffff)
#define SPI_SSPCR0_RESET _u(0x00000000)
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR0_SCR
// Description : Serial clock rate. The value SCR is used to generate the
// transmit and receive bit rate of the PrimeCell SSP. The bit
// rate is: F SSPCLK CPSDVSR x (1+SCR) where CPSDVSR is an even
// value from 2-254, programmed through the SSPCPSR register and
// SCR is a value from 0-255.
#define SPI_SSPCR0_SCR_RESET _u(0x00)
#define SPI_SSPCR0_SCR_BITS _u(0x0000ff00)
#define SPI_SSPCR0_SCR_MSB _u(15)
#define SPI_SSPCR0_SCR_LSB _u(8)
#define SPI_SSPCR0_SCR_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR0_SPH
// Description : SSPCLKOUT phase, applicable to Motorola SPI frame format only.
// See Motorola SPI frame format on page 2-10.
#define SPI_SSPCR0_SPH_RESET _u(0x0)
#define SPI_SSPCR0_SPH_BITS _u(0x00000080)
#define SPI_SSPCR0_SPH_MSB _u(7)
#define SPI_SSPCR0_SPH_LSB _u(7)
#define SPI_SSPCR0_SPH_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR0_SPO
// Description : SSPCLKOUT polarity, applicable to Motorola SPI frame format
// only. See Motorola SPI frame format on page 2-10.
#define SPI_SSPCR0_SPO_RESET _u(0x0)
#define SPI_SSPCR0_SPO_BITS _u(0x00000040)
#define SPI_SSPCR0_SPO_MSB _u(6)
#define SPI_SSPCR0_SPO_LSB _u(6)
#define SPI_SSPCR0_SPO_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR0_FRF
// Description : Frame format: 00 Motorola SPI frame format. 01 TI synchronous
// serial frame format. 10 National Microwire frame format. 11
// Reserved, undefined operation.
#define SPI_SSPCR0_FRF_RESET _u(0x0)
#define SPI_SSPCR0_FRF_BITS _u(0x00000030)
#define SPI_SSPCR0_FRF_MSB _u(5)
#define SPI_SSPCR0_FRF_LSB _u(4)
#define SPI_SSPCR0_FRF_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR0_DSS
// Description : Data Size Select: 0000 Reserved, undefined operation. 0001
// Reserved, undefined operation. 0010 Reserved, undefined
// operation. 0011 4-bit data. 0100 5-bit data. 0101 6-bit data.
// 0110 7-bit data. 0111 8-bit data. 1000 9-bit data. 1001 10-bit
// data. 1010 11-bit data. 1011 12-bit data. 1100 13-bit data.
// 1101 14-bit data. 1110 15-bit data. 1111 16-bit data.
#define SPI_SSPCR0_DSS_RESET _u(0x0)
#define SPI_SSPCR0_DSS_BITS _u(0x0000000f)
#define SPI_SSPCR0_DSS_MSB _u(3)
#define SPI_SSPCR0_DSS_LSB _u(0)
#define SPI_SSPCR0_DSS_ACCESS "RW"
// =============================================================================
// Register : SPI_SSPCR1
// Description : Control register 1, SSPCR1 on page 3-5
#define SPI_SSPCR1_OFFSET _u(0x00000004)
#define SPI_SSPCR1_BITS _u(0x0000000f)
#define SPI_SSPCR1_RESET _u(0x00000000)
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR1_SOD
// Description : Slave-mode output disable. This bit is relevant only in the
// slave mode, MS=1. In multiple-slave systems, it is possible for
// an PrimeCell SSP master to broadcast a message to all slaves in
// the system while ensuring that only one slave drives data onto
// its serial output line. In such systems the RXD lines from
// multiple slaves could be tied together. To operate in such
// systems, the SOD bit can be set if the PrimeCell SSP slave is
// not supposed to drive the SSPTXD line: 0 SSP can drive the
// SSPTXD output in slave mode. 1 SSP must not drive the SSPTXD
// output in slave mode.
#define SPI_SSPCR1_SOD_RESET _u(0x0)
#define SPI_SSPCR1_SOD_BITS _u(0x00000008)
#define SPI_SSPCR1_SOD_MSB _u(3)
#define SPI_SSPCR1_SOD_LSB _u(3)
#define SPI_SSPCR1_SOD_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR1_MS
// Description : Master or slave mode select. This bit can be modified only when
// the PrimeCell SSP is disabled, SSE=0: 0 Device configured as
// master, default. 1 Device configured as slave.
#define SPI_SSPCR1_MS_RESET _u(0x0)
#define SPI_SSPCR1_MS_BITS _u(0x00000004)
#define SPI_SSPCR1_MS_MSB _u(2)
#define SPI_SSPCR1_MS_LSB _u(2)
#define SPI_SSPCR1_MS_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR1_SSE
// Description : Synchronous serial port enable: 0 SSP operation disabled. 1 SSP
// operation enabled.
#define SPI_SSPCR1_SSE_RESET _u(0x0)
#define SPI_SSPCR1_SSE_BITS _u(0x00000002)
#define SPI_SSPCR1_SSE_MSB _u(1)
#define SPI_SSPCR1_SSE_LSB _u(1)
#define SPI_SSPCR1_SSE_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPCR1_LBM
// Description : Loop back mode: 0 Normal serial port operation enabled. 1
// Output of transmit serial shifter is connected to input of
// receive serial shifter internally.
#define SPI_SSPCR1_LBM_RESET _u(0x0)
#define SPI_SSPCR1_LBM_BITS _u(0x00000001)
#define SPI_SSPCR1_LBM_MSB _u(0)
#define SPI_SSPCR1_LBM_LSB _u(0)
#define SPI_SSPCR1_LBM_ACCESS "RW"
// =============================================================================
// Register : SPI_SSPDR
// Description : Data register, SSPDR on page 3-6
#define SPI_SSPDR_OFFSET _u(0x00000008)
#define SPI_SSPDR_BITS _u(0x0000ffff)
#define SPI_SSPDR_RESET "-"
// -----------------------------------------------------------------------------
// Field : SPI_SSPDR_DATA
// Description : Transmit/Receive FIFO: Read Receive FIFO. Write Transmit FIFO.
// You must right-justify data when the PrimeCell SSP is
// programmed for a data size that is less than 16 bits. Unused
// bits at the top are ignored by transmit logic. The receive
// logic automatically right-justifies.
#define SPI_SSPDR_DATA_RESET "-"
#define SPI_SSPDR_DATA_BITS _u(0x0000ffff)
#define SPI_SSPDR_DATA_MSB _u(15)
#define SPI_SSPDR_DATA_LSB _u(0)
#define SPI_SSPDR_DATA_ACCESS "RWF"
// =============================================================================
// Register : SPI_SSPSR
// Description : Status register, SSPSR on page 3-7
#define SPI_SSPSR_OFFSET _u(0x0000000c)
#define SPI_SSPSR_BITS _u(0x0000001f)
#define SPI_SSPSR_RESET _u(0x00000003)
// -----------------------------------------------------------------------------
// Field : SPI_SSPSR_BSY
// Description : PrimeCell SSP busy flag, RO: 0 SSP is idle. 1 SSP is currently
// transmitting and/or receiving a frame or the transmit FIFO is
// not empty.
#define SPI_SSPSR_BSY_RESET _u(0x0)
#define SPI_SSPSR_BSY_BITS _u(0x00000010)
#define SPI_SSPSR_BSY_MSB _u(4)
#define SPI_SSPSR_BSY_LSB _u(4)
#define SPI_SSPSR_BSY_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPSR_RFF
// Description : Receive FIFO full, RO: 0 Receive FIFO is not full. 1 Receive
// FIFO is full.
#define SPI_SSPSR_RFF_RESET _u(0x0)
#define SPI_SSPSR_RFF_BITS _u(0x00000008)
#define SPI_SSPSR_RFF_MSB _u(3)
#define SPI_SSPSR_RFF_LSB _u(3)
#define SPI_SSPSR_RFF_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPSR_RNE
// Description : Receive FIFO not empty, RO: 0 Receive FIFO is empty. 1 Receive
// FIFO is not empty.
#define SPI_SSPSR_RNE_RESET _u(0x0)
#define SPI_SSPSR_RNE_BITS _u(0x00000004)
#define SPI_SSPSR_RNE_MSB _u(2)
#define SPI_SSPSR_RNE_LSB _u(2)
#define SPI_SSPSR_RNE_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPSR_TNF
// Description : Transmit FIFO not full, RO: 0 Transmit FIFO is full. 1 Transmit
// FIFO is not full.
#define SPI_SSPSR_TNF_RESET _u(0x1)
#define SPI_SSPSR_TNF_BITS _u(0x00000002)
#define SPI_SSPSR_TNF_MSB _u(1)
#define SPI_SSPSR_TNF_LSB _u(1)
#define SPI_SSPSR_TNF_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPSR_TFE
// Description : Transmit FIFO empty, RO: 0 Transmit FIFO is not empty. 1
// Transmit FIFO is empty.
#define SPI_SSPSR_TFE_RESET _u(0x1)
#define SPI_SSPSR_TFE_BITS _u(0x00000001)
#define SPI_SSPSR_TFE_MSB _u(0)
#define SPI_SSPSR_TFE_LSB _u(0)
#define SPI_SSPSR_TFE_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPCPSR
// Description : Clock prescale register, SSPCPSR on page 3-8
#define SPI_SSPCPSR_OFFSET _u(0x00000010)
#define SPI_SSPCPSR_BITS _u(0x000000ff)
#define SPI_SSPCPSR_RESET _u(0x00000000)
// -----------------------------------------------------------------------------
// Field : SPI_SSPCPSR_CPSDVSR
// Description : Clock prescale divisor. Must be an even number from 2-254,
// depending on the frequency of SSPCLK. The least significant bit
// always returns zero on reads.
#define SPI_SSPCPSR_CPSDVSR_RESET _u(0x00)
#define SPI_SSPCPSR_CPSDVSR_BITS _u(0x000000ff)
#define SPI_SSPCPSR_CPSDVSR_MSB _u(7)
#define SPI_SSPCPSR_CPSDVSR_LSB _u(0)
#define SPI_SSPCPSR_CPSDVSR_ACCESS "RW"
// =============================================================================
// Register : SPI_SSPIMSC
// Description : Interrupt mask set or clear register, SSPIMSC on page 3-9
#define SPI_SSPIMSC_OFFSET _u(0x00000014)
#define SPI_SSPIMSC_BITS _u(0x0000000f)
#define SPI_SSPIMSC_RESET _u(0x00000000)
// -----------------------------------------------------------------------------
// Field : SPI_SSPIMSC_TXIM
// Description : Transmit FIFO interrupt mask: 0 Transmit FIFO half empty or
// less condition interrupt is masked. 1 Transmit FIFO half empty
// or less condition interrupt is not masked.
#define SPI_SSPIMSC_TXIM_RESET _u(0x0)
#define SPI_SSPIMSC_TXIM_BITS _u(0x00000008)
#define SPI_SSPIMSC_TXIM_MSB _u(3)
#define SPI_SSPIMSC_TXIM_LSB _u(3)
#define SPI_SSPIMSC_TXIM_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPIMSC_RXIM
// Description : Receive FIFO interrupt mask: 0 Receive FIFO half full or less
// condition interrupt is masked. 1 Receive FIFO half full or less
// condition interrupt is not masked.
#define SPI_SSPIMSC_RXIM_RESET _u(0x0)
#define SPI_SSPIMSC_RXIM_BITS _u(0x00000004)
#define SPI_SSPIMSC_RXIM_MSB _u(2)
#define SPI_SSPIMSC_RXIM_LSB _u(2)
#define SPI_SSPIMSC_RXIM_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPIMSC_RTIM
// Description : Receive timeout interrupt mask: 0 Receive FIFO not empty and no
// read prior to timeout period interrupt is masked. 1 Receive
// FIFO not empty and no read prior to timeout period interrupt is
// not masked.
#define SPI_SSPIMSC_RTIM_RESET _u(0x0)
#define SPI_SSPIMSC_RTIM_BITS _u(0x00000002)
#define SPI_SSPIMSC_RTIM_MSB _u(1)
#define SPI_SSPIMSC_RTIM_LSB _u(1)
#define SPI_SSPIMSC_RTIM_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPIMSC_RORIM
// Description : Receive overrun interrupt mask: 0 Receive FIFO written to while
// full condition interrupt is masked. 1 Receive FIFO written to
// while full condition interrupt is not masked.
#define SPI_SSPIMSC_RORIM_RESET _u(0x0)
#define SPI_SSPIMSC_RORIM_BITS _u(0x00000001)
#define SPI_SSPIMSC_RORIM_MSB _u(0)
#define SPI_SSPIMSC_RORIM_LSB _u(0)
#define SPI_SSPIMSC_RORIM_ACCESS "RW"
// =============================================================================
// Register : SPI_SSPRIS
// Description : Raw interrupt status register, SSPRIS on page 3-10
#define SPI_SSPRIS_OFFSET _u(0x00000018)
#define SPI_SSPRIS_BITS _u(0x0000000f)
#define SPI_SSPRIS_RESET _u(0x00000008)
// -----------------------------------------------------------------------------
// Field : SPI_SSPRIS_TXRIS
// Description : Gives the raw interrupt state, prior to masking, of the
// SSPTXINTR interrupt
#define SPI_SSPRIS_TXRIS_RESET _u(0x1)
#define SPI_SSPRIS_TXRIS_BITS _u(0x00000008)
#define SPI_SSPRIS_TXRIS_MSB _u(3)
#define SPI_SSPRIS_TXRIS_LSB _u(3)
#define SPI_SSPRIS_TXRIS_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPRIS_RXRIS
// Description : Gives the raw interrupt state, prior to masking, of the
// SSPRXINTR interrupt
#define SPI_SSPRIS_RXRIS_RESET _u(0x0)
#define SPI_SSPRIS_RXRIS_BITS _u(0x00000004)
#define SPI_SSPRIS_RXRIS_MSB _u(2)
#define SPI_SSPRIS_RXRIS_LSB _u(2)
#define SPI_SSPRIS_RXRIS_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPRIS_RTRIS
// Description : Gives the raw interrupt state, prior to masking, of the
// SSPRTINTR interrupt
#define SPI_SSPRIS_RTRIS_RESET _u(0x0)
#define SPI_SSPRIS_RTRIS_BITS _u(0x00000002)
#define SPI_SSPRIS_RTRIS_MSB _u(1)
#define SPI_SSPRIS_RTRIS_LSB _u(1)
#define SPI_SSPRIS_RTRIS_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPRIS_RORRIS
// Description : Gives the raw interrupt state, prior to masking, of the
// SSPRORINTR interrupt
#define SPI_SSPRIS_RORRIS_RESET _u(0x0)
#define SPI_SSPRIS_RORRIS_BITS _u(0x00000001)
#define SPI_SSPRIS_RORRIS_MSB _u(0)
#define SPI_SSPRIS_RORRIS_LSB _u(0)
#define SPI_SSPRIS_RORRIS_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPMIS
// Description : Masked interrupt status register, SSPMIS on page 3-11
#define SPI_SSPMIS_OFFSET _u(0x0000001c)
#define SPI_SSPMIS_BITS _u(0x0000000f)
#define SPI_SSPMIS_RESET _u(0x00000000)
// -----------------------------------------------------------------------------
// Field : SPI_SSPMIS_TXMIS
// Description : Gives the transmit FIFO masked interrupt state, after masking,
// of the SSPTXINTR interrupt
#define SPI_SSPMIS_TXMIS_RESET _u(0x0)
#define SPI_SSPMIS_TXMIS_BITS _u(0x00000008)
#define SPI_SSPMIS_TXMIS_MSB _u(3)
#define SPI_SSPMIS_TXMIS_LSB _u(3)
#define SPI_SSPMIS_TXMIS_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPMIS_RXMIS
// Description : Gives the receive FIFO masked interrupt state, after masking,
// of the SSPRXINTR interrupt
#define SPI_SSPMIS_RXMIS_RESET _u(0x0)
#define SPI_SSPMIS_RXMIS_BITS _u(0x00000004)
#define SPI_SSPMIS_RXMIS_MSB _u(2)
#define SPI_SSPMIS_RXMIS_LSB _u(2)
#define SPI_SSPMIS_RXMIS_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPMIS_RTMIS
// Description : Gives the receive timeout masked interrupt state, after
// masking, of the SSPRTINTR interrupt
#define SPI_SSPMIS_RTMIS_RESET _u(0x0)
#define SPI_SSPMIS_RTMIS_BITS _u(0x00000002)
#define SPI_SSPMIS_RTMIS_MSB _u(1)
#define SPI_SSPMIS_RTMIS_LSB _u(1)
#define SPI_SSPMIS_RTMIS_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPMIS_RORMIS
// Description : Gives the receive over run masked interrupt status, after
// masking, of the SSPRORINTR interrupt
#define SPI_SSPMIS_RORMIS_RESET _u(0x0)
#define SPI_SSPMIS_RORMIS_BITS _u(0x00000001)
#define SPI_SSPMIS_RORMIS_MSB _u(0)
#define SPI_SSPMIS_RORMIS_LSB _u(0)
#define SPI_SSPMIS_RORMIS_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPICR
// Description : Interrupt clear register, SSPICR on page 3-11
#define SPI_SSPICR_OFFSET _u(0x00000020)
#define SPI_SSPICR_BITS _u(0x00000003)
#define SPI_SSPICR_RESET _u(0x00000000)
// -----------------------------------------------------------------------------
// Field : SPI_SSPICR_RTIC
// Description : Clears the SSPRTINTR interrupt
#define SPI_SSPICR_RTIC_RESET _u(0x0)
#define SPI_SSPICR_RTIC_BITS _u(0x00000002)
#define SPI_SSPICR_RTIC_MSB _u(1)
#define SPI_SSPICR_RTIC_LSB _u(1)
#define SPI_SSPICR_RTIC_ACCESS "WC"
// -----------------------------------------------------------------------------
// Field : SPI_SSPICR_RORIC
// Description : Clears the SSPRORINTR interrupt
#define SPI_SSPICR_RORIC_RESET _u(0x0)
#define SPI_SSPICR_RORIC_BITS _u(0x00000001)
#define SPI_SSPICR_RORIC_MSB _u(0)
#define SPI_SSPICR_RORIC_LSB _u(0)
#define SPI_SSPICR_RORIC_ACCESS "WC"
// =============================================================================
// Register : SPI_SSPDMACR
// Description : DMA control register, SSPDMACR on page 3-12
#define SPI_SSPDMACR_OFFSET _u(0x00000024)
#define SPI_SSPDMACR_BITS _u(0x00000003)
#define SPI_SSPDMACR_RESET _u(0x00000000)
// -----------------------------------------------------------------------------
// Field : SPI_SSPDMACR_TXDMAE
// Description : Transmit DMA Enable. If this bit is set to 1, DMA for the
// transmit FIFO is enabled.
#define SPI_SSPDMACR_TXDMAE_RESET _u(0x0)
#define SPI_SSPDMACR_TXDMAE_BITS _u(0x00000002)
#define SPI_SSPDMACR_TXDMAE_MSB _u(1)
#define SPI_SSPDMACR_TXDMAE_LSB _u(1)
#define SPI_SSPDMACR_TXDMAE_ACCESS "RW"
// -----------------------------------------------------------------------------
// Field : SPI_SSPDMACR_RXDMAE
// Description : Receive DMA Enable. If this bit is set to 1, DMA for the
// receive FIFO is enabled.
#define SPI_SSPDMACR_RXDMAE_RESET _u(0x0)
#define SPI_SSPDMACR_RXDMAE_BITS _u(0x00000001)
#define SPI_SSPDMACR_RXDMAE_MSB _u(0)
#define SPI_SSPDMACR_RXDMAE_LSB _u(0)
#define SPI_SSPDMACR_RXDMAE_ACCESS "RW"
// =============================================================================
// Register : SPI_SSPPERIPHID0
// Description : Peripheral identification registers, SSPPeriphID0-3 on page
// 3-13
#define SPI_SSPPERIPHID0_OFFSET _u(0x00000fe0)
#define SPI_SSPPERIPHID0_BITS _u(0x000000ff)
#define SPI_SSPPERIPHID0_RESET _u(0x00000022)
// -----------------------------------------------------------------------------
// Field : SPI_SSPPERIPHID0_PARTNUMBER0
// Description : These bits read back as 0x22
#define SPI_SSPPERIPHID0_PARTNUMBER0_RESET _u(0x22)
#define SPI_SSPPERIPHID0_PARTNUMBER0_BITS _u(0x000000ff)
#define SPI_SSPPERIPHID0_PARTNUMBER0_MSB _u(7)
#define SPI_SSPPERIPHID0_PARTNUMBER0_LSB _u(0)
#define SPI_SSPPERIPHID0_PARTNUMBER0_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPPERIPHID1
// Description : Peripheral identification registers, SSPPeriphID0-3 on page
// 3-13
#define SPI_SSPPERIPHID1_OFFSET _u(0x00000fe4)
#define SPI_SSPPERIPHID1_BITS _u(0x000000ff)
#define SPI_SSPPERIPHID1_RESET _u(0x00000010)
// -----------------------------------------------------------------------------
// Field : SPI_SSPPERIPHID1_DESIGNER0
// Description : These bits read back as 0x1
#define SPI_SSPPERIPHID1_DESIGNER0_RESET _u(0x1)
#define SPI_SSPPERIPHID1_DESIGNER0_BITS _u(0x000000f0)
#define SPI_SSPPERIPHID1_DESIGNER0_MSB _u(7)
#define SPI_SSPPERIPHID1_DESIGNER0_LSB _u(4)
#define SPI_SSPPERIPHID1_DESIGNER0_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPPERIPHID1_PARTNUMBER1
// Description : These bits read back as 0x0
#define SPI_SSPPERIPHID1_PARTNUMBER1_RESET _u(0x0)
#define SPI_SSPPERIPHID1_PARTNUMBER1_BITS _u(0x0000000f)
#define SPI_SSPPERIPHID1_PARTNUMBER1_MSB _u(3)
#define SPI_SSPPERIPHID1_PARTNUMBER1_LSB _u(0)
#define SPI_SSPPERIPHID1_PARTNUMBER1_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPPERIPHID2
// Description : Peripheral identification registers, SSPPeriphID0-3 on page
// 3-13
#define SPI_SSPPERIPHID2_OFFSET _u(0x00000fe8)
#define SPI_SSPPERIPHID2_BITS _u(0x000000ff)
#define SPI_SSPPERIPHID2_RESET _u(0x00000034)
// -----------------------------------------------------------------------------
// Field : SPI_SSPPERIPHID2_REVISION
// Description : These bits return the peripheral revision
#define SPI_SSPPERIPHID2_REVISION_RESET _u(0x3)
#define SPI_SSPPERIPHID2_REVISION_BITS _u(0x000000f0)
#define SPI_SSPPERIPHID2_REVISION_MSB _u(7)
#define SPI_SSPPERIPHID2_REVISION_LSB _u(4)
#define SPI_SSPPERIPHID2_REVISION_ACCESS "RO"
// -----------------------------------------------------------------------------
// Field : SPI_SSPPERIPHID2_DESIGNER1
// Description : These bits read back as 0x4
#define SPI_SSPPERIPHID2_DESIGNER1_RESET _u(0x4)
#define SPI_SSPPERIPHID2_DESIGNER1_BITS _u(0x0000000f)
#define SPI_SSPPERIPHID2_DESIGNER1_MSB _u(3)
#define SPI_SSPPERIPHID2_DESIGNER1_LSB _u(0)
#define SPI_SSPPERIPHID2_DESIGNER1_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPPERIPHID3
// Description : Peripheral identification registers, SSPPeriphID0-3 on page
// 3-13
#define SPI_SSPPERIPHID3_OFFSET _u(0x00000fec)
#define SPI_SSPPERIPHID3_BITS _u(0x000000ff)
#define SPI_SSPPERIPHID3_RESET _u(0x00000000)
// -----------------------------------------------------------------------------
// Field : SPI_SSPPERIPHID3_CONFIGURATION
// Description : These bits read back as 0x00
#define SPI_SSPPERIPHID3_CONFIGURATION_RESET _u(0x00)
#define SPI_SSPPERIPHID3_CONFIGURATION_BITS _u(0x000000ff)
#define SPI_SSPPERIPHID3_CONFIGURATION_MSB _u(7)
#define SPI_SSPPERIPHID3_CONFIGURATION_LSB _u(0)
#define SPI_SSPPERIPHID3_CONFIGURATION_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPPCELLID0
// Description : PrimeCell identification registers, SSPPCellID0-3 on page 3-16
#define SPI_SSPPCELLID0_OFFSET _u(0x00000ff0)
#define SPI_SSPPCELLID0_BITS _u(0x000000ff)
#define SPI_SSPPCELLID0_RESET _u(0x0000000d)
// -----------------------------------------------------------------------------
// Field : SPI_SSPPCELLID0_SSPPCELLID0
// Description : These bits read back as 0x0D
#define SPI_SSPPCELLID0_SSPPCELLID0_RESET _u(0x0d)
#define SPI_SSPPCELLID0_SSPPCELLID0_BITS _u(0x000000ff)
#define SPI_SSPPCELLID0_SSPPCELLID0_MSB _u(7)
#define SPI_SSPPCELLID0_SSPPCELLID0_LSB _u(0)
#define SPI_SSPPCELLID0_SSPPCELLID0_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPPCELLID1
// Description : PrimeCell identification registers, SSPPCellID0-3 on page 3-16
#define SPI_SSPPCELLID1_OFFSET _u(0x00000ff4)
#define SPI_SSPPCELLID1_BITS _u(0x000000ff)
#define SPI_SSPPCELLID1_RESET _u(0x000000f0)
// -----------------------------------------------------------------------------
// Field : SPI_SSPPCELLID1_SSPPCELLID1
// Description : These bits read back as 0xF0
#define SPI_SSPPCELLID1_SSPPCELLID1_RESET _u(0xf0)
#define SPI_SSPPCELLID1_SSPPCELLID1_BITS _u(0x000000ff)
#define SPI_SSPPCELLID1_SSPPCELLID1_MSB _u(7)
#define SPI_SSPPCELLID1_SSPPCELLID1_LSB _u(0)
#define SPI_SSPPCELLID1_SSPPCELLID1_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPPCELLID2
// Description : PrimeCell identification registers, SSPPCellID0-3 on page 3-16
#define SPI_SSPPCELLID2_OFFSET _u(0x00000ff8)
#define SPI_SSPPCELLID2_BITS _u(0x000000ff)
#define SPI_SSPPCELLID2_RESET _u(0x00000005)
// -----------------------------------------------------------------------------
// Field : SPI_SSPPCELLID2_SSPPCELLID2
// Description : These bits read back as 0x05
#define SPI_SSPPCELLID2_SSPPCELLID2_RESET _u(0x05)
#define SPI_SSPPCELLID2_SSPPCELLID2_BITS _u(0x000000ff)
#define SPI_SSPPCELLID2_SSPPCELLID2_MSB _u(7)
#define SPI_SSPPCELLID2_SSPPCELLID2_LSB _u(0)
#define SPI_SSPPCELLID2_SSPPCELLID2_ACCESS "RO"
// =============================================================================
// Register : SPI_SSPPCELLID3
// Description : PrimeCell identification registers, SSPPCellID0-3 on page 3-16
#define SPI_SSPPCELLID3_OFFSET _u(0x00000ffc)
#define SPI_SSPPCELLID3_BITS _u(0x000000ff)
#define SPI_SSPPCELLID3_RESET _u(0x000000b1)
// -----------------------------------------------------------------------------
// Field : SPI_SSPPCELLID3_SSPPCELLID3
// Description : These bits read back as 0xB1
#define SPI_SSPPCELLID3_SSPPCELLID3_RESET _u(0xb1)
#define SPI_SSPPCELLID3_SSPPCELLID3_BITS _u(0x000000ff)
#define SPI_SSPPCELLID3_SSPPCELLID3_MSB _u(7)
#define SPI_SSPPCELLID3_SSPPCELLID3_LSB _u(0)
#define SPI_SSPPCELLID3_SSPPCELLID3_ACCESS "RO"
// =============================================================================
#endif // _HARDWARE_REGS_SPI_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,96 +0,0 @@
// THIS HEADER FILE IS AUTOMATICALLY GENERATED -- DO NOT EDIT
/**
* Copyright (c) 2024 Raspberry Pi Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _HARDWARE_STRUCTS_ADC_H
#define _HARDWARE_STRUCTS_ADC_H
/**
* \file rp2040/adc.h
*/
#include "hardware/address_mapped.h"
#include "hardware/regs/adc.h"
// Reference to datasheet: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#tab-registerlist_adc
//
// The _REG_ macro is intended to help make the register navigable in your IDE (for example, using the "Go to Definition" feature)
// _REG_(x) will link to the corresponding register in hardware/regs/adc.h.
//
// Bit-field descriptions are of the form:
// BITMASK [BITRANGE] FIELDNAME (RESETVALUE) DESCRIPTION
typedef struct {
_REG_(ADC_CS_OFFSET) // ADC_CS
// ADC Control and Status
// 0x001f0000 [20:16] RROBIN (0x00) Round-robin sampling
// 0x00007000 [14:12] AINSEL (0x0) Select analog mux input
// 0x00000400 [10] ERR_STICKY (0) Some past ADC conversion encountered an error
// 0x00000200 [9] ERR (0) The most recent ADC conversion encountered an error;...
// 0x00000100 [8] READY (0) 1 if the ADC is ready to start a new conversion
// 0x00000008 [3] START_MANY (0) Continuously perform conversions whilst this bit is 1
// 0x00000004 [2] START_ONCE (0) Start a single conversion
// 0x00000002 [1] TS_EN (0) Power on temperature sensor
// 0x00000001 [0] EN (0) Power on ADC and enable its clock
io_rw_32 cs;
_REG_(ADC_RESULT_OFFSET) // ADC_RESULT
// Result of most recent ADC conversion
// 0x00000fff [11:0] RESULT (0x000)
io_ro_32 result;
_REG_(ADC_FCS_OFFSET) // ADC_FCS
// FIFO control and status
// 0x0f000000 [27:24] THRESH (0x0) DREQ/IRQ asserted when level >= threshold
// 0x000f0000 [19:16] LEVEL (0x0) The number of conversion results currently waiting in the FIFO
// 0x00000800 [11] OVER (0) 1 if the FIFO has been overflowed
// 0x00000400 [10] UNDER (0) 1 if the FIFO has been underflowed
// 0x00000200 [9] FULL (0)
// 0x00000100 [8] EMPTY (0)
// 0x00000008 [3] DREQ_EN (0) If 1: assert DMA requests when FIFO contains data
// 0x00000004 [2] ERR (0) If 1: conversion error bit appears in the FIFO alongside...
// 0x00000002 [1] SHIFT (0) If 1: FIFO results are right-shifted to be one byte in size
// 0x00000001 [0] EN (0) If 1: write result to the FIFO after each conversion
io_rw_32 fcs;
_REG_(ADC_FIFO_OFFSET) // ADC_FIFO
// Conversion result FIFO
// 0x00008000 [15] ERR (-) 1 if this particular sample experienced a conversion error
// 0x00000fff [11:0] VAL (-)
io_ro_32 fifo;
_REG_(ADC_DIV_OFFSET) // ADC_DIV
// Clock divider
// 0x00ffff00 [23:8] INT (0x0000) Integer part of clock divisor
// 0x000000ff [7:0] FRAC (0x00) Fractional part of clock divisor
io_rw_32 div;
_REG_(ADC_INTR_OFFSET) // ADC_INTR
// Raw Interrupts
// 0x00000001 [0] FIFO (0) Triggered when the sample FIFO reaches a certain level
io_ro_32 intr;
_REG_(ADC_INTE_OFFSET) // ADC_INTE
// Interrupt Enable
// 0x00000001 [0] FIFO (0) Triggered when the sample FIFO reaches a certain level
io_rw_32 inte;
_REG_(ADC_INTF_OFFSET) // ADC_INTF
// Interrupt Force
// 0x00000001 [0] FIFO (0) Triggered when the sample FIFO reaches a certain level
io_rw_32 intf;
_REG_(ADC_INTS_OFFSET) // ADC_INTS
// Interrupt status after masking & forcing
// 0x00000001 [0] FIFO (0) Triggered when the sample FIFO reaches a certain level
io_ro_32 ints;
} adc_hw_t;
#define adc_hw ((adc_hw_t *)ADC_BASE)
static_assert(sizeof (adc_hw_t) == 0x0024, "");
#endif // _HARDWARE_STRUCTS_ADC_H

Some files were not shown because too many files have changed in this diff Show More