零基础开发小安派-Eyes-S1外设篇——ADC

描述

AiPi-Eyes-S1是安信可开源团队专门为Ai-M61-32S设计的一款开发板,支持WiFi6、BLE5.3。所搭载的Ai-M61-32S 模组具有丰富的外设接口,具体包括 DVP、MJPEG、Dispaly、AudioCodec、USB2.0、SDU、以太网 (EMAC)、SD/MMC(SDH)、SPI、UART、I2C、I2S、PWM、GPDAC、GPADC、ACOMP 和 GPIO 等。

AiPi-Eyes-S1集成了SPI屏幕接口,DVP摄像头接口,外置ES8388音频编解码芯片以及预留TF卡座,并且引出USB接口,可接入USB摄像头。

从零开始学习小安派:

1、零基础开发小安派-Eyes-S1【入门篇】——初识小安派-Eyes-S1

2、零基础开发小安派-Eyes-S1【入门篇】——安装VMware与Ubuntu

3、入门篇:零基础开发小安派-Eyes-S1——新建工程并烧录调试

4、零基础开发小安派-Eyes-S1入门篇——Win下SSH连接Linux

5、零基础开发小安派-Eyes-S1【入门篇】——Samba共享文件夹

6、零基础开发小安派-Eyes-S1【入门篇】——工程文件架构

7、零基础开发小安派-Eyes-S1【外设篇】——GPIO 输入输出

8、零基础开发小安派-Eyes-S1【外设篇】——GPIO中断编程

9、零基础开发小安派-Eyes-S1【外设篇】——PWM

10、零基础开发小安派-Eyes-S1【外设篇】——UART

11、零基础开发小安派-Eyes-S1【外设篇】——I2C

ADC 指的是模数转换器(Analog-to-Digital Converter),它是一种用于将模拟信号转换为数字信号的电子设备或电路。

模拟信号是连续变化的信号,可以取无限个可能的值,而数字信号则是离散的,只能表示有限个数值。ADC 的作用就是将模拟信号转换为离散的数字信号,以便数字电子系统进行处理、存储和传输。

ADC 的基本工作原理是通过一系列的采样和量化过程来实现模拟到数字的转换:

1.采样(Sampling):ADC 根据一定的时间间隔,从模拟信号中获取一系列离散的采样点。采样率决定了采样点的密度,较高的采样率可以更精确地表示原始模拟信号。

2.量化(Quantization):采样得到的模拟信号样本通常是连续的,量化则将每个采样点映射为一个特定的数字值。量化过程将连续的模拟信号离散化,并分配给每个样本一个数字值。

3.编码(Encoding):编码将量化后的数字值表示为二进制形式,以便于数字系统处理。常见的编码方式包括无符号二进制、补码和格雷码等。

总的来说,生活中会有许多“模拟量”,如一段从低音到高音的音频,在数字信号中,不能简单的把低音表示 0,高音表示 1,这样歌曲中间的音频变化是空的,没有小数点这些概念来显示他们从 0 到 0.5 到 1 的过程。所以需要有个模数转换的过程,称为 ADC。比如用 12 位精度的 ADC 来表示这段音频,低音就 0,高音就是 2^12 次也就是 4096,那么在这段音频中我们就可以通过 0-4096 的过程来判断音频的高和低,具体到高到有多高,低到有多低,而 ADC 的精度越高,如 16 位,就可达到 0-65535 的范围。

一、了解小安派-Eyes-S1 的 ADC

芯片内置一个 12bits 的逐次逼近式模拟数字转换器 (ADC),支持 12 路外部模拟输入和若干内部模拟信号选择。ADC 可以工作在 4 种模式下,转换结果为 12/14/16bits 左对齐模式。

ADC 拥有深度为 32 字节的 FIFO,支持多种中断,支持 DMA 操作。ADC 除了用于普通模拟信号测量外,还可以用于测量供电电压,此外 ADC 还可以通过测量内/外部二极管电压用于温度检测。

具有 12 路外部模拟通道,对应的 GPIO 如下:

开源

此外,还具有 2 路 DAC 内部通道,1 路 VBAT/2 通道,1 路 TSEN 通道

