Compare commits
2 Commits
work-close
...
work-heate
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1feb47dbd | ||
|
|
36b5595290 |
2
.github/workflows/build-test.yaml
vendored
2
.github/workflows/build-test.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
run: ./scripts/ci-build.sh 2>&1
|
run: ./scripts/ci-build.sh 2>&1
|
||||||
|
|
||||||
- name: Upload micro-controller data dictionaries
|
- name: Upload micro-controller data dictionaries
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: data-dict
|
name: data-dict
|
||||||
path: ci_build/dict
|
path: ci_build/dict
|
||||||
|
|||||||
20
.github/workflows/stale-issue-bot.yaml
vendored
20
.github/workflows/stale-issue-bot.yaml
vendored
@@ -11,14 +11,16 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v8
|
- uses: actions/stale@v8
|
||||||
with:
|
with:
|
||||||
stale-pr-message: |
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
stale-issue-message: |
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
It looks like there hasn't been any recent updates on this
|
It looks like there hasn't been any recent updates on this
|
||||||
github ticket. We prefer to only list tickets as "open" if
|
Klipper github issue. If you created this issue and no
|
||||||
they are actively being worked on. Feel free to provide an
|
longer consider it open, then please login to github and
|
||||||
update on this ticket. Otherwise the ticket will be
|
close the issue. Otherwise, if there is no further activity
|
||||||
automatically closed in a few days.
|
on this thread then it will be automatically closed in a few
|
||||||
|
days.
|
||||||
|
|
||||||
Best regards,
|
Best regards,
|
||||||
|
|
||||||
@@ -27,10 +29,10 @@ jobs:
|
|||||||
PS: I'm just an automated script, not a human being.
|
PS: I'm just an automated script, not a human being.
|
||||||
|
|
||||||
exempt-issue-labels: 'enhancement,bug'
|
exempt-issue-labels: 'enhancement,bug'
|
||||||
days-before-stale: 60
|
days-before-stale: 35
|
||||||
days-before-close: 7
|
days-before-close: 7
|
||||||
days-before-issue-stale: -1
|
days-before-pr-stale: -1
|
||||||
days-before-issue-close: -1
|
days-before-pr-close: -1
|
||||||
# Close tickets marked with "not on github" label
|
# Close tickets marked with "not on github" label
|
||||||
close_not_on_github:
|
close_not_on_github:
|
||||||
if: github.repository == 'Klipper3d/klipper'
|
if: github.repository == 'Klipper3d/klipper'
|
||||||
@@ -328,7 +330,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
# Lock closed issues after 6 months of inactivity and PRs after 1 year.
|
# Lock closed issues after 6 months of inactivity and PRs after 1 year.
|
||||||
lock:
|
lock:
|
||||||
name: Lock Closed Tickets
|
name: Lock Closed Issues
|
||||||
if: github.repository == 'Klipper3d/klipper'
|
if: github.repository == 'Klipper3d/klipper'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -4,14 +4,15 @@ Welcome to the Klipper project!
|
|||||||
|
|
||||||
https://www.klipper3d.org/
|
https://www.klipper3d.org/
|
||||||
|
|
||||||
The Klipper firmware controls 3d-Printers. It combines the power of a
|
Klipper is a 3d-Printer firmware. It combines the power of a general
|
||||||
general purpose computer with one or more micro-controllers. See the
|
purpose computer with one or more micro-controllers. See the
|
||||||
[features document](https://www.klipper3d.org/Features.html) for more
|
[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
|
Klipper is Free Software. See the [license](COPYING) or read the
|
||||||
the [documentation](https://www.klipper3d.org/Overview.html). We
|
[documentation](https://www.klipper3d.org/Overview.html). We depend on
|
||||||
depend on the generous support from our
|
the generous support from our
|
||||||
[sponsors](https://www.klipper3d.org/Sponsors.html).
|
[sponsors](https://www.klipper3d.org/Sponsors.html).
|
||||||
|
|||||||
@@ -95,4 +95,4 @@ max_z_accel: 100
|
|||||||
aliases:
|
aliases:
|
||||||
EXP1_1=PC6,EXP1_3=PB10,EXP1_5=PB14,EXP1_7=PB12,EXP1_9=<GND>,
|
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>,
|
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
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -77,14 +77,5 @@ heater_temp: 50.0
|
|||||||
pin: toolboard:PA9
|
pin: toolboard:PA9
|
||||||
z_offset: 20
|
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]
|
[mcu toolboard]
|
||||||
canbus_uuid: 4b194673554e
|
canbus_uuid: 4b194673554e
|
||||||
|
|||||||
@@ -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
|
discouraged. Use the "objects/subscribe" endpoint to obtain updates on
|
||||||
Klipper's state.
|
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
|
### motion_report/dump_stepper
|
||||||
|
|
||||||
This endpoint is used to subscribe to Klipper's internal 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",
|
`{"id": 123, "method":"hx71x/dump_hx71x",
|
||||||
"params": {"sensor": "load_cell", "response_template": {}}}`
|
"params": {"sensor": "load_cell", "response_template": {}}}`
|
||||||
and might return:
|
and might return:
|
||||||
`{"id": 123,"result":{"header":["time","counts","value"]}}`
|
`{"id": 123,"result":{"header":["time","counts"]}}`
|
||||||
and might later produce asynchronous messages such as:
|
and might later produce asynchronous messages such as:
|
||||||
`{"params":{"data":[[3292.432935, 562534, 0.067059278],
|
`{"params":{"data":[[3292.432935, 562534], [3292.4394937, 5625322]]}}`
|
||||||
[3292.4394937, 5625322, 0.670590639]]}}`
|
|
||||||
|
|
||||||
### ads1220/dump_ads1220
|
### ads1220/dump_ads1220
|
||||||
|
|
||||||
@@ -391,10 +406,9 @@ A request may look like:
|
|||||||
`{"id": 123, "method":"ads1220/dump_ads1220",
|
`{"id": 123, "method":"ads1220/dump_ads1220",
|
||||||
"params": {"sensor": "load_cell", "response_template": {}}}`
|
"params": {"sensor": "load_cell", "response_template": {}}}`
|
||||||
and might return:
|
and might return:
|
||||||
`{"id": 123,"result":{"header":["time","counts","value"]}}`
|
`{"id": 123,"result":{"header":["time","counts"]}}`
|
||||||
and might later produce asynchronous messages such as:
|
and might later produce asynchronous messages such as:
|
||||||
`{"params":{"data":[[3292.432935, 562534, 0.067059278],
|
`{"params":{"data":[[3292.432935, 562534], [3292.4394937, 5625322]]}}`
|
||||||
[3292.4394937, 5625322, 0.670590639]]}}`
|
|
||||||
|
|
||||||
### pause_resume/cancel
|
### pause_resume/cancel
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
> **Tip:** Make sure the [probe X and Y offsets](Config_Reference.md#probe) are
|
||||||
> correctly set as they greatly influence calibration.
|
> correctly set as they greatly influence calibration.
|
||||||
|
|
||||||
### Basic Usage: X-Axis Calibration
|
1. After setting up the [axis_twist_compensation] module,
|
||||||
1. After setting up the ```[axis_twist_compensation]``` module, run:
|
perform `AXIS_TWIST_COMPENSATION_CALIBRATE`
|
||||||
```
|
* The calibration wizard will prompt you to measure the probe Z offset at a few
|
||||||
AXIS_TWIST_COMPENSATION_CALIBRATE
|
points along the bed
|
||||||
```
|
* The calibration defaults to 3 points but you can use the option
|
||||||
This command will calibrate the X-axis by default.
|
`SAMPLE_COUNT=` to use a different number.
|
||||||
- The calibration wizard will prompt you to measure the probe Z offset at
|
2. [Adjust your Z offset](Probe_Calibrate.md#calibrating-probe-z-offset)
|
||||||
several points along the bed.
|
3. Perform automatic/probe-based bed tramming operations, such as
|
||||||
- By default, the calibration uses 3 points, but you can specify a different
|
[Screws Tilt Adjust](G-Codes.md#screws_tilt_adjust),
|
||||||
number with the option:
|
[Z Tilt Adjust](G-Codes.md#z_tilt_adjust) etc
|
||||||
``
|
4. Home all axis, then perform a [Bed Mesh](Bed_Mesh.md) if required
|
||||||
SAMPLE_COUNT=<value>
|
5. Perform a test print, followed by any
|
||||||
``
|
[fine-tuning](Axis_Twist_Compensation.md#fine-tuning) as desired
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
> **Tip:** Bed temperature and nozzle temperature and size do not seem to have
|
> **Tip:** Bed temperature and nozzle temperature and size do not seem to have
|
||||||
> an influence to the calibration process.
|
> an influence to the calibration process.
|
||||||
|
|||||||
@@ -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)
|
mesh. In this configuration it is possible offset the mesh so that the (X, Y)
|
||||||
`reference position` applies zero adjustment. The `reference postion` should
|
`reference position` applies zero adjustment. The `reference postion` should
|
||||||
be the location on the bed where a
|
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
|
paper test is performed. The bed_mesh module provides the
|
||||||
`zero_reference_position` option for specifying this coordinate:
|
`zero_reference_position` option for specifying this coordinate:
|
||||||
|
|
||||||
|
|||||||
@@ -354,26 +354,6 @@ micro-controller.
|
|||||||
| 1 stepper (200Mhz) | 39 |
|
| 1 stepper (200Mhz) | 39 |
|
||||||
| 3 stepper (200Mhz) | 181 |
|
| 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 ###
|
### AR100 step rate benchmark ###
|
||||||
|
|
||||||
The following configuration sequence is used on AR100 CPU (Allwinner A64):
|
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
|
`or1k-linux-musl-gcc (GCC) 9.2.0` on an Allwinner A64-H
|
||||||
micro-controller.
|
micro-controller.
|
||||||
|
|
||||||
@@ -395,9 +375,9 @@ micro-controller.
|
|||||||
| 1 stepper | 85 |
|
| 1 stepper | 85 |
|
||||||
| 3 stepper | 359 |
|
| 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
|
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
|
finalize_config crc=0
|
||||||
```
|
```
|
||||||
|
|
||||||
The test was last run on commit `f6718291` with gcc version
|
The test was last run on commit `59314d99` with gcc version
|
||||||
`arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0` on Raspberry Pi
|
`arm-none-eabi-gcc (Fedora 10.2.0-4.fc34) 10.2.0` on a Raspberry Pi
|
||||||
Pico and Pico 2 boards.
|
Pico board.
|
||||||
|
|
||||||
| rp2040 (*) | ticks |
|
| rp2040 | ticks |
|
||||||
| -------------------- | ----- |
|
| -------------------- | ----- |
|
||||||
| 1 stepper | 5 |
|
| 1 stepper | 5 |
|
||||||
| 3 stepper | 22 |
|
| 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
|
### Linux MCU step rate benchmark
|
||||||
|
|
||||||
The following configuration sequence is used on a Raspberry Pi:
|
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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| rp2040 (USB) | 873K | c5667193 | arm-none-eabi-gcc (Fedora 10.2.0-4.fc34) 10.2.0 |
|
||||||
| rp2350 (USB) | 885K | f6718291 | arm-none-eabi-gcc (Fedora 14.1.0-1.fc40) 14.1.0 |
|
|
||||||
|
|
||||||
## Host Benchmarks
|
## Host Benchmarks
|
||||||
|
|
||||||
|
|||||||
@@ -359,10 +359,10 @@ Useful steps:
|
|||||||
be efficient as it is typically only called during homing and
|
be efficient as it is typically only called during homing and
|
||||||
probing operations.
|
probing operations.
|
||||||
5. Other methods. Implement the `check_move()`, `get_status()`,
|
5. Other methods. Implement the `check_move()`, `get_status()`,
|
||||||
`get_steppers()`, `home()`, `clear_homing_state()`, and `set_position()`
|
`get_steppers()`, `home()`, and `set_position()` methods. These
|
||||||
methods. These functions are typically used to provide kinematic
|
functions are typically used to provide kinematic specific checks.
|
||||||
specific checks. However, at the start of development one can use
|
However, at the start of development one can use boiler-plate code
|
||||||
boiler-plate code here.
|
here.
|
||||||
6. Implement test cases. Create a g-code file with a series of moves
|
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
|
that can test important cases for the given kinematics. Follow the
|
||||||
[debugging documentation](Debugging.md) to convert this g-code file
|
[debugging documentation](Debugging.md) to convert this g-code file
|
||||||
|
|||||||
@@ -8,37 +8,6 @@ All dates in this document are approximate.
|
|||||||
|
|
||||||
## Changes
|
## 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]`
|
20240415: The `on_error_gcode` parameter in the `[virtual_sdcard]`
|
||||||
config section now has a default. If this parameter is not specified
|
config section now has a default. If this parameter is not specified
|
||||||
it now defaults to `TURN_OFF_HEATERS`. If the previous behavior is
|
it now defaults to `TURN_OFF_HEATERS`. If the previous behavior is
|
||||||
|
|||||||
@@ -1675,9 +1675,8 @@ Support for LIS2DW accelerometers.
|
|||||||
|
|
||||||
```
|
```
|
||||||
[lis2dw]
|
[lis2dw]
|
||||||
#cs_pin:
|
cs_pin:
|
||||||
# The SPI enable pin for the sensor. This parameter must be provided
|
# The SPI enable pin for the sensor. This parameter must be provided.
|
||||||
# if using SPI.
|
|
||||||
#spi_speed: 5000000
|
#spi_speed: 5000000
|
||||||
# The SPI speed (in hz) to use when communicating with the chip.
|
# The SPI speed (in hz) to use when communicating with the chip.
|
||||||
# The default is 5000000.
|
# The default is 5000000.
|
||||||
@@ -1687,46 +1686,6 @@ Support for LIS2DW accelerometers.
|
|||||||
#spi_software_miso_pin:
|
#spi_software_miso_pin:
|
||||||
# See the "common SPI settings" section for a description of the
|
# See the "common SPI settings" section for a description of the
|
||||||
# above parameters.
|
# 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
|
#axes_map: x, y, z
|
||||||
# See the "adxl345" section for information on this parameter.
|
# 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
|
# auto-calibration (with 'SHAPER_CALIBRATE' command). By default no
|
||||||
# maximum smoothing is specified. Refer to Measuring_Resonances guide
|
# maximum smoothing is specified. Refer to Measuring_Resonances guide
|
||||||
# for more details on using this feature.
|
# 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
|
#min_freq: 5
|
||||||
# Minimum frequency to test for resonances. The default is 5 Hz.
|
# Minimum frequency to test for resonances. The default is 5 Hz.
|
||||||
#max_freq: 133.33
|
#max_freq: 133.33
|
||||||
# Maximum frequency to test for resonances. The default is 133.33 Hz.
|
# 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
|
# This parameter is used to determine which acceleration to use to
|
||||||
# test a specific frequency: accel = accel_per_hz * freq. Higher the
|
# test a specific frequency: accel = accel_per_hz * freq. Higher the
|
||||||
# value, the higher is the energy of the oscillations. Can be set to
|
# 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
|
# 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
|
# will decrease the precision of the test. The default value is 1.0
|
||||||
# (Hz/sec == sec^-2).
|
# (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
|
## Config file helpers
|
||||||
@@ -2093,9 +2042,9 @@ sensor_type: ldc1612
|
|||||||
|
|
||||||
### [axis_twist_compensation]
|
### [axis_twist_compensation]
|
||||||
|
|
||||||
A tool to compensate for inaccurate probe readings due to twist in X or Y
|
A tool to compensate for inaccurate probe readings due to twist in X gantry. See
|
||||||
gantry. See the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md)
|
the [Axis Twist Compensation Guide](Axis_Twist_Compensation.md) for more
|
||||||
for more detailed information regarding symptoms, configuration and setup.
|
detailed information regarding symptoms, configuration and setup.
|
||||||
|
|
||||||
```
|
```
|
||||||
[axis_twist_compensation]
|
[axis_twist_compensation]
|
||||||
@@ -2108,33 +2057,16 @@ for more detailed information regarding symptoms, configuration and setup.
|
|||||||
calibrate_start_x: 20
|
calibrate_start_x: 20
|
||||||
# Defines the minimum X coordinate of the calibration
|
# Defines the minimum X coordinate of the calibration
|
||||||
# This should be the X coordinate that positions the nozzle at the starting
|
# 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
|
calibrate_end_x: 200
|
||||||
# Defines the maximum X coordinate of the calibration
|
# Defines the maximum X coordinate of the calibration
|
||||||
# This should be the X coordinate that positions the nozzle at the ending
|
# 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
|
calibrate_y: 112.5
|
||||||
# Defines the Y coordinate of the calibration
|
# Defines the Y coordinate of the calibration
|
||||||
# This should be the Y coordinate that positions the nozzle during the
|
# 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
|
# 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
|
## Additional stepper motors and extruders
|
||||||
@@ -2526,10 +2458,6 @@ postfix for both sections.
|
|||||||
# "calibration_extruder_temp" option is set. Its recommended to heat
|
# "calibration_extruder_temp" option is set. Its recommended to heat
|
||||||
# the extruder some distance from the bed to minimize its impact on
|
# the extruder some distance from the bed to minimize its impact on
|
||||||
# the probe coil temperature. The default is 50.
|
# 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
|
## Temperature sensors
|
||||||
@@ -3846,7 +3774,6 @@ run_current:
|
|||||||
#driver_SEIMIN: 0
|
#driver_SEIMIN: 0
|
||||||
#driver_SFILT: 0
|
#driver_SFILT: 0
|
||||||
#driver_SG4_ANGLE_OFFSET: 1
|
#driver_SG4_ANGLE_OFFSET: 1
|
||||||
#driver_SLOPE_CONTROL: 0
|
|
||||||
# Set the given register during the configuration of the TMC2240
|
# Set the given register during the configuration of the TMC2240
|
||||||
# chip. This may be used to set custom motor parameters. The
|
# chip. This may be used to set custom motor parameters. The
|
||||||
# defaults for each parameter are next to the parameter name in 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]
|
[display]
|
||||||
lcd_type:
|
lcd_type:
|
||||||
# The type of LCD chip in use. This may be "hd44780", "hd44780_spi",
|
# The type of LCD chip in use. This may be "hd44780", "hd44780_spi",
|
||||||
# "aip31068_spi", "st7920", "emulated_st7920", "uc1701", "ssd1306", or
|
# "st7920", "emulated_st7920", "uc1701", "ssd1306", or "sh1106".
|
||||||
# "sh1106".
|
|
||||||
# See the display sections below for information on each type and
|
# See the display sections below for information on each type and
|
||||||
# additional parameters they provide. This parameter must be
|
# additional parameters they provide. This parameter must be
|
||||||
# provided.
|
# provided.
|
||||||
#display_group:
|
#display_group:
|
||||||
# The name of the display_data group to show on the display. This
|
# The name of the display_data group to show on the display. This
|
||||||
# controls the content of the screen (see the "display_data" section
|
# controls the content of the screen (see the "display_data" section
|
||||||
# for more information). The default is _default_20x4 for hd44780 or
|
# for more information). The default is _default_20x4 for hd44780
|
||||||
# aip31068_spi displays and _default_16x4 for other displays.
|
# displays and _default_16x4 for other displays.
|
||||||
#menu_timeout:
|
#menu_timeout:
|
||||||
# Timeout for menu. Being inactive this amount of seconds will
|
# Timeout for menu. Being inactive this amount of seconds will
|
||||||
# trigger menu exit or return to root menu when having autorun
|
# 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
|
#### st7920 display
|
||||||
|
|
||||||
Information on configuring st7920 displays (which is used in
|
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.
|
# 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
|
This is a 24 bit low sample rate chip using "bit-bang" communications. It is
|
||||||
suitable for filament scales.
|
suitable for filament scales.
|
||||||
```
|
```
|
||||||
@@ -4824,30 +4725,13 @@ data_ready_pin:
|
|||||||
#gain: 128
|
#gain: 128
|
||||||
# Valid gain values are 128, 64, 32, 16, 8, 4, 2, 1
|
# Valid gain values are 128, 64, 32, 16, 8, 4, 2, 1
|
||||||
# The default is 128
|
# 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
|
#sample_rate: 660
|
||||||
# This chip supports two ranges of sample rates, Normal and Turbo. In turbo
|
# 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.
|
# speed is also doubled.
|
||||||
# Normal sample rates: 20, 45, 90, 175, 330, 600, 1000
|
# Normal sample rates: 20, 45, 90, 175, 330, 600, 1000
|
||||||
# Turbo sample rates: 40, 90, 180, 350, 660, 1200, 2000
|
# Turbo sample rates: 40, 90, 180, 350, 660, 1200, 2000
|
||||||
# The default is 660
|
# 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
|
## Board specific hardware support
|
||||||
@@ -4936,50 +4820,6 @@ vssa_pin:
|
|||||||
# noise. The default is 2 seconds.
|
# 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]
|
||||||
|
|
||||||
Replicape support - see the [beaglebone guide](Beaglebone.md) and the
|
Replicape support - see the [beaglebone guide](Beaglebone.md) and the
|
||||||
@@ -5085,9 +4925,8 @@ serial:
|
|||||||
### [angle]
|
### [angle]
|
||||||
|
|
||||||
Magnetic hall angle sensor support for reading stepper motor angle
|
Magnetic hall angle sensor support for reading stepper motor angle
|
||||||
shaft measurements using a1333, as5047d, mt6816, mt6826s,
|
shaft measurements using a1333, as5047d, or tle5012b SPI chips. The
|
||||||
or tle5012b SPI chips.
|
measurements are available via the [API Server](API_Server.md) and
|
||||||
The measurements are available via the [API Server](API_Server.md) and
|
|
||||||
[motion analysis tool](Debugging.md#motion-analysis-and-data-logging).
|
[motion analysis tool](Debugging.md#motion-analysis-and-data-logging).
|
||||||
See the [G-Code reference](G-Codes.md#angle) for available commands.
|
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]
|
[angle my_angle_sensor]
|
||||||
sensor_type:
|
sensor_type:
|
||||||
# The type of the magnetic hall sensor chip. Available choices are
|
# 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.
|
# specified.
|
||||||
#sample_period: 0.000400
|
#sample_period: 0.000400
|
||||||
# The query period (in seconds) to use during measurements. The
|
# 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
|
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)
|
[set in the operating system](RPi_microcontroller.md#optional-enabling-i2c)
|
||||||
and the `i2c_speed` parameter is otherwise ignored. The Klipper
|
and the `i2c_speed` parameter is otherwise ignored. The Klipper
|
||||||
"RP2040" micro-controller and ATmega AVR family and some STM32
|
"RP2040" micro-controller and ATmega AVR family support a rate of 400000
|
||||||
(F0, G0, G4, L4, F7, H7) support a rate of 400000 via the `i2c_speed` parameter.
|
via the `i2c_speed` parameter. All other Klipper micro-controllers use a
|
||||||
All other Klipper micro-controllers use a
|
|
||||||
100000 rate and ignore the `i2c_speed` parameter.
|
100000 rate and ignore the `i2c_speed` parameter.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -132,10 +132,3 @@ There are several
|
|||||||
you have questions on the code then you can also ask in the
|
you have questions on the code then you can also ask in the
|
||||||
[Klipper Discourse Forum](#discourse-forum) or on the
|
[Klipper Discourse Forum](#discourse-forum) or on the
|
||||||
[Klipper Discord Chat](#discord-chat).
|
[Klipper Discord Chat](#discord-chat).
|
||||||
|
|
||||||
## Professional Services
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Custom software development, software support, and solutions:
|
|
||||||
[https://ko-fi.com/koconnor](https://ko-fi.com/koconnor)
|
|
||||||
|
|||||||
@@ -78,9 +78,7 @@ for further details on how to configure a `temperature_probe`. It is
|
|||||||
advised to configure the `calibration_position`,
|
advised to configure the `calibration_position`,
|
||||||
`calibration_extruder_temp`, `extruder_heating_z`, and
|
`calibration_extruder_temp`, `extruder_heating_z`, and
|
||||||
`calibration_bed_temp` options, as doing so will automate some of the
|
`calibration_bed_temp` options, as doing so will automate some of the
|
||||||
steps outlined below. If the printer to be calibrated is enclosed, it
|
steps outlined below.
|
||||||
is strongly recommended to set the `max_validation_temp` option to a value
|
|
||||||
between 100 and 120.
|
|
||||||
|
|
||||||
Eddy probe manufacturers may offer a stock drift calibration that can be
|
Eddy probe manufacturers may offer a stock drift calibration that can be
|
||||||
manually added to `drift_calibration` option of the `[probe_eddy_current]`
|
manually added to `drift_calibration` option of the `[probe_eddy_current]`
|
||||||
|
|||||||
@@ -190,8 +190,6 @@ represent total number of steps per second on the micro-controller.
|
|||||||
| AR100 | 3529K | 2507K |
|
| AR100 | 3529K | 2507K |
|
||||||
| STM32F407 | 3652K | 2459K |
|
| STM32F407 | 3652K | 2459K |
|
||||||
| STM32F446 | 3913K | 2634K |
|
| STM32F446 | 3913K | 2634K |
|
||||||
| RP2350 | 4167K | 2663K |
|
|
||||||
| SAME70 | 6667K | 4737K |
|
|
||||||
| STM32H743 | 9091K | 6061K |
|
| STM32H743 | 9091K | 6061K |
|
||||||
|
|
||||||
If unsure of the micro-controller on a particular board, find the
|
If unsure of the micro-controller on a particular board, find the
|
||||||
|
|||||||
118
docs/G-Codes.md
118
docs/G-Codes.md
@@ -127,14 +127,6 @@ use this tool the Python "numpy" package must be installed (see the
|
|||||||
[measuring resonance document](Measuring_Resonances.md#software-installation)
|
[measuring resonance document](Measuring_Resonances.md#software-installation)
|
||||||
for more information).
|
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
|
||||||
`ANGLE_DEBUG_READ CHIP=<config_name> REG=<register>`: Queries sensor
|
`ANGLE_DEBUG_READ CHIP=<config_name> REG=<register>`: Queries sensor
|
||||||
register "register" (e.g. 44 or 0x2C). Can be useful for debugging
|
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.
|
section](Config_Reference.md#axis_twist_compensation) is enabled.
|
||||||
|
|
||||||
#### AXIS_TWIST_COMPENSATION_CALIBRATE
|
#### AXIS_TWIST_COMPENSATION_CALIBRATE
|
||||||
`AXIS_TWIST_COMPENSATION_CALIBRATE [AXIS=<X|Y>] [AUTO=<True|False>]
|
`AXIS_TWIST_COMPENSATION_CALIBRATE [SAMPLE_COUNT=<value>]`: Initiates the X
|
||||||
[SAMPLE_COUNT=<value>]`
|
twist calibration wizard. `SAMPLE_COUNT` specifies the number of points along
|
||||||
|
the X axis to calibrate at and defaults to 3.
|
||||||
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.
|
|
||||||
|
|
||||||
### [bed_mesh]
|
### [bed_mesh]
|
||||||
|
|
||||||
@@ -494,20 +476,6 @@ enabled.
|
|||||||
`SET_FAN_SPEED FAN=config_name SPEED=<speed>` This command sets the
|
`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.
|
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]
|
### [filament_switch_sensor]
|
||||||
|
|
||||||
The following command is available when a
|
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.
|
intended for low-level diagnostics and debugging.
|
||||||
|
|
||||||
#### SET_KINEMATIC_POSITION
|
#### SET_KINEMATIC_POSITION
|
||||||
`SET_KINEMATIC_POSITION [X=<value>] [Y=<value>] [Z=<value>]
|
`SET_KINEMATIC_POSITION [X=<value>] [Y=<value>] [Z=<value>]`: Force
|
||||||
[CLEAR=<[X][Y][Z]>]`: Force the low-level kinematic code to believe the
|
the low-level kinematic code to believe the toolhead is at the given
|
||||||
toolhead is at the given cartesian position. This is a diagnostic and
|
cartesian position. This is a diagnostic and debugging command; use
|
||||||
debugging command; use SET_GCODE_OFFSET and/or G92 for regular axis
|
SET_GCODE_OFFSET and/or G92 for regular axis transformations. If an
|
||||||
transformations. If an axis is not specified then it will default to the
|
axis is not specified then it will default to the position that the
|
||||||
position that the head was last commanded to. Setting an incorrect or
|
head was last commanded to. Setting an incorrect or invalid position
|
||||||
invalid position may lead to internal software errors. Use the CLEAR
|
may lead to internal software errors. This command may invalidate
|
||||||
parameter to forget the homing state for the given axes. Note that CLEAR
|
future boundary checks; issue a G28 afterwards to reset the
|
||||||
will not override the previous functionality; if an axis is not specified
|
kinematics.
|
||||||
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.
|
|
||||||
|
|
||||||
### [gcode]
|
### [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
|
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.
|
`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]
|
### [palette2]
|
||||||
|
|
||||||
The following commands are available when the
|
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
|
SET_PIN command without an explicit CYCLE_TIME parameter will use the
|
||||||
`cycle_time` specified in the pwm_cycle_time config section).
|
`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]
|
### [query_adc]
|
||||||
|
|
||||||
The query_adc module is automatically loaded.
|
The query_adc module is automatically loaded.
|
||||||
@@ -1120,19 +1056,20 @@ is enabled (also see the
|
|||||||
all enabled accelerometer chips.
|
all enabled accelerometer chips.
|
||||||
|
|
||||||
#### TEST_RESONANCES
|
#### 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>]
|
[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>]
|
[HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
|
||||||
[POINT=x,y,z] [INPUT_SHAPING=<0:1>]`: Runs the resonance
|
[POINT=x,y,z] [INPUT_SHAPING=[<0:1>]]`: Runs the resonance
|
||||||
test in all configured probe points for the requested "axis" and
|
test in all configured probe points for the requested "axis" and
|
||||||
measures the acceleration using the accelerometer chips configured for
|
measures the acceleration using the accelerometer chips configured for
|
||||||
the respective axis. "axis" can either be X or Y, or specify an
|
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
|
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
|
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`
|
`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
|
and `AXIS=-dx,-dy` is equivalent. `adxl345_chip_name` can be one or
|
||||||
more configured accel chips, delimited with comma, for example
|
more configured adxl345 chip,delimited with comma, for example
|
||||||
`CHIPS="adxl345, adxl345 rpi"`. If POINT is specified it will override the point(s)
|
`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),
|
configured in `[resonance_tester]`. If `INPUT_SHAPING=0` or not set(default),
|
||||||
disables input shaping for the resonance testing, because
|
disables input shaping for the resonance testing, because
|
||||||
it is not valid to run the resonance testing with the input shaper
|
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
|
||||||
`SHAPER_CALIBRATE [AXIS=<axis>] [NAME=<name>] [FREQ_START=<min_freq>]
|
`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>]
|
[FREQ_END=<max_freq>] [HZ_PER_SEC=<hz_per_sec>] [CHIPS=<adxl345_chip_name>]
|
||||||
[CHIPS=<chip_name>] [MAX_SMOOTHING=<max_smoothing>] [INPUT_SHAPING=<0:1>]`:
|
[MAX_SMOOTHING=<max_smoothing>]`: Similarly to `TEST_RESONANCES`, runs
|
||||||
Similarly to `TEST_RESONANCES`, runs
|
|
||||||
the resonance test as configured, and tries to find the optimal
|
the resonance test as configured, and tries to find the optimal
|
||||||
parameters for the input shaper for the requested axis (or both X and
|
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
|
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 config section](Config_Reference.md#z_tilt) is enabled.
|
||||||
|
|
||||||
#### Z_TILT_ADJUST
|
#### Z_TILT_ADJUST
|
||||||
`Z_TILT_ADJUST [RETRIES=<value>] [RETRY_TOLERANCE=<value>]
|
`Z_TILT_ADJUST [HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This
|
||||||
[HORIZONTAL_MOVE_Z=<value>] [<probe_parameter>=<value>]`: This command
|
command will probe the points specified in the config and then make independent
|
||||||
will probe the points specified in the config and then make
|
adjustments to each Z stepper to compensate for tilt. See the PROBE command for
|
||||||
independent adjustments to each Z stepper to compensate for tilt. See
|
details on the optional probe parameters. The optional `HORIZONTAL_MOVE_Z`
|
||||||
the PROBE command for details on the optional probe parameters. The
|
value overrides the `horizontal_move_z` option specified in the config file.
|
||||||
optional `RETRIES`, `RETRY_TOLERANCE`, and `HORIZONTAL_MOVE_Z` values
|
|
||||||
override those options specified in the config file.
|
|
||||||
|
|
||||||
### [temperature_probe]
|
### [temperature_probe]
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
These instructions assume the software will run on a linux based host
|
These instructions assume the software will run on a Raspberry Pi
|
||||||
running a Klipper compatible front end. It is recommended that a
|
computer in conjunction with OctoPrint. It is recommended that a
|
||||||
SBC(Small Board Computer) such as a Raspberry Pi or Debian based Linux
|
Raspberry Pi 2 (or later) be used as the host machine (see the
|
||||||
device be used as the host machine (see the
|
|
||||||
[FAQ](FAQ.md#can-i-run-klipper-on-something-other-than-a-raspberry-pi-3)
|
[FAQ](FAQ.md#can-i-run-klipper-on-something-other-than-a-raspberry-pi-3)
|
||||||
for other options).
|
for other machines).
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Obtain a Klipper Configuration File
|
## Obtain a Klipper Configuration File
|
||||||
|
|
||||||
Most Klipper settings are determined by a "printer 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
|
file can often be found by looking in the Klipper
|
||||||
[config directory](../config/) for a file starting with a "printer-"
|
[config directory](../config/) for a file starting with a "printer-"
|
||||||
prefix that corresponds to the target printer. The Klipper
|
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 file](../config/) and use the Klipper
|
||||||
[config reference](Config_Reference.md) for further information.
|
[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
|
Start by installing [OctoPi](https://github.com/guysoft/OctoPi) on the
|
||||||
interact with it.
|
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
|
After installing OctoPi and upgrading OctoPrint, it will be necessary
|
||||||
the [Moonraker web API](https://moonraker.readthedocs.io/) and there is also
|
to ssh into the target machine to run a handful of system commands. If
|
||||||
the option to use [Octoprint](https://octoprint.org/) to control Klipper.
|
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
|
git clone https://github.com/Klipper3d/klipper
|
||||||
make an informed decision.
|
./klipper/scripts/install-octopi.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Obtaining an OS image for SBC's
|
The above will download Klipper, install some system dependencies,
|
||||||
|
setup Klipper to run at system startup, and start the Klipper host
|
||||||
There are many ways to obtain an OS image for Klipper for SBC use, most depend on
|
software. It will require an internet connection and it may take a few
|
||||||
what front end you wish to use. Some manafactures of these SBC boards also provide
|
minutes to complete.
|
||||||
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
|
|
||||||
|
|
||||||
## Building and flashing the micro-controller
|
## Building and flashing the micro-controller
|
||||||
|
|
||||||
To compile the micro-controller code, start by running these commands
|
To compile the micro-controller code, start by running these commands
|
||||||
on your host device:
|
on the Raspberry Pi:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd ~/klipper/
|
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.
|
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
|
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,
|
possible there may be multiple lines in the above output - if so,
|
||||||
choose the line corresponding to the micro-controller. If many
|
choose the line corresponding to the micro-controller (see the
|
||||||
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
|
|
||||||
[FAQ](FAQ.md#wheres-my-serial-port) for more information).
|
[FAQ](FAQ.md#wheres-my-serial-port) for more information).
|
||||||
|
|
||||||
For common micro-controllers with STM32 or clone chips, LPC chips and
|
For common micro-controllers, the code can be flashed with something
|
||||||
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
|
|
||||||
similar to:
|
similar to:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -152,38 +123,53 @@ sudo service klipper start
|
|||||||
Be sure to update the FLASH_DEVICE with the printer's unique serial
|
Be sure to update the FLASH_DEVICE with the printer's unique serial
|
||||||
port name.
|
port name.
|
||||||
|
|
||||||
For common micro-controllers using RP2040 chips, the code can be flashed
|
When flashing for the first time, make sure that OctoPrint is not
|
||||||
with something similar to:
|
connected directly to the printer (from the OctoPrint web page, under
|
||||||
|
the "Connection" section, click "Disconnect").
|
||||||
|
|
||||||
```
|
## Configuring OctoPrint to use Klipper
|
||||||
sudo service klipper stop
|
|
||||||
make flash FLASH_DEVICE=first
|
|
||||||
sudo service klipper start
|
|
||||||
```
|
|
||||||
|
|
||||||
It is important to note that RP2040 chips may need to be put into Boot mode
|
The OctoPrint web server needs to be configured to communicate with
|
||||||
before this operation.
|
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
|
## Configuring Klipper
|
||||||
|
|
||||||
The next step is to copy the
|
The next step is to copy the
|
||||||
[printer configuration file](#obtain-a-klipper-configuration-file) to
|
[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
|
Arguably the easiest way to set the Klipper configuration file is to
|
||||||
built in editors in Mainsail or Fluidd. These will allow the user to open
|
use a desktop editor that supports editing files over the "scp" and/or
|
||||||
the configuration examples and save them to be printer.cfg.
|
"sftp" protocols. There are freely available tools that support this
|
||||||
|
(eg, Notepad++, WinSCP, and Cyberduck). Load the printer config file
|
||||||
Another option is to use a desktop editor that supports editing files
|
in the editor and then save it as a file named `printer.cfg` in the
|
||||||
over the "scp" and/or "sftp" protocols. There are freely available tools
|
home directory of the pi user (ie, `/home/pi/printer.cfg`).
|
||||||
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
|
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
|
sure to update the command to use the appropriate printer config
|
||||||
filename):
|
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
|
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
|
"status" command will report the printer is ready if the Klipper
|
||||||
config file is successfully read and the micro-controller is
|
config file is successfully read and the micro-controller is
|
||||||
successfully found and configured.
|
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"
|
necessary corrections to the printer config file and issue "restart"
|
||||||
until "status" reports the printer is ready.
|
until "status" reports the printer is ready.
|
||||||
|
|
||||||
Klipper reports error messages via the command console and via pop up in
|
Klipper reports error messages via the OctoPrint terminal tab. The
|
||||||
Fluidd and Mainsail. The "status" command can be used to re-report error
|
"status" command can be used to re-report error messages. The default
|
||||||
messages. A log is available and usually located in ~/printer_data/logs
|
Klipper startup script also places a log in **/tmp/klippy.log** which
|
||||||
this is named klippy.log
|
provides more detailed information.
|
||||||
|
|
||||||
After Klipper reports that the printer is ready, proceed to the
|
After Klipper reports that the printer is ready, proceed to the
|
||||||
[config check document](Config_checks.md) to perform some basic checks
|
[config check document](Config_checks.md) to perform some basic checks
|
||||||
|
|||||||
@@ -1,26 +1,24 @@
|
|||||||
# Measuring Resonances
|
# 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
|
accelerometers which can be used to measure resonance frequencies of the printer
|
||||||
for different axes, and auto-tune [input shapers](Resonance_Compensation.md) to
|
for different axes, and auto-tune [input shapers](Resonance_Compensation.md) to
|
||||||
compensate for resonances. Note that using accelerometers requires some
|
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
|
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
|
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
|
interface of an MCU board that supports 400kbit/s *fast mode* in Klipper.
|
||||||
LIS2DW and LIS3DH can be connected to either SPI or I2C with the same considerations
|
|
||||||
as above.
|
|
||||||
|
|
||||||
When sourcing accelerometers, be aware that there are a variety of different PCB
|
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
|
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.
|
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).
|
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
|
For MPU-9250/MPU-9255/MPU-6515/MPU-6050/MPU-6500s there are also a variety of
|
||||||
a variety of board designs and clones with different I2C pull-up resistors which
|
board designs and clones with different I2C pull-up resistors which will need
|
||||||
will need supplementing.
|
supplementing.
|
||||||
|
|
||||||
## MCUs with Klipper I2C *fast-mode* Support
|
## MCUs with Klipper I2C *fast-mode* Support
|
||||||
|
|
||||||
@@ -29,7 +27,6 @@ will need supplementing.
|
|||||||
| Raspberry Pi | 3B+, Pico | 3A, 3A+, 3B, 4 |
|
| Raspberry Pi | 3B+, Pico | 3A, 3A+, 3B, 4 |
|
||||||
| AVR ATmega | ATmega328p | ATmega32u4, ATmega128, ATmega168, ATmega328, ATmega644p, ATmega1280, ATmega1284, ATmega2560 |
|
| AVR ATmega | ATmega328p | ATmega32u4, ATmega128, ATmega168, ATmega328, ATmega644p, ATmega1280, ATmega1284, ATmega2560 |
|
||||||
| AVR AT90 | - | AT90usb646, AT90usb1286 |
|
| AVR AT90 | - | AT90usb646, AT90usb1286 |
|
||||||
| SAMD | SAMC21G18 | SAMC21G18, SAMD21G18, SAMD21E18, SAMD21J18, SAMD21E15, SAMD51G19, SAMD51J19, SAMD51N19, SAMD51P20, SAME51J19, SAME51N19, SAME54P20 |
|
|
||||||
|
|
||||||
## Installation instructions
|
## 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:
|
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*
|
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
|
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. On some occasions, if the board has too little RAM
|
||||||
the installation may fail and you will need to enable swap. Also note
|
the installation may fail and you will need to enable swap.
|
||||||
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.
|
|
||||||
|
|
||||||
#### Configure ADXL345 With RPi
|
#### 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.
|
Restart Klipper via the `RESTART` command.
|
||||||
|
|
||||||
#### Configure LIS2DW series over SPI
|
#### Configure LIS2DW series
|
||||||
|
|
||||||
```
|
```
|
||||||
[mcu lis]
|
[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
|
suggested shaper configuration is almost the same as what you got during the
|
||||||
previous calibration, this step can be skipped.
|
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
|
### Testing custom axes
|
||||||
|
|
||||||
`TEST_RESONANCES` command supports custom axes. While this is not really
|
`TEST_RESONANCES` command supports custom axes. While this is not really
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -17,7 +17,6 @@ communication with the Klipper developers.
|
|||||||
## Installation and Configuration
|
## Installation and Configuration
|
||||||
|
|
||||||
- [Installation](Installation.md): Guide to installing Klipper.
|
- [Installation](Installation.md): Guide to installing Klipper.
|
||||||
- [Octoprint](OctoPrint.md): Guide to installing Octoprint with Klipper.
|
|
||||||
- [Config Reference](Config_Reference.md): Description of config
|
- [Config Reference](Config_Reference.md): Description of config
|
||||||
parameters.
|
parameters.
|
||||||
- [Rotation Distance](Rotation_Distance.md): Calculating the
|
- [Rotation Distance](Rotation_Distance.md): Calculating the
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ serve the 3D printing community better. Follow them on
|
|||||||
## Sponsors
|
## 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/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
|
## Klipper Developers
|
||||||
|
|
||||||
|
|||||||
@@ -256,6 +256,11 @@ object is available if any heater is defined):
|
|||||||
e.g. `["tmc2240 stepper_x"]`. While a temperature sensor is always
|
e.g. `["tmc2240 stepper_x"]`. While a temperature sensor is always
|
||||||
available to read, a temperature monitor may not be available and
|
available to read, a temperature monitor may not be available and
|
||||||
will return null in such case.
|
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
|
## idle_timeout
|
||||||
|
|
||||||
|
|||||||
@@ -88,9 +88,7 @@ nav:
|
|||||||
- Config_Changes.md
|
- Config_Changes.md
|
||||||
- Contact.md
|
- Contact.md
|
||||||
- Installation and Configuration:
|
- Installation and Configuration:
|
||||||
- Installation:
|
- Installation.md
|
||||||
- Installation.md
|
|
||||||
- OctoPrint.md
|
|
||||||
- Configuration Reference:
|
- Configuration Reference:
|
||||||
- Config_Reference.md
|
- Config_Reference.md
|
||||||
- Rotation_Distance.md
|
- Rotation_Distance.md
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 5.9 KiB |
@@ -6,16 +6,13 @@ title: Welcome
|
|||||||
|
|
||||||
{ .center-image }
|
{ .center-image }
|
||||||
|
|
||||||
The Klipper firmware controls 3d-Printers. It combines the power of a
|
Klipper is a 3d-Printer firmware. It combines the power of a general
|
||||||
general purpose computer with one or more micro-controllers. See the
|
purpose computer with one or more micro-controllers. See the
|
||||||
[features document](https://www.klipper3d.org/Features.html) for more
|
[features](Features.md) document for more information on why you
|
||||||
information on why you should use the Klipper software.
|
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
|
Klipper is Free Software. Read the [documentation](Overview.md) or
|
||||||
[documentation](https://www.klipper3d.org/Overview.html), see the
|
view [the Klipper code on github](https://github.com/Klipper3d/klipper).
|
||||||
[license](COPYING), or
|
We depend on the generous support from our [sponsors](Sponsors.md).
|
||||||
[download](https://github.com/Klipper3d/Klipper) the software. We
|
|
||||||
depend on the generous support from our
|
|
||||||
[sponsors](https://www.klipper3d.org/Sponsors.html).
|
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
# Code for reading and writing the Klipper config file
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import sys, os, glob, re, time, logging, configparser, io
|
import sys, os, glob, re, time, logging, configparser, io
|
||||||
|
|
||||||
error = configparser.Error
|
error = configparser.Error
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
# Config section parsing helper
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
class sentinel:
|
class sentinel:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -139,13 +134,30 @@ class ConfigWrapper:
|
|||||||
pconfig = self.printer.lookup_object("configfile")
|
pconfig = self.printer.lookup_object("configfile")
|
||||||
pconfig.deprecate(self.section, option, value, msg)
|
pconfig.deprecate(self.section, option, value, msg)
|
||||||
|
|
||||||
|
AUTOSAVE_HEADER = """
|
||||||
|
#*# <---------------------- SAVE_CONFIG ---------------------->
|
||||||
|
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
|
||||||
|
#*#
|
||||||
|
"""
|
||||||
|
|
||||||
######################################################################
|
class PrinterConfig:
|
||||||
# Config file parsing (with include file support)
|
def __init__(self, printer):
|
||||||
######################################################################
|
self.printer = printer
|
||||||
|
self.autosave = None
|
||||||
class ConfigFileReader:
|
self.deprecated = {}
|
||||||
def read_config_file(self, filename):
|
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:
|
try:
|
||||||
f = open(filename, 'r')
|
f = open(filename, 'r')
|
||||||
data = f.read()
|
data = f.read()
|
||||||
@@ -155,102 +167,6 @@ class ConfigFileReader:
|
|||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise error(msg)
|
raise error(msg)
|
||||||
return data.replace('\r\n', '\n')
|
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):
|
def _find_autosave_data(self, data):
|
||||||
regular_data = data
|
regular_data = data
|
||||||
autosave_data = ""
|
autosave_data = ""
|
||||||
@@ -259,7 +175,7 @@ class ConfigAutoSave:
|
|||||||
regular_data = data[:pos]
|
regular_data = data[:pos]
|
||||||
autosave_data = data[pos + len(AUTOSAVE_HEADER):].strip()
|
autosave_data = data[pos + len(AUTOSAVE_HEADER):].strip()
|
||||||
# Check for errors and strip line prefixes
|
# 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"
|
logging.warning("Can't read autosave from config file"
|
||||||
" - autosave state corrupted")
|
" - autosave state corrupted")
|
||||||
return data, ""
|
return data, ""
|
||||||
@@ -276,7 +192,7 @@ class ConfigAutoSave:
|
|||||||
return regular_data, "\n".join(out)
|
return regular_data, "\n".join(out)
|
||||||
comment_r = re.compile('[#;].*$')
|
comment_r = re.compile('[#;].*$')
|
||||||
value_r = re.compile('[^A-Za-z0-9_].*$')
|
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'
|
# Comment out fields in 'data' that are defined in 'config'
|
||||||
lines = data.split('\n')
|
lines = data.split('\n')
|
||||||
section = None
|
section = None
|
||||||
@@ -294,31 +210,152 @@ class ConfigAutoSave:
|
|||||||
section = pruned_line[1:-1].strip()
|
section = pruned_line[1:-1].strip()
|
||||||
continue
|
continue
|
||||||
field = self.value_r.sub('', pruned_line)
|
field = self.value_r.sub('', pruned_line)
|
||||||
if fileconfig.has_option(section, field):
|
if config.fileconfig.has_option(section, field):
|
||||||
is_dup_field = True
|
is_dup_field = True
|
||||||
lines[lineno] = '#' + lines[lineno]
|
lines[lineno] = '#' + lines[lineno]
|
||||||
return "\n".join(lines)
|
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']
|
filename = self.printer.get_start_args()['config_file']
|
||||||
cfgrdr = ConfigFileReader()
|
data = self._read_config_file(filename)
|
||||||
data = cfgrdr.read_config_file(filename)
|
|
||||||
regular_data, autosave_data = self._find_autosave_data(data)
|
regular_data, autosave_data = self._find_autosave_data(data)
|
||||||
regular_fileconfig = cfgrdr.build_fileconfig_with_includes(
|
regular_config = self._build_config_wrapper(regular_data, filename)
|
||||||
regular_data, filename)
|
autosave_data = self._strip_duplicates(autosave_data, regular_config)
|
||||||
autosave_data = self._strip_duplicates(autosave_data,
|
self.autosave = self._build_config_wrapper(autosave_data, filename)
|
||||||
regular_fileconfig)
|
cfg = self._build_config_wrapper(regular_data + autosave_data, filename)
|
||||||
self.fileconfig = cfgrdr.build_fileconfig(autosave_data, filename)
|
return cfg
|
||||||
cfgrdr.append_fileconfig(regular_fileconfig,
|
def check_unused_options(self, config):
|
||||||
autosave_data, '*AUTOSAVE*')
|
fileconfig = config.fileconfig
|
||||||
return regular_fileconfig, self.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):
|
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}
|
'save_config_pending_items': self.status_save_pending}
|
||||||
|
# Autosave functions
|
||||||
def set(self, section, option, value):
|
def set(self, section, option, value):
|
||||||
if not self.fileconfig.has_section(section):
|
if not self.autosave.fileconfig.has_section(section):
|
||||||
self.fileconfig.add_section(section)
|
self.autosave.fileconfig.add_section(section)
|
||||||
svalue = str(value)
|
svalue = str(value)
|
||||||
self.fileconfig.set(section, option, svalue)
|
self.autosave.fileconfig.set(section, option, svalue)
|
||||||
pending = dict(self.status_save_pending)
|
pending = dict(self.status_save_pending)
|
||||||
if not section in pending or pending[section] is None:
|
if not section in pending or pending[section] is None:
|
||||||
pending[section] = {}
|
pending[section] = {}
|
||||||
@@ -329,8 +366,8 @@ class ConfigAutoSave:
|
|||||||
self.save_config_pending = True
|
self.save_config_pending = True
|
||||||
logging.info("save_config: set [%s] %s = %s", section, option, svalue)
|
logging.info("save_config: set [%s] %s = %s", section, option, svalue)
|
||||||
def remove_section(self, section):
|
def remove_section(self, section):
|
||||||
if self.fileconfig.has_section(section):
|
if self.autosave.fileconfig.has_section(section):
|
||||||
self.fileconfig.remove_section(section)
|
self.autosave.fileconfig.remove_section(section)
|
||||||
pending = dict(self.status_save_pending)
|
pending = dict(self.status_save_pending)
|
||||||
pending[section] = None
|
pending[section] = None
|
||||||
self.status_save_pending = pending
|
self.status_save_pending = pending
|
||||||
@@ -341,20 +378,21 @@ class ConfigAutoSave:
|
|||||||
del pending[section]
|
del pending[section]
|
||||||
self.status_save_pending = pending
|
self.status_save_pending = pending
|
||||||
self.save_config_pending = True
|
self.save_config_pending = True
|
||||||
def _disallow_include_conflicts(self, regular_fileconfig):
|
def _disallow_include_conflicts(self, regular_data, cfgname, gcode):
|
||||||
for section in self.fileconfig.sections():
|
config = self._build_config_wrapper(regular_data, cfgname)
|
||||||
for option in self.fileconfig.options(section):
|
for section in self.autosave.fileconfig.sections():
|
||||||
if regular_fileconfig.has_option(section, option):
|
for option in self.autosave.fileconfig.options(section):
|
||||||
|
if config.fileconfig.has_option(section, option):
|
||||||
msg = ("SAVE_CONFIG section '%s' option '%s' conflicts "
|
msg = ("SAVE_CONFIG section '%s' option '%s' conflicts "
|
||||||
"with included value" % (section, option))
|
"with included value" % (section, option))
|
||||||
raise self.printer.command_error(msg)
|
raise gcode.error(msg)
|
||||||
cmd_SAVE_CONFIG_help = "Overwrite config file and restart"
|
cmd_SAVE_CONFIG_help = "Overwrite config file and restart"
|
||||||
def cmd_SAVE_CONFIG(self, gcmd):
|
def cmd_SAVE_CONFIG(self, gcmd):
|
||||||
if not self.fileconfig.sections():
|
if not self.autosave.fileconfig.sections():
|
||||||
return
|
return
|
||||||
|
gcode = self.printer.lookup_object('gcode')
|
||||||
# Create string containing autosave data
|
# Create string containing autosave data
|
||||||
cfgrdr = ConfigFileReader()
|
autosave_data = self._build_config_string(self.autosave)
|
||||||
autosave_data = cfgrdr.build_config_string(self.fileconfig)
|
|
||||||
lines = [('#*# ' + l).strip()
|
lines = [('#*# ' + l).strip()
|
||||||
for l in autosave_data.split('\n')]
|
for l in autosave_data.split('\n')]
|
||||||
lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip())
|
lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip())
|
||||||
@@ -363,27 +401,16 @@ class ConfigAutoSave:
|
|||||||
# Read in and validate current config file
|
# Read in and validate current config file
|
||||||
cfgname = self.printer.get_start_args()['config_file']
|
cfgname = self.printer.get_start_args()['config_file']
|
||||||
try:
|
try:
|
||||||
data = cfgrdr.read_config_file(cfgname)
|
data = self._read_config_file(cfgname)
|
||||||
except error as e:
|
regular_data, old_autosave_data = self._find_autosave_data(data)
|
||||||
msg = "Unable to read existing config on SAVE_CONFIG"
|
config = self._build_config_wrapper(regular_data, cfgname)
|
||||||
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)
|
|
||||||
except error as e:
|
except error as e:
|
||||||
msg = "Unable to parse existing config on SAVE_CONFIG"
|
msg = "Unable to parse existing config on SAVE_CONFIG"
|
||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise gcmd.error(msg)
|
raise gcode.error(msg)
|
||||||
self._disallow_include_conflicts(regular_fileconfig)
|
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
|
# Determine filenames
|
||||||
datestr = time.strftime("-%Y%m%d_%H%M%S")
|
datestr = time.strftime("-%Y%m%d_%H%M%S")
|
||||||
backup_name = cfgname + datestr
|
backup_name = cfgname + datestr
|
||||||
@@ -403,135 +430,6 @@ class ConfigAutoSave:
|
|||||||
except:
|
except:
|
||||||
msg = "Unable to write config file during SAVE_CONFIG"
|
msg = "Unable to write config file during SAVE_CONFIG"
|
||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise gcmd.error(msg)
|
raise gcode.error(msg)
|
||||||
# Request a restart
|
# Request a restart
|
||||||
gcode = self.printer.lookup_object('gcode')
|
|
||||||
gcode.request_restart('restart')
|
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)
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def hexify(byte_array):
|
|||||||
return "[%s]" % (", ".join([hex(b) for b in byte_array]))
|
return "[%s]" % (", ".join([hex(b) for b in byte_array]))
|
||||||
|
|
||||||
|
|
||||||
class ADS1220:
|
class ADS1220():
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = printer = config.get_printer()
|
self.printer = printer = config.get_printer()
|
||||||
self.name = config.get_name().split()[-1]
|
self.name = config.get_name().split()[-1]
|
||||||
@@ -42,35 +42,8 @@ class ADS1220:
|
|||||||
'660': 660, '1200': 1200, '2000': 2000}
|
'660': 660, '1200': 1200, '2000': 2000}
|
||||||
self.sps_options = self.sps_normal.copy()
|
self.sps_options = self.sps_normal.copy()
|
||||||
self.sps_options.update(self.sps_turbo)
|
self.sps_options.update(self.sps_turbo)
|
||||||
self.sps = config.getchoice('sample_rate', self.sps_options,
|
self.sps = config.getchoice('sps', self.sps_options, default='660')
|
||||||
default='660')
|
|
||||||
self.is_turbo = str(self.sps) in self.sps_turbo
|
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 Setup
|
||||||
spi_speed = 512000 if self.is_turbo else 256000
|
spi_speed = 512000 if self.is_turbo else 256000
|
||||||
self.spi = bus.MCU_SPI_from_config(config, 1, default_speed=spi_speed)
|
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.printer, self._process_batch, self._start_measurements,
|
||||||
self._finish_measurements, UPDATE_INTERVAL)
|
self._finish_measurements, UPDATE_INTERVAL)
|
||||||
# publish raw samples to the socket
|
# publish raw samples to the socket
|
||||||
hdr = {'header': ('time', 'counts', 'value')}
|
|
||||||
self.batch_bulk.add_mux_endpoint("ads1220/dump_ads1220", "sensor",
|
self.batch_bulk.add_mux_endpoint("ads1220/dump_ads1220", "sensor",
|
||||||
self.name, hdr)
|
self.name,
|
||||||
|
{'header': ('time', 'counts')})
|
||||||
# Command Configuration
|
# Command Configuration
|
||||||
mcu.add_config_cmd(
|
mcu.add_config_cmd(
|
||||||
"config_ads1220 oid=%d spi_oid=%d data_ready_pin=%s"
|
"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
|
mode = 0x2 if self.is_turbo else 0x0 # turbo mode
|
||||||
sps_list = self.sps_turbo if self.is_turbo else self.sps_normal
|
sps_list = self.sps_turbo if self.is_turbo else self.sps_normal
|
||||||
data_rate = list(sps_list.keys()).index(str(self.sps))
|
data_rate = list(sps_list.keys()).index(str(self.sps))
|
||||||
reg_values = [(self.mux << 4) | (self.gain << 1) | int(self.pga_bypass),
|
reg_values = [(self.gain << 1),
|
||||||
(data_rate << 5) | (mode << 3) | (continuous << 2),
|
(data_rate << 5) | (mode << 3) | (continuous << 2)]
|
||||||
(self.vref << 6),
|
|
||||||
0x0]
|
|
||||||
self.write_reg(0x0, reg_values)
|
self.write_reg(0x0, reg_values)
|
||||||
# start measurements immediately
|
# start measurements immediately
|
||||||
self.send_command(START_SYNC_CMD)
|
self.send_command(START_SYNC_CMD)
|
||||||
@@ -206,7 +177,7 @@ class ADS1220:
|
|||||||
write_command.extend(register_bytes)
|
write_command.extend(register_bytes)
|
||||||
self.spi.spi_send(write_command)
|
self.spi.spi_send(write_command)
|
||||||
stored_val = self.read_reg(reg, len(register_bytes))
|
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(
|
raise self.printer.command_error(
|
||||||
"Failed to set ADS1220 register [0x%x] to %s: got %s. "
|
"Failed to set ADS1220 register [0x%x] to %s: got %s. "
|
||||||
"This may be a connection problem (e.g. faulty wiring)" % (
|
"This may be a connection problem (e.g. faulty wiring)" % (
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -411,196 +411,6 @@ class HelperTLE5012B:
|
|||||||
parser=lambda x: int(x, 0))
|
parser=lambda x: int(x, 0))
|
||||||
self._write_reg(reg, val)
|
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
|
BYTES_PER_SAMPLE = 3
|
||||||
SAMPLES_PER_BLOCK = bulk_sensor.MAX_BULK_MSG_SIZE // BYTES_PER_SAMPLE
|
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.start_clock = self.time_shift = self.sample_ticks = 0
|
||||||
self.last_sequence = self.last_angle = 0
|
self.last_sequence = self.last_angle = 0
|
||||||
# Sensor type
|
# Sensor type
|
||||||
sensors = { "a1333": HelperA1333,
|
sensors = { "a1333": HelperA1333, "as5047d": HelperAS5047D,
|
||||||
"as5047d": HelperAS5047D,
|
"tle5012b": HelperTLE5012B }
|
||||||
"tle5012b": HelperTLE5012B,
|
|
||||||
"mt6816": HelperMT6816,
|
|
||||||
"mt6826s": HelperMT6826S }
|
|
||||||
sensor_type = config.getchoice('sensor_type', {s: s for s in sensors})
|
sensor_type = config.getchoice('sensor_type', {s: s for s in sensors})
|
||||||
sensor_class = sensors[sensor_type]
|
sensor_class = sensors[sensor_type]
|
||||||
self.spi = bus.MCU_SPI_from_config(config, sensor_class.SPI_MODE,
|
self.spi = bus.MCU_SPI_from_config(config, sensor_class.SPI_MODE,
|
||||||
|
|||||||
@@ -23,27 +23,18 @@ class AxisTwistCompensation:
|
|||||||
self.horizontal_move_z = config.getfloat('horizontal_move_z',
|
self.horizontal_move_z = config.getfloat('horizontal_move_z',
|
||||||
DEFAULT_HORIZONTAL_MOVE_Z)
|
DEFAULT_HORIZONTAL_MOVE_Z)
|
||||||
self.speed = config.getfloat('speed', DEFAULT_SPEED)
|
self.speed = config.getfloat('speed', DEFAULT_SPEED)
|
||||||
self.calibrate_start_x = config.getfloat('calibrate_start_x',
|
self.calibrate_start_x = config.getfloat('calibrate_start_x')
|
||||||
default=None)
|
self.calibrate_end_x = config.getfloat('calibrate_end_x')
|
||||||
self.calibrate_end_x = config.getfloat('calibrate_end_x', default=None)
|
self.calibrate_y = config.getfloat('calibrate_y')
|
||||||
self.calibrate_y = config.getfloat('calibrate_y', default=None)
|
|
||||||
self.z_compensations = config.getlists('z_compensations',
|
self.z_compensations = config.getlists('z_compensations',
|
||||||
default=[], parser=float)
|
default=[], parser=float)
|
||||||
self.compensation_start_x = config.getfloat('compensation_start_x',
|
self.compensation_start_x = config.getfloat('compensation_start_x',
|
||||||
default=None)
|
default=None)
|
||||||
self.compensation_end_x = config.getfloat('compensation_end_x',
|
self.compensation_end_x = config.getfloat('compensation_start_y',
|
||||||
default=None)
|
default=None)
|
||||||
|
|
||||||
self.calibrate_start_y = config.getfloat('calibrate_start_y',
|
self.m = None
|
||||||
default=None)
|
self.b = 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)
|
|
||||||
|
|
||||||
# setup calibrater
|
# setup calibrater
|
||||||
self.calibrater = Calibrater(self, config)
|
self.calibrater = Calibrater(self, config)
|
||||||
@@ -52,46 +43,28 @@ class AxisTwistCompensation:
|
|||||||
self._update_z_compensation_value)
|
self._update_z_compensation_value)
|
||||||
|
|
||||||
def _update_z_compensation_value(self, pos):
|
def _update_z_compensation_value(self, pos):
|
||||||
if self.z_compensations:
|
if not self.z_compensations:
|
||||||
pos[2] += self._get_interpolated_z_compensation(
|
return
|
||||||
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
|
|
||||||
):
|
|
||||||
|
|
||||||
|
x_coord = pos[0]
|
||||||
|
z_compensations = self.z_compensations
|
||||||
sample_count = len(z_compensations)
|
sample_count = len(z_compensations)
|
||||||
spacing = ((comp_end - comp_start)
|
spacing = ((self.calibrate_end_x - self.calibrate_start_x)
|
||||||
/ (sample_count - 1))
|
/ (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 = int(math.floor(interpolate_t))
|
||||||
interpolate_i = bed_mesh.constrain(interpolate_i, 0, sample_count - 2)
|
interpolate_i = bed_mesh.constrain(interpolate_i, 0, sample_count - 2)
|
||||||
interpolate_t -= interpolate_i
|
interpolate_t -= interpolate_i
|
||||||
interpolated_z_compensation = bed_mesh.lerp(
|
interpolated_z_compensation = bed_mesh.lerp(
|
||||||
interpolate_t, z_compensations[interpolate_i],
|
interpolate_t, z_compensations[interpolate_i],
|
||||||
z_compensations[interpolate_i + 1])
|
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:
|
class Calibrater:
|
||||||
def __init__(self, compensation, config):
|
def __init__(self, compensation, config):
|
||||||
@@ -107,14 +80,10 @@ class Calibrater:
|
|||||||
self._handle_connect)
|
self._handle_connect)
|
||||||
self.speed = compensation.speed
|
self.speed = compensation.speed
|
||||||
self.horizontal_move_z = compensation.horizontal_move_z
|
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)
|
compensation.calibrate_y)
|
||||||
self.x_end_point = (compensation.calibrate_end_x,
|
self.end_point = (compensation.calibrate_end_x,
|
||||||
compensation.calibrate_y)
|
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.results = None
|
||||||
self.current_point_index = None
|
self.current_point_index = None
|
||||||
self.gcmd = None
|
self.gcmd = None
|
||||||
@@ -150,88 +119,20 @@ class Calibrater:
|
|||||||
def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd):
|
def cmd_AXIS_TWIST_COMPENSATION_CALIBRATE(self, gcmd):
|
||||||
self.gcmd = gcmd
|
self.gcmd = gcmd
|
||||||
sample_count = gcmd.get_int('SAMPLE_COUNT', DEFAULT_SAMPLE_COUNT)
|
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
|
# check for valid sample_count
|
||||||
if sample_count < 2:
|
if sample_count is None or sample_count < 2:
|
||||||
raise self.gcmd.error(
|
raise self.gcmd.error(
|
||||||
"SAMPLE_COUNT to probe must be at least 2")
|
"SAMPLE_COUNT to probe must be at least 2")
|
||||||
|
|
||||||
# calculate the points to put the probe at, returned as a list of tuples
|
# clear the current config
|
||||||
nozzle_points = []
|
self.compensation.clear_compensations()
|
||||||
|
|
||||||
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.")
|
|
||||||
|
|
||||||
|
# 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(
|
probe_points = self._calculate_probe_points(
|
||||||
nozzle_points, self.probe_x_offset, self.probe_y_offset)
|
nozzle_points, self.probe_x_offset, self.probe_y_offset)
|
||||||
|
|
||||||
@@ -241,155 +142,16 @@ class Calibrater:
|
|||||||
# begin calibration
|
# begin calibration
|
||||||
self.current_point_index = 0
|
self.current_point_index = 0
|
||||||
self.results = []
|
self.results = []
|
||||||
self.current_axis = axis
|
|
||||||
self._calibration(probe_points, nozzle_points, interval_dist)
|
self._calibration(probe_points, nozzle_points, interval_dist)
|
||||||
|
|
||||||
def _calculate_corrections(self, coordinates):
|
def _calculate_nozzle_points(self, sample_count, interval_dist):
|
||||||
# Extracting x, y, and z values from coordinates
|
# calculate the points to put the probe at, returned as a list of tuples
|
||||||
x_coords = [coord[0] for coord in coordinates]
|
nozzle_points = []
|
||||||
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 = []
|
|
||||||
for i in range(sample_count):
|
for i in range(sample_count):
|
||||||
for j in range(sample_count):
|
x = self.start_point[0] + i * interval_dist
|
||||||
if(not flip):
|
y = self.start_point[1]
|
||||||
idx = j
|
nozzle_points.append((x, y))
|
||||||
else:
|
return nozzle_points
|
||||||
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
|
|
||||||
|
|
||||||
def _calculate_probe_points(self, nozzle_points,
|
def _calculate_probe_points(self, nozzle_points,
|
||||||
probe_x_offset, probe_y_offset):
|
probe_x_offset, probe_y_offset):
|
||||||
@@ -476,31 +238,14 @@ class Calibrater:
|
|||||||
configfile = self.printer.lookup_object('configfile')
|
configfile = self.printer.lookup_object('configfile')
|
||||||
values_as_str = ', '.join(["{:.6f}".format(x)
|
values_as_str = ', '.join(["{:.6f}".format(x)
|
||||||
for x in self.results])
|
for x in self.results])
|
||||||
|
configfile.set(self.configname, 'z_compensations', values_as_str)
|
||||||
if(self.current_axis == 'X'):
|
configfile.set(self.configname, 'compensation_start_x',
|
||||||
|
self.start_point[0])
|
||||||
configfile.set(self.configname, 'z_compensations', values_as_str)
|
configfile.set(self.configname, 'compensation_end_x',
|
||||||
configfile.set(self.configname, 'compensation_start_x',
|
self.end_point[0])
|
||||||
self.x_start_point[0])
|
self.compensation.z_compensations = self.results
|
||||||
configfile.set(self.configname, 'compensation_end_x',
|
self.compensation.compensation_start_x = self.start_point[0]
|
||||||
self.x_end_point[0])
|
self.compensation.compensation_end_x = self.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]
|
|
||||||
|
|
||||||
self.gcode.respond_info(
|
self.gcode.respond_info(
|
||||||
"AXIS_TWIST_COMPENSATION state has been saved "
|
"AXIS_TWIST_COMPENSATION state has been saved "
|
||||||
"for the current session. The SAVE_CONFIG command will "
|
"for the current session. The SAVE_CONFIG command will "
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ BMP180_REGS = {
|
|||||||
STATUS_MEASURING = 1 << 3
|
STATUS_MEASURING = 1 << 3
|
||||||
STATUS_IM_UPDATE = 1
|
STATUS_IM_UPDATE = 1
|
||||||
MODE = 1
|
MODE = 1
|
||||||
MODE_PERIODIC = 3
|
|
||||||
RUN_GAS = 1 << 4
|
RUN_GAS = 1 << 4
|
||||||
NB_CONV_0 = 0
|
NB_CONV_0 = 0
|
||||||
EAS_NEW_DATA = 1 << 7
|
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_temp - 1), pow(2, self.os_hum - 1),
|
||||||
pow(2, self.os_pres - 1)))
|
pow(2, self.os_pres - 1)))
|
||||||
logging.info("BMxx80: IIR: %dx" % (pow(2, self.iir_filter) - 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.temp = self.pressure = self.humidity = self.gas = self.t_fine = 0.
|
||||||
self.min_temp = self.max_temp = self.range_switching_error = 0.
|
self.min_temp = self.max_temp = self.range_switching_error = 0.
|
||||||
@@ -157,7 +155,6 @@ class BME280:
|
|||||||
return
|
return
|
||||||
self.printer.register_event_handler("klippy:connect",
|
self.printer.register_event_handler("klippy:connect",
|
||||||
self.handle_connect)
|
self.handle_connect)
|
||||||
self.last_gas_time = 0
|
|
||||||
|
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
self._init_bmxx80()
|
self._init_bmxx80()
|
||||||
@@ -284,7 +281,7 @@ class BME280:
|
|||||||
self.chip_type, self.i2c.i2c_address))
|
self.chip_type, self.i2c.i2c_address))
|
||||||
|
|
||||||
# Reset chip
|
# 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)
|
self.reactor.pause(self.reactor.monotonic() + .5)
|
||||||
|
|
||||||
# Make sure non-volatile memory has been copied to registers
|
# Make sure non-volatile memory has been copied to registers
|
||||||
@@ -296,15 +293,15 @@ class BME280:
|
|||||||
status = self.read_register('STATUS', 1)[0]
|
status = self.read_register('STATUS', 1)[0]
|
||||||
|
|
||||||
if self.chip_type == 'BME680':
|
if self.chip_type == 'BME680':
|
||||||
self.max_sample_time = \
|
self.max_sample_time = 0.5
|
||||||
(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_bme680)
|
self.sample_timer = self.reactor.register_timer(self._sample_bme680)
|
||||||
self.chip_registers = BME680_REGS
|
self.chip_registers = BME680_REGS
|
||||||
elif self.chip_type == 'BMP180':
|
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.sample_timer = self.reactor.register_timer(self._sample_bmp180)
|
||||||
self.chip_registers = BMP180_REGS
|
self.chip_registers = BMP180_REGS
|
||||||
elif self.chip_type == 'BMP388':
|
elif self.chip_type == 'BMP388':
|
||||||
|
self.max_sample_time = 0.5
|
||||||
self.chip_registers = BMP388_REGS
|
self.chip_registers = BMP388_REGS
|
||||||
self.write_register(
|
self.write_register(
|
||||||
"PWR_CTRL",
|
"PWR_CTRL",
|
||||||
@@ -321,18 +318,15 @@ class BME280:
|
|||||||
self.write_register("INT_CTRL", [BMP388_REG_VAL_DRDY_EN])
|
self.write_register("INT_CTRL", [BMP388_REG_VAL_DRDY_EN])
|
||||||
|
|
||||||
self.sample_timer = self.reactor.register_timer(self._sample_bmp388)
|
self.sample_timer = self.reactor.register_timer(self._sample_bmp388)
|
||||||
elif self.chip_type == 'BME280':
|
else:
|
||||||
self.max_sample_time = \
|
self.max_sample_time = \
|
||||||
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
|
(1.25 + (2.3 * self.os_temp) + ((2.3 * self.os_pres) + .575)
|
||||||
+ ((2.3 * self.os_hum) + .575)) / 1000
|
+ ((2.3 * self.os_hum) + .575)) / 1000
|
||||||
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
|
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
|
||||||
self.chip_registers = BME280_REGS
|
self.chip_registers = BME280_REGS
|
||||||
else:
|
|
||||||
self.max_sample_time = \
|
if self.chip_type in ('BME680', 'BME280'):
|
||||||
(1.25 + (2.3 * self.os_temp)
|
self.write_register('CONFIG', (self.iir_filter & 0x07) << 2)
|
||||||
+ ((2.3 * self.os_pres) + .575)) / 1000
|
|
||||||
self.sample_timer = self.reactor.register_timer(self._sample_bme280)
|
|
||||||
self.chip_registers = BME280_REGS
|
|
||||||
|
|
||||||
# Read out and calculate the trimming parameters
|
# Read out and calculate the trimming parameters
|
||||||
if self.chip_type == 'BMP180':
|
if self.chip_type == 'BMP180':
|
||||||
@@ -353,64 +347,21 @@ class BME280:
|
|||||||
elif self.chip_type == 'BMP388':
|
elif self.chip_type == 'BMP388':
|
||||||
self.dig = read_calibration_data_bmp388(cal_1)
|
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):
|
def _sample_bme280(self, eventtime):
|
||||||
# In normal mode data shadowing is performed
|
# Enter forced mode
|
||||||
# So reading can be done while measurements are in process
|
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:
|
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':
|
if self.chip_type == 'BME280':
|
||||||
data = self.read_register('PRESSURE_MSB', 8)
|
data = self.read_register('PRESSURE_MSB', 8)
|
||||||
elif self.chip_type == 'BMP280':
|
elif self.chip_type == 'BMP280':
|
||||||
@@ -511,40 +462,36 @@ class BME280:
|
|||||||
return comp_press
|
return comp_press
|
||||||
|
|
||||||
def _sample_bme680(self, eventtime):
|
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)
|
new_data = (stat & EAS_NEW_DATA)
|
||||||
gas_done = not (stat & GAS_DONE)
|
gas_done = not (stat & GAS_DONE)
|
||||||
meas_done = not (stat & MEASURE_DONE)
|
meas_done = not (stat & MEASURE_DONE)
|
||||||
if not run_gas:
|
|
||||||
gas_done = True
|
|
||||||
return new_data and gas_done and meas_done
|
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
|
# Enter forced mode
|
||||||
meas = self.os_temp << 5 | self.os_pres << 2 | MODE
|
meas = meas | MODE
|
||||||
self.write_register('CTRL_MEAS', meas, wait=True)
|
self.write_register('CTRL_MEAS', meas)
|
||||||
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)
|
|
||||||
try:
|
try:
|
||||||
# wait until results are ready
|
# wait until results are ready
|
||||||
status = self.read_register('EAS_STATUS_0', 1)[0]
|
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.pause(
|
||||||
self.reactor.monotonic() + self.max_sample_time)
|
self.reactor.monotonic() + self.max_sample_time)
|
||||||
status = self.read_register('EAS_STATUS_0', 1)[0]
|
status = self.read_register('EAS_STATUS_0', 1)[0]
|
||||||
|
|
||||||
data = self.read_register('PRESSURE_MSB', 8)
|
data = self.read_register('PRESSURE_MSB', 8)
|
||||||
gas_data = [0, 0]
|
gas_data = self.read_register('GAS_R_MSB', 2)
|
||||||
if run_gas:
|
|
||||||
gas_data = self.read_register('GAS_R_MSB', 2)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("BME680: Error reading data")
|
logging.exception("BME680: Error reading data")
|
||||||
self.temp = self.pressure = self.humidity = self.gas = .0
|
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_raw = (gas_data[0] << 2) | ((gas_data[1] & 0xC0) >> 6)
|
||||||
gas_range = (gas_data[1] & 0x0F)
|
gas_range = (gas_data[1] & 0x0F)
|
||||||
self.gas = self._compensate_gas(gas_raw, gas_range)
|
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:
|
if self.temp < self.min_temp or self.temp > self.max_temp:
|
||||||
self.printer.invoke_shutdown(
|
self.printer.invoke_shutdown(
|
||||||
@@ -700,7 +643,7 @@ class BME280:
|
|||||||
gas_raw - 512. + var1)
|
gas_raw - 512. + var1)
|
||||||
return gas
|
return gas
|
||||||
|
|
||||||
def _calc_gas_heater_resistance(self, target_temp):
|
def _calculate_gas_heater_resistance(self, target_temp):
|
||||||
amb_temp = self.temp
|
amb_temp = self.temp
|
||||||
heater_data = self.read_register('RES_HEAT_VAL', 3)
|
heater_data = self.read_register('RES_HEAT_VAL', 3)
|
||||||
res_heat_val = get_signed_byte(heater_data[0])
|
res_heat_val = get_signed_byte(heater_data[0])
|
||||||
@@ -715,7 +658,7 @@ class BME280:
|
|||||||
* (1. / (1. + (res_heat_val * 0.002)))) - 25))
|
* (1. / (1. + (res_heat_val * 0.002)))) - 25))
|
||||||
return int(res_heat)
|
return int(res_heat)
|
||||||
|
|
||||||
def _calc_gas_heater_duration(self, duration_ms):
|
def _calculate_gas_heater_duration(self, duration_ms):
|
||||||
if duration_ms >= 4032:
|
if duration_ms >= 4032:
|
||||||
duration_reg = 0xff
|
duration_reg = 0xff
|
||||||
else:
|
else:
|
||||||
@@ -776,15 +719,12 @@ class BME280:
|
|||||||
params = self.i2c.i2c_read(regs, read_len)
|
params = self.i2c.i2c_read(regs, read_len)
|
||||||
return bytearray(params['response'])
|
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:
|
if type(data) is not list:
|
||||||
data = [data]
|
data = [data]
|
||||||
reg = self.chip_registers[reg_name]
|
reg = self.chip_registers[reg_name]
|
||||||
data.insert(0, reg)
|
data.insert(0, reg)
|
||||||
if not wait:
|
self.i2c.i2c_write(data)
|
||||||
self.i2c.i2c_write(data)
|
|
||||||
else:
|
|
||||||
self.i2c.i2c_write_wait_ack(data)
|
|
||||||
|
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
data = {
|
data = {
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class MCU_I2C:
|
|||||||
% (self.oid, speed, addr))
|
% (self.oid, speed, addr))
|
||||||
self.cmd_queue = self.mcu.alloc_command_queue()
|
self.cmd_queue = self.mcu.alloc_command_queue()
|
||||||
self.mcu.register_config_callback(self.build_config)
|
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):
|
def get_oid(self):
|
||||||
return self.oid
|
return self.oid
|
||||||
def get_mcu(self):
|
def get_mcu(self):
|
||||||
@@ -180,6 +180,9 @@ class MCU_I2C:
|
|||||||
"i2c_read oid=%c reg=%*s read_len=%u",
|
"i2c_read oid=%c reg=%*s read_len=%u",
|
||||||
"i2c_read_response oid=%c response=%*s", oid=self.oid,
|
"i2c_read_response oid=%c response=%*s", oid=self.oid,
|
||||||
cq=self.cmd_queue)
|
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):
|
def i2c_write(self, data, minclock=0, reqclock=0):
|
||||||
if self.i2c_write_cmd is None:
|
if self.i2c_write_cmd is None:
|
||||||
# Send setup message via mcu initialization
|
# Send setup message via mcu initialization
|
||||||
@@ -194,6 +197,19 @@ class MCU_I2C:
|
|||||||
minclock=minclock, reqclock=reqclock)
|
minclock=minclock, reqclock=reqclock)
|
||||||
def i2c_read(self, write, read_len):
|
def i2c_read(self, write, read_len):
|
||||||
return self.i2c_read_cmd.send([self.oid, 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):
|
def MCU_I2C_from_config(config, default_addr=None, default_speed=100000):
|
||||||
# Load bus parameters
|
# Load bus parameters
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ class ControllerFan:
|
|||||||
self.last_on += 1
|
self.last_on += 1
|
||||||
if speed != self.last_speed:
|
if speed != self.last_speed:
|
||||||
self.last_speed = 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.
|
return eventtime + 1.
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging, os, ast
|
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
|
# Normal time between each screen redraw
|
||||||
REDRAW_TIME = 0.500
|
REDRAW_TIME = 0.500
|
||||||
@@ -17,8 +17,7 @@ LCD_chips = {
|
|||||||
'st7920': st7920.ST7920, 'emulated_st7920': st7920.EmulatedST7920,
|
'st7920': st7920.ST7920, 'emulated_st7920': st7920.EmulatedST7920,
|
||||||
'hd44780': hd44780.HD44780, 'uc1701': uc1701.UC1701,
|
'hd44780': hd44780.HD44780, 'uc1701': uc1701.UC1701,
|
||||||
'ssd1306': uc1701.SSD1306, 'sh1106': uc1701.SH1106,
|
'ssd1306': uc1701.SSD1306, 'sh1106': uc1701.SH1106,
|
||||||
'hd44780_spi': hd44780_spi.hd44780_spi,
|
'hd44780_spi': hd44780_spi.hd44780_spi
|
||||||
'aip31068_spi':aip31068_spi.aip31068_spi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Storage of [display_template my_template] config sections
|
# Storage of [display_template my_template] config sections
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Support for "dotstar" leds
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
from . import bus, led
|
from . import bus
|
||||||
|
|
||||||
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
||||||
|
|
||||||
@@ -22,8 +22,9 @@ class PrinterDotstar:
|
|||||||
self.spi = bus.MCU_SPI(mcu, None, None, 0, 500000, sw_spi_pins)
|
self.spi = bus.MCU_SPI(mcu, None, None, 0, 500000, sw_spi_pins)
|
||||||
# Initialize color data
|
# Initialize color data
|
||||||
self.chain_count = config.getint('chain_count', 1, minval=1)
|
self.chain_count = config.getint('chain_count', 1, minval=1)
|
||||||
self.led_helper = led.LEDHelper(config, self.update_leds,
|
pled = printer.load_object(config, "led")
|
||||||
self.chain_count)
|
self.led_helper = pled.setup_helper(config, self.update_leds,
|
||||||
|
self.chain_count)
|
||||||
self.prev_data = None
|
self.prev_data = None
|
||||||
# Register commands
|
# Register commands
|
||||||
printer.register_event_handler("klippy:connect", self.handle_connect)
|
printer.register_event_handler("klippy:connect", self.handle_connect)
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
# Printer cooling fan
|
# 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.
|
# 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:
|
class Fan:
|
||||||
def __init__(self, config, default_shutdown_speed=0.):
|
def __init__(self, config, default_shutdown_speed=0.):
|
||||||
self.printer = config.get_printer()
|
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
|
# Read config
|
||||||
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
|
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
|
||||||
self.kick_start_time = config.getfloat('kick_start_time', 0.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 = ppins.setup_pin('digital_out', enable_pin)
|
||||||
self.enable_pin.setup_max_duration(0.)
|
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
|
# Setup tachometer
|
||||||
self.tachometer = FanTachometer(config)
|
self.tachometer = FanTachometer(config)
|
||||||
|
|
||||||
@@ -46,37 +45,37 @@ class Fan:
|
|||||||
|
|
||||||
def get_mcu(self):
|
def get_mcu(self):
|
||||||
return self.mcu_fan.get_mcu()
|
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:
|
if value < self.off_below:
|
||||||
value = 0.
|
value = 0.
|
||||||
value = max(0., min(self.max_power, value * self.max_power))
|
value = max(0., min(self.max_power, value * self.max_power))
|
||||||
if value == self.last_fan_value:
|
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 self.enable_pin:
|
||||||
if value > 0 and self.last_fan_value == 0:
|
if value > 0 and self.last_fan_value == 0:
|
||||||
self.enable_pin.set_digital(print_time, 1)
|
self.enable_pin.set_digital(print_time, 1)
|
||||||
elif value == 0 and self.last_fan_value > 0:
|
elif value == 0 and self.last_fan_value > 0:
|
||||||
self.enable_pin.set_digital(print_time, 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)):
|
and (not self.last_fan_value or value - self.last_fan_value > .5)):
|
||||||
# Run fan at full speed for specified kick_start_time
|
# 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)
|
self.mcu_fan.set_pwm(print_time, self.max_power)
|
||||||
return "delay", self.kick_start_time
|
print_time += self.kick_start_time
|
||||||
self.last_fan_value = self.last_req_value = value
|
|
||||||
self.mcu_fan.set_pwm(print_time, value)
|
self.mcu_fan.set_pwm(print_time, value)
|
||||||
def set_speed(self, value, print_time=None):
|
self.last_fan_time = print_time
|
||||||
self.gcrq.send_async_request(value, print_time)
|
self.last_fan_value = value
|
||||||
def set_speed_from_command(self, 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):
|
def _handle_request_restart(self, print_time):
|
||||||
self.set_speed(0., print_time)
|
self.set_speed(print_time, 0.)
|
||||||
|
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
tachometer_status = self.tachometer.get_status(eventtime)
|
tachometer_status = self.tachometer.get_status(eventtime)
|
||||||
return {
|
return {
|
||||||
'speed': self.last_req_value,
|
'speed': self.last_fan_value,
|
||||||
'rpm': tachometer_status['rpm'],
|
'rpm': tachometer_status['rpm'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
# Support fans that are controlled by gcode
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging
|
from . import fan
|
||||||
from . import fan, output_pin
|
|
||||||
|
|
||||||
class PrinterFanGeneric:
|
class PrinterFanGeneric:
|
||||||
cmd_SET_FAN_SPEED_help = "Sets the speed of a fan"
|
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 = fan.Fan(config, default_shutdown_speed=0.)
|
||||||
self.fan_name = config.get_name().split()[-1]
|
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 = self.printer.lookup_object("gcode")
|
||||||
gcode.register_mux_command("SET_FAN_SPEED", "FAN",
|
gcode.register_mux_command("SET_FAN_SPEED", "FAN",
|
||||||
self.fan_name,
|
self.fan_name,
|
||||||
@@ -24,21 +20,8 @@ class PrinterFanGeneric:
|
|||||||
|
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return self.fan.get_status(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):
|
def cmd_SET_FAN_SPEED(self, gcmd):
|
||||||
speed = gcmd.get_float('SPEED', None, 0.)
|
speed = gcmd.get_float('SPEED', 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
|
|
||||||
self.fan.set_speed_from_command(speed)
|
self.fan.set_speed_from_command(speed)
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
|
|||||||
@@ -131,12 +131,8 @@ class ForceMove:
|
|||||||
x = gcmd.get_float('X', curpos[0])
|
x = gcmd.get_float('X', curpos[0])
|
||||||
y = gcmd.get_float('Y', curpos[1])
|
y = gcmd.get_float('Y', curpos[1])
|
||||||
z = gcmd.get_float('Z', curpos[2])
|
z = gcmd.get_float('Z', curpos[2])
|
||||||
clear = gcmd.get('CLEAR', '').lower()
|
logging.info("SET_KINEMATIC_POSITION pos=%.3f,%.3f,%.3f", x, y, z)
|
||||||
clear_axes = "".join([a for a in "xyz" if a in clear])
|
toolhead.set_position([x, y, z, curpos[3]], homing_axes=(0, 1, 2))
|
||||||
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)
|
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return ForceMove(config)
|
return ForceMove(config)
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ class ArcSupport:
|
|||||||
self.gcode.register_command("G18", self.cmd_G18)
|
self.gcode.register_command("G18", self.cmd_G18)
|
||||||
self.gcode.register_command("G19", self.cmd_G19)
|
self.gcode.register_command("G19", self.cmd_G19)
|
||||||
|
|
||||||
|
self.Coord = self.gcode.Coord
|
||||||
|
|
||||||
# backwards compatibility, prior implementation only supported XY
|
# backwards compatibility, prior implementation only supported XY
|
||||||
self.plane = ARC_PLANE_X_Y
|
self.plane = ARC_PLANE_X_Y
|
||||||
|
|
||||||
@@ -62,36 +64,52 @@ class ArcSupport:
|
|||||||
if not gcodestatus['absolute_coordinates']:
|
if not gcodestatus['absolute_coordinates']:
|
||||||
raise gcmd.error("G2/G3 does not support relative move mode")
|
raise gcmd.error("G2/G3 does not support relative move mode")
|
||||||
currentPos = gcodestatus['gcode_position']
|
currentPos = gcodestatus['gcode_position']
|
||||||
absolut_extrude = gcodestatus['absolute_extrude']
|
|
||||||
|
|
||||||
# Parse parameters
|
# Parse parameters
|
||||||
asTarget = [gcmd.get_float("X", currentPos[0]),
|
asTarget = self.Coord(x=gcmd.get_float("X", currentPos[0]),
|
||||||
gcmd.get_float("Y", currentPos[1]),
|
y=gcmd.get_float("Y", currentPos[1]),
|
||||||
gcmd.get_float("Z", currentPos[2])]
|
z=gcmd.get_float("Z", currentPos[2]),
|
||||||
|
e=None)
|
||||||
|
|
||||||
if gcmd.get_float("R", None) is not None:
|
if gcmd.get_float("R", None) is not None:
|
||||||
raise gcmd.error("G2/G3 does not support R moves")
|
raise gcmd.error("G2/G3 does not support R moves")
|
||||||
|
|
||||||
# determine the plane coordinates and the helical axis
|
# determine the plane coordinates and the helical axis
|
||||||
I = gcmd.get_float('I', 0.)
|
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('IJ') ]
|
||||||
J = gcmd.get_float('J', 0.)
|
|
||||||
asPlanar = (I, J)
|
|
||||||
axes = (X_AXIS, Y_AXIS, Z_AXIS)
|
axes = (X_AXIS, Y_AXIS, Z_AXIS)
|
||||||
if self.plane == ARC_PLANE_X_Z:
|
if self.plane == ARC_PLANE_X_Z:
|
||||||
K = gcmd.get_float('K', 0.)
|
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('IK') ]
|
||||||
asPlanar = (I, K)
|
|
||||||
axes = (X_AXIS, Z_AXIS, Y_AXIS)
|
axes = (X_AXIS, Z_AXIS, Y_AXIS)
|
||||||
elif self.plane == ARC_PLANE_Y_Z:
|
elif self.plane == ARC_PLANE_Y_Z:
|
||||||
K = gcmd.get_float('K', 0.)
|
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('JK') ]
|
||||||
asPlanar = (J, K)
|
|
||||||
axes = (Y_AXIS, Z_AXIS, X_AXIS)
|
axes = (Y_AXIS, Z_AXIS, X_AXIS)
|
||||||
|
|
||||||
if not (asPlanar[0] or asPlanar[1]):
|
if not (asPlanar[0] or asPlanar[1]):
|
||||||
raise gcmd.error("G2/G3 requires IJ, IK or JK parameters")
|
raise gcmd.error("G2/G3 requires IJ, IK or JK parameters")
|
||||||
|
|
||||||
# Build linear coordinates to move
|
asE = gcmd.get_float("E", None)
|
||||||
self.planArc(currentPos, asTarget, asPlanar, clockwise,
|
asF = gcmd.get_float("F", None)
|
||||||
gcmd, absolut_extrude, *axes)
|
|
||||||
|
# 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()
|
# function planArc() originates from marlin plan_arc()
|
||||||
# https://github.com/MarlinFirmware/Marlin
|
# https://github.com/MarlinFirmware/Marlin
|
||||||
@@ -102,7 +120,6 @@ class ArcSupport:
|
|||||||
#
|
#
|
||||||
# alpha and beta axes are the current plane, helical axis is linear travel
|
# alpha and beta axes are the current plane, helical axis is linear travel
|
||||||
def planArc(self, currentPos, targetPos, offset, clockwise,
|
def planArc(self, currentPos, targetPos, offset, clockwise,
|
||||||
gcmd, absolut_extrude,
|
|
||||||
alpha_axis, beta_axis, helical_axis):
|
alpha_axis, beta_axis, helical_axis):
|
||||||
# todo: sometimes produces full circles
|
# todo: sometimes produces full circles
|
||||||
|
|
||||||
@@ -142,42 +159,23 @@ class ArcSupport:
|
|||||||
# Generate coordinates
|
# Generate coordinates
|
||||||
theta_per_segment = angular_travel / segments
|
theta_per_segment = angular_travel / segments
|
||||||
linear_per_segment = linear_travel / segments
|
linear_per_segment = linear_travel / segments
|
||||||
|
coords = []
|
||||||
asE = gcmd.get_float("E", None)
|
for i in range(1, int(segments)):
|
||||||
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):
|
|
||||||
dist_Helical = i * linear_per_segment
|
dist_Helical = i * linear_per_segment
|
||||||
c_theta = i * theta_per_segment
|
cos_Ti = math.cos(i * theta_per_segment)
|
||||||
cos_Ti = math.cos(c_theta)
|
sin_Ti = math.sin(i * theta_per_segment)
|
||||||
sin_Ti = math.sin(c_theta)
|
|
||||||
r_P = -offset[0] * cos_Ti + offset[1] * sin_Ti
|
r_P = -offset[0] * cos_Ti + offset[1] * sin_Ti
|
||||||
r_Q = -offset[0] * sin_Ti - offset[1] * cos_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[alpha_axis] = center_P + r_P
|
||||||
c[beta_axis] = center_Q + r_Q
|
c[beta_axis] = center_Q + r_Q
|
||||||
c[helical_axis] = currentPos[helical_axis] + dist_Helical
|
c[helical_axis] = currentPos[helical_axis] + dist_Helical
|
||||||
|
coords.append(self.Coord(*c))
|
||||||
|
|
||||||
|
coords.append(targetPos)
|
||||||
if i == segments:
|
return coords
|
||||||
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)
|
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return ArcSupport(config)
|
return ArcSupport(config)
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ class PrinterHeaterFan:
|
|||||||
speed = self.fan_speed
|
speed = self.fan_speed
|
||||||
if speed != self.last_speed:
|
if speed != self.last_speed:
|
||||||
self.last_speed = 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.
|
return eventtime + 1.
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Tracking of PWM controlled heaters and their temperature control
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import os, logging, threading
|
import os, logging, threading
|
||||||
@@ -14,7 +14,6 @@ KELVIN_TO_CELSIUS = -273.15
|
|||||||
MAX_HEAT_TIME = 5.0
|
MAX_HEAT_TIME = 5.0
|
||||||
AMBIENT_TEMP = 25.
|
AMBIENT_TEMP = 25.
|
||||||
PID_PARAM_BASE = 255.
|
PID_PARAM_BASE = 255.
|
||||||
MAX_MAINTHREAD_TIME = 5.0
|
|
||||||
|
|
||||||
class Heater:
|
class Heater:
|
||||||
def __init__(self, config, sensor):
|
def __init__(self, config, sensor):
|
||||||
@@ -38,7 +37,8 @@ class Heater:
|
|||||||
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
|
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
|
||||||
self.smooth_time = config.getfloat('smooth_time', 1., above=0.)
|
self.smooth_time = config.getfloat('smooth_time', 1., above=0.)
|
||||||
self.inv_smooth_time = 1. / self.smooth_time
|
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.lock = threading.Lock()
|
||||||
self.last_temp = self.smoothed_temp = self.target_temp = 0.
|
self.last_temp = self.smoothed_temp = self.target_temp = 0.
|
||||||
self.last_temp_time = 0.
|
self.last_temp_time = 0.
|
||||||
@@ -64,10 +64,13 @@ class Heater:
|
|||||||
gcode.register_mux_command("SET_HEATER_TEMPERATURE", "HEATER",
|
gcode.register_mux_command("SET_HEATER_TEMPERATURE", "HEATER",
|
||||||
short_name, self.cmd_SET_HEATER_TEMPERATURE,
|
short_name, self.cmd_SET_HEATER_TEMPERATURE,
|
||||||
desc=self.cmd_SET_HEATER_TEMPERATURE_help)
|
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.printer.register_event_handler("klippy:shutdown",
|
||||||
self._handle_shutdown)
|
self._handle_shutdown)
|
||||||
def set_pwm(self, read_time, value):
|
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.
|
value = 0.
|
||||||
if ((read_time < self.next_pwm_time or not self.last_pwm_value)
|
if ((read_time < self.next_pwm_time or not self.last_pwm_value)
|
||||||
and abs(value - self.last_pwm_value) < 0.05):
|
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)
|
self.can_extrude = (self.smoothed_temp >= self.min_extrude_temp)
|
||||||
#logging.debug("temp: %.3f %f = %f", read_time, temp)
|
#logging.debug("temp: %.3f %f = %f", read_time, temp)
|
||||||
def _handle_shutdown(self):
|
def _handle_shutdown(self):
|
||||||
self.verify_mainthread_time = -999.
|
self.is_shutdown = True
|
||||||
# External commands
|
# External commands
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self.name
|
return self.name
|
||||||
@@ -107,6 +110,7 @@ class Heater:
|
|||||||
raise self.printer.command_error(
|
raise self.printer.command_error(
|
||||||
"Requested temperature (%.1f) out of range (%.1f:%.1f)"
|
"Requested temperature (%.1f) out of range (%.1f:%.1f)"
|
||||||
% (degrees, self.min_temp, self.max_temp))
|
% (degrees, self.min_temp, self.max_temp))
|
||||||
|
self.set_temp_count += 1
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.target_temp = degrees
|
self.target_temp = degrees
|
||||||
def get_temp(self, eventtime):
|
def get_temp(self, eventtime):
|
||||||
@@ -130,9 +134,6 @@ class Heater:
|
|||||||
target_temp = max(self.min_temp, min(self.max_temp, target_temp))
|
target_temp = max(self.min_temp, min(self.max_temp, target_temp))
|
||||||
self.target_temp = target_temp
|
self.target_temp = target_temp
|
||||||
def stats(self, eventtime):
|
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:
|
with self.lock:
|
||||||
target_temp = self.target_temp
|
target_temp = self.target_temp
|
||||||
last_temp = self.last_temp
|
last_temp = self.last_temp
|
||||||
@@ -147,11 +148,17 @@ class Heater:
|
|||||||
last_pwm_value = self.last_pwm_value
|
last_pwm_value = self.last_pwm_value
|
||||||
return {'temperature': round(smoothed_temp, 2), 'target': target_temp,
|
return {'temperature': round(smoothed_temp, 2), 'target': target_temp,
|
||||||
'power': last_pwm_value}
|
'power': last_pwm_value}
|
||||||
|
def get_set_temp_count(self):
|
||||||
|
return self.set_temp_count
|
||||||
cmd_SET_HEATER_TEMPERATURE_help = "Sets a heater temperature"
|
cmd_SET_HEATER_TEMPERATURE_help = "Sets a heater temperature"
|
||||||
def cmd_SET_HEATER_TEMPERATURE(self, gcmd):
|
def cmd_SET_HEATER_TEMPERATURE(self, gcmd):
|
||||||
temp = gcmd.get_float('TARGET', 0.)
|
temp = gcmd.get_float('TARGET', 0.)
|
||||||
pheaters = self.printer.lookup_object('heaters')
|
pheaters = self.printer.lookup_object('heaters')
|
||||||
pheaters.set_temperature(self, temp)
|
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_heaters = []
|
||||||
self.available_sensors = []
|
self.available_sensors = []
|
||||||
self.available_monitors = []
|
self.available_monitors = []
|
||||||
|
self.in_temperature_wait = None
|
||||||
self.has_started = self.have_load_sensors = False
|
self.has_started = self.have_load_sensors = False
|
||||||
self.printer.register_event_handler("klippy:ready", self._handle_ready)
|
self.printer.register_event_handler("klippy:ready", self._handle_ready)
|
||||||
self.printer.register_event_handler("gcode:request_restart",
|
self.printer.register_event_handler("gcode:request_restart",
|
||||||
@@ -263,8 +271,7 @@ class PrinterHeaters:
|
|||||||
try:
|
try:
|
||||||
dconfig = pconfig.read_config(filename)
|
dconfig = pconfig.read_config(filename)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("Unable to load temperature_sensors.cfg")
|
raise config.config_error("Cannot load config '%s'" % (filename,))
|
||||||
raise config.error("Cannot load config '%s'" % (filename,))
|
|
||||||
for c in dconfig.get_prefix_sections(''):
|
for c in dconfig.get_prefix_sections(''):
|
||||||
self.printer.load_object(dconfig, c.get_name())
|
self.printer.load_object(dconfig, c.get_name())
|
||||||
def add_sensor_factory(self, sensor_type, sensor_factory):
|
def add_sensor_factory(self, sensor_type, sensor_factory):
|
||||||
@@ -310,7 +317,8 @@ class PrinterHeaters:
|
|||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return {'available_heaters': self.available_heaters,
|
return {'available_heaters': self.available_heaters,
|
||||||
'available_sensors': self.available_sensors,
|
'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.):
|
def turn_off_all_heaters(self, print_time=0.):
|
||||||
for heater in self.heaters.values():
|
for heater in self.heaters.values():
|
||||||
heater.set_temp(0.)
|
heater.set_temp(0.)
|
||||||
@@ -341,14 +349,23 @@ class PrinterHeaters:
|
|||||||
# Helper to wait on heater.check_busy() and report M105 temperatures
|
# Helper to wait on heater.check_busy() and report M105 temperatures
|
||||||
if self.printer.get_start_args().get('debugoutput') is not None:
|
if self.printer.get_start_args().get('debugoutput') is not None:
|
||||||
return
|
return
|
||||||
|
full_name = heater.get_name()
|
||||||
|
set_temp_count = heater.get_set_temp_count()
|
||||||
toolhead = self.printer.lookup_object("toolhead")
|
toolhead = self.printer.lookup_object("toolhead")
|
||||||
gcode = self.printer.lookup_object("gcode")
|
gcode = self.printer.lookup_object("gcode")
|
||||||
reactor = self.printer.get_reactor()
|
reactor = self.printer.get_reactor()
|
||||||
eventtime = reactor.monotonic()
|
eventtime = reactor.monotonic()
|
||||||
while not self.printer.is_shutdown() and heater.check_busy(eventtime):
|
while not self.printer.is_shutdown() and heater.check_busy(eventtime):
|
||||||
|
self.in_temperature_wait = full_name
|
||||||
print_time = toolhead.get_last_move_time()
|
print_time = toolhead.get_last_move_time()
|
||||||
gcode.respond_raw(self._get_temp(eventtime))
|
gcode.respond_raw(self._get_temp(eventtime))
|
||||||
eventtime = reactor.pause(eventtime + 1.)
|
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):
|
def set_temperature(self, heater, temp, wait=False):
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
toolhead.register_lookahead_callback((lambda pt: None))
|
toolhead.register_lookahead_callback((lambda pt: None))
|
||||||
@@ -367,8 +384,12 @@ class PrinterHeaters:
|
|||||||
"Error on 'TEMPERATURE_WAIT': missing MINIMUM or MAXIMUM.")
|
"Error on 'TEMPERATURE_WAIT': missing MINIMUM or MAXIMUM.")
|
||||||
if self.printer.get_start_args().get('debugoutput') is not None:
|
if self.printer.get_start_args().get('debugoutput') is not None:
|
||||||
return
|
return
|
||||||
|
full_name = sensor_name
|
||||||
|
set_temp_count = None
|
||||||
if sensor_name in self.heaters:
|
if sensor_name in self.heaters:
|
||||||
sensor = self.heaters[sensor_name]
|
sensor = self.heaters[sensor_name]
|
||||||
|
full_name = sensor.get_name()
|
||||||
|
set_temp_count = sensor.get_set_temp_count()
|
||||||
else:
|
else:
|
||||||
sensor = self.printer.lookup_object(sensor_name)
|
sensor = self.printer.lookup_object(sensor_name)
|
||||||
toolhead = self.printer.lookup_object("toolhead")
|
toolhead = self.printer.lookup_object("toolhead")
|
||||||
@@ -377,10 +398,18 @@ class PrinterHeaters:
|
|||||||
while not self.printer.is_shutdown():
|
while not self.printer.is_shutdown():
|
||||||
temp, target = sensor.get_temp(eventtime)
|
temp, target = sensor.get_temp(eventtime)
|
||||||
if temp >= min_temp and temp <= max_temp:
|
if temp >= min_temp and temp <= max_temp:
|
||||||
return
|
break
|
||||||
|
self.in_temperature_wait = full_name
|
||||||
print_time = toolhead.get_last_move_time()
|
print_time = toolhead.get_last_move_time()
|
||||||
gcmd.respond_raw(self._get_temp(eventtime))
|
gcmd.respond_raw(self._get_temp(eventtime))
|
||||||
eventtime = reactor.pause(eventtime + 1.)
|
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):
|
def load_config(config):
|
||||||
return PrinterHeaters(config)
|
return PrinterHeaters(config)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Helper code for implementing homing operations
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging, math
|
import logging, math
|
||||||
@@ -29,17 +29,10 @@ class StepperPosition:
|
|||||||
self.endstop_name = endstop_name
|
self.endstop_name = endstop_name
|
||||||
self.stepper_name = stepper.get_name()
|
self.stepper_name = stepper.get_name()
|
||||||
self.start_pos = stepper.get_mcu_position()
|
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
|
self.halt_pos = self.trig_pos = None
|
||||||
def note_home_end(self, trigger_time):
|
def note_home_end(self, trigger_time):
|
||||||
self.halt_pos = self.stepper.get_mcu_position()
|
self.halt_pos = self.stepper.get_mcu_position()
|
||||||
self.trig_pos = self.stepper.get_past_mcu_position(trigger_time)
|
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
|
# Implementation of homing/probing moves
|
||||||
class HomingMove:
|
class HomingMove:
|
||||||
@@ -128,9 +121,6 @@ class HomingMove:
|
|||||||
haltpos = trigpos = self.calc_toolhead_pos(kin_spos, trig_steps)
|
haltpos = trigpos = self.calc_toolhead_pos(kin_spos, trig_steps)
|
||||||
if trig_steps != halt_steps:
|
if trig_steps != halt_steps:
|
||||||
haltpos = self.calc_toolhead_pos(kin_spos, 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:
|
else:
|
||||||
haltpos = trigpos = movepos
|
haltpos = trigpos = movepos
|
||||||
over_steps = {sp.stepper_name: sp.halt_pos - sp.trig_pos
|
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()
|
halt_kin_spos = {s.get_name(): s.get_commanded_position()
|
||||||
for s in kin.get_steppers()}
|
for s in kin.get_steppers()}
|
||||||
haltpos = self.calc_toolhead_pos(halt_kin_spos, over_steps)
|
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
|
# Signal homing/probing move complete
|
||||||
try:
|
try:
|
||||||
self.printer.send_event("homing:homing_move_end", self)
|
self.printer.send_event("homing:homing_move_end", self)
|
||||||
@@ -187,8 +177,7 @@ class Homing:
|
|||||||
# Notify of upcoming homing operation
|
# Notify of upcoming homing operation
|
||||||
self.printer.send_event("homing:home_rails_begin", self, rails)
|
self.printer.send_event("homing:home_rails_begin", self, rails)
|
||||||
# Alter kinematics class to think printer is at forcepos
|
# 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 = [axis for axis in range(3) if forcepos[axis] is not None]
|
||||||
homing_axes = "".join(["xyz"[i] for i in force_axes])
|
|
||||||
startpos = self._fill_coord(forcepos)
|
startpos = self._fill_coord(forcepos)
|
||||||
homepos = self._fill_coord(movepos)
|
homepos = self._fill_coord(movepos)
|
||||||
self.toolhead.set_position(startpos, homing_axes=homing_axes)
|
self.toolhead.set_position(startpos, homing_axes=homing_axes)
|
||||||
@@ -232,7 +221,7 @@ class Homing:
|
|||||||
+ self.adjust_pos.get(s.get_name(), 0.))
|
+ self.adjust_pos.get(s.get_name(), 0.))
|
||||||
for s in kin.get_steppers()}
|
for s in kin.get_steppers()}
|
||||||
newpos = kin.calc_position(kin_spos)
|
newpos = kin.calc_position(kin_spos)
|
||||||
for axis in force_axes:
|
for axis in homing_axes:
|
||||||
homepos[axis] = newpos[axis]
|
homepos[axis] = newpos[axis]
|
||||||
self.toolhead.set_position(homepos)
|
self.toolhead.set_position(homepos)
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ class HomingOverride:
|
|||||||
# Calculate forced position (if configured)
|
# Calculate forced position (if configured)
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
pos = toolhead.get_position()
|
pos = toolhead.get_position()
|
||||||
homing_axes = ""
|
homing_axes = []
|
||||||
for axis, loc in enumerate(self.start_pos):
|
for axis, loc in enumerate(self.start_pos):
|
||||||
if loc is not None:
|
if loc is not None:
|
||||||
pos[axis] = loc
|
pos[axis] = loc
|
||||||
homing_axes += "xyz"[axis]
|
homing_axes.append(axis)
|
||||||
toolhead.set_position(pos, homing_axes=homing_axes)
|
toolhead.set_position(pos, homing_axes=homing_axes)
|
||||||
# Perform homing
|
# Perform homing
|
||||||
context = self.template.create_template_context()
|
context = self.template.create_template_context()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ SAMPLE_ERROR_DESYNC = -0x80000000
|
|||||||
SAMPLE_ERROR_LONG_READ = 0x40000000
|
SAMPLE_ERROR_LONG_READ = 0x40000000
|
||||||
|
|
||||||
# Implementation of HX711 and HX717
|
# Implementation of HX711 and HX717
|
||||||
class HX71xBase:
|
class HX71xBase():
|
||||||
def __init__(self, config, sensor_type,
|
def __init__(self, config, sensor_type,
|
||||||
sample_rate_options, default_sample_rate,
|
sample_rate_options, default_sample_rate,
|
||||||
gain_options, default_gain):
|
gain_options, default_gain):
|
||||||
@@ -53,8 +53,8 @@ class HX71xBase:
|
|||||||
self._finish_measurements, UPDATE_INTERVAL)
|
self._finish_measurements, UPDATE_INTERVAL)
|
||||||
# publish raw samples to the socket
|
# publish raw samples to the socket
|
||||||
dump_path = "%s/dump_%s" % (sensor_type, sensor_type)
|
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,
|
||||||
self.batch_bulk.add_mux_endpoint(dump_path, "sensor", self.name, hdr)
|
{'header': ('time', 'counts')})
|
||||||
# Command Configuration
|
# Command Configuration
|
||||||
self.query_hx71x_cmd = None
|
self.query_hx71x_cmd = None
|
||||||
mcu.add_config_cmd(
|
mcu.add_config_cmd(
|
||||||
@@ -145,21 +145,23 @@ class HX71xBase:
|
|||||||
'overflows': self.ffreader.get_last_overflows()}
|
'overflows': self.ffreader.get_last_overflows()}
|
||||||
|
|
||||||
|
|
||||||
def HX711(config):
|
class HX711(HX71xBase):
|
||||||
return HX71xBase(config, "hx711",
|
def __init__(self, config):
|
||||||
# HX711 sps options
|
super(HX711, self).__init__(config, "hx711",
|
||||||
{80: 80, 10: 10}, 80,
|
# HX711 sps options
|
||||||
# HX711 gain/channel options
|
{80: 80, 10: 10}, 80,
|
||||||
{'A-128': 1, 'B-32': 2, 'A-64': 3}, 'A-128')
|
# HX711 gain/channel options
|
||||||
|
{'A-128': 1, 'B-32': 2, 'A-64': 3}, 'A-128')
|
||||||
|
|
||||||
|
|
||||||
def HX717(config):
|
class HX717(HX71xBase):
|
||||||
return HX71xBase(config, "hx717",
|
def __init__(self, config):
|
||||||
# HX717 sps options
|
super(HX717, self).__init__(config, "hx717",
|
||||||
{320: 320, 80: 80, 20: 20, 10: 10}, 320,
|
# HX717 sps options
|
||||||
# HX717 gain/channel options
|
{320: 320, 80: 80, 20: 20, 10: 10}, 320,
|
||||||
{'A-128': 1, 'B-64': 2, 'A-64': 3,
|
# HX717 gain/channel options
|
||||||
'B-8': 4}, 'A-128')
|
{'A-128': 1, 'B-64': 2, 'A-64': 3,
|
||||||
|
'B-8': 4}, 'A-128')
|
||||||
|
|
||||||
|
|
||||||
HX71X_SENSOR_TYPES = {
|
HX71X_SENSOR_TYPES = {
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
# Support for PWM driven LEDs
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging
|
import logging, ast
|
||||||
from . import output_pin
|
from .display import display
|
||||||
|
|
||||||
|
# Time between each led template update
|
||||||
|
RENDER_TIME = 0.500
|
||||||
|
|
||||||
# Helper code for common LED initialization and control
|
# Helper code for common LED initialization and control
|
||||||
class LEDHelper:
|
class LEDHelper:
|
||||||
@@ -19,22 +22,14 @@ class LEDHelper:
|
|||||||
blue = config.getfloat('initial_BLUE', 0., minval=0., maxval=1.)
|
blue = config.getfloat('initial_BLUE', 0., minval=0., maxval=1.)
|
||||||
white = config.getfloat('initial_WHITE', 0., minval=0., maxval=1.)
|
white = config.getfloat('initial_WHITE', 0., minval=0., maxval=1.)
|
||||||
self.led_state = [(red, green, blue, white)] * led_count
|
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
|
# Register commands
|
||||||
name = config.get_name().split()[-1]
|
name = config.get_name().split()[-1]
|
||||||
gcode = self.printer.lookup_object('gcode')
|
gcode = self.printer.lookup_object('gcode')
|
||||||
gcode.register_mux_command("SET_LED", "LED", name, self.cmd_SET_LED,
|
gcode.register_mux_command("SET_LED", "LED", name, self.cmd_SET_LED,
|
||||||
desc=self.cmd_SET_LED_help)
|
desc=self.cmd_SET_LED_help)
|
||||||
gcode.register_mux_command("SET_LED_TEMPLATE", "LED", name,
|
def get_led_count(self):
|
||||||
self.cmd_SET_LED_TEMPLATE,
|
return self.led_count
|
||||||
desc=self.cmd_SET_LED_TEMPLATE_help)
|
def set_color(self, index, color):
|
||||||
def get_status(self, eventtime=None):
|
|
||||||
return {'color_data': self.led_state}
|
|
||||||
def _set_color(self, index, color):
|
|
||||||
if index is None:
|
if index is None:
|
||||||
new_led_state = [color] * self.led_count
|
new_led_state = [color] * self.led_count
|
||||||
if self.led_state == new_led_state:
|
if self.led_state == new_led_state:
|
||||||
@@ -46,17 +41,7 @@ class LEDHelper:
|
|||||||
new_led_state[index - 1] = color
|
new_led_state[index - 1] = color
|
||||||
self.led_state = new_led_state
|
self.led_state = new_led_state
|
||||||
self.need_transmit = True
|
self.need_transmit = True
|
||||||
def _template_update(self, index, text):
|
def check_transmit(self, print_time):
|
||||||
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):
|
|
||||||
if not self.need_transmit:
|
if not self.need_transmit:
|
||||||
return
|
return
|
||||||
self.need_transmit = False
|
self.need_transmit = False
|
||||||
@@ -77,9 +62,9 @@ class LEDHelper:
|
|||||||
color = (red, green, blue, white)
|
color = (red, green, blue, white)
|
||||||
# Update and transmit data
|
# Update and transmit data
|
||||||
def lookahead_bgfunc(print_time):
|
def lookahead_bgfunc(print_time):
|
||||||
self._set_color(index, color)
|
self.set_color(index, color)
|
||||||
if transmit:
|
if transmit:
|
||||||
self._check_transmit(print_time)
|
self.check_transmit(print_time)
|
||||||
if sync:
|
if sync:
|
||||||
#Sync LED Update with print time and send
|
#Sync LED Update with print time and send
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
@@ -87,15 +72,112 @@ class LEDHelper:
|
|||||||
else:
|
else:
|
||||||
#Send update now (so as not to wake toolhead and reset idle_timeout)
|
#Send update now (so as not to wake toolhead and reset idle_timeout)
|
||||||
lookahead_bgfunc(None)
|
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"
|
cmd_SET_LED_TEMPLATE_help = "Assign a display_template to an LED"
|
||||||
def cmd_SET_LED_TEMPLATE(self, gcmd):
|
def cmd_SET_LED_TEMPLATE(self, gcmd):
|
||||||
index = gcmd.get_int("INDEX", None, minval=1, maxval=self.led_count)
|
led_name = gcmd.get("LED")
|
||||||
set_template = self.template_eval.set_template
|
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:
|
if index is not None:
|
||||||
set_template(gcmd, self.tcallbacks[index-1], self._check_transmit)
|
self._activate_template(led_helper, index, template, lparams)
|
||||||
else:
|
else:
|
||||||
for i in range(self.led_count):
|
for i in range(led_count):
|
||||||
set_template(gcmd, self.tcallbacks[i], self._check_transmit)
|
self._activate_template(led_helper, i+1, template, lparams)
|
||||||
|
self._activate_timer()
|
||||||
|
|
||||||
PIN_MIN_TIME = 0.100
|
PIN_MIN_TIME = 0.100
|
||||||
MAX_SCHEDULE_TIME = 5.0
|
MAX_SCHEDULE_TIME = 5.0
|
||||||
@@ -123,7 +205,8 @@ class PrinterPWMLED:
|
|||||||
% (config.get_name(),))
|
% (config.get_name(),))
|
||||||
self.last_print_time = 0.
|
self.last_print_time = 0.
|
||||||
# Initialize color data
|
# 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]
|
self.prev_color = color = self.led_helper.get_status()['color_data'][0]
|
||||||
for idx, mcu_pin in self.pins:
|
for idx, mcu_pin in self.pins:
|
||||||
mcu_pin.setup_start_value(color[idx], 0.)
|
mcu_pin.setup_start_value(color[idx], 0.)
|
||||||
@@ -142,5 +225,8 @@ class PrinterPWMLED:
|
|||||||
def get_status(self, eventtime=None):
|
def get_status(self, eventtime=None):
|
||||||
return self.led_helper.get_status(eventtime)
|
return self.led_helper.get_status(eventtime)
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return PrinterLED(config)
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
return PrinterPWMLED(config)
|
return PrinterPWMLED(config)
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ REG_LIS2DW_WHO_AM_I_ADDR = 0x0F
|
|||||||
REG_LIS2DW_CTRL_REG1_ADDR = 0x20
|
REG_LIS2DW_CTRL_REG1_ADDR = 0x20
|
||||||
REG_LIS2DW_CTRL_REG2_ADDR = 0x21
|
REG_LIS2DW_CTRL_REG2_ADDR = 0x21
|
||||||
REG_LIS2DW_CTRL_REG3_ADDR = 0x22
|
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_CTRL_REG6_ADDR = 0x25
|
||||||
REG_LIS2DW_STATUS_REG_ADDR = 0x27
|
REG_LIS2DW_STATUS_REG_ADDR = 0x27
|
||||||
REG_LIS2DW_OUT_XL_ADDR = 0x28
|
REG_LIS2DW_OUT_XL_ADDR = 0x28
|
||||||
@@ -28,57 +26,26 @@ REG_MOD_READ = 0x80
|
|||||||
# REG_MOD_MULTI = 0x40
|
# REG_MOD_MULTI = 0x40
|
||||||
|
|
||||||
LIS2DW_DEV_ID = 0x44
|
LIS2DW_DEV_ID = 0x44
|
||||||
LIS3DH_DEV_ID = 0x33
|
|
||||||
|
|
||||||
LIS_I2C_ADDR = 0x19
|
|
||||||
|
|
||||||
# Right shift for left justified registers.
|
|
||||||
FREEFALL_ACCEL = 9.80665
|
FREEFALL_ACCEL = 9.80665
|
||||||
LIS2DW_SCALE = FREEFALL_ACCEL * 1.952 / 4
|
SCALE = FREEFALL_ACCEL * 1.952 / 4
|
||||||
LIS3DH_SCALE = FREEFALL_ACCEL * 3.906 / 16
|
|
||||||
|
|
||||||
BATCH_UPDATES = 0.100
|
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
|
# Printer class that controls LIS2DW chip
|
||||||
class LIS2DW:
|
class LIS2DW:
|
||||||
def __init__(self, config, lis_type):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
adxl345.AccelCommandHelper(config, self)
|
adxl345.AccelCommandHelper(config, self)
|
||||||
self.lis_type = lis_type
|
self.axes_map = adxl345.read_axes_map(config, SCALE, SCALE, SCALE)
|
||||||
if self.lis_type == LIS2DW_TYPE:
|
self.data_rate = 1600
|
||||||
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
|
|
||||||
# Setup mcu sensor_lis2dw bulk query code
|
# Setup mcu sensor_lis2dw bulk query code
|
||||||
if self.bus_type == SPI_SERIAL_TYPE:
|
self.spi = bus.MCU_SPI_from_config(config, 3, default_speed=5000000)
|
||||||
self.bus = bus.MCU_SPI_from_config(config,
|
self.mcu = mcu = self.spi.get_mcu()
|
||||||
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.oid = oid = mcu.create_oid()
|
self.oid = oid = mcu.create_oid()
|
||||||
self.query_lis2dw_cmd = None
|
self.query_lis2dw_cmd = None
|
||||||
mcu.add_config_cmd("config_lis2dw oid=%d bus_oid=%d bus_oid_type=%s "
|
mcu.add_config_cmd("config_lis2dw oid=%d spi_oid=%d"
|
||||||
"lis_chip_type=%s" % (oid, self.bus.get_oid(),
|
% (oid, self.spi.get_oid()))
|
||||||
self.bus_type, self.lis_type))
|
|
||||||
mcu.add_config_cmd("query_lis2dw oid=%d rest_ticks=0"
|
mcu.add_config_cmd("query_lis2dw oid=%d rest_ticks=0"
|
||||||
% (oid,), on_restart=True)
|
% (oid,), on_restart=True)
|
||||||
mcu.register_config_callback(self._build_config)
|
mcu.register_config_callback(self._build_config)
|
||||||
@@ -96,23 +63,17 @@ class LIS2DW:
|
|||||||
self.name, {'header': hdr})
|
self.name, {'header': hdr})
|
||||||
|
|
||||||
def _build_config(self):
|
def _build_config(self):
|
||||||
cmdqueue = self.bus.get_command_queue()
|
cmdqueue = self.spi.get_command_queue()
|
||||||
self.query_lis2dw_cmd = self.mcu.lookup_command(
|
self.query_lis2dw_cmd = self.mcu.lookup_command(
|
||||||
"query_lis2dw oid=%c rest_ticks=%u", cq=cmdqueue)
|
"query_lis2dw oid=%c rest_ticks=%u", cq=cmdqueue)
|
||||||
self.ffreader.setup_query_command("query_lis2dw_status oid=%c",
|
self.ffreader.setup_query_command("query_lis2dw_status oid=%c",
|
||||||
oid=self.oid, cq=cmdqueue)
|
oid=self.oid, cq=cmdqueue)
|
||||||
def read_reg(self, reg):
|
def read_reg(self, reg):
|
||||||
if self.bus_type == SPI_SERIAL_TYPE:
|
params = self.spi.spi_transfer([reg | REG_MOD_READ, 0x00])
|
||||||
params = self.bus.spi_transfer([reg | REG_MOD_READ, 0x00])
|
response = bytearray(params['response'])
|
||||||
response = bytearray(params['response'])
|
return response[1]
|
||||||
return response[1]
|
|
||||||
params = self.bus.i2c_read([reg], 1)
|
|
||||||
return bytearray(params['response'])[0]
|
|
||||||
def set_reg(self, reg, val, minclock=0):
|
def set_reg(self, reg, val, minclock=0):
|
||||||
if self.bus_type == SPI_SERIAL_TYPE:
|
self.spi.spi_send([reg, val & 0xFF], minclock=minclock)
|
||||||
self.bus.spi_send([reg, val & 0xFF], minclock=minclock)
|
|
||||||
else:
|
|
||||||
self.bus.i2c_write([reg, val & 0xFF], minclock=minclock)
|
|
||||||
stored_val = self.read_reg(reg)
|
stored_val = self.read_reg(reg)
|
||||||
if stored_val != val:
|
if stored_val != val:
|
||||||
raise self.printer.command_error(
|
raise self.printer.command_error(
|
||||||
@@ -141,48 +102,26 @@ class LIS2DW:
|
|||||||
# noise or wrong signal as a correctly initialized device
|
# noise or wrong signal as a correctly initialized device
|
||||||
dev_id = self.read_reg(REG_LIS2DW_WHO_AM_I_ADDR)
|
dev_id = self.read_reg(REG_LIS2DW_WHO_AM_I_ADDR)
|
||||||
logging.info("lis2dw_dev_id: %x", dev_id)
|
logging.info("lis2dw_dev_id: %x", dev_id)
|
||||||
if self.lis_type == LIS2DW_TYPE:
|
if dev_id != LIS2DW_DEV_ID:
|
||||||
if dev_id != LIS2DW_DEV_ID:
|
raise self.printer.command_error(
|
||||||
raise self.printer.command_error(
|
"Invalid lis2dw id (got %x vs %x).\n"
|
||||||
"Invalid lis2dw id (got %x vs %x).\n"
|
"This is generally indicative of connection problems\n"
|
||||||
"This is generally indicative of connection problems\n"
|
"(e.g. faulty wiring) or a faulty lis2dw chip."
|
||||||
"(e.g. faulty wiring) or a faulty lis2dw chip."
|
% (dev_id, LIS2DW_DEV_ID))
|
||||||
% (dev_id, LIS2DW_DEV_ID))
|
# Setup chip in requested query rate
|
||||||
# Setup chip in requested query rate
|
# ODR/2, +-16g, low-pass filter, Low-noise abled
|
||||||
# ODR/2, +-16g, low-pass filter, Low-noise abled
|
self.set_reg(REG_LIS2DW_CTRL_REG6_ADDR, 0x34)
|
||||||
self.set_reg(REG_LIS2DW_CTRL_REG6_ADDR, 0x34)
|
# Continuous mode: If the FIFO is full
|
||||||
# Continuous mode: If the FIFO is full
|
# the new sample overwrites the older sample.
|
||||||
# the new sample overwrites the older sample.
|
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0xC0)
|
||||||
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0xC0)
|
# High-Performance / Low-Power mode 1600/200 Hz
|
||||||
# High-Performance / Low-Power mode 1600/200 Hz
|
# High-Performance Mode (14-bit resolution)
|
||||||
# High-Performance Mode (14-bit resolution)
|
self.set_reg(REG_LIS2DW_CTRL_REG1_ADDR, 0x94)
|
||||||
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)
|
|
||||||
# Start bulk reading
|
# Start bulk reading
|
||||||
rest_ticks = self.mcu.seconds_to_clock(4. / self.data_rate)
|
rest_ticks = self.mcu.seconds_to_clock(4. / self.data_rate)
|
||||||
self.query_lis2dw_cmd.send([self.oid, rest_ticks])
|
self.query_lis2dw_cmd.send([self.oid, rest_ticks])
|
||||||
if self.lis_type == LIS2DW_TYPE:
|
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0xC0)
|
||||||
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0xC0)
|
|
||||||
else:
|
|
||||||
self.set_reg(REG_LIS2DW_FIFO_CTRL, 0x80)
|
|
||||||
logging.info("LIS2DW starting '%s' measurements", self.name)
|
logging.info("LIS2DW starting '%s' measurements", self.name)
|
||||||
# Initialize clock tracking
|
# Initialize clock tracking
|
||||||
self.ffreader.note_start()
|
self.ffreader.note_start()
|
||||||
@@ -203,7 +142,7 @@ class LIS2DW:
|
|||||||
'overflows': self.ffreader.get_last_overflows()}
|
'overflows': self.ffreader.get_last_overflows()}
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return LIS2DW(config, LIS2DW_TYPE)
|
return LIS2DW(config)
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
return LIS2DW(config, LIS2DW_TYPE)
|
return LIS2DW(config)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -109,7 +109,7 @@ class ManualStepper:
|
|||||||
self.sync_print_time()
|
self.sync_print_time()
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
return [self.rail.get_commanded_position(), 0., 0., 0.]
|
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])
|
self.do_set_position(newpos[0])
|
||||||
def get_last_move_time(self):
|
def get_last_move_time(self):
|
||||||
self.sync_print_time()
|
self.sync_print_time()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging
|
import logging
|
||||||
from . import led
|
|
||||||
|
|
||||||
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
||||||
|
|
||||||
@@ -41,7 +40,9 @@ class PrinterNeoPixel:
|
|||||||
if len(self.color_map) > MAX_MCU_SIZE:
|
if len(self.color_map) > MAX_MCU_SIZE:
|
||||||
raise config.error("neopixel chain too long")
|
raise config.error("neopixel chain too long")
|
||||||
# Initialize color data
|
# 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.color_data = bytearray(len(self.color_map))
|
||||||
self.update_color_data(self.led_helper.get_status()['color_data'])
|
self.update_color_data(self.led_helper.get_status()['color_data'])
|
||||||
self.old_color_data = bytearray([d ^ 1 for d in self.color_data])
|
self.old_color_data = bytearray([d ^ 1 for d in self.color_data])
|
||||||
|
|||||||
@@ -3,180 +3,9 @@
|
|||||||
# Copyright (C) 2017-2024 Kevin O'Connor <kevin@koconnor.net>
|
# Copyright (C) 2017-2024 Kevin O'Connor <kevin@koconnor.net>
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# 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
|
PIN_MIN_TIME = 0.100
|
||||||
|
RESEND_HOST_TIME = 0.300 + PIN_MIN_TIME
|
||||||
# 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
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
MAX_SCHEDULE_TIME = 5.0
|
MAX_SCHEDULE_TIME = 5.0
|
||||||
|
|
||||||
class PrinterOutputPin:
|
class PrinterOutputPin:
|
||||||
@@ -195,18 +24,30 @@ class PrinterOutputPin:
|
|||||||
else:
|
else:
|
||||||
self.mcu_pin = ppins.setup_pin('digital_out', config.get('pin'))
|
self.mcu_pin = ppins.setup_pin('digital_out', config.get('pin'))
|
||||||
self.scale = 1.
|
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
|
# Determine start and shutdown values
|
||||||
self.last_value = config.getfloat(
|
static_value = config.getfloat('static_value', None,
|
||||||
'value', 0., minval=0., maxval=self.scale) / self.scale
|
minval=0., maxval=self.scale)
|
||||||
self.shutdown_value = config.getfloat(
|
if static_value is not None:
|
||||||
'shutdown_value', 0., minval=0., maxval=self.scale) / self.scale
|
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)
|
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
|
# Register commands
|
||||||
pin_name = config.get_name().split()[1]
|
pin_name = config.get_name().split()[1]
|
||||||
gcode = self.printer.lookup_object('gcode')
|
gcode = self.printer.lookup_object('gcode')
|
||||||
@@ -215,36 +56,45 @@ class PrinterOutputPin:
|
|||||||
desc=self.cmd_SET_PIN_help)
|
desc=self.cmd_SET_PIN_help)
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return {'value': self.last_value}
|
return {'value': self.last_value}
|
||||||
def _set_pin(self, print_time, value):
|
def _set_pin(self, print_time, value, is_resend=False):
|
||||||
if value == self.last_value:
|
if value == self.last_value and not is_resend:
|
||||||
return "discard", 0.
|
return
|
||||||
self.last_value = value
|
print_time = max(print_time, self.last_print_time + PIN_MIN_TIME)
|
||||||
if self.is_pwm:
|
if self.is_pwm:
|
||||||
self.mcu_pin.set_pwm(print_time, value)
|
self.mcu_pin.set_pwm(print_time, value)
|
||||||
else:
|
else:
|
||||||
self.mcu_pin.set_digital(print_time, value)
|
self.mcu_pin.set_digital(print_time, value)
|
||||||
def _template_update(self, text):
|
self.last_value = value
|
||||||
try:
|
self.last_print_time = print_time
|
||||||
value = float(text)
|
if self.resend_interval and self.resend_timer is None:
|
||||||
except ValueError as e:
|
self.resend_timer = self.reactor.register_timer(
|
||||||
logging.exception("output_pin template render error")
|
self._resend_current_val, self.reactor.NOW)
|
||||||
self.gcrq.send_async_request(value)
|
|
||||||
cmd_SET_PIN_help = "Set the value of an output pin"
|
cmd_SET_PIN_help = "Set the value of an output pin"
|
||||||
def cmd_SET_PIN(self, gcmd):
|
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
|
# Read requested value
|
||||||
|
value = gcmd.get_float('VALUE', minval=0., maxval=self.scale)
|
||||||
value /= self.scale
|
value /= self.scale
|
||||||
if not self.is_pwm and value not in [0., 1.]:
|
if not self.is_pwm and value not in [0., 1.]:
|
||||||
raise gcmd.error("Invalid pin value")
|
raise gcmd.error("Invalid pin value")
|
||||||
# Queue requested value
|
# Obtain print_time and apply requested settings
|
||||||
self.gcrq.queue_gcode_request(value)
|
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):
|
def load_config_prefix(config):
|
||||||
return PrinterOutputPin(config)
|
return PrinterOutputPin(config)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging
|
import logging
|
||||||
from . import bus, led
|
from . import bus
|
||||||
|
|
||||||
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
||||||
|
|
||||||
@@ -16,7 +16,8 @@ class PCA9533:
|
|||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.i2c = bus.MCU_I2C_from_config(config, default_addr=98)
|
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_PWM0, 85])
|
||||||
self.i2c.i2c_write([PCA9533_PWM1, 170])
|
self.i2c.i2c_write([PCA9533_PWM1, 170])
|
||||||
self.update_leds(self.led_helper.get_status()['color_data'], None)
|
self.update_leds(self.led_helper.get_status()['color_data'], None)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Copyright (C) 2022 Ricardo Alcantara <ricardo@vulcanolabs.com>
|
# Copyright (C) 2022 Ricardo Alcantara <ricardo@vulcanolabs.com>
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# 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
|
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
||||||
|
|
||||||
@@ -34,7 +34,8 @@ class PCA9632:
|
|||||||
raise config.error("Invalid color_order '%s'" % (color_order,))
|
raise config.error("Invalid color_order '%s'" % (color_order,))
|
||||||
self.color_map = ["RGBW".index(c) for c in color_order]
|
self.color_map = ["RGBW".index(c) for c in color_order]
|
||||||
self.prev_regs = {}
|
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)
|
printer.register_event_handler("klippy:connect", self.handle_connect)
|
||||||
def reg_write(self, reg, val, minclock=0):
|
def reg_write(self, reg, val, minclock=0):
|
||||||
if self.prev_regs.get(reg) == val:
|
if self.prev_regs.get(reg) == val:
|
||||||
|
|||||||
@@ -45,96 +45,40 @@ def _parse_axis(gcmd, raw_axis):
|
|||||||
"Unable to parse axis direction '%s'" % (raw_axis,))
|
"Unable to parse axis direction '%s'" % (raw_axis,))
|
||||||
return TestAxis(vib_dir=(dir_x, dir_y))
|
return TestAxis(vib_dir=(dir_x, dir_y))
|
||||||
|
|
||||||
class VibrationPulseTestGenerator:
|
class VibrationPulseTest:
|
||||||
def __init__(self, config):
|
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.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.)
|
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.,
|
self.hz_per_sec = config.getfloat('hz_per_sec', 1.,
|
||||||
minval=0.1, maxval=2.)
|
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):
|
def prepare_test(self, gcmd):
|
||||||
self.freq_start = gcmd.get_float("FREQ_START", self.min_freq, minval=1.)
|
self.freq_start = gcmd.get_float("FREQ_START", self.min_freq, minval=1.)
|
||||||
self.freq_end = gcmd.get_float("FREQ_END", self.max_freq,
|
self.freq_end = gcmd.get_float("FREQ_END", self.max_freq,
|
||||||
minval=self.freq_start, maxval=300.)
|
minval=self.freq_start, maxval=300.)
|
||||||
self.test_accel_per_hz = gcmd.get_float("ACCEL_PER_HZ",
|
self.hz_per_sec = gcmd.get_float("HZ_PER_SEC", self.hz_per_sec,
|
||||||
self.accel_per_hz, above=0.)
|
above=0., maxval=2.)
|
||||||
self.test_hz_per_sec = gcmd.get_float("HZ_PER_SEC", self.hz_per_sec,
|
def run_test(self, axis, gcmd):
|
||||||
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()
|
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
X, Y, Z, E = toolhead.get_position()
|
X, Y, Z, E = toolhead.get_position()
|
||||||
|
sign = 1.
|
||||||
|
freq = self.freq_start
|
||||||
# Override maximum acceleration and acceleration to
|
# Override maximum acceleration and acceleration to
|
||||||
# deceleration based on the maximum test frequency
|
# deceleration based on the maximum test frequency
|
||||||
systime = reactor.monotonic()
|
systime = self.printer.get_reactor().monotonic()
|
||||||
toolhead_info = toolhead.get_status(systime)
|
toolhead_info = toolhead.get_status(systime)
|
||||||
old_max_accel = toolhead_info['max_accel']
|
old_max_accel = toolhead_info['max_accel']
|
||||||
old_minimum_cruise_ratio = toolhead_info['minimum_cruise_ratio']
|
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(
|
self.gcode.run_script_from_command(
|
||||||
"SET_VELOCITY_LIMIT ACCEL=%.3f MINIMUM_CRUISE_RATIO=0"
|
"SET_VELOCITY_LIMIT ACCEL=%.3f MINIMUM_CRUISE_RATIO=0"
|
||||||
% (max_accel,))
|
% (max_accel,))
|
||||||
@@ -144,46 +88,24 @@ class ResonanceTestExecutor:
|
|||||||
gcmd.respond_info("Disabled [input_shaper] for resonance testing")
|
gcmd.respond_info("Disabled [input_shaper] for resonance testing")
|
||||||
else:
|
else:
|
||||||
input_shaper = None
|
input_shaper = None
|
||||||
last_v = last_t = last_accel = last_freq = 0.
|
gcmd.respond_info("Testing frequency %.0f Hz" % (freq,))
|
||||||
for next_t, accel, freq in test_seq:
|
while freq <= self.freq_end + 0.000001:
|
||||||
t_seg = next_t - last_t
|
t_seg = .25 / freq
|
||||||
|
accel = self.accel_per_hz * freq
|
||||||
|
max_v = accel * t_seg
|
||||||
toolhead.cmd_M204(self.gcode.create_gcode_command(
|
toolhead.cmd_M204(self.gcode.create_gcode_command(
|
||||||
"M204", "M204", {"S": abs(accel)}))
|
"M204", "M204", {"S": accel}))
|
||||||
v = last_v + accel * t_seg
|
L = .5 * accel * t_seg**2
|
||||||
abs_v = abs(v)
|
dX, dY = axis.get_point(L)
|
||||||
if abs_v < 0.000001:
|
nX = X + sign * dX
|
||||||
v = abs_v = 0.
|
nY = Y + sign * dY
|
||||||
abs_last_v = abs(last_v)
|
toolhead.move([nX, nY, Z, E], max_v)
|
||||||
v2 = v * v
|
toolhead.move([X, Y, Z, E], max_v)
|
||||||
last_v2 = last_v * last_v
|
sign = -sign
|
||||||
half_inv_accel = .5 / accel
|
old_freq = freq
|
||||||
d = (v2 - last_v2) * half_inv_accel
|
freq += 2. * t_seg * self.hz_per_sec
|
||||||
dX, dY = axis.get_point(d)
|
if math.floor(freq) > math.floor(old_freq):
|
||||||
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):
|
|
||||||
gcmd.respond_info("Testing frequency %.0f Hz" % (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
|
# Restore the original acceleration values
|
||||||
self.gcode.run_script_from_command(
|
self.gcode.run_script_from_command(
|
||||||
"SET_VELOCITY_LIMIT ACCEL=%.3f MINIMUM_CRUISE_RATIO=%.3f"
|
"SET_VELOCITY_LIMIT ACCEL=%.3f MINIMUM_CRUISE_RATIO=%.3f"
|
||||||
@@ -192,13 +114,14 @@ class ResonanceTestExecutor:
|
|||||||
if input_shaper is not None:
|
if input_shaper is not None:
|
||||||
input_shaper.enable_shaping()
|
input_shaper.enable_shaping()
|
||||||
gcmd.respond_info("Re-enabled [input_shaper]")
|
gcmd.respond_info("Re-enabled [input_shaper]")
|
||||||
|
def get_max_freq(self):
|
||||||
|
return self.freq_end
|
||||||
|
|
||||||
class ResonanceTester:
|
class ResonanceTester:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.move_speed = config.getfloat('move_speed', 50., above=0.)
|
self.move_speed = config.getfloat('move_speed', 50., above=0.)
|
||||||
self.generator = SweepingVibrationsTestGenerator(config)
|
self.test = VibrationPulseTest(config)
|
||||||
self.executor = ResonanceTestExecutor(config)
|
|
||||||
if not config.get('accel_chip_x', None):
|
if not config.get('accel_chip_x', None):
|
||||||
self.accel_chip_names = [('xy', config.get('accel_chip').strip())]
|
self.accel_chip_names = [('xy', config.get('accel_chip').strip())]
|
||||||
else:
|
else:
|
||||||
@@ -208,8 +131,6 @@ class ResonanceTester:
|
|||||||
if self.accel_chip_names[0][1] == self.accel_chip_names[1][1]:
|
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.accel_chip_names = [('xy', self.accel_chip_names[0][1])]
|
||||||
self.max_smoothing = config.getfloat('max_smoothing', None, minval=0.05)
|
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 = self.printer.lookup_object('gcode')
|
||||||
self.gcode.register_command("MEASURE_AXES_NOISE",
|
self.gcode.register_command("MEASURE_AXES_NOISE",
|
||||||
@@ -233,9 +154,12 @@ class ResonanceTester:
|
|||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
calibration_data = {axis: None for axis in axes}
|
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:
|
for point in test_points:
|
||||||
toolhead.manual_move(point, self.move_speed)
|
toolhead.manual_move(point, self.move_speed)
|
||||||
@@ -260,8 +184,7 @@ class ResonanceTester:
|
|||||||
raw_values.append((axis, aclient, chip.name))
|
raw_values.append((axis, aclient, chip.name))
|
||||||
|
|
||||||
# Generate moves
|
# Generate moves
|
||||||
test_seq = self.generator.gen_test()
|
self.test.run_test(axis, gcmd)
|
||||||
self.executor.run_test(test_seq, axis, gcmd)
|
|
||||||
for chip_axis, aclient, chip_name in raw_values:
|
for chip_axis, aclient, chip_name in raw_values:
|
||||||
aclient.finish_measurements()
|
aclient.finish_measurements()
|
||||||
if raw_name_suffix is not None:
|
if raw_name_suffix is not None:
|
||||||
@@ -289,11 +212,15 @@ class ResonanceTester:
|
|||||||
def _parse_chips(self, accel_chips):
|
def _parse_chips(self, accel_chips):
|
||||||
parsed_chips = []
|
parsed_chips = []
|
||||||
for chip_name in accel_chips.split(','):
|
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)
|
parsed_chips.append(chip)
|
||||||
return parsed_chips
|
return parsed_chips
|
||||||
def _get_max_calibration_freq(self):
|
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")
|
cmd_TEST_RESONANCES_help = ("Runs the resonance test for a specifed axis")
|
||||||
def cmd_TEST_RESONANCES(self, gcmd):
|
def cmd_TEST_RESONANCES(self, gcmd):
|
||||||
# Parse parameters
|
# Parse parameters
|
||||||
|
|||||||
@@ -37,10 +37,11 @@ class SafeZHoming:
|
|||||||
if 'z' not in kin_status['homed_axes']:
|
if 'z' not in kin_status['homed_axes']:
|
||||||
# Always perform the z_hop if the Z axis is not homed
|
# Always perform the z_hop if the Z axis is not homed
|
||||||
pos[2] = 0
|
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],
|
toolhead.manual_move([None, None, self.z_hop],
|
||||||
self.z_hop_speed)
|
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:
|
elif pos[2] < self.z_hop:
|
||||||
# If the Z axis is homed, and below z_hop, lift it to 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],
|
toolhead.manual_move([None, None, self.z_hop],
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class ScrewsTiltAdjust:
|
|||||||
self.config = config
|
self.config = config
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.screws = []
|
self.screws = []
|
||||||
self.results = {}
|
self.results = []
|
||||||
self.max_diff = None
|
self.max_diff = None
|
||||||
self.max_diff_error = False
|
self.max_diff_error = False
|
||||||
# Read config
|
# Read config
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Support for servos
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
from . import output_pin
|
|
||||||
|
|
||||||
SERVO_SIGNAL_PERIOD = 0.020
|
SERVO_SIGNAL_PERIOD = 0.020
|
||||||
|
PIN_MIN_TIME = 0.100
|
||||||
|
|
||||||
class PrinterServo:
|
class PrinterServo:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
@@ -18,7 +18,7 @@ class PrinterServo:
|
|||||||
self.max_angle = config.getfloat('maximum_servo_angle', 180.)
|
self.max_angle = config.getfloat('maximum_servo_angle', 180.)
|
||||||
self.angle_to_width = (self.max_width - self.min_width) / self.max_angle
|
self.angle_to_width = (self.max_width - self.min_width) / self.max_angle
|
||||||
self.width_to_value = 1. / SERVO_SIGNAL_PERIOD
|
self.width_to_value = 1. / SERVO_SIGNAL_PERIOD
|
||||||
self.last_value = 0.
|
self.last_value = self.last_value_time = 0.
|
||||||
initial_pwm = 0.
|
initial_pwm = 0.
|
||||||
iangle = config.getfloat('initial_angle', None, minval=0., maxval=360.)
|
iangle = config.getfloat('initial_angle', None, minval=0., maxval=360.)
|
||||||
if iangle is not None:
|
if iangle is not None:
|
||||||
@@ -33,9 +33,6 @@ class PrinterServo:
|
|||||||
self.mcu_servo.setup_max_duration(0.)
|
self.mcu_servo.setup_max_duration(0.)
|
||||||
self.mcu_servo.setup_cycle_time(SERVO_SIGNAL_PERIOD)
|
self.mcu_servo.setup_cycle_time(SERVO_SIGNAL_PERIOD)
|
||||||
self.mcu_servo.setup_start_value(initial_pwm, 0.)
|
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
|
# Register commands
|
||||||
servo_name = config.get_name().split()[1]
|
servo_name = config.get_name().split()[1]
|
||||||
gcode = self.printer.lookup_object('gcode')
|
gcode = self.printer.lookup_object('gcode')
|
||||||
@@ -46,9 +43,11 @@ class PrinterServo:
|
|||||||
return {'value': self.last_value}
|
return {'value': self.last_value}
|
||||||
def _set_pwm(self, print_time, value):
|
def _set_pwm(self, print_time, value):
|
||||||
if value == self.last_value:
|
if value == self.last_value:
|
||||||
return "discard", 0.
|
return
|
||||||
self.last_value = value
|
print_time = max(print_time, self.last_value_time + PIN_MIN_TIME)
|
||||||
self.mcu_servo.set_pwm(print_time, value)
|
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):
|
def _get_pwm_from_angle(self, angle):
|
||||||
angle = max(0., min(self.max_angle, angle))
|
angle = max(0., min(self.max_angle, angle))
|
||||||
width = self.min_width + angle * self.angle_to_width
|
width = self.min_width + angle * self.angle_to_width
|
||||||
@@ -65,7 +64,9 @@ class PrinterServo:
|
|||||||
else:
|
else:
|
||||||
angle = gcmd.get_float('ANGLE')
|
angle = gcmd.get_float('ANGLE')
|
||||||
value = self._get_pwm_from_angle(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):
|
def load_config_prefix(config):
|
||||||
return PrinterServo(config)
|
return PrinterServo(config)
|
||||||
|
|||||||
@@ -48,9 +48,7 @@ class CalibrationData:
|
|||||||
# Avoid division by zero errors
|
# Avoid division by zero errors
|
||||||
psd /= self.freq_bins + .1
|
psd /= self.freq_bins + .1
|
||||||
# Remove low-frequency noise
|
# Remove low-frequency noise
|
||||||
low_freqs = self.freq_bins < 2. * MIN_FREQ
|
psd[self.freq_bins < MIN_FREQ] = 0.
|
||||||
psd[low_freqs] *= self.numpy.exp(
|
|
||||||
-(2. * MIN_FREQ / (self.freq_bins[low_freqs] + .1))**2 + 1.)
|
|
||||||
def get_psd(self, axis='all'):
|
def get_psd(self, axis='all'):
|
||||||
return self._psd_map[axis]
|
return self._psd_map[axis]
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ class PrinterStepperEnable:
|
|||||||
print_time = toolhead.get_last_move_time()
|
print_time = toolhead.get_last_move_time()
|
||||||
for el in self.enable_lines.values():
|
for el in self.enable_lines.values():
|
||||||
el.motor_disable(print_time)
|
el.motor_disable(print_time)
|
||||||
toolhead.get_kinematics().clear_homing_state("xyz")
|
|
||||||
self.printer.send_event("stepper_enable:motor_off", print_time)
|
self.printer.send_event("stepper_enable:motor_off", print_time)
|
||||||
toolhead.dwell(DISABLE_STALL_TIME)
|
toolhead.dwell(DISABLE_STALL_TIME)
|
||||||
def motor_debug_enable(self, stepper, enable):
|
def motor_debug_enable(self, stepper, enable):
|
||||||
|
|||||||
@@ -38,17 +38,19 @@ class SX1509(object):
|
|||||||
REG_INPUT_DISABLE : 0, REG_ANALOG_DRIVER_ENABLE : 0}
|
REG_INPUT_DISABLE : 0, REG_ANALOG_DRIVER_ENABLE : 0}
|
||||||
self.reg_i_on_dict = {reg : 0 for reg in REG_I_ON}
|
self.reg_i_on_dict = {reg : 0 for reg in REG_I_ON}
|
||||||
def _build_config(self):
|
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._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
||||||
self._oid, REG_RESET, 0x12))
|
self._oid, REG_RESET, 0x12))
|
||||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
||||||
self._oid, REG_RESET, 0x34))
|
self._oid, REG_RESET, 0x34))
|
||||||
# Enable Oscillator
|
# Enable Oscillator
|
||||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
self._mcu.add_config_cmd("i2c_modify_bits oid=%d reg=%02x"
|
||||||
self._oid, REG_CLOCK, (1 << 6)))
|
" clear_set_bits=%02x%02x" % (
|
||||||
|
self._oid, REG_CLOCK, 0, (1 << 6)))
|
||||||
# Setup Clock Divider
|
# Setup Clock Divider
|
||||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
self._mcu.add_config_cmd("i2c_modify_bits oid=%d reg=%02x"
|
||||||
self._oid, REG_MISC, (1 << 4)))
|
" clear_set_bits=%02x%02x" % (
|
||||||
|
self._oid, REG_MISC, 0, (1 << 4)))
|
||||||
# Transfer all regs with their initial cached state
|
# Transfer all regs with their initial cached state
|
||||||
for _reg, _data in self.reg_dict.items():
|
for _reg, _data in self.reg_dict.items():
|
||||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%04x" % (
|
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%04x" % (
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class TemperatureFan:
|
|||||||
self.cmd_SET_TEMPERATURE_FAN_TARGET,
|
self.cmd_SET_TEMPERATURE_FAN_TARGET,
|
||||||
desc=self.cmd_SET_TEMPERATURE_FAN_TARGET_help)
|
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.:
|
if value <= 0.:
|
||||||
value = 0.
|
value = 0.
|
||||||
elif value < self.min_speed:
|
elif value < self.min_speed:
|
||||||
@@ -60,7 +60,7 @@ class TemperatureFan:
|
|||||||
speed_time = read_time + self.speed_delay
|
speed_time = read_time + self.speed_delay
|
||||||
self.next_speed_time = speed_time + 0.75 * MAX_FAN_TIME
|
self.next_speed_time = speed_time + 0.75 * MAX_FAN_TIME
|
||||||
self.last_speed_value = value
|
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):
|
def temperature_callback(self, read_time, temp):
|
||||||
self.last_temp = temp
|
self.last_temp = temp
|
||||||
self.control.temperature_callback(read_time, temp)
|
self.control.temperature_callback(read_time, temp)
|
||||||
@@ -128,10 +128,10 @@ class ControlBangBang:
|
|||||||
and temp <= target_temp-self.max_delta):
|
and temp <= target_temp-self.max_delta):
|
||||||
self.heating = True
|
self.heating = True
|
||||||
if self.heating:
|
if self.heating:
|
||||||
self.temperature_fan.set_tf_speed(read_time, 0.)
|
self.temperature_fan.set_speed(read_time, 0.)
|
||||||
else:
|
else:
|
||||||
self.temperature_fan.set_tf_speed(
|
self.temperature_fan.set_speed(read_time,
|
||||||
read_time, self.temperature_fan.get_max_speed())
|
self.temperature_fan.get_max_speed())
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Proportional Integral Derivative (PID) control algo
|
# Proportional Integral Derivative (PID) control algo
|
||||||
@@ -171,7 +171,7 @@ class ControlPID:
|
|||||||
# Calculate output
|
# Calculate output
|
||||||
co = self.Kp*temp_err + self.Ki*temp_integ - self.Kd*temp_deriv
|
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))
|
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(),
|
read_time, max(self.temperature_fan.get_min_speed(),
|
||||||
self.temperature_fan.get_max_speed() - bounded_co))
|
self.temperature_fan.get_max_speed() - bounded_co))
|
||||||
# Store state for next measurement
|
# Store state for next measurement
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class PrinterTemperatureMCU:
|
|||||||
self.mcu_type = mcu.get_constants().get("MCU", "")
|
self.mcu_type = mcu.get_constants().get("MCU", "")
|
||||||
# Run MCU specific configuration
|
# Run MCU specific configuration
|
||||||
cfg_funcs = [
|
cfg_funcs = [
|
||||||
('rp2', self.config_rp2040),
|
('rp2040', self.config_rp2040),
|
||||||
('sam3', self.config_sam3), ('sam4', self.config_sam4),
|
('sam3', self.config_sam3), ('sam4', self.config_sam4),
|
||||||
('same70', self.config_same70), ('samd21', self.config_samd21),
|
('same70', self.config_same70), ('samd21', self.config_samd21),
|
||||||
('samd51', self.config_samd51), ('same5', self.config_samd51),
|
('samd51', self.config_samd51), ('same5', self.config_samd51),
|
||||||
|
|||||||
@@ -490,7 +490,6 @@ class EddyDriftCompensation:
|
|||||||
self.cal_temp = config.getfloat("calibration_temp", 0.)
|
self.cal_temp = config.getfloat("calibration_temp", 0.)
|
||||||
self.drift_calibration = None
|
self.drift_calibration = None
|
||||||
self.calibration_samples = 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.)
|
self.dc_min_temp = config.getfloat("drift_calibration_min_temp", 0.)
|
||||||
dc = config.getlists(
|
dc = config.getlists(
|
||||||
"drift_calibration", None, seps=(',', '\n'), parser=float
|
"drift_calibration", None, seps=(',', '\n'), parser=float
|
||||||
@@ -504,8 +503,7 @@ class EddyDriftCompensation:
|
|||||||
)
|
)
|
||||||
self.drift_calibration = [Polynomial2d(*coefs) for coefs in dc]
|
self.drift_calibration = [Polynomial2d(*coefs) for coefs in dc]
|
||||||
cal = self.drift_calibration
|
cal = self.drift_calibration
|
||||||
start_temp, end_temp = self.dc_min_temp, self.max_valid_temp
|
self._check_calibration(cal, self.dc_min_temp, config.error)
|
||||||
self._check_calibration(cal, start_temp, end_temp, config.error)
|
|
||||||
low_poly = self.drift_calibration[-1]
|
low_poly = self.drift_calibration[-1]
|
||||||
self.min_freq = min([low_poly(temp) for temp in range(121)])
|
self.min_freq = min([low_poly(temp) for temp in range(121)])
|
||||||
cal_str = "\n".join([repr(p) for p in cal])
|
cal_str = "\n".join([repr(p) for p in cal])
|
||||||
@@ -640,15 +638,13 @@ class EddyDriftCompensation:
|
|||||||
"calbration error, not enough samples"
|
"calbration error, not enough samples"
|
||||||
)
|
)
|
||||||
min_temp, _ = cal_samples[0][0]
|
min_temp, _ = cal_samples[0][0]
|
||||||
max_temp, _ = cal_samples[-1][0]
|
|
||||||
polynomials = []
|
polynomials = []
|
||||||
for i, coords in enumerate(cal_samples):
|
for i, coords in enumerate(cal_samples):
|
||||||
height = .05 + i * .5
|
height = .05 + i * .5
|
||||||
poly = Polynomial2d.fit(coords)
|
poly = Polynomial2d.fit(coords)
|
||||||
polynomials.append(poly)
|
polynomials.append(poly)
|
||||||
logging.info("Polynomial at Z=%.2f: %s" % (height, repr(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)
|
||||||
self._check_calibration(polynomials, min_temp, end_vld_temp)
|
|
||||||
coef_cfg = "\n" + "\n".join([str(p) for p in polynomials])
|
coef_cfg = "\n" + "\n".join([str(p) for p in polynomials])
|
||||||
configfile = self.printer.lookup_object('configfile')
|
configfile = self.printer.lookup_object('configfile')
|
||||||
configfile.set(self.name, "drift_calibration", coef_cfg)
|
configfile.set(self.name, "drift_calibration", coef_cfg)
|
||||||
@@ -660,11 +656,10 @@ class EddyDriftCompensation:
|
|||||||
% (self.name, len(polynomials))
|
% (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
|
error = error or self.printer.command_error
|
||||||
start = int(start_temp)
|
start = int(start_temp)
|
||||||
end = int(end_temp) + 1
|
for temp in range(start, 121, 1):
|
||||||
for temp in range(start, end, 1):
|
|
||||||
last_freq = calibration[0](temp)
|
last_freq = calibration[0](temp)
|
||||||
for i, poly in enumerate(calibration[1:]):
|
for i, poly in enumerate(calibration[1:]):
|
||||||
next_freq = poly(temp)
|
next_freq = poly(temp)
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ class TMC2240:
|
|||||||
if config.get("uart_pin", None) is not None:
|
if config.get("uart_pin", None) is not None:
|
||||||
# use UART for communication
|
# use UART for communication
|
||||||
self.mcu_tmc = tmc_uart.MCU_TMC_uart(config, Registers, self.fields,
|
self.mcu_tmc = tmc_uart.MCU_TMC_uart(config, Registers, self.fields,
|
||||||
7, TMC_FREQUENCY)
|
3, TMC_FREQUENCY)
|
||||||
else:
|
else:
|
||||||
# Use SPI bus for communication
|
# Use SPI bus for communication
|
||||||
self.mcu_tmc = tmc2130.MCU_TMC_SPI(config, Registers, self.fields,
|
self.mcu_tmc = tmc2130.MCU_TMC_SPI(config, Registers, self.fields,
|
||||||
@@ -408,8 +408,6 @@ class TMC2240:
|
|||||||
set_config_field(config, "tpowerdown", 10)
|
set_config_field(config, "tpowerdown", 10)
|
||||||
# SG4_THRS
|
# SG4_THRS
|
||||||
set_config_field(config, "sg4_angle_offset", 1)
|
set_config_field(config, "sg4_angle_offset", 1)
|
||||||
# DRV_CONF
|
|
||||||
set_config_field(config, "slope_control", 0)
|
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
return TMC2240(config)
|
return TMC2240(config)
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class RetryHelper:
|
|||||||
return self.increasing > 1
|
return self.increasing > 1
|
||||||
def check_retry(self, z_positions):
|
def check_retry(self, z_positions):
|
||||||
if self.max_retries == 0:
|
if self.max_retries == 0:
|
||||||
return "done"
|
return
|
||||||
error = round(max(z_positions) - min(z_positions),6)
|
error = round(max(z_positions) - min(z_positions),6)
|
||||||
self.gcode.respond_info(
|
self.gcode.respond_info(
|
||||||
"Retries: %d/%d %s: %0.6f tolerance: %0.6f" % (
|
"Retries: %d/%d %s: %0.6f tolerance: %0.6f" % (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Parse gcode commands
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import os, re, logging, collections, shlex
|
import os, re, logging, collections, shlex
|
||||||
@@ -28,18 +28,19 @@ class GCodeCommand:
|
|||||||
return self._params
|
return self._params
|
||||||
def get_raw_command_parameters(self):
|
def get_raw_command_parameters(self):
|
||||||
command = self._command
|
command = self._command
|
||||||
origline = self._commandline
|
if command.startswith("M117 ") or command.startswith("M118 "):
|
||||||
param_start = len(command)
|
command = command[:4]
|
||||||
param_end = len(origline)
|
rawparams = self._commandline
|
||||||
if origline[:param_start].upper() != command:
|
urawparams = rawparams.upper()
|
||||||
# Skip any gcode line-number and ignore any trailing checksum
|
if not urawparams.startswith(command):
|
||||||
param_start += origline.upper().find(command)
|
rawparams = rawparams[urawparams.find(command):]
|
||||||
end = origline.rfind('*')
|
end = rawparams.rfind('*')
|
||||||
if end >= 0 and origline[end+1:].isdigit():
|
if end >= 0:
|
||||||
param_end = end
|
rawparams = rawparams[:end]
|
||||||
if origline[param_start:param_start+1].isspace():
|
rawparams = rawparams[len(command):]
|
||||||
param_start += 1
|
if rawparams.startswith(' '):
|
||||||
return origline[param_start:param_end]
|
rawparams = rawparams[1:]
|
||||||
|
return rawparams
|
||||||
def ack(self, msg=None):
|
def ack(self, msg=None):
|
||||||
if not self._need_ack:
|
if not self._need_ack:
|
||||||
return False
|
return False
|
||||||
@@ -132,10 +133,6 @@ class GCodeDispatch:
|
|||||||
raise self.printer.config_error(
|
raise self.printer.config_error(
|
||||||
"gcode command %s already registered" % (cmd,))
|
"gcode command %s already registered" % (cmd,))
|
||||||
if not self.is_traditional_gcode(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
|
origfunc = func
|
||||||
func = lambda params: origfunc(self._get_extended_params(params))
|
func = lambda params: origfunc(self._get_extended_params(params))
|
||||||
self.ready_gcode_handlers[cmd] = func
|
self.ready_gcode_handlers[cmd] = func
|
||||||
@@ -187,7 +184,7 @@ class GCodeDispatch:
|
|||||||
self._build_status_commands()
|
self._build_status_commands()
|
||||||
self._respond_state("Ready")
|
self._respond_state("Ready")
|
||||||
# Parse input into commands
|
# 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):
|
def _process_commands(self, commands, need_ack=True):
|
||||||
for line in commands:
|
for line in commands:
|
||||||
# Ignore comments and leading/trailing spaces
|
# Ignore comments and leading/trailing spaces
|
||||||
@@ -197,14 +194,16 @@ class GCodeDispatch:
|
|||||||
line = line[:cpos]
|
line = line[:cpos]
|
||||||
# Break line into parts and determine command
|
# Break line into parts and determine command
|
||||||
parts = self.args_r.split(line.upper())
|
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
|
# Skip line number at start of command
|
||||||
cmd = ''.join(parts[3:5]).strip()
|
cmd = parts[3] + parts[4].strip()
|
||||||
else:
|
|
||||||
cmd = ''.join(parts[:3]).strip()
|
|
||||||
# Build gcode "params" dictionary
|
# Build gcode "params" dictionary
|
||||||
params = { parts[i]: parts[i+1].strip()
|
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)
|
gcmd = GCodeCommand(self, cmd, origline, params, need_ack)
|
||||||
# Invoke handler for command
|
# Invoke handler for command
|
||||||
handler = self.gcode_handlers.get(cmd, self.cmd_default)
|
handler = self.gcode_handlers.get(cmd, self.cmd_default)
|
||||||
@@ -252,22 +251,26 @@ class GCodeDispatch:
|
|||||||
def _respond_state(self, state):
|
def _respond_state(self, state):
|
||||||
self.respond_info("Klipper state: %s" % (state,), log=False)
|
self.respond_info("Klipper state: %s" % (state,), log=False)
|
||||||
# Parameter parsing helpers
|
# 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):
|
def _get_extended_params(self, gcmd):
|
||||||
rawparams = gcmd.get_raw_command_parameters()
|
m = self.extended_r.match(gcmd.get_commandline())
|
||||||
# Extract args while allowing shell style quoting
|
if m is None:
|
||||||
s = shlex.shlex(rawparams, posix=True)
|
raise self.error("Malformed command '%s'"
|
||||||
s.whitespace_split = True
|
% (gcmd.get_commandline(),))
|
||||||
s.commenters = '#;'
|
eargs = m.group('args')
|
||||||
try:
|
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 }
|
eparams = { k.upper(): v for k, v in eparams }
|
||||||
|
gcmd._params.clear()
|
||||||
|
gcmd._params.update(eparams)
|
||||||
|
return gcmd
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise self.error("Malformed command '%s'"
|
raise self.error("Malformed command '%s'"
|
||||||
% (gcmd.get_commandline(),))
|
% (gcmd.get_commandline(),))
|
||||||
# Update gcmd with new parameters
|
|
||||||
gcmd._params.clear()
|
|
||||||
gcmd._params.update(eparams)
|
|
||||||
return gcmd
|
|
||||||
# G-Code special command handlers
|
# G-Code special command handlers
|
||||||
def cmd_default(self, gcmd):
|
def cmd_default(self, gcmd):
|
||||||
cmd = gcmd.get_command()
|
cmd = gcmd.get_command()
|
||||||
@@ -286,15 +289,12 @@ class GCodeDispatch:
|
|||||||
if cmdline:
|
if cmdline:
|
||||||
logging.debug(cmdline)
|
logging.debug(cmdline)
|
||||||
return
|
return
|
||||||
if ' ' in cmd:
|
if cmd.startswith("M117 ") or cmd.startswith("M118 "):
|
||||||
# Handle M117/M118 gcode with numeric and special characters
|
# Handle M117/M118 gcode with numeric and special characters
|
||||||
realcmd = cmd.split()[0]
|
handler = self.gcode_handlers.get(cmd[:4], None)
|
||||||
if realcmd in ["M117", "M118", "M23"]:
|
if handler is not None:
|
||||||
handler = self.gcode_handlers.get(realcmd, None)
|
handler(gcmd)
|
||||||
if handler is not None:
|
return
|
||||||
gcmd._command = realcmd
|
|
||||||
handler(gcmd)
|
|
||||||
return
|
|
||||||
elif cmd in ['M140', 'M104'] and not gcmd.get_float('S', 0.):
|
elif cmd in ['M140', 'M104'] and not gcmd.get_float('S', 0.):
|
||||||
# Don't warn about requests to turn off heaters when not present
|
# Don't warn about requests to turn off heaters when not present
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class CartKinematics:
|
|||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
toolhead.register_step_generator(s.generate_steps)
|
||||||
|
self.printer.register_event_handler("stepper_enable:motor_off",
|
||||||
|
self._motor_off)
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat('max_z_velocity', 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):
|
def set_position(self, newpos, homing_axes):
|
||||||
for i, rail in enumerate(self.rails):
|
for i, rail in enumerate(self.rails):
|
||||||
rail.set_position(newpos)
|
rail.set_position(newpos)
|
||||||
for axis_name in homing_axes:
|
for axis in homing_axes:
|
||||||
axis = "xyz".index(axis_name)
|
|
||||||
if self.dc_module and axis == self.dc_module.axis:
|
if self.dc_module and axis == self.dc_module.axis:
|
||||||
rail = self.dc_module.get_primary_rail().get_rail()
|
rail = self.dc_module.get_primary_rail().get_rail()
|
||||||
else:
|
else:
|
||||||
rail = self.rails[axis]
|
rail = self.rails[axis]
|
||||||
self.limits[axis] = rail.get_range()
|
self.limits[axis] = rail.get_range()
|
||||||
def clear_homing_state(self, clear_axes):
|
def note_z_not_homed(self):
|
||||||
for axis, axis_name in enumerate("xyz"):
|
# Helper for Safe Z Home
|
||||||
if axis_name in clear_axes:
|
self.limits[2] = (1.0, -1.0)
|
||||||
self.limits[axis] = (1.0, -1.0)
|
|
||||||
def home_axis(self, homing_state, axis, rail):
|
def home_axis(self, homing_state, axis, rail):
|
||||||
# Determine movement
|
# Determine movement
|
||||||
position_min, position_max = rail.get_range()
|
position_min, position_max = rail.get_range()
|
||||||
@@ -96,6 +96,8 @@ class CartKinematics:
|
|||||||
self.dc_module.home(homing_state)
|
self.dc_module.home(homing_state)
|
||||||
else:
|
else:
|
||||||
self.home_axis(homing_state, axis, self.rails[axis])
|
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):
|
def _check_endstops(self, move):
|
||||||
end_pos = move.end_pos
|
end_pos = move.end_pos
|
||||||
for i in (0, 1, 2):
|
for i in (0, 1, 2):
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ class CoreXYKinematics:
|
|||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
toolhead.register_step_generator(s.generate_steps)
|
||||||
|
config.get_printer().register_event_handler("stepper_enable:motor_off",
|
||||||
|
self._motor_off)
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
@@ -39,12 +41,11 @@ class CoreXYKinematics:
|
|||||||
def set_position(self, newpos, homing_axes):
|
def set_position(self, newpos, homing_axes):
|
||||||
for i, rail in enumerate(self.rails):
|
for i, rail in enumerate(self.rails):
|
||||||
rail.set_position(newpos)
|
rail.set_position(newpos)
|
||||||
if "xyz"[i] in homing_axes:
|
if i in homing_axes:
|
||||||
self.limits[i] = rail.get_range()
|
self.limits[i] = rail.get_range()
|
||||||
def clear_homing_state(self, clear_axes):
|
def note_z_not_homed(self):
|
||||||
for axis, axis_name in enumerate("xyz"):
|
# Helper for Safe Z Home
|
||||||
if axis_name in clear_axes:
|
self.limits[2] = (1.0, -1.0)
|
||||||
self.limits[axis] = (1.0, -1.0)
|
|
||||||
def home(self, homing_state):
|
def home(self, homing_state):
|
||||||
# Each axis is homed independently and in order
|
# Each axis is homed independently and in order
|
||||||
for axis in homing_state.get_axes():
|
for axis in homing_state.get_axes():
|
||||||
@@ -61,6 +62,8 @@ class CoreXYKinematics:
|
|||||||
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
|
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
|
||||||
# Perform homing
|
# Perform homing
|
||||||
homing_state.home_rails([rail], forcepos, homepos)
|
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):
|
def _check_endstops(self, move):
|
||||||
end_pos = move.end_pos
|
end_pos = move.end_pos
|
||||||
for i in (0, 1, 2):
|
for i in (0, 1, 2):
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ class CoreXZKinematics:
|
|||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
toolhead.register_step_generator(s.generate_steps)
|
||||||
|
config.get_printer().register_event_handler("stepper_enable:motor_off",
|
||||||
|
self._motor_off)
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
@@ -39,12 +41,11 @@ class CoreXZKinematics:
|
|||||||
def set_position(self, newpos, homing_axes):
|
def set_position(self, newpos, homing_axes):
|
||||||
for i, rail in enumerate(self.rails):
|
for i, rail in enumerate(self.rails):
|
||||||
rail.set_position(newpos)
|
rail.set_position(newpos)
|
||||||
if "xyz"[i] in homing_axes:
|
if i in homing_axes:
|
||||||
self.limits[i] = rail.get_range()
|
self.limits[i] = rail.get_range()
|
||||||
def clear_homing_state(self, clear_axes):
|
def note_z_not_homed(self):
|
||||||
for axis, axis_name in enumerate("xyz"):
|
# Helper for Safe Z Home
|
||||||
if axis_name in clear_axes:
|
self.limits[2] = (1.0, -1.0)
|
||||||
self.limits[axis] = (1.0, -1.0)
|
|
||||||
def home(self, homing_state):
|
def home(self, homing_state):
|
||||||
# Each axis is homed independently and in order
|
# Each axis is homed independently and in order
|
||||||
for axis in homing_state.get_axes():
|
for axis in homing_state.get_axes():
|
||||||
@@ -61,6 +62,8 @@ class CoreXZKinematics:
|
|||||||
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
|
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
|
||||||
# Perform homing
|
# Perform homing
|
||||||
homing_state.home_rails([rail], forcepos, homepos)
|
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):
|
def _check_endstops(self, move):
|
||||||
end_pos = move.end_pos
|
end_pos = move.end_pos
|
||||||
for i in (0, 1, 2):
|
for i in (0, 1, 2):
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class DeltaKinematics:
|
|||||||
stepper_configs[2], need_position_minmax = False,
|
stepper_configs[2], need_position_minmax = False,
|
||||||
default_position_endstop=a_endstop)
|
default_position_endstop=a_endstop)
|
||||||
self.rails = [rail_a, rail_b, rail_c]
|
self.rails = [rail_a, rail_b, rail_c]
|
||||||
|
config.get_printer().register_event_handler("stepper_enable:motor_off",
|
||||||
|
self._motor_off)
|
||||||
# Setup max velocity
|
# Setup max velocity
|
||||||
self.max_velocity, self.max_accel = toolhead.get_max_velocity()
|
self.max_velocity, self.max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
@@ -88,7 +90,7 @@ class DeltaKinematics:
|
|||||||
math.sqrt(self.very_slow_xy2)))
|
math.sqrt(self.very_slow_xy2)))
|
||||||
self.axes_min = toolhead.Coord(-max_xy, -max_xy, self.min_z, 0.)
|
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.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):
|
def get_steppers(self):
|
||||||
return [s for rail in self.rails for s in rail.get_steppers()]
|
return [s for rail in self.rails for s in rail.get_steppers()]
|
||||||
def _actuator_to_cartesian(self, spos):
|
def _actuator_to_cartesian(self, spos):
|
||||||
@@ -101,19 +103,17 @@ class DeltaKinematics:
|
|||||||
for rail in self.rails:
|
for rail in self.rails:
|
||||||
rail.set_position(newpos)
|
rail.set_position(newpos)
|
||||||
self.limit_xy2 = -1.
|
self.limit_xy2 = -1.
|
||||||
if homing_axes == "xyz":
|
if tuple(homing_axes) == (0, 1, 2):
|
||||||
self.need_home = False
|
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):
|
def home(self, homing_state):
|
||||||
# All axes are homed simultaneously
|
# All axes are homed simultaneously
|
||||||
homing_state.set_axes([0, 1, 2])
|
homing_state.set_axes([0, 1, 2])
|
||||||
forcepos = list(self.home_position)
|
forcepos = list(self.home_position)
|
||||||
forcepos[2] = -1.5 * math.sqrt(max(self.arm2)-self.max_xy2)
|
forcepos[2] = -1.5 * math.sqrt(max(self.arm2)-self.max_xy2)
|
||||||
homing_state.home_rails(self.rails, forcepos, self.home_position)
|
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):
|
def check_move(self, move):
|
||||||
end_pos = move.end_pos
|
end_pos = move.end_pos
|
||||||
end_xy2 = end_pos[0]**2 + end_pos[1]**2
|
end_xy2 = end_pos[0]**2 + end_pos[1]**2
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ class DeltesianKinematics:
|
|||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
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
|
self.limits = [(1.0, -1.0)] * 3
|
||||||
# X axis limits
|
# X axis limits
|
||||||
min_angle = config.getfloat('min_angle', MIN_ANGLE,
|
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_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.axes_max = toolhead.Coord(*[l[1] for l in self.limits], e=0.)
|
||||||
self.homed_axis = [False] * 3
|
self.homed_axis = [False] * 3
|
||||||
self.set_position([0., 0., 0.], "")
|
self.set_position([0., 0., 0.], ())
|
||||||
def get_steppers(self):
|
def get_steppers(self):
|
||||||
return [s for rail in self.rails for s in rail.get_steppers()]
|
return [s for rail in self.rails for s in rail.get_steppers()]
|
||||||
def _actuator_to_cartesian(self, sp):
|
def _actuator_to_cartesian(self, sp):
|
||||||
@@ -113,13 +115,8 @@ class DeltesianKinematics:
|
|||||||
def set_position(self, newpos, homing_axes):
|
def set_position(self, newpos, homing_axes):
|
||||||
for rail in self.rails:
|
for rail in self.rails:
|
||||||
rail.set_position(newpos)
|
rail.set_position(newpos)
|
||||||
for axis_name in homing_axes:
|
for n in homing_axes:
|
||||||
axis = "xyz".index(axis_name)
|
self.homed_axis[n] = True
|
||||||
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
|
|
||||||
def home(self, homing_state):
|
def home(self, homing_state):
|
||||||
homing_axes = homing_state.get_axes()
|
homing_axes = homing_state.get_axes()
|
||||||
home_xz = 0 in homing_axes or 2 in homing_axes
|
home_xz = 0 in homing_axes or 2 in homing_axes
|
||||||
@@ -145,6 +142,8 @@ class DeltesianKinematics:
|
|||||||
else:
|
else:
|
||||||
forcepos[1] += 1.5 * (position_max - hi.position_endstop)
|
forcepos[1] += 1.5 * (position_max - hi.position_endstop)
|
||||||
homing_state.home_rails([self.rails[2]], forcepos, homepos)
|
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):
|
def check_move(self, move):
|
||||||
limits = list(map(list, self.limits))
|
limits = list(map(list, self.limits))
|
||||||
spos, epos = move.start_pos, move.end_pos
|
spos, epos = move.start_pos, move.end_pos
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class HybridCoreXYKinematics:
|
|||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
toolhead.register_step_generator(s.generate_steps)
|
||||||
|
self.printer.register_event_handler("stepper_enable:motor_off",
|
||||||
|
self._motor_off)
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
@@ -67,17 +69,15 @@ class HybridCoreXYKinematics:
|
|||||||
def set_position(self, newpos, homing_axes):
|
def set_position(self, newpos, homing_axes):
|
||||||
for i, rail in enumerate(self.rails):
|
for i, rail in enumerate(self.rails):
|
||||||
rail.set_position(newpos)
|
rail.set_position(newpos)
|
||||||
for axis_name in homing_axes:
|
for axis in homing_axes:
|
||||||
axis = "xyz".index(axis_name)
|
|
||||||
if self.dc_module and axis == self.dc_module.axis:
|
if self.dc_module and axis == self.dc_module.axis:
|
||||||
rail = self.dc_module.get_primary_rail().get_rail()
|
rail = self.dc_module.get_primary_rail().get_rail()
|
||||||
else:
|
else:
|
||||||
rail = self.rails[axis]
|
rail = self.rails[axis]
|
||||||
self.limits[axis] = rail.get_range()
|
self.limits[axis] = rail.get_range()
|
||||||
def clear_homing_state(self, clear_axes):
|
def note_z_not_homed(self):
|
||||||
for axis, axis_name in enumerate("xyz"):
|
# Helper for Safe Z Home
|
||||||
if axis_name in clear_axes:
|
self.limits[2] = (1.0, -1.0)
|
||||||
self.limits[axis] = (1.0, -1.0)
|
|
||||||
def home_axis(self, homing_state, axis, rail):
|
def home_axis(self, homing_state, axis, rail):
|
||||||
position_min, position_max = rail.get_range()
|
position_min, position_max = rail.get_range()
|
||||||
hi = rail.get_homing_info()
|
hi = rail.get_homing_info()
|
||||||
@@ -96,6 +96,8 @@ class HybridCoreXYKinematics:
|
|||||||
self.dc_module.home(homing_state)
|
self.dc_module.home(homing_state)
|
||||||
else:
|
else:
|
||||||
self.home_axis(homing_state, axis, self.rails[axis])
|
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):
|
def _check_endstops(self, move):
|
||||||
end_pos = move.end_pos
|
end_pos = move.end_pos
|
||||||
for i in (0, 1, 2):
|
for i in (0, 1, 2):
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class HybridCoreXZKinematics:
|
|||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
toolhead.register_step_generator(s.generate_steps)
|
||||||
|
self.printer.register_event_handler("stepper_enable:motor_off",
|
||||||
|
self._motor_off)
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
@@ -67,17 +69,15 @@ class HybridCoreXZKinematics:
|
|||||||
def set_position(self, newpos, homing_axes):
|
def set_position(self, newpos, homing_axes):
|
||||||
for i, rail in enumerate(self.rails):
|
for i, rail in enumerate(self.rails):
|
||||||
rail.set_position(newpos)
|
rail.set_position(newpos)
|
||||||
for axis_name in homing_axes:
|
for axis in homing_axes:
|
||||||
axis = "xyz".index(axis_name)
|
|
||||||
if self.dc_module and axis == self.dc_module.axis:
|
if self.dc_module and axis == self.dc_module.axis:
|
||||||
rail = self.dc_module.get_primary_rail().get_rail()
|
rail = self.dc_module.get_primary_rail().get_rail()
|
||||||
else:
|
else:
|
||||||
rail = self.rails[axis]
|
rail = self.rails[axis]
|
||||||
self.limits[axis] = rail.get_range()
|
self.limits[axis] = rail.get_range()
|
||||||
def clear_homing_state(self, clear_axes):
|
def note_z_not_homed(self):
|
||||||
for axis, axis_name in enumerate("xyz"):
|
# Helper for Safe Z Home
|
||||||
if axis_name in clear_axes:
|
self.limits[2] = (1.0, -1.0)
|
||||||
self.limits[axis] = (1.0, -1.0)
|
|
||||||
def home_axis(self, homing_state, axis, rail):
|
def home_axis(self, homing_state, axis, rail):
|
||||||
position_min, position_max = rail.get_range()
|
position_min, position_max = rail.get_range()
|
||||||
hi = rail.get_homing_info()
|
hi = rail.get_homing_info()
|
||||||
@@ -96,6 +96,8 @@ class HybridCoreXZKinematics:
|
|||||||
self.dc_module.home(homing_state)
|
self.dc_module.home(homing_state)
|
||||||
else:
|
else:
|
||||||
self.home_axis(homing_state, axis, self.rails[axis])
|
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):
|
def _check_endstops(self, move):
|
||||||
end_pos = move.end_pos
|
end_pos = move.end_pos
|
||||||
for i in (0, 1, 2):
|
for i in (0, 1, 2):
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ class NoneKinematics:
|
|||||||
return [0, 0, 0]
|
return [0, 0, 0]
|
||||||
def set_position(self, newpos, homing_axes):
|
def set_position(self, newpos, homing_axes):
|
||||||
pass
|
pass
|
||||||
def clear_homing_state(self, clear_axes):
|
|
||||||
pass
|
|
||||||
def home(self, homing_state):
|
def home(self, homing_state):
|
||||||
pass
|
pass
|
||||||
def check_move(self, move):
|
def check_move(self, move):
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class PolarKinematics:
|
|||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
toolhead.register_step_generator(s.generate_steps)
|
||||||
|
config.get_printer().register_event_handler("stepper_enable:motor_off",
|
||||||
|
self._motor_off)
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
@@ -45,16 +47,13 @@ class PolarKinematics:
|
|||||||
def set_position(self, newpos, homing_axes):
|
def set_position(self, newpos, homing_axes):
|
||||||
for s in self.steppers:
|
for s in self.steppers:
|
||||||
s.set_position(newpos)
|
s.set_position(newpos)
|
||||||
if "z" in homing_axes:
|
if 2 in homing_axes:
|
||||||
self.limit_z = self.rails[1].get_range()
|
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
|
self.limit_xy2 = self.rails[0].get_range()[1]**2
|
||||||
def clear_homing_state(self, clear_axes):
|
def note_z_not_homed(self):
|
||||||
if "x" in clear_axes or "y" in clear_axes:
|
# Helper for Safe Z Home
|
||||||
# X and Y cannot be cleared separately
|
self.limit_z = (1.0, -1.0)
|
||||||
self.limit_xy2 = -1.
|
|
||||||
if "z" in clear_axes:
|
|
||||||
self.limit_z = (1.0, -1.0)
|
|
||||||
def _home_axis(self, homing_state, axis, rail):
|
def _home_axis(self, homing_state, axis, rail):
|
||||||
# Determine movement
|
# Determine movement
|
||||||
position_min, position_max = rail.get_range()
|
position_min, position_max = rail.get_range()
|
||||||
@@ -86,6 +85,9 @@ class PolarKinematics:
|
|||||||
self._home_axis(homing_state, 0, self.rails[0])
|
self._home_axis(homing_state, 0, self.rails[0])
|
||||||
if home_z:
|
if home_z:
|
||||||
self._home_axis(homing_state, 2, self.rails[1])
|
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):
|
def check_move(self, move):
|
||||||
end_pos = move.end_pos
|
end_pos = move.end_pos
|
||||||
xy2 = end_pos[0]**2 + end_pos[1]**2
|
xy2 = end_pos[0]**2 + end_pos[1]**2
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ class RotaryDeltaKinematics:
|
|||||||
stepper_configs[2], need_position_minmax=False,
|
stepper_configs[2], need_position_minmax=False,
|
||||||
default_position_endstop=a_endstop, units_in_radians=True)
|
default_position_endstop=a_endstop, units_in_radians=True)
|
||||||
self.rails = [rail_a, rail_b, rail_c]
|
self.rails = [rail_a, rail_b, rail_c]
|
||||||
|
config.get_printer().register_event_handler("stepper_enable:motor_off",
|
||||||
|
self._motor_off)
|
||||||
# Read config
|
# Read config
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat('max_z_velocity', 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)
|
max_xy = math.sqrt(self.max_xy2)
|
||||||
self.axes_min = toolhead.Coord(-max_xy, -max_xy, self.min_z, 0.)
|
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.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):
|
def get_steppers(self):
|
||||||
return [s for rail in self.rails for s in rail.get_steppers()]
|
return [s for rail in self.rails for s in rail.get_steppers()]
|
||||||
def calc_position(self, stepper_positions):
|
def calc_position(self, stepper_positions):
|
||||||
@@ -84,13 +86,8 @@ class RotaryDeltaKinematics:
|
|||||||
for rail in self.rails:
|
for rail in self.rails:
|
||||||
rail.set_position(newpos)
|
rail.set_position(newpos)
|
||||||
self.limit_xy2 = -1.
|
self.limit_xy2 = -1.
|
||||||
if homing_axes == "xyz":
|
if tuple(homing_axes) == (0, 1, 2):
|
||||||
self.need_home = False
|
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):
|
def home(self, homing_state):
|
||||||
# All axes are homed simultaneously
|
# All axes are homed simultaneously
|
||||||
homing_state.set_axes([0, 1, 2])
|
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] = self.calibration.actuator_to_cartesian(min_angles)[2]
|
||||||
forcepos[2] = -1.
|
forcepos[2] = -1.
|
||||||
homing_state.home_rails(self.rails, forcepos, self.home_position)
|
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):
|
def check_move(self, move):
|
||||||
end_pos = move.end_pos
|
end_pos = move.end_pos
|
||||||
end_xy2 = end_pos[0]**2 + end_pos[1]**2
|
end_xy2 = end_pos[0]**2 + end_pos[1]**2
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class WinchKinematics:
|
|||||||
acoords = list(zip(*self.anchors))
|
acoords = list(zip(*self.anchors))
|
||||||
self.axes_min = toolhead.Coord(*[min(a) for a in acoords], e=0.)
|
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.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):
|
def get_steppers(self):
|
||||||
return list(self.steppers)
|
return list(self.steppers)
|
||||||
def calc_position(self, stepper_positions):
|
def calc_position(self, stepper_positions):
|
||||||
@@ -36,9 +36,6 @@ class WinchKinematics:
|
|||||||
def set_position(self, newpos, homing_axes):
|
def set_position(self, newpos, homing_axes):
|
||||||
for s in self.steppers:
|
for s in self.steppers:
|
||||||
s.set_position(newpos)
|
s.set_position(newpos)
|
||||||
def clear_homing_state(self, clear_axes):
|
|
||||||
# XXX - homing not implemented
|
|
||||||
pass
|
|
||||||
def home(self, homing_state):
|
def home(self, homing_state):
|
||||||
# XXX - homing not implemented
|
# XXX - homing not implemented
|
||||||
homing_state.set_axes([0, 1, 2])
|
homing_state.set_axes([0, 1, 2])
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ class Printer:
|
|||||||
logging.info("Reactor garbage collection: %s",
|
logging.info("Reactor garbage collection: %s",
|
||||||
self.reactor.get_gc_stats())
|
self.reactor.get_gc_stats())
|
||||||
self.send_event("klippy:notify_mcu_shutdown", msg, details)
|
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(
|
self.reactor.register_async_callback(
|
||||||
(lambda e: self.invoke_shutdown(msg, details)))
|
(lambda e: self.invoke_shutdown(msg, details)))
|
||||||
def register_event_handler(self, event, callback):
|
def register_event_handler(self, event, callback):
|
||||||
|
|||||||
@@ -832,10 +832,9 @@ class MCU:
|
|||||||
systime = self._reactor.monotonic()
|
systime = self._reactor.monotonic()
|
||||||
get_clock = self._clocksync.get_clock
|
get_clock = self._clocksync.get_clock
|
||||||
calc_freq = get_clock(systime + 1) - get_clock(systime)
|
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)
|
mcu_freq_mhz = int(mcu_freq / 1000000. + 0.5)
|
||||||
calc_freq_mhz = int(calc_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')
|
pconfig = self._printer.lookup_object('configfile')
|
||||||
msg = ("MCU '%s' configured for %dMhz but running at %dMhz!"
|
msg = ("MCU '%s' configured for %dMhz but running at %dMhz!"
|
||||||
% (self._name, mcu_freq_mhz, calc_freq_mhz))
|
% (self._name, mcu_freq_mhz, calc_freq_mhz))
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ class MessageParser:
|
|||||||
def create_command(self, msg):
|
def create_command(self, msg):
|
||||||
parts = msg.strip().split()
|
parts = msg.strip().split()
|
||||||
if not parts:
|
if not parts:
|
||||||
return []
|
return ""
|
||||||
msgname = parts[0]
|
msgname = parts[0]
|
||||||
mp = self.messages_by_name.get(msgname)
|
mp = self.messages_by_name.get(msgname)
|
||||||
if mp is None:
|
if mp is None:
|
||||||
|
|||||||
@@ -138,10 +138,8 @@ class MCU_stepper:
|
|||||||
def get_commanded_position(self):
|
def get_commanded_position(self):
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
return ffi_lib.itersolve_get_commanded_pos(self._stepper_kinematics)
|
return ffi_lib.itersolve_get_commanded_pos(self._stepper_kinematics)
|
||||||
def get_mcu_position(self, cmd_pos=None):
|
def get_mcu_position(self):
|
||||||
if cmd_pos is None:
|
mcu_pos_dist = self.get_commanded_position() + self._mcu_position_offset
|
||||||
cmd_pos = self.get_commanded_position()
|
|
||||||
mcu_pos_dist = cmd_pos + self._mcu_position_offset
|
|
||||||
mcu_pos = mcu_pos_dist / self._step_dist
|
mcu_pos = mcu_pos_dist / self._step_dist
|
||||||
if mcu_pos >= 0.:
|
if mcu_pos >= 0.:
|
||||||
return int(mcu_pos + 0.5)
|
return int(mcu_pos + 0.5)
|
||||||
@@ -403,7 +401,7 @@ class PrinterRail:
|
|||||||
changed_invert = pin_params['invert'] != endstop['invert']
|
changed_invert = pin_params['invert'] != endstop['invert']
|
||||||
changed_pullup = pin_params['pullup'] != endstop['pullup']
|
changed_pullup = pin_params['pullup'] != endstop['pullup']
|
||||||
if changed_invert or changed_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" % (
|
"must specify the same pullup/invert settings" % (
|
||||||
self.get_name(), pin_name))
|
self.get_name(), pin_name))
|
||||||
mcu_endstop.add_stepper(stepper)
|
mcu_endstop.add_stepper(stepper)
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ class Move:
|
|||||||
self.delta_v2 = 2.0 * move_d * self.accel
|
self.delta_v2 = 2.0 * move_d * self.accel
|
||||||
self.max_smoothed_v2 = 0.
|
self.max_smoothed_v2 = 0.
|
||||||
self.smooth_delta_v2 = 2.0 * move_d * toolhead.max_accel_to_decel
|
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):
|
def limit_speed(self, speed, accel):
|
||||||
speed2 = speed**2
|
speed2 = speed**2
|
||||||
if speed2 < self.max_cruise_v2:
|
if speed2 < self.max_cruise_v2:
|
||||||
@@ -56,8 +55,6 @@ class Move:
|
|||||||
self.accel = min(self.accel, accel)
|
self.accel = min(self.accel, accel)
|
||||||
self.delta_v2 = 2.0 * self.move_d * self.accel
|
self.delta_v2 = 2.0 * self.move_d * self.accel
|
||||||
self.smooth_delta_v2 = min(self.smooth_delta_v2, self.delta_v2)
|
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"):
|
def move_error(self, msg="Move out of range"):
|
||||||
ep = self.end_pos
|
ep = self.end_pos
|
||||||
m = "%s: %.3f %.3f %.3f [%.3f]" % (msg, ep[0], ep[1], ep[2], ep[3])
|
m = "%s: %.3f %.3f %.3f [%.3f]" % (msg, ep[0], ep[1], ep[2], ep[3])
|
||||||
@@ -67,33 +64,32 @@ class Move:
|
|||||||
return
|
return
|
||||||
# Allow extruder to calculate its maximum junction
|
# Allow extruder to calculate its maximum junction
|
||||||
extruder_v2 = self.toolhead.extruder.calc_junction(prev_move, self)
|
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"
|
# Find max velocity using "approximated centripetal velocity"
|
||||||
axes_r = self.axes_r
|
axes_r = self.axes_r
|
||||||
prev_axes_r = prev_move.axes_r
|
prev_axes_r = prev_move.axes_r
|
||||||
junction_cos_theta = -(axes_r[0] * prev_axes_r[0]
|
junction_cos_theta = -(axes_r[0] * prev_axes_r[0]
|
||||||
+ axes_r[1] * prev_axes_r[1]
|
+ axes_r[1] * prev_axes_r[1]
|
||||||
+ axes_r[2] * prev_axes_r[2])
|
+ axes_r[2] * prev_axes_r[2])
|
||||||
sin_theta_d2 = math.sqrt(max(0.5*(1.0-junction_cos_theta), 0.))
|
if junction_cos_theta > 0.999999:
|
||||||
cos_theta_d2 = math.sqrt(max(0.5*(1.0+junction_cos_theta), 0.))
|
return
|
||||||
one_minus_sin_theta_d2 = 1. - sin_theta_d2
|
junction_cos_theta = max(junction_cos_theta, -0.999999)
|
||||||
if one_minus_sin_theta_d2 > 0. and cos_theta_d2 > 0.:
|
sin_theta_d2 = math.sqrt(0.5*(1.0-junction_cos_theta))
|
||||||
R_jd = sin_theta_d2 / one_minus_sin_theta_d2
|
R_jd = sin_theta_d2 / (1. - sin_theta_d2)
|
||||||
move_jd_v2 = R_jd * self.junction_deviation * self.accel
|
# Approximated circle must contact moves no further away than mid-move
|
||||||
pmove_jd_v2 = R_jd * prev_move.junction_deviation * prev_move.accel
|
tan_theta_d2 = sin_theta_d2 / math.sqrt(0.5*(1.0+junction_cos_theta))
|
||||||
# Approximated circle must contact moves no further than mid-move
|
move_centripetal_v2 = .5 * self.move_d * tan_theta_d2 * self.accel
|
||||||
# centripetal_v2 = .5 * self.move_d * self.accel * tan_theta_d2
|
prev_move_centripetal_v2 = (.5 * prev_move.move_d * tan_theta_d2
|
||||||
quarter_tan_theta_d2 = .25 * sin_theta_d2 / cos_theta_d2
|
* prev_move.accel)
|
||||||
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)
|
|
||||||
# Apply limits
|
# 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(
|
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):
|
def set_junction(self, start_v2, cruise_v2, end_v2):
|
||||||
# Determine accel, cruise, and decel portions of the move distance
|
# Determine accel, cruise, and decel portions of the move distance
|
||||||
half_inv_accel = .5 / self.accel
|
half_inv_accel = .5 / self.accel
|
||||||
@@ -457,7 +453,7 @@ class ToolHead:
|
|||||||
# Movement commands
|
# Movement commands
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
return list(self.commanded_pos)
|
return list(self.commanded_pos)
|
||||||
def set_position(self, newpos, homing_axes=""):
|
def set_position(self, newpos, homing_axes=()):
|
||||||
self.flush_step_generation()
|
self.flush_step_generation()
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
ffi_lib.trapq_set_position(self.trapq, self.print_time,
|
ffi_lib.trapq_set_position(self.trapq, self.print_time,
|
||||||
@@ -465,10 +461,6 @@ class ToolHead:
|
|||||||
self.commanded_pos[:] = newpos
|
self.commanded_pos[:] = newpos
|
||||||
self.kin.set_position(newpos, homing_axes)
|
self.kin.set_position(newpos, homing_axes)
|
||||||
self.printer.send_event("toolhead:set_position")
|
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):
|
def move(self, newpos, speed):
|
||||||
move = Move(self, self.commanded_pos, newpos, speed)
|
move = Move(self, self.commanded_pos, newpos, speed)
|
||||||
if not move.move_d:
|
if not move.move_d:
|
||||||
|
|||||||
17
lib/README
17
lib/README
@@ -105,23 +105,16 @@ The stm32h7 directory contains code from:
|
|||||||
version v1.9.0 (ccb11556044540590ca6e45056e6b65cdca2deb2). Contents
|
version v1.9.0 (ccb11556044540590ca6e45056e6b65cdca2deb2). Contents
|
||||||
taken from the Drivers/CMSIS/Device/ST/STM32H7xx/ directory.
|
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
|
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
|
modified so that it can build outside of the pico sdk. See
|
||||||
pico-sdk.patch for the modifications.
|
rp2040.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.
|
|
||||||
|
|
||||||
The rp2040_flash directory contains a light-weight bootsel flash tool.
|
The rp2040_flash directory contains a light-weight bootsel flash tool.
|
||||||
It uses C part of the the `picoboot_connection` directory found in:
|
It uses C part of the the `picoboot_connection` directory found in:
|
||||||
https://github.com/raspberrypi/picotool.git
|
https://github.com/raspberrypi/picotool.git
|
||||||
version 2.0.0 (8a9af99ab10b20b1c6afb30cd9384e562a6647f9). Note that
|
version v1.1.0 (55fd880c3dc029b961fc1a0967a6cfdc0af02721).
|
||||||
Makefile and main.c are locally developed files (the remaining files
|
|
||||||
are from the picotool repo).
|
|
||||||
|
|
||||||
The hub-ctrl directory contains code from:
|
The hub-ctrl directory contains code from:
|
||||||
https://github.com/codazoda/hub-ctrl.c/
|
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:
|
The can2040 directory contains code from:
|
||||||
https://github.com/KevinOConnor/can2040
|
https://github.com/KevinOConnor/can2040
|
||||||
version v1.7.0 (90515f53ce89442f1bcc3033aae222e9eb77818c).
|
version v1.6.0 (af3d21e5d61b8408c63fbdfb0aceb21d69d91693)
|
||||||
|
|
||||||
The Huada HC32F460 directory contains code from:
|
The Huada HC32F460 directory contains code from:
|
||||||
https://www.hdsc.com.cn/Category83-1490
|
https://www.hdsc.com.cn/Category83-1490
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// Software CANbus implementation for rp2040
|
// 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.
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
#include <stdint.h> // uint32_t
|
#include <stdint.h> // uint32_t
|
||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
|
#include "RP2040.h" // hw_set_bits
|
||||||
#include "can2040.h" // can2040_setup
|
#include "can2040.h" // can2040_setup
|
||||||
#include "cmsis_gcc.h" // __DMB
|
|
||||||
#include "hardware/regs/dreq.h" // DREQ_PIO0_RX1
|
#include "hardware/regs/dreq.h" // DREQ_PIO0_RX1
|
||||||
#include "hardware/structs/dma.h" // dma_hw
|
#include "hardware/structs/dma.h" // dma_hw
|
||||||
#include "hardware/structs/iobank0.h" // iobank0_hw
|
#include "hardware/structs/iobank0.h" // iobank0_hw
|
||||||
@@ -20,13 +20,6 @@
|
|||||||
* rp2040 and low-level helper functions
|
* 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
|
// Helper compiler definitions
|
||||||
#define barrier() __asm__ __volatile__("": : :"memory")
|
#define barrier() __asm__ __volatile__("": : :"memory")
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
#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_RX_DATA PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS
|
||||||
#define SI_TXPENDING PIO_IRQ0_INTE_SM1_BITS // Misc bit manually forced
|
#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)
|
// Setup PIO "sync" state machine (state machine 0)
|
||||||
static void
|
static void
|
||||||
pio_sync_setup(struct can2040 *cd)
|
pio_sync_setup(struct can2040 *cd)
|
||||||
{
|
{
|
||||||
pio_hw_t *pio_hw = cd->pio_hw;
|
pio_hw_t *pio_hw = cd->pio_hw;
|
||||||
pio_sm_hw_t *sm = &pio_hw->sm[0];
|
struct pio_sm_hw *sm = &pio_hw->sm[0];
|
||||||
uint32_t gpio_rx = (cd->gpio_rx - pio_gpiobase(cd)) & 0x1f;
|
|
||||||
sm->execctrl = (
|
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_end - 1) << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
|
||||||
| can2040_offset_sync_signal_start << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
|
| can2040_offset_sync_signal_start << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
|
||||||
sm->pinctrl = (
|
sm->pinctrl = (
|
||||||
1 << PIO_SM0_PINCTRL_SET_COUNT_LSB
|
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->instr = 0xe080; // set pindirs, 0
|
||||||
sm->pinctrl = 0;
|
sm->pinctrl = 0;
|
||||||
pio_hw->txf[0] = 9 + 6 * PIO_CLOCK_PER_BIT / 2;
|
pio_hw->txf[0] = 9 + 6 * PIO_CLOCK_PER_BIT / 2;
|
||||||
@@ -165,12 +148,11 @@ static void
|
|||||||
pio_rx_setup(struct can2040 *cd)
|
pio_rx_setup(struct can2040 *cd)
|
||||||
{
|
{
|
||||||
pio_hw_t *pio_hw = cd->pio_hw;
|
pio_hw_t *pio_hw = cd->pio_hw;
|
||||||
pio_sm_hw_t *sm = &pio_hw->sm[1];
|
struct pio_sm_hw *sm = &pio_hw->sm[1];
|
||||||
uint32_t gpio_rx = (cd->gpio_rx - pio_gpiobase(cd)) & 0x1f;
|
|
||||||
sm->execctrl = (
|
sm->execctrl = (
|
||||||
(can2040_offset_shared_rx_end - 1) << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
|
(can2040_offset_shared_rx_end - 1) << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
|
||||||
| can2040_offset_shared_rx_read << PIO_SM0_EXECCTRL_WRAP_BOTTOM_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 = 0; // flush fifo on a restart
|
||||||
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS
|
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS
|
||||||
| PIO_RX_WAKE_BITS << PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB
|
| PIO_RX_WAKE_BITS << PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB
|
||||||
@@ -183,16 +165,15 @@ static void
|
|||||||
pio_match_setup(struct can2040 *cd)
|
pio_match_setup(struct can2040 *cd)
|
||||||
{
|
{
|
||||||
pio_hw_t *pio_hw = cd->pio_hw;
|
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 = (
|
sm->execctrl = (
|
||||||
(can2040_offset_match_end - 1) << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
|
(can2040_offset_match_end - 1) << PIO_SM0_EXECCTRL_WRAP_TOP_LSB
|
||||||
| can2040_offset_shared_rx_read << PIO_SM0_EXECCTRL_WRAP_BOTTOM_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 = cd->gpio_rx << PIO_SM0_PINCTRL_IN_BASE_LSB;
|
||||||
sm->pinctrl = gpio_rx << PIO_SM0_PINCTRL_IN_BASE_LSB;
|
|
||||||
sm->shiftctrl = 0;
|
sm->shiftctrl = 0;
|
||||||
sm->instr = 0xe040; // set y, 0
|
sm->instr = 0xe040; // set y, 0
|
||||||
sm->instr = 0xa0e2; // mov osr, y
|
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
|
sm->instr = can2040_offset_match_load_next; // jmp match_load_next
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,19 +182,17 @@ static void
|
|||||||
pio_tx_setup(struct can2040 *cd)
|
pio_tx_setup(struct can2040 *cd)
|
||||||
{
|
{
|
||||||
pio_hw_t *pio_hw = cd->pio_hw;
|
pio_hw_t *pio_hw = cd->pio_hw;
|
||||||
pio_sm_hw_t *sm = &pio_hw->sm[3];
|
struct pio_sm_hw *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;
|
|
||||||
sm->execctrl = (
|
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_TOP_LSB
|
||||||
| can2040_offset_tx_conflict << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
|
| can2040_offset_tx_conflict << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB);
|
||||||
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS
|
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS
|
||||||
| PIO_SM0_SHIFTCTRL_AUTOPULL_BITS);
|
| PIO_SM0_SHIFTCTRL_AUTOPULL_BITS);
|
||||||
sm->pinctrl = (1 << PIO_SM0_PINCTRL_SET_COUNT_LSB
|
sm->pinctrl = (1 << PIO_SM0_PINCTRL_SET_COUNT_LSB
|
||||||
| 1 << PIO_SM0_PINCTRL_OUT_COUNT_LSB
|
| 1 << PIO_SM0_PINCTRL_OUT_COUNT_LSB
|
||||||
| gpio_tx << PIO_SM0_PINCTRL_SET_BASE_LSB
|
| cd->gpio_tx << PIO_SM0_PINCTRL_SET_BASE_LSB
|
||||||
| gpio_tx << PIO_SM0_PINCTRL_OUT_BASE_LSB);
|
| cd->gpio_tx << PIO_SM0_PINCTRL_OUT_BASE_LSB);
|
||||||
sm->instr = 0xe001; // set pins, 1
|
sm->instr = 0xe001; // set pins, 1
|
||||||
sm->instr = 0xe081; // set pindirs, 1
|
sm->instr = 0xe081; // set pindirs, 1
|
||||||
}
|
}
|
||||||
@@ -276,7 +255,7 @@ pio_tx_reset(struct can2040 *cd)
|
|||||||
| (0x08 << PIO_CTRL_SM_RESTART_LSB));
|
| (0x08 << PIO_CTRL_SM_RESTART_LSB));
|
||||||
pio_hw->irq = (SI_MATCHED | SI_ACKDONE) >> 8; // clear PIO irq flags
|
pio_hw->irq = (SI_MATCHED | SI_ACKDONE) >> 8; // clear PIO irq flags
|
||||||
// Clear tx fifo
|
// 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 = 0;
|
||||||
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS
|
sm->shiftctrl = (PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS
|
||||||
| PIO_SM0_SHIFTCTRL_AUTOPULL_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;
|
uint32_t i;
|
||||||
for (i=0; i<count; i++)
|
for (i=0; i<count; i++)
|
||||||
pio_hw->txf[3] = data[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 = 0xe001; // set pins, 1
|
||||||
sm->instr = 0x6021; // out x, 1
|
sm->instr = 0x6021; // out x, 1
|
||||||
sm->instr = can2040_offset_tx_write_pin; // jmp tx_write_pin
|
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_tx_reset(cd);
|
||||||
pio_hw->instr_mem[can2040_offset_tx_got_recessive] = 0xc023; // irq wait 3
|
pio_hw->instr_mem[can2040_offset_tx_got_recessive] = 0xc023; // irq wait 3
|
||||||
pio_hw->txf[3] = 0x7fffffff;
|
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 = 0xe001; // set pins, 1
|
||||||
sm->instr = 0x6021; // out x, 1
|
sm->instr = 0x6021; // out x, 1
|
||||||
sm->instr = can2040_offset_tx_write_pin; // jmp tx_write_pin
|
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
|
// Configure pio0 clock
|
||||||
uint32_t rb = cd->pio_num ? RESETS_RESET_PIO1_BITS : RESETS_RESET_PIO0_BITS;
|
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);
|
rp2040_clear_reset(rb);
|
||||||
|
|
||||||
// Setup and sync pio state machine clocks
|
// 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++)
|
for (i=0; i<4; i++)
|
||||||
pio_hw->sm[i].clkdiv = div << PIO_SM0_CLKDIV_FRAC_LSB;
|
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
|
// Configure state machines
|
||||||
pio_sm_setup(cd);
|
pio_sm_setup(cd);
|
||||||
|
|
||||||
// Map Rx/Tx gpios
|
// 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_rx, pio_func, 1);
|
||||||
rp2040_gpio_peripheral(cd->gpio_tx, pio_func, 0);
|
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() )
|
// Pull bits from unstuffer (as specified in unstuf_set_count() )
|
||||||
static int
|
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 sb = bu->stuffed_bits, edges = sb ^ (sb >> 1);
|
||||||
uint32_t e2 = edges | (edges >> 1), e4 = e2 | (e2 >> 2), rm_bits = ~e4;
|
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
|
// Return most recent raw (still stuffed) bits
|
||||||
static uint32_t
|
static uint32_t
|
||||||
unstuf_get_raw(struct can2040_bitunstuffer *bu)
|
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
|
// Stuff 'num_bits' bits in '*pb' - upper bits must already be stuffed
|
||||||
static uint32_t
|
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;
|
uint32_t b = *pb, count = num_bits;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -663,34 +590,6 @@ done:
|
|||||||
return count;
|
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
|
// State storage for building bit stuffed transmit messages
|
||||||
struct bitstuffer_s {
|
struct bitstuffer_s {
|
||||||
uint32_t prev_stuffed, bitpos, *buf;
|
uint32_t prev_stuffed, bitpos, *buf;
|
||||||
@@ -1427,12 +1326,6 @@ can2040_setup(struct can2040 *cd, uint32_t pio_num)
|
|||||||
memset(cd, 0, sizeof(*cd));
|
memset(cd, 0, sizeof(*cd));
|
||||||
cd->pio_num = !!pio_num;
|
cd->pio_num = !!pio_num;
|
||||||
cd->pio_hw = cd->pio_num ? pio1_hw : pio0_hw;
|
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
|
// API function to configure callback
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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 */
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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
@@ -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
|
|
||||||
@@ -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
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
|
|
||||||
@@ -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
@@ -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
Reference in New Issue
Block a user