USB CDC (Virtual COM Port) adapter for SAE J2716 SENT sensors. Talks to the host using the SLCAN text protocol over any serial terminal.
| MCU | STM32F042G4UX (UFQFPN28, 48 MHz HSI48, no crystal) |
| PA2 | SENT RX — TIM2 CH3 input capture, internal pull-up |
| PA4 | SENT TX — GPIO push-pull driven by TIM14 |
| USB | Full-speed CDC (Virtual COM Port, no driver needed on Win10+) |
Sensor ──SENT signal──► PA2 (TIM2 CH3, RISING edge capture)
│
ISR: timestamp stored in RX HAL ring buffer
│
Main loop: bridge decodes batch of 10 edges
│
SLCAN 't' frame → USB → host
- Host sends
O\r— bridge enters RX mode. - Every RISING edge on PA2 triggers the TIM2 CH3 capture ISR, which records the raw 16-bit counter value. A separate overflow ISR extends timestamps across the 16-bit rollover (~1.4 ms at 48 MHz).
SentApp_Process()(main loop) polls the RX HAL for complete batches of 10 edges: sync + status + 6 data nibbles + CRC + leading edge of next interval.- The bridge converts timestamps to µs intervals, extracts nibbles, validates CRC, and produces a CAN frame.
- The frame is serialised as
t<ID><DLC><data>\rand flushed to USB.
Default config (MLX90377): 3 µs/tick, 6 nibbles, DATA_ONLY CRC, seed 0x03, output CAN ID 0x510.
Host ──SLCAN 't' frame──► USB CDC RX callback (ISR context)
│
bridge → TX HAL queue
tim14_kick(): start TIM14 if idle
│
TIM14 ISR fires once per half-interval (3 µs/tick):
Phase 0: pop next interval → PA4 LOW for 15 µs
Phase 1: PA4 HIGH for remaining ticks
Repeat until queue empty → PA4 idle HIGH, TIM14 stops
- TIM14 prescaler 143 → 1 tick = 3 µs (1 SENT tick at standard rate).
- Each SENT interval = 15 µs active-LOW pulse + HIGH for
(N − 5)ticks. - The bridge builds the full interval sequence (sync 56T + status + nibbles + CRC + 12T pause) and pushes it to the TX HAL. The ISR drains it without any main-loop involvement.
Switch to TX mode and send one frame:
O\r open SLCAN channel
t600102\r start TX mode
t52050100123456\r transmit: status=1, nibbles [1,2,3,4,5,6]
| Command | Effect |
|---|---|
O\r |
Open — start SENT RX |
C\r |
Close — stop |
V\r / v\r |
Hardware / firmware version |
N\r |
Serial number (unique per MCU, XOR-fold of 96-bit UID) |
F\r |
Status flags |
t<ID><DLC><data>\r |
Send CAN frame |
Control frames (CAN ID 0x600, DLC = 1):
| data[0] | Action |
|---|---|
01 |
Start RX |
02 |
Start TX |
03 |
Stop |
04 |
Learn tick period / nibble count / CRC mode from live signal |
TX data frame (CAN ID 0x520):
t52050100123456\r
↑↑ ↑
││ └─ status byte then data nibbles packed (status=0x01, nibbles=0x00,0x12,0x34,0x56)
│└─── DLC = 5
└──── CAN ID 0x520
Decoded RX frame (device → host, default CAN ID 0x510):
t5103AABBCC\r DLC=3, 3 bytes = 6 nibbles packed high-nibble-first
| Script | Purpose |
|---|---|
sent_MLX - Copy.py |
MLX90377 GUI — live angle gauge, decoded nibbles, frame log. Connect sensor to PA2, open port in the GUI; data appears automatically. |
sent_viewer.py |
General SLCAN monitor GUI with TX panel and Learn mode |
sent_test.py |
CLI — open port, apply config, print received frames for N seconds |
check_rx.py |
Loopback sanity check: pings COM8 (TX), listens on COM9 (RX) |
tx_signal.py |
Continuous TX every 10 ms for logic-analyzer capture on PA4 |
verify_sent.py |
Logic2 automation: capture PA4 signal and cross-check with USB output |
pip install pyserial
verify_sent.py additionally needs pip install saleae and Logic2 running with the automation API enabled (port 10430).
python "sent_MLX - Copy.py"
Select the COM port, click Connect. Firmware auto-starts RX on O\r; angle and magnitude update live.
STM32CubeIDE manages the Makefile. To build from the command line, set the paths to the bundled tools and run make inside the Debug folder:
set MAKE=C:\ST\STM32CubeIDE_1.19.0\STM32CubeIDE\plugins\com.st.stm32cube.ide.mcu.externaltools.make.win32_2.2.0.202409170845\tools\bin\make.exe
set GCC=C:\ST\STM32CubeIDE_1.19.0\STM32CubeIDE\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.13.3.rel1.win32_1.0.0.202411081344\tools\bin
set PATH=%GCC%;%PATH%
cd Debug
%MAKE% -j4 allOutput: Debug/SENTToUSB.elf (~28 KB flash, ~6 KB RAM on STM32F042G4 with 32 KB flash / 6 KB SRAM).
set CLI=C:\ST\STM32CubeIDE_1.19.0\STM32CubeIDE\plugins\com.st.stm32cube.ide.mcu.externaltools.cubeprogrammer.win32_2.2.200.202503041107\tools\bin\STM32_Programmer_CLI.exe
%CLI% -c port=SWD freq=4000 -w Debug/SENTToUSB.elf -v -hardRstStep 1 — trigger DFU from the running device (replace COM8 with your port):
python -c "import serial,time; s=serial.Serial('COM8',115200,timeout=1); s.write(b'B\r'); time.sleep(0.3); s.close()"
Step 2 — flash (~3 s after step 1, once the device enumerates as DFU):
%CLI% -c port=USB1 -w Debug/SENTToUSB.elf -v -hardRstThe B command writes a magic value (0xDEADBEEF) to a .noinit RAM variable and resets.
On the next boot the firmware detects the magic, clears it, and jumps to the STM32F042 ROM bootloader at 0x1FFFC400.