adc clock div

对 adc 时钟再一次进行分频。分频后的时钟必须小于等于 500K。

ADC CLK = CLK_SOURCE/CLK_DIV/adc_clk_div

#define ADC_CLK_DIV_4 1
#define ADC_CLK_DIV_8 2
#define ADC_CLK_DIV_12 3
#define ADC_CLK_DIV_16 4
#define ADC_CLK_DIV_20 5
#define ADC_CLK_DIV_24 6
#define ADC_CLK_DIV_32 7

adc resolution

adc 位数,可以选择 12B、14B、16B。其中 14B 和 16B 自带过采样处理

#define ADC_RESOLUTION_12B 0
#define ADC_RESOLUTION_14B 2
#define ADC_RESOLUTION_16B 4

adc vref

adc 内置参考电压选择,可以选择 2.0 V 或者 3.2V

#define ADC_VREF_3P2V 0
#define ADC_VREF_2P0V 1

二、结构体与 API

struct bflb_adc_config_s

说明:adc 初始化配置结构体。

struct bflb_adc_config_s {
uint8_t clk_div;
uint8_t scan_conv_mode;
uint8_t continuous_conv_mode;
uint8_t differential_mode;
uint8_t resolution;
uint8_t vref;
};

开源

扫描模式:可按照用户指定通道个数和顺序逐个转换,结果推入 ADC 的 FIFO

连续转换模式:允许 ADC 连续不断地执行模数转换,只需要调用一次 ADC 启动

差分模式:ADC 支持单端输入模式和差分模式,选择单端时负极输入通道选择 GND

struct bflb_adc_channel_s

说明:配置 adc 通道时使用。

struct bflb_adc_channel_s {
uint8_t pos_chan;
uint8_t neg_chan;
};

开源

struct bflb_adc_result_s

说明:adc 标准转换结果

struct bflb_adc_result_s {
int8_t pos_chan;
int8_t neg_chan;
int32_t value;
int32_t millivolt;
};

开源

bflb_adc_init

说明: 初始化 adc。adc 使用之前需要开启 adc ip 时钟、设置 adc 时钟源和分频值、选择使用的 gpio 为 analog 模式。

void bflb_adc_init(struct bflb_device_s *dev, const struct bflb_adc_config_s *config);

开源

bflb_adc_deinit

说明: 反初始化 adc。

void bflb_adc_deinit(struct bflb_device_s *dev);

开源

bflb_adc_link_rxdma

说明: adc dma 功能开关。

void bflb_adc_link_rxdma(struct bflb_device_s *dev, bool enable);

开源

bflb_adc_channel_config

说明: 配置 adc 通道。

int bflb_adc_channel_config(struct bflb_device_s *dev, struct bflb_adc_channel_s *chan, uint8_t channels);

开源

bflb_adc_start_conversion

说明: 启动 adc 转换。连续转换模式下只需要调用一次。

void bflb_adc_start_conversion(struct bflb_device_s *dev);

开源

bflb_adc_stop_conversion

说明: 停止 adc 转换。

void bflb_adc_stop_conversion(struct bflb_device_s *dev);

开源

bflb_adc_get_count

说明: 获取 adc 转换个数。

uint8_t bflb_adc_get_count(struct bflb_device_s *dev);

开源

bflb_adc_read_raw

说明: 读取一次 adc 转换值。

uint32_t bflb_adc_read_raw(struct bflb_device_s *dev);

开源

bflb_adc_rxint_mask

说明: adc 转换完成中断开关。

void bflb_adc_rxint_mask(struct bflb_device_s *dev, bool mask);

开源

bflb_adc_errint_mask

说明: adc 错误中断开关。

void bflb_adc_errint_mask(struct bflb_device_s *dev, bool mask);

开源

bflb_adc_get_intstatus

说明: adc 中断标志。

uint32_t bflb_adc_get_intstatus(struct bflb_device_s *dev);

开源

返回值如下:

ADC_INTSTS_NEG_SATURATIONADC_INTSTS_NEG_SATURATION
ADC_INTSTS_FIFO_UNDERRUN
ADC_INTSTS_FIFO_OVERRUN
ADC_INTSTS_ADC_READY

