Skip to content

Concepts First

  • AUADC / AUDAC: the audio ADC (capture from microphone) and audio DAC (drive speaker); this page makes them "loop back" — what is captured is played.
  • Sample rate: how many sound samples per second (32 kHz here); higher rate = better quality but more data.
  • PDM vs ADC microphone: PDM mics output a digital pulse stream (enable CONFIG_AUDIO_MIC_PDM_MODE); analog mics use the ADC differential channels.

Example Overview

This page is based on the auadc_audac_loopback example in the official Bouffalo SDK (examples/peripherals/audio/auadc_audac_loopback), which demonstrates audio capture (AUADC) + playback (AUDAC) loopback:

  • AUADC captures the microphone at 32 kHz, 16-bit (DMA-fed); AUDAC plays the same data to the speaker via DMA;
  • Capture and playback each use a DMA channel with LLI lists, forming a record-play loop;
  • Each DMA cycle triggers a callback printing scyle_n:N to show the data flow is continuous.
  • Sibling examples (examples/peripherals/audio/): auadc_record_to_mem (record to memory), audac_play_from_mem (play from memory), audio_dac_adc (DAC+ADC), audio_dac_i2s_rec (DAC playback + I2S recording).

Operation Steps

1
Prepare the Hardware

Connect a microphone to the AUADC capture pins from board_auadc_gpio_init() (the example uses ADC differential channels CH4/CH7) and a speaker/headphone to the AUDAC output pins from board_audac_gpio_init().

2
Enter the Example Directory

Open a terminal and enter the audio example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/peripherals/audio/auadc_audac_loopback
3
Build the Project

Run the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:

make CHIP=bl616 BOARD=bl616dk
4
Flash the Firmware

Connect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
Run and Verify

Open a serial tool (baud rate 2000000). The example prints audac case start, starts capture and playback, and you should hear your voice from the speaker (loopback); the DMA callback prints scyle_n:N per cycle, ending with audac case end.

Code Execution Flow

The complete execution flow from startup to running is shown below (loop arrows mean repeated execution):

APIs Used by the Example

bflb_auadc_init(auadc, config)

Initializes the audio ADC. In struct bflb_auadc_init_config_s: sampling_rate = AUADC_SAMPLING_RATE_32K, input_mode (ADC/PDM), data_format = 16BIT, fifo_threshold.

Parameters:

  • auadc: AUADC device handle (bflb_device_get_by_name("auadc"))
  • config: pointer to the config struct

Return: 0 on success; negative error code on failure

Links the AUADC RX direction to DMA for automatic sample transfer.

Parameters:

  • auadc: AUADC device handle
  • enable: true link / false unlink

Return: 0 on success; negative error code on failure

bflb_auadc_feature_control(auadc, AUADC_CMD_RECORD_START, 0)

Starts/stops recording capture.

Parameters:

  • auadc: AUADC device handle
  • cmd: e.g. AUADC_CMD_RECORD_START

Return: 0 on success; negative error code on failure

bflb_audac_init / bflb_audac_feature_control(AUDAC_CMD_PLAY_START)(...)

Initializes the audio DAC and starts playback (DMA output).

Parameters: same usage as AUADC

Return: 0 on success; negative error code on failure

Complete Code

The complete source below matches the effect described on this page. It is based on the official example (examples/peripherals/audio/auadc_audac_loopback); the LED pins are adapted to the Ai-M61/62-32S-Kit onboard RGB LED. Collapsed by default, click to expand:

📜 Click to expand auadc_audac_loopback/main.c full code
c
#include "bflb_auadc.h"
#include "bflb_audac.h"
#include "bflb_gpio.h"
#include "bflb_dma.h"
#include "board.h"

struct bflb_device_s *auadc_dma_hd;
struct bflb_device_s *audac_dma_hd;

struct bflb_device_s *auadc_hd;
struct bflb_device_s *audac_hd;

struct bflb_dma_channel_lli_pool_s auadc_dma_lli_pool[10];
struct bflb_dma_channel_lli_pool_s audac_dma_lli_pool[10];
uint16_t audio_loopback_buff[8 * 1000];

