Two-board STM32 system that wirelessly drives a small RC car from a joystick, written bare-metal at the register level.
STM32L476 (Cortex-M4) Bare-Metal C nRF24L01 SPI PWM ADC
Highlights
Programmed two NUCLEO-L476RG boards bare-metal with no HAL: a joystick controller and the car itself.
Wrote register-level SPI and nRF24L01 drivers (2 Mbps, 2-byte payloads). Control stayed stable out to about 500 ft, reaching about 1,000 ft with clear line of sight.
The controller samples the joystick through a 10-bit ADC and transmits on a 400 µs SysTick interrupt; the ISR takes about 180 µs, leaving headroom for context switching.
On the car, a falling-edge EXTI interrupt from the radio’s IRQ pin handles each payload, so the main loop stays empty.
Drove the motors with 2 kHz PWM from TIM2 (80 MHz ÷ 40 ÷ 1000), with the duty cycle set by the joystick.
Wrote a 4-bit-mode driver for a 16×2 LCD through a 3.3 V → 5 V level shifter.
Measured the motor step response and calculated Cohen–Coon PID gains for a planned cruise-control loop using encoder feedback.
Gallery
Select any image or video to view it full size.
Fig. 1 The assembled car driving outdoors, controlled from the joystick board.
Fig. 2 The car: a NUCLEO-L476RG, an nRF24L01 radio with external antenna, and an H-bridge motor driver on a two-wheel chassis.
Fig. 3 The 16×2 LCD running in 4-bit mode through 3.3 V → 5 V level shifters.
Fig. 4 ADC test: the on-board LED turns off when the joystick pulls the input below mid-scale (1.65 V).
Fig. 5 SysTick timing: the ISR (yellow) that reads the ADC and transmits runs about 176 µs of every 400 µs period.
Fig. 6 SPI clock (yellow) and MOSI (cyan) sending the test message “Jose” to the nRF24L01.
Fig. 7 Debugging both boards at once: the transmitter (right) sends “Jose” and it lands in the receiver’s rxBuffer (left), with status 0x40 flagging the new payload.
Fig. 8 Transmit (cyan) and receive (yellow) IRQ lines: about 5 µs from send to receive with the radios a foot apart.
Fig. 9 PWM output test at a 50% duty cycle.