mirror of
https://github.com/ArduPilot/ardupilot.git
synced 2026-10-06 19:00:27 +08:00
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.