void auadc_dma_callback(void *arg)
{
    static uint16_t num = 0;
    num++;
    printf("scyle_n:%d\r\n", num);
}

/* audio adc init */
static void auadc_init(void)
{
    /* audio adc config */
    struct bflb_auadc_init_config_s auadc_init_cfg = {
        .sampling_rate = AUADC_SAMPLING_RATE_32K,
#ifdef CONFIG_AUDIO_MIC_PDM_MODE
        .input_mode = AUADC_INPUT_MODE_PDM_L,
#else
        .input_mode = AUADC_INPUT_MODE_ADC,
#endif
        .data_format = AUADC_DATA_FORMAT_16BIT,
        .fifo_threshold = 3,
    };

    /* auadc init */
    auadc_hd = bflb_device_get_by_name("auadc");
    bflb_auadc_init(auadc_hd, &auadc_init_cfg);
#ifndef CONFIG_AUDIO_MIC_PDM_MODE
    /* audio adc analog config */
    struct bflb_auadc_adc_init_config_s auadc_analog_init_cfg = {
        .auadc_analog_en = true,
        .adc_mode = AUADC_ADC_MODE_AUDIO,
        .adc_pga_mode = AUADC_ADC_PGA_MODE_AC_DIFFER,
        .adc_pga_posi_ch = AUADC_ADC_ANALOG_CH_4,
        .adc_pga_nega_ch = AUADC_ADC_ANALOG_CH_7,
        .adc_pga_gain = 21,
    };
    bflb_auadc_adc_init(auadc_hd, &auadc_analog_init_cfg);
#endif
    /* auadc enable dma */
    bflb_auadc_link_rxdma(auadc_hd, true);
}

/* audio adc dma init */
void auadc_dma_init(void)
{
    uint32_t dma_lli_cnt;
    struct bflb_dma_channel_lli_transfer_s transfers[1];
    struct bflb_dma_channel_config_s auadc_dma_cfg;

    auadc_dma_cfg.direction = DMA_PERIPH_TO_MEMORY;
    auadc_dma_cfg.src_req = DMA_REQUEST_AUADC_RX;
    auadc_dma_cfg.dst_req = DMA_REQUEST_NONE;
    auadc_dma_cfg.src_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
    auadc_dma_cfg.dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
    auadc_dma_cfg.src_burst_count = DMA_BURST_INCR4;
    auadc_dma_cfg.dst_burst_count = DMA_BURST_INCR4;
    auadc_dma_cfg.src_width = DMA_DATA_WIDTH_16BIT;
    auadc_dma_cfg.dst_width = DMA_DATA_WIDTH_16BIT;

    auadc_dma_hd = bflb_device_get_by_name("dma0_ch0");
    bflb_dma_channel_init(auadc_dma_hd, &auadc_dma_cfg);
    bflb_dma_channel_irq_attach(auadc_dma_hd, auadc_dma_callback, NULL);

    transfers[0].src_addr = (uint32_t)DMA_ADDR_AUADC_RDR;
    transfers[0].dst_addr = (uint32_t)audio_loopback_buff;
    transfers[0].nbytes = sizeof(audio_loopback_buff);

    dma_lli_cnt = bflb_dma_channel_lli_reload(auadc_dma_hd, auadc_dma_lli_pool, 10, transfers, 1);
    bflb_dma_channel_lli_link_head(auadc_dma_hd, auadc_dma_lli_pool, dma_lli_cnt);
}

