[经验分享] 【平头哥RVB2601创意应用开发】环境监测终端04-声音采集和动感光条显示

sipower   2022-4-17 16:34 楼主

本篇文章介绍使用RVB2601开发板自带的麦克风采集周围的声音,记录单位时间内的最大值,用以表示环境噪声水平。为了更直观显示,在OLED显示屏上设计了一个动感光条,能够随着声音节奏跳动

1、准备工作

开始动工之前首先是查阅资料,做准备工作。作为推出有一段时间的单片机系统,网上早有大神做过声音方面的试验。我的本次试验就是先学习了“我爱下载”同学的《麦克风录音测试》文章,整明白声音采集原理后开始动手的,原贴链接如下。

https://occ.t-head.cn/community/post/detail?spm=a2cl5.26076654.0.0.56981f9cfBQl94&id=3945884479054815232

2、硬件原理

首先介绍一下硬件原理。RVB2601 通过 I2S 和 I2C 总线连接 ES7210 ADC 芯片,实现硅麦的音频信号采样。原理图如下。

001-原理图.png

1、音频采样原理

CH2601采用I2C接口完成ES7210的配置,采用I2S接口读取ES7210的转换数据,其接口如图所示。

002-硬件接口.png

2、硬件接口

3、软件设计

软件驱动设计使用了CODEC编解码库。CODEC在这里指的是同时具有D/A(数字讯号转换成模拟讯号)和A/D(模拟讯号转换成数字讯号)转换功能的编解码器,播放音乐的时候用到的是D/A转换功能。在录音的时候用到的是A/D转换功能。具体的详细介绍和使用方法见如下官方链接。

https://yoc.docs.t-head.cn/yocbook/Chapter3-AliOS/CSI%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8%E6%8E%A5%E5%8F%A3/CSI2/CODEC.html

在接口中,D/A指的是输出通道,A/D指的是输入通道。本次实验主要使用AD的输入通道。用到的CODEC的CSI接口如下所示:

003-软件接口.png

3、软件接口

4、代码编写

下载官方例程ch2601_ft_demo,截取其中录音部分代码。因为本次应用只需要声音幅值,对其代码大幅删减改造,将缓存缩小,只保留一个采样周期就行,每次采样完成,计算这个周期内的音频数据最大值,这个结果就作为要输出的实时音量数据。可以通过串口打印输出,也可以传递给其他任务用于显示。音频采样代码如下。

#include <stdlib.h>
#include <aos/aos.h>

#include "board_config.h"
#include "drv/codec.h"
#include "drv/dma.h"
#include <drv/ringbuffer.h>
#include "snd_get.h"

csi_codec_t codec;
csi_codec_input_t  codec_input_ch;

csi_dma_ch_t dma_ch_input_handle;

#define INPUT_BUF_SIZE  2048
uint8_t input_buf[INPUT_BUF_SIZE];

ringbuffer_t input_ring_buffer;

volatile uint32_t cb_input_transfer_flag = 0;

int16_t sound_buff[512];
volatile int16_t sound_max=0;

static void codec_input_event_cb_fun(csi_codec_input_t *i2s, csi_codec_event_t event, void *arg)
{
    if (event == CODEC_EVENT_PERIOD_READ_COMPLETE) {
        cb_input_transfer_flag += 1;
    }
}

void cmd_ft_mic_handler(uint32_t channel_status)
{
    csi_error_t ret;
    csi_codec_input_config_t input_config;
    ret = csi_codec_init(&codec, 0);

    if (ret != CSI_OK) {
        printf("csi_codec_init error\n");
        return ;
    }

    codec_input_ch.ring_buf = &input_ring_buffer;
    csi_codec_input_open(&codec, &codec_input_ch, 0);
    /* input ch config */
    csi_codec_input_attach_callback(&codec_input_ch, codec_input_event_cb_fun, NULL);
    input_config.bit_width = 16;
    input_config.sample_rate = 8000;
    input_config.buffer = input_buf;
    input_config.buffer_size = INPUT_BUF_SIZE;
    input_config.period = 1024;
    input_config.mode = CODEC_INPUT_DIFFERENCE;
    csi_codec_input_config(&codec_input_ch, &input_config);
    csi_codec_input_analog_gain(&codec_input_ch, 0xbf);
    csi_codec_input_link_dma(&codec_input_ch, &dma_ch_input_handle);

    printf("start sound get\n");
    csi_codec_input_start(&codec_input_ch);

    while (1) 
	{
		if (cb_input_transfer_flag) 
		{
			csi_codec_input_read_async(&codec_input_ch, sound_buff, 1024);
			cb_input_transfer_flag = 0U;
			
			sound_max=0;
			int ii=0;
			while(ii < 512)
			{
				if(sound_max < sound_buff[ii])
				{
					sound_max = sound_buff[ii];
				}
				ii++;
			}
//			printf("max sound:%d\n",sound_max);
		}
		aos_msleep(10);
	}
    return;
}

下图是用串口打印输出的实时音量数据,我对着麦克风用不同音量说话,可以看到,实际数据最小在30左右,最大为32767,平均底噪在50左右。

004-串口输出音量.png

4、串口输出音量

5、动感光条实现

从串口数据看还不是很直观,我改造了一下显示程序,将音量变化情况实时显示到OLED屏上。

