libDaisy Documentation

repository·master·Indexed 19 days ago

https://github.com/electro-smith/libdaisy

A hardware abstraction library for the Daisy Audio Platform providing high-level access to audio, MIDI, USB, and peripheral drivers. It includes support for GPIO, serial printing, and audio callback management for hardware such as the Daisy Seed, Field, Patch, Petal, and Pod.

Tokens
21.6K
Snippets
72
Records
97
Agent score
66%

What's inside libDaisy

  1. Overview of libDaisy features

    master

    libDaisy is a hardware abstraction library for the Daisy Audio Platform. It provides high-level access to various hardware components and communication protocols, including:

    • Audio: Configurable audio callbacks for real-time processing.
    • Controls: User-level interface elements like encoders and switches.
    • MIDI: Drivers for MIDI communication.
    • USB Communication: Support for Audio, MIDI, Serial, and more.
    • Peripheral Device Drivers: Low-level access to SPI, I2S, I2C, and GPIO.
  2. Customize bootloader programs with custom linkers

    master

    The default BOOT_SRAM and BOOT_QSPI configurations have specific memory trade-offs (e.g., BOOT_SRAM uses DTCMRAM for code). You can customize these trade-offs by providing a custom linker script (.lds).

    Requirements for Custom Linkers: To be compatible with the Daisy bootloader, your custom linker must place the .isr_vector and .text sections in either the SRAM or QSPI regions.

    Reference Linkers: You can find the default linker scripts in the libDaisy/core folder:

    • STM32H750IB_flash.lds: For standard non-bootloader programs (Internal Flash).
    • Corresponding scripts for BOOT_SRAM and BOOT_QSPI configurations.
  3. Initialize SDRAM-resident buffers

    master

    Because SDRAM is not automatically zeroed and requires hardware initialization, you should not rely on constructors for data setup. Instead, use an Init function pattern.

    1. Call DaisySeed::Init() first.
    2. Call a custom initialization function to zero out or fill your SDRAM buffers.

    Recommended Class Design Pattern: To make classes compatible with large external memory, design them to accept a pointer to a buffer during an initialization phase rather than allocating the buffer internally via a constructor. This allows the buffer to be declared in any memory region (internal or SDRAM) and passed in at runtime.

    // Example of a class designed to accept an external buffer
    class MyClass {
    public:
        void Init(float* buffer, size_t size) {
            internal_buffer_ = buffer;
            buffer_size_ = size;
            // Perform manual initialization/zeroing here
        }
    private:
        float *internal_buffer_;
        size_t buffer_size_;
    };
    
    // Usage:
    DSY_SDRAM_BSS float my_sdram_buffer[1024];
    MyClass my_instance;
    
    // Inside your main setup/init function:
    // 1. DaisySeed::Init();
    // 2. my_instance.Init(my_sdram_buffer, 1024);
  4. Handling hardware dependencies in unit tests using UNIT_TEST macro

    master

    Unit tests run on your development computer, not on the Daisy hardware. Consequently, code that uses inline assembly or hardware peripherals (like GPIO) cannot be tested directly.

    To resolve this, use the UNIT_TEST macro. When the unit test Makefile compiles your code, the UNIT_TEST macro is automatically defined. You can use this macro to provide dummy implementations for hardware-dependent code.

    Example Pattern:

    #ifdef UNIT_TEST
        // Provide a dummy/mock implementation for testing on PC
        void gpio_set_high() { /* do nothing or set a mock flag */ }
    #else
        // Actual hardware implementation
        void gpio_set_high() { /* assembly or register access */ }
    #endif
  5. Print floating point numbers

    master

    By default, floating point support is disabled to save memory. You have three primary ways to print floats:

    1. Enable standard %f support

    If you have sufficient flash memory, you can enable standard printf float support by adding a flag to your application's Makefile. This allows you to use the %f specifier directly.

    Makefile flag: LDFLAGS += -u _printf_float

    2. Use Logger macros (Low memory cost)

    For minimal memory impact, use the FLT_FMT and FLT_VAR macros provided by the Logger class. These allow you to specify precision without the large overhead of the standard float library.

    3. Use FixedCapStr (Flexible/UI focused)

    Use the FixedCapStr class and its AppendFloat method. This is useful for building complex strings or UIs. You must ensure the template size is large enough for the resulting string.

    Note: AppendFloat rounds to 2 decimal places by default unless a second argument is provided.

    // Option 1: Standard %f (requires LDFLAGS += -u _printf_float)
    float my_flt = 123.456f;
    hw.PrintLine("My Float: %f", my_flt);
    
    // Option 2: Macros (Low memory cost)
    // Using fixed precision (3 decimal places)
    hw.PrintLine("My Float: " FLT_FMT3, FLT_VAR3(my_flt));
    // Using generic precision (6 decimal places)
    hw.PrintLine("My Float: " FLT_FMT(6), FLT_VAR(6, my_flt));
    
    // Option 3: FixedCapStr
    FixedCapStr<16> str("Value: ");
    str.AppendFloat(123.456f, 3); // 3 decimal places
    hw.PrintLine(str);
  6. How ADC works in libDaisy

    master

    The Analog to Digital Converter (ADC) is used to read variable signals (e.g., potentiometers, photoresistors) by converting voltage into digital values.

    On most Daisy hardware, ADC inputs expect a signal range of 0V to 3.3V. (Note: Specific hardware like the Daisy Patch SM may have different ranges, such as -5V to 5V for CV inputs).

    In libDaisy, the ADC is managed via the AdcHandle object. The ADC peripheral scans through all configured inputs in the background using multiplexing, which means it does not consume CPU time during the scanning process.

  7. Understand libDaisy namespace prefixes

    master

    libDaisy uses specific prefixes to categorize its API components. Understanding these helps you navigate the library structure:

    • sys: System-level configuration (clocks, DMA, etc.).
    • per: Peripheral-level, internal to the MCU (I2C, SPI, etc.).
    • dev: External device support (external flash chips, DACs, codecs, etc.).
    • hid: User-level interface elements (encoders, switches, audio, etc.).
    • ui: User interface building blocks (menu systems, event queues, etc.).
    • util: Library-level utility elements (not included via daisy.h).
    • daisy: Core API files.
  8. Understand Daisy bootloader behavior and grace period

    master

    Grace Period and LED Indicators

    • Startup: Upon boot, the bootloader enters a 2.5-second 'grace period' indicated by sinusoidal LED blinks. During this time, it listens for DFU transactions over USB and scans connected media (SD/USB) for .bin files.
    • Extending Grace Period: Press the BOOT button to extend the grace period indefinitely. The bootloader will respond with rapid blinks.
    • Errors: If the bootloader encounters an error (e.g., an invalid program), the user LED will emit an SOS pattern.

    Memory and Storage Details

    • QSPI Flash: Programs are stored on the QSPI flash chip starting at address 0x90040000 (the first four 64kB sectors are reserved).
    • SRAM Limits: The bootloader uses 32kB of SRAM for its own processes at the end of the region. Consequently, programs running in SRAM cannot exceed ~480kB.
    • Media Search: The bootloader scans the root directory of connected media for any file ending in .bin. It checks SD cards before USB drives. If a valid .bin is found on an SD card, the USB drive search is skipped.
  9. How GPIO works in libdaisy

    master

    GPIO (General Purpose Input/Output) is used to interact with external digital components like switches, LEDs, and encoders. In libdaisy, GPIO operations are managed through three primary C++ objects:

    1. DaisySeed: A class that manages the hardware on the Seed board. It must be initialized via .Init() before using other peripherals.
    2. Pin: A class used to describe a specific physical pin on the hardware (e.g., D0, D1). These are passed to other objects to define which hardware pin they control.
    3. GPIO: The class used for the basic reading (input) and writing (output) of digital signals (HIGH or LOW states).

    Digital signals have two states: HIGH and LOW.

    using namespace daisy;
    using namespace daisy::seed;
    
    DaisySeed hw;
    GPIO my_button;
    // my_button is now ready to be initialized with a Pin
  10. Set up libDaisy unit testing environment

    master

    To use the integrated unit testing framework, you must first initialize the googletest submodules and install the necessary build tools for your operating system.

    1. Initialize Submodules

    Run the following command to ensure the googletest code is checked out:

    git submodule update --init --recursive

    2. Install Build Tools

    • Linux: Install the build-essential package.
    • macOS: Install Xcode and the Xcode command line tools.
    • Windows: Install Cygwin with the g++, gdb, and make packages. Ensure these are added to your PATH environment variable.
    git submodule update --init --recursive
  11. Enable Serial Printing via USB

    master

    You can log information from your Daisy code to a computer via the built-in USB port. This is an effective way to debug without extra hardware.

    To use this feature:

    1. Initialize the hardware using hw.Init().
    2. Enable logging using hw.StartLog().
    3. Use hw.PrintLine() to send formatted strings to the serial monitor.

    Important: Avoiding missed messages When the Daisy starts, it executes code immediately. If you connect your Serial Monitor after the program has already started, you might miss the initial output. To prevent this, call hw.StartLog(true). This tells the program to wait indefinitely until a USB Host (your computer) connects to the COM port before continuing execution.

    #include "daisy_seed.h"
    using namespace daisy;
    
    DaisySeed hw;
    
    int main(void) {
      hw.Init();
    
      // Passing 'true' makes the program wait for a USB connection
      // before proceeding, ensuring you don't miss early logs.
      hw.StartLog(true);
    
      hw.PrintLine("Hello World!");
    
      while(1) {}
    }