/* audio dac init */
static void audac_init(void)
{
    /* audio dac config */
    struct bflb_audac_init_config_s audac_init_cfg = {
        .sampling_rate = AUDAC_SAMPLING_RATE_32K,
        .output_mode = AUDAC_OUTPUT_MODE_PWM,
        .source_channels_num = AUDAC_SOURCE_CHANNEL_SINGLE,
        .mixer_mode = AUDAC_MIXER_MODE_ONLY_L,
        .data_format = AUDAC_DATA_FORMAT_16BIT,
        .fifo_threshold = 3,
    };

    /* audio dac volume config */
    struct bflb_audac_volume_config_s audac_volume_cfg = {
        .mute_ramp_en = true,
        .mute_up_ramp_rate = AUDAC_RAMP_RATE_FS_32,
        .mute_down_ramp_rate = AUDAC_RAMP_RATE_FS_8,
        .volume_update_mode = AUDAC_VOLUME_UPDATE_MODE_RAMP,
        .volume_ramp_rate = AUDAC_RAMP_RATE_FS_128,
        .volume_zero_cross_timeout = AUDAC_RAMP_RATE_FS_128,
    };

    /* audac init */
    audac_hd = bflb_device_get_by_name("audac");
    bflb_audac_init(audac_hd, &audac_init_cfg);
    bflb_audac_feature_control(audac_hd, AUDAC_CMD_SET_VOLUME_VAL, (size_t)(-5 * 2));
    bflb_audac_volume_init(audac_hd, &audac_volume_cfg);
    /* audac enable dma */
    bflb_audac_link_rxdma(audac_hd, true);
}

/* audio dac dma init */
void audac_dma_init(void)
{
    uint32_t dma_lli_cnt;
    struct bflb_dma_channel_lli_transfer_s transfers[1];
    struct bflb_dma_channel_config_s audac_dma_cfg;

    audac_dma_cfg.direction = DMA_MEMORY_TO_PERIPH;
    audac_dma_cfg.src_req = DMA_REQUEST_NONE;
    audac_dma_cfg.dst_req = DMA_REQUEST_AUDAC_TX;
    audac_dma_cfg.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
    audac_dma_cfg.dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
    audac_dma_cfg.src_burst_count = DMA_BURST_INCR4;
    audac_dma_cfg.dst_burst_count = DMA_BURST_INCR4;
    audac_dma_cfg.src_width = DMA_DATA_WIDTH_16BIT;
    audac_dma_cfg.dst_width = DMA_DATA_WIDTH_16BIT;

    audac_dma_hd = bflb_device_get_by_name("dma0_ch1");
    bflb_dma_channel_init(audac_dma_hd, &audac_dma_cfg);

    transfers[0].src_addr = (uint32_t)audio_loopback_buff;
    transfers[0].dst_addr = (uint32_t)DMA_ADDR_AUDAC_TDR;
    transfers[0].nbytes = sizeof(audio_loopback_buff);

    dma_lli_cnt = bflb_dma_channel_lli_reload(audac_dma_hd, audac_dma_lli_pool, 10, transfers, 1);
    bflb_dma_channel_lli_link_head(audac_dma_hd, audac_dma_lli_pool, dma_lli_cnt);
}

int main(void)
{
    /* board init */
    board_init();

    printf("audac case start \r\n");

    /* gpio init */
    board_auadc_gpio_init();
    board_audac_gpio_init();

    /* auadc init */
    auadc_init();
    auadc_dma_init();
    bflb_dma_channel_start(auadc_dma_hd);
    bflb_auadc_feature_control(auadc_hd, AUADC_CMD_RECORD_START, 0);

    /* delay 10ms */
    bflb_mtimer_delay_ms(10);

    /* audac init */
    audac_init();
    audac_dma_init();
    bflb_dma_channel_start(audac_dma_hd);
    bflb_audac_feature_control(audac_hd, AUDAC_CMD_PLAY_START, 0);

    printf("audac case end \r\n");

    while (1) {
        bflb_mtimer_delay_ms(1);
    }
}

FAQ

No sound from the speaker

Check the microphone/speaker wiring (capture CH4/CH7, AUDAC output pins), that bflb_dma_channel_start was called, and that the DMA interrupt works; PDM mics require CONFIG_AUDIO_MIC_PDM_MODE and PDM pins.

scyle_n does not increase

The DMA callback is not firing: check for a busy DMA channel, a too-small LLI pool (auadc_dma_lli_pool[10]), or a mismatch between the FIFO threshold and the DMA config.

Echo or low volume

The loopback example has no noise reduction or gain — that is expected. Adjust the microphone gain adc_pga_gain in bflb_auadc_adc_init.

Have questions?

For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23