MAVLINKHUD

HAL & Hardware Drivers

Executive Summary

The power of AP_HAL_ChibiOS lies in its ability to abstract complex STM32 peripherals into standard interfaces. The most complex and critical part of this is the Shared DMA system, which allows ArduPilot to use more peripherals than there are DMA channels available.

Theory & Concepts

1. The ChibiOS HAL Driver Model

ChibiOS provides "High Level Drivers" (e.g., SerialDriver, SPIDriver) that abstract the register interface.

  • Config: Drivers are configured with Config structs (baud rate, start bits) passed to sdStart().
  • Events: Drivers trigger callbacks or wake threads on interrupts (TX Complete, RX Not Empty).

2. DMA Contention

STM32 chips have limited DMA Streams (e.g., 2 controllers x 8 streams = 16 streams).

  • The Problem: We might have 10 UARTs, 3 SPIs, 2 I2Cs, and SD Card. That's > 16 potential users.
  • The Solution: Shared_DMA. A custom ArduPilot allocator that assigns a DMA channel to a driver only while a transaction is active and releases it immediately after.

3. Bounce Buffers

DMA engines access RAM directly. If that RAM is in a specific region (like CCM RAM on F4) or cached (D-Cache on F7/H7), DMA fails.

  • Solution: We use "Bounce Buffers" in DMA-safe memory regions (SRAM1/2).
  • Flow: User Buffer -> memcpy -> Bounce Buffer -> DMA -> Peripheral.

Codebase Investigation

1. UART Driver: UARTDriver.cpp

Located in libraries/AP_HAL_ChibiOS/UARTDriver.cpp.

  • Initialization: Calls sdStart(&SDx, &config).
  • RX: Uses a dedicated thread (uart_rx_thread) or 1kHz timer (_rx_timer_tick) to move data from the ChibiOS ring buffer to the ArduPilot ring buffer.
  • DMA: dmaStreamAllocI requests a stream.

2. Shared DMA: shared_dma.cpp

This file implements a mutex-protected allocator.

  • Locking: Before starting an SPI transaction, the bus driver calls dma.lock().
  • Contention: If the DMA stream is busy (used by another peripheral on the same stream ID), it blocks until available.

3. SPI Device: SPIDevice.cpp

Handles the complexity of the "Device" abstraction (Sensors on a Bus).

  • Transactions: get_semaphore()->take() ensures exclusive access to the bus (e.g., MPU6000 and Barometer on the same SPI bus).
  • CS Pin: Manually toggles the Chip Select pin via palClearLine(dev->cs_pin).

Source Code Reference

Practical Guide: Porting Drivers

If you are adding a new sensor:

  1. Don't write raw registers.
  2. Use AP_HAL::get_HAL().spi->device(...) to get a handle.
  3. Respect the semaphore. Always wrap transfers in bus->get_semaphore()->take().