bflb_adc_int_clear

说明: 清除 adc 中断标志。

void bflb_adc_int_clear(struct bflb_device_s *dev, uint32_t int_clear);

开源

int_clear 可以填入以下参数:

ADC_INTCLR_NEG_SATURATIONADC_INTCLR_POS_SATURATION
ADC_INTCLR_FIFO_UNDERRUN
ADC_INTCLR_FIFO_OVERRUN
ADC_INTCLR_ADC_READY

bflb_adc_parse_result

说明: 对 adc 转换结果进行解析。

void bflb_adc_parse_result(struct bflb_device_s *dev, uint32_t *buffer, struct bflb_adc_result_s *result, uint16_t count);

开源

bflb_adc_tsen_init

说明: 初始化 adc tsen 模块。

void bflb_adc_tsen_init(struct bflb_device_s *dev, uint8_t tsen_mod);

开源

bflb_adc_tsen_get_temp

说明: 获取 adc tsen 模块采集的温度。

float bflb_adc_tsen_get_temp(struct bflb_device_s *dev);

开源

bflb_adc_vbat_enable

说明: 开启 vbat 。

void bflb_adc_vbat_enable(struct bflb_device_s *dev);

开源

bflb_adc_vbat_disable

说明: 关闭 vbat。

void bflb_adc_vbat_disable(struct bflb_device_s *dev);

开源

三、示例——连续读取 IO 的 ADC 值,ADC 转换完成中断

Main

#include "bflb_adc.h"
#include "bflb_mtimer.h"
#include "board.h"
#include "bflb_gpio.h"
//头文件
struct bflb_device_s *adc;
//外设结构体设置
void My_adc_gpio_init()
{
struct bflb_device_s *gpio;
gpio = bflb_device_get_by_name("gpio");
bflb_gpio_init(gpio, GPIO_PIN_0, GPIO_ANALOG | GPIO_SMT_EN | GPIO_DRV_0);
}
//设置需要AD采集的IO脚,对应的通道和IO要匹配
volatile uint32_t raw_data;
//接收AD值变量
void adc_isr(int irq, void *arg)
{
uint32_t intstatus = bflb_adc_get_intstatus(adc);
if (intstatus & ADC_INTSTS_ADC_READY) {
bflb_adc_int_clear(adc, ADC_INTCLR_ADC_READY);
raw_data= bflb_adc_read_raw(adc);
}
}
//中断函数,清除中断标志位,将读取的AD变量赋给raw_data
int main(void)
{
board_init();
My_adc_gpio_init();
adc = bflb_device_get_by_name("adc");
/* adc clock = XCLK / 2 / 32 */
struct bflb_adc_config_s cfg;
cfg.clk_div = ADC_CLK_DIV_32;
cfg.scan_conv_mode = false;
cfg.continuous_conv_mode = false;
cfg.differential_mode = false;
cfg.resolution = ADC_RESOLUTION_16B;
cfg.vref = ADC_VREF_3P2V;
//adc结构体配置
struct bflb_adc_channel_s chan;
chan.pos_chan = ADC_CHANNEL_9;
chan.neg_chan = ADC_CHANNEL_GND;
//通道配置,单端模式下neg选择GND,pos注意对应IO口的通道
bflb_adc_init(adc, &cfg);
bflb_adc_channel_config(adc, &chan, 1);
bflb_adc_rxint_mask(adc, false);
bflb_irq_attach(adc->irq_num, adc_isr, NULL);
bflb_irq_enable(adc->irq_num);
//中断使能配置
while (1) {
struct bflb_adc_result_s result;
bflb_adc_start_conversion(adc);
bflb_adc_parse_result(adc, (uint32_t *)&raw_data, &result, 1);
printf("rnpos chan %drnADC Value = %drnCurrent Voltage = %d mvrn", result.pos_chan, result.value, result.millivolt);
bflb_adc_stop_conversion(adc);
bflb_mtimer_delay_ms(1000);
//主函数读取AD值并转化为电压
}
}

四、效果

开源

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分