mirror of
https://github.com/MCKero6423/uv-k5-v3-emulator.git
synced 2026-10-02 19:17:24 +00:00
Adds a QEMU machine for the Puya PY32F071 (Cortex-M0+) so Quansheng UV-K5 V3 firmware can run on a PC. The firmware boots to its main loop in about five seconds and the LCD contents are readable. Register layouts come from the vendor CMSIS header shipped with the firmware rather than guesswork. Modelled: RCC, GPIO, ADC, both SPI controllers, DMA1 and the PY25Q16 flash; everything else answers through a logging catch-all, which is how the next thing worth modelling gets identified. Seven things had to be right before it would boot, each found by watching where the firmware stopped: flash aliased at the application offset, clock ready bits, self-clearing ADC calibration, SPI transfer flags, DMA-driven flash reads, SysTick poll acceleration, and the bit-banged transceiver bus idling low. SysTick needs explanation. SYSTICK_DelayUs polls the counter and accumulates differences; under emulation a register read costs far more relative to guest time, so a measured 120 ms delay would have taken about 7.7 hours. Lowering the clock does not help because the bottleneck is loop iterations, not counter speed. Reporting a value that runs ahead of the real counter does, via a new poll-boost property on SysTick. Guest time therefore runs fast during delays: fine for exercising menus and control flow, wrong for judging signal timing. Also includes the host build of the CW timing chain (harness, stubs, shim, tests), which compiles app/cwkeyer.c and app/cwmacro.c unmodified against stub drivers with a virtual clock and scripted paddle input. Known gap: keypad rows reach the firmware's scan and KEYBOARD_Poll returns the right key code, but the UI does not react yet. Not modelled, and not intended to be: radio behaviour. The transceiver chip has no public datasheet, so keying envelopes and emissions need real hardware.
46 lines
1.6 KiB
C
46 lines
1.6 KiB
C
/* Scripted paddle / straight-key input.
|
|
*
|
|
* Replaces the GPIO reads in app/cwhardware.c. A scenario is written as a list
|
|
* of "hold these contacts for N ms" steps, which is how an operator's hand
|
|
* actually looks to the keyer, and the harness plays it against the virtual
|
|
* clock.
|
|
*
|
|
* Contacts are named after the hardware: TIP is dit by default, RING is dah,
|
|
* and the keyer's REVERSED flag swaps them. PTT doubles as the dit paddle in
|
|
* Buttons mode and as the straight key in handkey modes, so it is tracked
|
|
* separately rather than folded into TIP.
|
|
*/
|
|
|
|
#ifndef SIM_PADDLE_H
|
|
#define SIM_PADDLE_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
typedef enum {
|
|
SIM_CONTACT_NONE = 0,
|
|
SIM_CONTACT_TIP = 1u << 0, // dit paddle (PTT in Buttons mode)
|
|
SIM_CONTACT_RING = 1u << 1, // dah paddle (SIDE1 in Buttons mode)
|
|
} SIM_Contact_t;
|
|
|
|
void SIM_PaddleReset(void);
|
|
|
|
// Queues "hold this contact set for duration_ms". Steps play in order; the
|
|
// timeline holds the last state once exhausted (i.e. keys released).
|
|
void SIM_PaddleHold(uint32_t contacts, uint32_t duration_ms);
|
|
|
|
// Convenience for the common "press, then release" pair.
|
|
void SIM_PaddleTap(uint32_t contacts, uint32_t hold_ms, uint32_t gap_ms);
|
|
|
|
// Current contact state, resolved against the virtual clock. The cwhardware
|
|
// stubs call this; tests normally use the queueing functions above.
|
|
uint32_t SIM_PaddleState(void);
|
|
|
|
// True when every queued step has played out.
|
|
bool SIM_PaddleDrained(void);
|
|
|
|
// Total queued duration, so a test can run the clock exactly long enough.
|
|
uint32_t SIM_PaddleTotalMs(void);
|
|
|
|
#endif
|