模拟电路工程化设计课程 第一部分 音频信号采集前端电路
模拟电路工程化设计课程 第一部分 阶段性报告 基本实现了一个音频信号采集前端电路 信号幅度0.1mVpp-1Vpp 信号频率 100Hz-16kHz
标签
嵌入式系统
ESP32
模拟电路
模拟前端
模拟电路工程化设计课程
hucuyuu
更新2023-03-21
1105

模拟电路工程化设计课程 第一部分 音频信号采集前端电路 实物展示

N+4AAAAASUVORK5CYII=

实现功能

测量音频信号,音频信号从3.5mm接口的1,4号引脚输入

信号幅度0.1mVpp-1Vpp

信号频率 100Hz-16kHz

搭建一个带通滤波器,滤除高频和低频部分

使用esp32c3单片机采集信号,并通过串口发送到电脑。

使用vofa上位机软件显示信号波形。

j8OHMwKDub+qAAAAABJRU5ErkJggg==

输入信号 16kHz 0.2Vpp单片机的采样率偏小,输入频率增大的时候可以观察到混叠现象。

HwlmVJ8KJbWqAAAAAElFTkSuQmCC

输入信号 1kHz 0.2Vpp

qgLhHvAAAAAElFTkSuQmCC

音频信号的采集,这里截图不是很清楚,视频中可以看到音频信号的特征还是很明显的。

原理思路

1.运放的选型。由于选择的电源电压是3.3V,尽量选择轨到轨的运放,是能充分使用更大的信号范围。其次,注意到adalp2000里的运放,增益带宽积区别很大。ad8542 分Gain Bandwidth Product是1MHz,可以满足题目要求。

xtwrskSZTWiUAAAAABJRU5ErkJggg==

hdhI9ZOGerMAAAAASUVORK5CYII=

2.供电部分使用单片机开发板上引出的3.3V电源。运放使用单电源供电。单电源供电会损失一定的性能,达不到手册中标注的增益带宽积,但是使用单电源供电电路简单,并且不需要负电源。

D9VtXxzgKVf3gAAAABJRU5ErkJggg==

3.信号抬升,使用两个47k电阻分压,获得3.3V/2 = 1.65V电压,输入的电压抬升至1.65V。并且使用一个1uF的去耦电容,滤除来自电源的噪声。这里R5//R4和C3也能看成是一组高通滤波器,C3选个大一点的电阻就可以确保截止频率在通带外。H73ahvtBfUTjAAAAAElFTkSuQmCC

4.滤波器的计算。这里选择一阶巴特沃斯滤波器。巴特沃斯滤波器的特点是通频带内的频率响应曲线最大限度平坦,没有纹波,而在阻频带则逐渐下降为零。其次,一阶巴特沃斯滤波器使用f=1/2*pi*R*C这个公式来确定截止频率。由于adalp2000的套件内电阻电容有限,这里用一个excel表格来计算能使用的电阻和电容。

w8vxAIlw852ugAAAABJRU5ErkJggg==D+hme0v0CZwlAAAAAElFTkSuQmCC

ZpIabOMbvzUAAAAASUVORK5CYII=

5.计算放大倍数。ad7920输入电压范围时0-Vdd,这里选择了3.3V电压,ad7920最小能分辨的电压为3.3/2^12 = 0.81mV。如果要测到0.1mVpp的电压,再留一定的裕量,运放的放大倍数为10倍,可以满足题目要求。电压放大倍数约为Rf/Rg。C1与R1串联可以隔离反馈环路中的直流信号,仅放大交流信号。最终的输出电压表达式为Vout=1.65+10*Vin。

这里要满足G=Rf/Rg的近似,需要Rg和C1组成的高通滤波器,截止频率在通带外。这里满足100Hz的要求即可。参考了ad4891数据手册中的一个typical design。

5jrG06Bur7kAAAAASUVORK5CYII=

egAAAAASUVORK5CYII=

6.阻抗匹配。使用一个1Mohm的电阻进行阻抗匹配,此时前端电路的输入电阻约为1Mohm。

Qn8P8oCr6al0KiiAAAAAElFTkSuQmCC

单片机代码

单片机平台使用合宙esp32-c3开发板,开发板自带一个串口芯片,通过uart0来下载代码合调试代码。单片机通过spi接口连接ad7920,这里有一个坑,esp32的spi有四种spi mode,官网手册里没有写出这四种spi mode 的区别,最后在csdn找到了解决方法。不同的spi mode用于data信号和cs信号的极性匹配,对照spi mode 和ad7920手册中的时序图,可以选出正确的spi mode。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_log.h"

#define PIN_NUM_MISO 10
#define PIN_NUM_MOSI -1
#define PIN_NUM_CLK 2
#define PIN_NUM_CS 7
#define AD7920_HOST SPI2_HOST
#define PIN_LED0 12
#define PIN_LED1 13
#define GPIO_OUTPUT_PIN_SEL ((1ULL << PIN_LED0) | (1ULL << PIN_LED1))
#define PIN_BOOT0 9
#define GPIO_INPUT_PIN_SEL (1ULL << PIN_BOOT0)
#define ESP_INTR_FLAG_DEFAULT 0

void gpio_output_init()
{
    gpio_config_t io_conf = {};
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);
}

static xQueueHandle gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    uint32_t gpio_num = (uint32_t)arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

