AP_Periph only instantiates an AP_Airspeed object when
AP_PERIPH_AIRSPEED_ENABLED is set, but AP_AIRSPEED_ENABLED defaulted to
1 regardless, leaving the singleton null on other periph builds. Tie
the two together as is already done for other sensor libraries.
This reverts commit 239f5c1f97.
PR #34359 was merged by mistake. On the bench, with an ESC on
YJUAV_A6SE_H743 output 1, the hold presents a continuous high (full
throttle to a PWM ESC) for about 10.5s after every reset, and for as
long as safety is engaged when output 1 is a motor outside
BRD_SAFETY_MASK, which is Copter's default. The ESC started to enter
throttle calibration. Back the change out until the hold can be
chosen per output.
The STM32 JTAG pins JTDI (PA15) and NJTRST (PB4) come out of every
reset, and out of power-on, with an internal pull-up enabled, and 84
hwdef directories in the tree route a PWM output through one of them:
61 through PA15, 38 through PB4, 15 through both. A servo on such an
output sees the line pulled high from then until firmware reconfigures
the pin. On the H743 that measured 692us to the bootloader's board init
on a warm reset, and 496us and 520us on two cold power-ons. Each is a
pulse inside the range servos accept, so the servo drives to an endpoint
and holds it through the rest of the boot. Nothing can shorten that
window, since no code runs during it.
Give hwdef a HOLD_HIGH keyword for PWM pins. The generator emits such a
pin as a pulled-up input with its timer alternate function preset, and a
HAL_PWM_HOLD_HIGH_MASK of the channels concerned. It rejects the keyword
on any pin RCOutput will never hand back to a timer: anything other than
a PWM(n) timer output in the main configuration, so also an RC input, the
alarm and an ALT(n) pin. One predicate decides both that and which pins
get a mask bit, so the two cannot disagree. The check is made when the
pin is parsed, which covers bootloader builds too. STM32F1 is rejected
outright, since its pin setup does not honour the keyword at all.
RCOutput keeps each channel an input until the first non-zero value is
pushed to it, in the PWM and DShot output paths, and only then switches
the pin to the timer. With the bootloader also keeping the pull-up, the
line is high continuously from the reset until the first real frame,
which for a channel outside BRD_SAFETY_MASK means until safety is
disarmed, so the servo sees a high far longer than any valid pulse
rather than a plausible short one followed by seconds of silence. On
the bench the test servo did not move at all across ten reboots.
The mode switch is an unlocked read-modify-write of the port registers,
and other threads change the modes of other pins on the same port, so
a single write can be lost. The channel is therefore not marked handed
over until a later output finds the pin already in alternate mode; until
then each output writes the mode again, so a lost write is repaired one
output later rather than leaving the pin an input until reboot.
Several other owners of a pad have to take it explicitly, because none
of them goes through the normal output path:
- the alarm driver configures a timer and never the pad, and disables
the group's channels first, so the hold on the alarm's own channel is
released before that. Only that channel: no real frame will ever
arrive for the rest of the group, so a HOLD_HIGH pin among them is
correctly left held;
- soft serial saves and restores the pad's mode, so BLHeli passthrough
hands the pin over when it selects it, or it would save "input" and
transmit nothing;
- neopixel and ProfiLED output is chosen at runtime by SERVOx_FUNCTION
on an ordinary PWM(n) pin and is sent without push_local(), so the
serial LED path releases its group's pads before driving them;
- DShot commands such as beeps are sent before arming, when nothing has
released the pad yet, so the command path releases every channel it
transmits on;
- bidirectional DShot, when enabled at runtime, takes the pads of a
whole group during init, so it clears the pending bit there. A pin
merely declared BIDIR keeps its hold; one whose group has bidirectional
DShot enabled gets no protection. The hwdef allows HOLD_HIGH and BIDIR
together deliberately.
Verified under Renode on YJUAV_A6SE_H743 with ArduPlane: the
application's board init leaves PA15 an input, it stays one through
24.8s of init, and the mode write that puts it on TIM2 lands at the same
microsecond as the first non-zero CCR1 write. With the first mode write
dropped on purpose, the next output found PA15 still an input and wrote
it; unmodified, the second output sees the mode has held and later
outputs no longer read the port. MatekF405-TE and the IOMCU
firmware still build, and a no-mask board's firmware is byte-identical.
Add an early boot service that exports the microSD block device over
USB mass storage before the filesystem and normal flight application
start. The service retains exclusive ownership until a power cycle and
continues servicing the watchdog.
Enable the service on supported boards, provide explicit build control,
increase the MSD worker stacks for the SD wait path, scope the ChibiOS
fixed-width serial warning suppression to the MSD object, and reject
explicit enable requests on unsupported boards.
Three places looped over af_labels asking whether a label named an
alternative function, and none of them used the entry which matched.
str.startswith() takes a tuple of prefixes and does the whole test at
once, so ask it that way instead; two of the three are in
get_alt_function(), which is called for every pin line.
Processing all 443 ChibiOS hwdefs goes from 0.335s to 0.325s. The
parse state of all 490 boards is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
valid_type() still ran nine uncompiled re.match() calls for every pin
line, cross-checking the peripheral number in a pin's type against the
one in its label. Compile them once as class members.
Processing all 443 ChibiOS hwdefs goes from 0.35s to 0.32s. The parse
state of all 490 boards is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every line was split into words three times over: once by
process_file() to see whether it was an include line, once by the
HAL-specific process_line(), and once again by the base
process_line() the HAL-specific one delegates to.
Have process_line() take the words the caller has already split out,
and only split in process_file() for lines which could be an include.
That takes the number of splits done to process all 443 ChibiOS hwdefs
from 140374 to 56389, and the time from 0.40s to 0.35s.
The parse state of all 490 boards, and the hwdef.h, ldscript.ld and
hw.dat generated for a sample of 14 boards and their bootloaders, are
unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
valid_type() rebuilt an 18-entry list of patterns and ran up to 18
uncompiled re.match() calls for every pin line in every hwdef. Compile
the alternation once as a class member instead.
get_mcu_lib() called importlib.import_module() every time, which is
also once per pin line; remember the module we found.
Together these take processing all 443 ChibiOS hwdefs from 0.45s to
0.40s. The parse state of all 490 boards is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shlex is a character-at-a-time lexer written in Python, and it
dominated the cost of processing hwdefs: 65% of the time spent parsing
all 443 ChibiOS hwdefs was inside shlex.split().
Almost no hwdef line needs it. A line containing none of shlex's
special characters, whose whitespace is only spaces and tabs, is split
identically by str.split(); 99.1% of the lines the parser sees qualify.
Take that path when it applies and fall back to shlex otherwise.
Processing all 443 ChibiOS hwdefs goes from 1.39s to 0.45s. The parse
state of all 490 boards, and the hwdef.h, ldscript.ld and hw.dat
generated for a sample of 13 boards and their bootloaders, are
unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ALLOW_ARM_NO_COMPASS has not had the stated effect since Nov 2019; remove it from
the template output of the betaflight hwdef conversion script.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ChibiOS I2Cv4 LLD (used on STM32G0/G4/C0/U0/U3/H5/L4+) uses a
single DMA channel per I2C peripheral, shared between TX and RX,
configured via STM32_I2C_I2Cx_DMA_CHANNEL. The previous code
emitted the separate STM32_I2C_I2Cx_RX/TX_DMA_STREAM defines used
by I2Cv2/I2Cv3, which no longer satisfy the I2Cv4 driver.
Detect I2Cv4 MCUs from CHIBIOS_PLATFORM_MK, allocate a single DMA
channel per I2C peripheral in dma_resolver.py (no _RX/_TX split),
and generate HAL_I2Cn_CONFIG entries that feed the single channel
into the shared DMA slot with SHARED_DMA_NONE as the partner.
Also replace the unconditional STM32_I2C_USE_I2Cn TRUE lines in
stm32g4_mcuconf.h with #ifndef-guarded defaults to FALSE, so
boards only enable the I2C peripherals their hwdef actually uses.
Without this, the I2Cv4 driver demanded DMA channels for every
I2C peripheral on every G4 board regardless of pin usage.
The ROMFS / ROMFS_WILDCARD / ROMFS_DIRECTORY hwdef keywords were
previously only understood by the ChibiOS hwdef parser, which has
duplicated implementations of romfs_add / romfs_wildcard / romfs_add_dir
and a write_ROMFS step that publishes env_vars['ROMFS_FILES']. Lift
all of that into the base HWDef class so any HAL with a hwdef parser
(SITL, future HALs) gets ROMFS handling for free.
- Base hwdef.py owns self.romfs, the romfs_*() methods, the
ROMFS / ROMFS_WILDCARD / ROMFS_DIRECTORY process_line cases,
the undef ROMFS handler, and write_ROMFS(); base run() calls
write_ROMFS() at the end so subclasses that don't override run()
publish ROMFS automatically.
- ChibiOS keeps a thin romfs_add_dir() override solely for its
bootloader-build no-op guard; everything else is inherited.
No behaviour change for ChibiOS boards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
e.g.:
./Tools/autotest/vehicle_test_suite.py:8607:89: B006 Do not use mutable data structures for argument defaults. They are created during function definition time. All calls to the function reuse this one instance of that data structure, persisting changes between them.
Don't get its path unless we are about to generate it to avoid an
inaccurate list of generated files. Do this by keeping track of the path
in a variable instead of checking for the result on the filesystem.
This is somewhat strange and mixed in with the romfs file and
AP_Param.cpp, to disentangle later...
This will allow waf to avoid cleaning them and avoid unnecessary
regenerations.
Some slight changes to centralize the use of the output directory are
also performed.
Read from the passed in variable instead of the global args object. By
default the variable is set to the args object's contents so there is no
change in behavior.
Note that much of this logic is fishy and/or wrong; that is preserved
and left to be fixed another day.
keeps these out of the main codebase which is nice, but also means we can catch these at configure-time rather than build time, which means CI will catch them