由于大部分时间都是晚上测试这个板子,看着这个OLED屏幕全亮时还是比较刺眼的,为了能看起来更舒服一点,也增加屏幕寿命,减少功耗,我对显示底层程序做了一点改动,将颜色取反了一下,就看起来舒服多了。具体是修改oled.c这个文件里面的画点函数。代码如下。

void oled_draw_point(uint8_t r, uint8_t c, uint8_t t)
{
    if (t) {
        CLR_BIT(g_oled_ram[r / 8][c], ((r % 8)));
    } else {
        SET_BIT(g_oled_ram[r / 8][c], (r % 8));
    }
	
//	if (t) {
//        SET_BIT(g_oled_ram[r / 8][c], ((r % 8)));
//    } else {
//        CLR_BIT(g_oled_ram[r / 8][c], (r % 8));
//    }
}

然后设计动感光条代码,思路就是按照套娃方法,先画一个大一点的正色矩形,用于显示边框;然后画一个小一圈的反色矩形做衬底;最后将量化后的音量值作为参数,传入后作为长度,用于画第三层正色矩形,就实现了光条设计。连续调用的时候,随着传入的数值变化,光条长短在变化,就实现了动态光条设计,具体代码如下。

//num_p = 1...100
void oled_draw_bar(uint8_t num_p)
{
    unsigned char i, j, k;

    for (i = 54; i < 64; i++) {
        for (j = 0; j < 128; j++) {
            oled_draw_point(i, j, 0);
        }
    }
	for (i = 55; i < 63; i++) {
        for (j = 1; j < 127; j++) {
            oled_draw_point(i, j, 1);
        }
    }
	k = num_p * 124 / 100;
	for (i = 56; i < 62; i++) {
        for (j = 2; j < k; j++) {
            oled_draw_point(i, j, 0);
        }
    }
	oled_reflesh();
}

传入不同数值时,光条动态效果如下。

005-动态光条效果.gif

5、动态光条效果

然后在主函数里面创建一个任务,将动态光条和声音采样关联起来,就可实现随音乐跳动的炫动光条效果了。代码如下。

/*
 * Copyright (C) 2019-2020 Alibaba Group Holding Limited
 */


#include <stdlib.h>
#include <string.h>
#include <aos/aos.h>
#include "aos/cli.h"
#include "main.h"
#include "app_init.h"
#include "oled.h"
#include "snd_get.h"

#define TAG "app"

static void snd_get_task(void *arg);

extern int16_t sound_max;

int main(void)
{
	int i=0;
	
    board_yoc_init();
	
	aos_task_new("snd", snd_get_task, NULL, 10 * 1024);
	
    LOGD(TAG, "%s\n", aos_get_app_version());
    oled_init();
    while (1) 
	{
		aos_msleep(100);
		
		i = sound_max /20;
		i++;
		if(i>100)  i=100;
		printf("%d\n",i);
		oled_draw_bar( i);
		
    }

    return 0;
}

static void snd_get_task(void *arg)
{
	cmd_ft_mic_handler(0);
}

最终的音乐光条效果如下视频。音频采集到的数值比较大,为了在普通音量下效果比较明显。我做了一点简单处理,但是声音很大时光柱会显示饱和。要想更好的效果,可以对原始数据进行一阶高通数字滤波,只截取高频波动部分显示,就能够适应更宽的音量范围。


 

6、音乐光条

最后,我将声音采集代码移植到我上一篇文章介绍的显示界面中,从下面视频可以看出实时响应效果有些滞后,这个是LVGL的刷新不够造成的。但是在我预期的应用中,影响不大,因为我预计一秒钟以上才上传一次平均值,对实时性要求不高。


 

7、在LVGL中显示音量

本次试验主要是从定性的角度进行声音采集和显示,没有定量校准,如果后面能搞到分贝仪,可以尝试对声音采集部分进行校准,就能比较准确显示音量分贝值了。

回复评论 (8)

你离项目实现越来越近了。我碰到的,就一步一坚难,是不是上帝给我磨难吧
点赞  2022-4-17 19:41
引用: lugl4313820 发表于 2022-4-17 19:41 你离项目实现越来越近了。我碰到的,就一步一坚难,是不是上帝给我磨难吧

期待后面越来越顺。

加油!在电子行业默默贡献自己的力量!:)
点赞  2022-4-18 10:11

加油加油

加油!在电子行业默默贡献自己的力量!:)
点赞  2022-4-18 10:11
引用: soso 发表于 2022-4-18 10:11 加油加油

我昨天更新了使用固件,现在下载不了程序了,可能要翻车了。

点赞  2022-4-18 10:34
引用: lugl4313820 发表于 2022-4-18 10:34 我昨天更新了使用固件,现在下载不了程序了,可能要翻车了。

找平头哥支持下:)

 

加油!在电子行业默默贡献自己的力量!:)
点赞  2022-4-18 10:58
引用: lugl4313820 发表于 2022-4-17 19:41 你离项目实现越来越近了。我碰到的,就一步一坚难,是不是上帝给我磨难吧

好事多磨,多研究肯定能成功。而且你的文章含金量挺高的,给大家帮助也很多。

点赞  2022-4-18 12:08
引用: lugl4313820 发表于 2022-4-18 10:34 我昨天更新了使用固件,现在下载不了程序了,可能要翻车了。

提交工单吧,他们服务还是很到位的

点赞  2022-4-18 12:10
大佬好厉害啊,干活满满的,有很多地方值得学习,我做的产品也是环境监测的,可以多参考大佬的思路了。
点赞  2022-4-18 22:44
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复