void gpio_input_init()
{
    gpio_config_t io_conf = {};
    io_conf.intr_type = GPIO_INTR_ANYEDGE;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);
}

static void gpio_task_example(void *arg)
{
    uint32_t io_num;
    for (;;)
    {
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY))
        {
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
        }
    }
}

void gpio_isr_init()
{
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    gpio_isr_handler_add(PIN_BOOT0, gpio_isr_handler, (void *)PIN_BOOT0);
}

void app_main(void)
{
    gpio_output_init();
    gpio_input_init();
    gpio_isr_init();
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
    // spi
    esp_err_t ret;
    spi_device_handle_t spi;
    spi_bus_config_t buscfg = {
        .miso_io_num = PIN_NUM_MISO,
        .mosi_io_num = PIN_NUM_MOSI,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 16 * 10};
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 10 * 1000 * 1000, // kHz
        .mode = 3,                          // SPI mode
        .spics_io_num = PIN_NUM_CS,         // CS pin
        .queue_size = 10,                   //
    };

    // Initialize the SPI bus
    ret = spi_bus_initialize(AD7920_HOST, &buscfg, SPI_DMA_CH_AUTO);
    ESP_ERROR_CHECK(ret);
    // Attach the device to the SPI bus
    ret = spi_bus_add_device(AD7920_HOST, &devcfg, &spi);
    ESP_ERROR_CHECK(ret);
    printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());

    int cnt = 0;
    while (1)
    {
        // printf("cnt: %d\n", cnt++);
        vTaskDelay(0.01 / portTICK_RATE_MS);
        // gpio_set_level(PIN_LED0, !(cnt % 2));
        // gpio_set_level(PIN_LED1, cnt % 2);
        spi_transaction_t t = {
            .length = 16,
            .rxlength = 0, // 0 defaults this to the value of length
            .flags = SPI_TRANS_USE_RXDATA,
        };

        spi_device_polling_transmit(spi, &t);
        uint16_t receivedData = 0x00;
        receivedData += (t.rx_data[0] << 8);
        receivedData += t.rx_data[1];
        double voltage = receivedData * 1.0 / 4096 * 3.3;
        printf("%lf\r\n", voltage);
    }

}

仿真结果

瞬态仿真主要观察信号的时域特征,重点观察信号有没有正确抬升至1.65V。

b77wAAAAASUVORK5CYII=

16kHz 0.2Vpp

+f8BIzFLgTQgh+cAAAAASUVORK5CYII=

100Hz 0.2Vpp

w9ABrOKawxi+QAAAABJRU5ErkJggg==

2kHz 0.2Vpp

8fzDfbXN2tRGgAAAAASUVORK5CYII=

频域仿真重点观察通带内的幅频响应,100Hz-16kHz通带内的平坦度。在100Hz和16kHz处应该出现3dB的衰减。

频域仿真放大倍数理论上应为20dB,但是仿真曲线显示放大倍数只有1dB左右,曲线特征基本正确

遇到的问题

  1. 在ad7920的Vin脚测得的信号中包含了一个高频的毛刺。示波器开启20MHz的软件滤波器后,毛刺现象减弱。这个毛刺也很头疼,不知道从哪里来的。

zIAAAAASUVORK5CYII=

  1. 无法测量小信号,手头上没有能产生0.1mVpp的信号发生器。手上最小只能测到0.2Vpp,没有能成功测试到最小分辨信号。而且使用3.5mm音频口产生信号会有一个10mVpp噪声,电脑此时静音,这个噪声不知道怎么消除。推测这个信号来自与电源,某种程度上和usb解耦耦合。P9tHEH5FpqiHgAAAABJRU5ErkJggg==
  2. 没有做增益的切换,理论上10x和1x增益可以实现0.1mVpp-1Vpp的要求。尝试了使用mosFET搭建模拟开关,但是没有成功。如果使用模拟开关,可以更简便的实现增益切换的功能。
  3. 不是很熟悉esp32的编程,采样率最多只能跑到10kSps左右。不知道使printf占用了时间还是单片机的spi跑的不够快。freertos也不是很熟悉。之前大部分上手项目都是裸机开发,但是项目复杂的情况下,freertos能更好的解决问题。
  4. 时间上不足,虽然已经参考github完成了u8g2的移植,但是还没有实现屏幕显示,将来可以使用抽帧的方法把信号显示到0.96inch的小屏幕上。选购的这款ssd1306是IIC接口,应该选择SPI接口来获得更快的刷新率。

总结

在实现本项目的过程中,参考了杨建国老师的《你好,放大器》,曾经阅读过一边但只是之上谈兵,这次项目过程中重读了一遍,更深刻的理解了书中讲解的运算放大器的噪声和误差。也参考了adi官方的一些datasheet,一些typical design对我的启发很大。datasheet中layout的知识也很丰富,如果时间充足,进行pcb制板,前端电路的性能讲更加优越。

项目实现了部分题目指标,由于本人水平欠佳,没有实现所有的题目要求。通过这次的项目经历,我熟悉了如何为ad设计一个前端模拟电路。在群里和大家讨论模拟电路设计的相关问题,让我这个模拟电路初学者获益良多。非常感谢硬禾能提供的这样一个学习机会,希望大家共同进步!

 

 

附件下载
Draft2.asc
ltspice仿真
test_ad7920.zip
esp32工程
团队介绍
hucuyuu
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号