1/*
  2 * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
  3 *
  4 * SPDX-License-Identifier: Apache-2.0
  5 * 
  6 */
  7
  8#include <stdlib.h>
  9#include <string.h>
 10
 11#include "hardware/adc.h"
 12#include "hardware/clocks.h"
 13#include "hardware/dma.h"
 14#include "hardware/irq.h"
 15
 16#include "pico/analog_microphone.h"
 17
 18#define ANALOG_RAW_BUFFER_COUNT 2
 19
 20static struct {
 21    struct analog_microphone_config config;
 22    int dma_channel;
 23    uint16_t* raw_buffer[ANALOG_RAW_BUFFER_COUNT];
 24    volatile int raw_buffer_write_index;
 25    volatile int raw_buffer_read_index;
 26    uint buffer_size;
 27    int16_t bias;
 28    uint dma_irq;
 29    analog_samples_ready_handler_t samples_ready_handler;
 30} analog_mic;
 31
 32static void analog_dma_handler();
 33
 34int analog_microphone_init(const struct analog_microphone_config* config) {
 35    memset(&analog_mic, 0x00, sizeof(analog_mic));
 36    memcpy(&analog_mic.config, config, sizeof(analog_mic.config));
 37
 38    if (config->gpio < 26 || config->gpio > 29) {
 39        return -1;
 40    }
 41
 42    size_t raw_buffer_size = config->sample_buffer_size * sizeof(analog_mic.raw_buffer[0][0]);
 43
 44    analog_mic.buffer_size = config->sample_buffer_size;
 45    analog_mic.bias = ((int16_t)((config->bias_voltage * 4095) / 3.3));
 46
 47    for (int i = 0; i < ANALOG_RAW_BUFFER_COUNT; i++) {
 48        analog_mic.raw_buffer[i] = malloc(raw_buffer_size);
 49        if (analog_mic.raw_buffer[i] == NULL) {
 50            analog_microphone_deinit();
 51
 52            return -1;   
 53        }
 54    }
 55
 56    analog_mic.dma_channel = dma_claim_unused_channel(true);
 57    if (analog_mic.dma_channel < 0) {
 58        analog_microphone_deinit();
 59
 60        return -1;
 61    }
 62
 63    float clk_div = (clock_get_hz(clk_adc) / (1.0 * config->sample_rate)) - 1;
 64
 65    dma_channel_config dma_channel_cfg = dma_channel_get_default_config(analog_mic.dma_channel);
 66
 67    channel_config_set_transfer_data_size(&dma_channel_cfg, DMA_SIZE_16);
 68    channel_config_set_read_increment(&dma_channel_cfg, false);
 69    channel_config_set_write_increment(&dma_channel_cfg, true);
 70    channel_config_set_dreq(&dma_channel_cfg, DREQ_ADC);
 71
 72    analog_mic.dma_irq = DMA_IRQ_0;
 73
 74    dma_channel_configure(
 75        analog_mic.dma_channel,
 76        &dma_channel_cfg,
 77        analog_mic.raw_buffer[0],
 78        &adc_hw->fifo,
 79        analog_mic.buffer_size,
 80        false
 81    );
 82
 83    adc_gpio_init(config->gpio);
 84
 85    adc_init();
 86    adc_select_input(config->gpio - 26);
 87    adc_fifo_setup(
 88        true,    // Write each completed conversion to the sample FIFO
 89        true,    // Enable DMA data request (DREQ)
 90        1,       // DREQ (and IRQ) asserted when at least 1 sample present
 91        false,   // We won't see the ERR bit because of 8 bit reads; disable.
 92        false    // Don't shift each sample to 8 bits when pushing to FIFO
 93    );
 94
 95    adc_set_clkdiv(clk_div);
 96}
 97
 98void analog_microphone_deinit() {
 99    for (int i = 0; i < ANALOG_RAW_BUFFER_COUNT; i++) {
100        if (analog_mic.raw_buffer[i]) {
101            free(analog_mic.raw_buffer[i]);
102
103            analog_mic.raw_buffer[i] = NULL;
104        }
105    }
106
107    if (analog_mic.dma_channel > -1) {
108        dma_channel_unclaim(analog_mic.dma_channel);
109
110        analog_mic.dma_channel = -1;
111    }
112}
113
114int analog_microphone_start() {
115    irq_set_enabled(analog_mic.dma_irq, true);
116    irq_set_exclusive_handler(analog_mic.dma_irq, analog_dma_handler);
117
118    if (analog_mic.dma_irq == DMA_IRQ_0) {
119        dma_channel_set_irq0_enabled(analog_mic.dma_channel, true);
120    } else if (analog_mic.dma_irq == DMA_IRQ_1) {
121        dma_channel_set_irq1_enabled(analog_mic.dma_channel, true);
122    } else {
123        return -1;
124    }
125
126    analog_mic.raw_buffer_write_index = 0;
127    analog_mic.raw_buffer_read_index = 0;
128
129    dma_channel_transfer_to_buffer_now(
130        analog_mic.dma_channel,
131        analog_mic.raw_buffer[0],
132        analog_mic.buffer_size
133    );
134
135    adc_run(true); // start running the adc
136}
137
138void analog_microphone_stop() {
139    adc_run(false); // stop running the adc
140
141    dma_channel_abort(analog_mic.dma_channel);
142
143    if (analog_mic.dma_irq == DMA_IRQ_0) {
144        dma_channel_set_irq0_enabled(analog_mic.dma_channel, false);
145    } else if (analog_mic.dma_irq == DMA_IRQ_1) {
146        dma_channel_set_irq1_enabled(analog_mic.dma_channel, false);
147    }
148
149    irq_set_enabled(analog_mic.dma_irq, false);
150}
151
152static void analog_dma_handler() {
153    // clear IRQ
154    if (analog_mic.dma_irq == DMA_IRQ_0) {
155        dma_hw->ints0 = (1u << analog_mic.dma_channel);
156    } else if (analog_mic.dma_irq == DMA_IRQ_1) {
157        dma_hw->ints1 = (1u << analog_mic.dma_channel);
158    }
159
160    // get the current buffer index
161    analog_mic.raw_buffer_read_index = analog_mic.raw_buffer_write_index;
162
163    // get the next capture index to send the dma to start
164    analog_mic.raw_buffer_write_index = (analog_mic.raw_buffer_write_index + 1) % ANALOG_RAW_BUFFER_COUNT;
165
166    // give the channel a new buffer to write to and re-trigger it
167    dma_channel_transfer_to_buffer_now(
168        analog_mic.dma_channel,
169        analog_mic.raw_buffer[analog_mic.raw_buffer_write_index],
170        analog_mic.buffer_size
171    );
172
173    if (analog_mic.samples_ready_handler) {
174        analog_mic.samples_ready_handler();
175    }
176}
177
178void analog_microphone_set_samples_ready_handler(analog_samples_ready_handler_t handler) {
179    analog_mic.samples_ready_handler = handler;
180}
181
182int analog_microphone_read(int16_t* buffer, size_t samples) {
183    if (samples > analog_mic.config.sample_buffer_size) {
184        samples = analog_mic.config.sample_buffer_size;
185    }
186
187    if (analog_mic.raw_buffer_write_index == analog_mic.raw_buffer_read_index) {
188        return 0;
189    }
190
191    uint16_t* in = analog_mic.raw_buffer[analog_mic.raw_buffer_read_index];
192    int16_t* out = buffer;
193    int16_t bias = analog_mic.bias;
194
195    analog_mic.raw_buffer_read_index++;
196
197    for (int i = 0; i < samples; i++) {
198        *out++ = *in++ - bias;
199    }
200
201    return samples;
202}