从零开始学嵌入式之STM32——34.ADC-模数转换

目录

一、ADC模数转换简介

二、AD转换工作原理

(1)采样阶段

(2)量化阶段

三、ADC主要参数

(1)参考电压

(2)通道数

(3)采样率

(4)分辨率

(5)转换时间

四、使用STM32中的ADC

(1)功能框图

(2)输入通道

1.规则通道组

2.注入通道组

[五、STM32 ADC 寄存器方式配置与使用](#五、STM32 ADC 寄存器方式配置与使用)

[5.1 ADC 时钟配置](#5.1 ADC 时钟配置)

[5.2 ADC 转换方式配置](#5.2 ADC 转换方式配置)

[5.2.1 扫描模式(ADC_CR1->SCAN)](#5.2.1 扫描模式(ADC_CR1->SCAN))

[5.2.2 连续/单次转换(ADC_CR2->CONT)](#5.2.2 连续/单次转换(ADC_CR2->CONT))

[5.3 数据对齐模式选择](#5.3 数据对齐模式选择)

[5.4 转换时间配置](#5.4 转换时间配置)

[5.4.1 总转换时间计算](#5.4.1 总转换时间计算)

[5.4.2 采样时间配置寄存器](#5.4.2 采样时间配置寄存器)

[5.5 ADC 编码转换为电压数据](#5.5 ADC 编码转换为电压数据)

[5.6 ADC 核心寄存器详解与配置](#5.6 ADC 核心寄存器详解与配置)

[5.6.1 控制寄存器2(ADC_CR2)关键位](#5.6.1 控制寄存器2(ADC_CR2)关键位)

[5.6.2 规则序列寄存器(ADC_SQR1~3)](#5.6.2 规则序列寄存器(ADC_SQR1~3))

[5.6.3 采样时间寄存器(ADC_SMPR1)](#5.6.3 采样时间寄存器(ADC_SMPR1))

[5.7 寄存器方式 ADC 完整配置流程(总结)](#5.7 寄存器方式 ADC 完整配置流程(总结))

六、寄存器方式使用ADC读取芯片内部温度与参考电压的实现

(1)整体思路

(2)实现代码

七、HAL库实现

(1)基础配置(按实际情况)

(2)ADC与DMA配置

(3)调用HAL库实现业务


一、ADC模数转换简介

ADC(analog digital convert)模拟-数字转换。
在嵌入式领域,ADC模块是现实世界与数字世界的桥梁,在现实世界中,信号是连续变化的模拟量,ADC模块通过采集不断变化的模拟电压信号,将其转换为具有特定含义的二进制编码,实现信号的输入。

二、AD转换工作原理

AD转换指的就是模数转换。转换的阶段可以分为 采样阶段量化阶段 。采样实现 时间离散 ,量化实现 幅值离散,再经编码完成模数转换。

(1)采样阶段

对于模拟量,不可能每时每刻都采集到它的值,在实际应用中,采用的策略是:每一段时间采集一次,这样得到一组离散的数据量。
**tips:**若采样频率满足采样定理,便可由采样后的离散数据无失真还原原始模拟波形。

(2)量化阶段

将采样得到的 连续电压幅值 ,按照 ADC 的分辨率划分成若干个 等级刻度 ,把无限连续的电压值 近似归整 到最接近的数字刻度上。再对每个量化后的等级进行 二进制编码,最终输出单片机能识别的数字量。
量化的方法:

  • 逐次逼近型:最常见,通过逼近法模拟信号的大小,性能均衡。
  • **sigma-delta:**通过比较两个信号的累积值和一个参考值,产生一个高精度的输出,精度高,但是转换时间长
  • **flash:**通过一组比较器和编码器,以高速并行的方式进行转换,速度快,但是分辨率低,精度一般。
    (3)逐次逼近型工作原理简介
    在STM32中的ADC模使用的就是逐次逼近型ADC,其工作原理类似砝码天平称重的原理。下图是芯片中的ADC电路图:

    假如8位DA转换器的参考电压是256V(方便计算,这样二进制数每增加1,就代表了电压增加1)
    待测电压33V
    在测量时,会按照如下步骤测量:
    规则:

    每位单独置位电压:D7=128V、D6=64V、D5=32V、D4=16V、D3=8V、D2=4V、D1=2V、D0=1V
  • 逻辑控制电路先将 8 位寄存器D7 位置位 ,DA 转换器输出电压128V
  • 比较器与待测电压比较,待测电压低于 128V,比较器输出低电平
  • 8 位寄存器D7 清零D6 位置位 ,DA 转换器输出64V
  • 比较器与待测电压比较,待测电压低于 64V,比较器输出低电平
  • 8 位寄存器D6 清零D5 位置位 ,DA 转换器输出32V
  • 比较器与待测电压比较,待测电压高于 32V,比较器输出高电平
  • 8 位寄存器D5 保持为 1D4 位置位 ,DA 转换器输出 48V
  • 比较器与待测电压比较,待测电压低于 48V,比较器输出低电平
  • 8 位寄存器D5 保持、D4 清零D3 位置位 ,DA 转换器输出 40V
  • 比较器与待测电压比较,待测电压低于 40V,比较器输出低电平
  • 8 位寄存器D5 保持、D4 清零、D3 清零D2 位置位 ,DA 转换器输出 36V
  • 比较器与待测电压比较,待测电压低于 36V,比较器输出低电平
  • 8 位寄存器D5 保持、D4/D3/D2 清零D1 位置位 ,DA 转换器输出32V
  • 比较器与待测电压比较,待测电压高于 32V,比较器输出高电平
  • 8 位寄存器D5、D1 保持D0 位置位 ,DA 转换器输出33V
  • 比较器与待测电压比较,等于待测电压,比较器输出高电平
    总结:
  1. 以 8 位逐次逼近型 ADC、参考电压 256V 为例,逻辑控制电路从最高位 D7 到最低位 D0 依次逐位试探:先将最高位置 1,由 DAC 输出对应权值电压,与待测电压送入比较器对比;若待测电压低于 DAC 输出电压,该位清零 ;若高于 ,该位保留置 1;逐位试探完毕后,8 位寄存器中保存的二进制数值,即为待测电压对应的数字量。
  2. 比较器仅能输出高低电平,只能判断电压大小,无法识别电压相等;因此逐次逼近 ADC 必须从最高位到最低位逐位全部比较完成,不能中途提前结束转换。

三、ADC主要参数

(1)参考电压

作为 ADC 模数转换的电压基准,决定了 可测量模拟电压的最大幅度范围,所有模拟输入电压都以参考电压为量程上限进行量化换算。

(2)通道数

ADC 具备多路独立模拟输入接口, 通道数越多,可同时或分时采集更多路不同来源的模拟信号,如温度、电压、光照等多路模拟量。

(3)采样率

指 ADC 每秒能够完成的采样次数。采样率越高,ADC 捕捉高频变化模拟信号的能力越强,不易发生信号失真。

(4)分辨率

表示 ADC 能把满量程模拟电压划分成多少个 离散量化等级,通常以位数表示。
例如:12 位 ADC,总级数为 2^12 = 4096 个离散级别;位数越高,划分越精细,电压识别精度越高。

(5)转换时间

指 ADC 完成 一次模数转换所需要的时间,由 ADC 工作时钟频率、分辨率以及转换架构共同决定;分辨率越高,通常单次转换时间越长。

四、使用STM32中的ADC

在STM32中,集成了ADC功能,不同的系列里的资源有所不同,部分型号参考下图:

不论如何,所有系列至少都有一个ADC1,并且其功能基本上是通用的,我们就再ADC1上展开讨论。

(1)功能框图

(2)输入通道

ADC的输入通道用来输入模拟电压的通道,ADC1模块有18个通道:
*

  • 16个外部通道(ADCx_IN ~ ADCx_IN15),对应16个IO口;
  • 内部温度传感器(测量芯片温度):通道16;
  • 内部电压,(测量芯片供电电压):通道17。
    所有的通道都接到了模拟多路开关,可以通过编程对通道进行控制,实现输入信号的选择和切换。
    ADC同一时刻只能转换一路信号,在需要采集多路信号时,可以将多路信号进行排列,按照一定的顺序依次进行检测、转换、输出。在芯片中,为ADC设置了规则通道组和插入通道组两种类型的队列。
  • 规则通道组:其实更应该翻译为常规组。
  • 插入通道组:更应该翻译为特殊组,可以插队。

1.规则通道组

可理解为 常规 / 普通通道组,是最常用的转换队列。

  • 最多可配置 16 个通道;
  • 转换完成后,数据存入共用的规则数据寄存器 DR
  • 由于多通道共用一个寄存器,新数据会直接覆盖旧数据
  • 为避免数据丢失,必须及时读取数据 ,或开启 DMA 自动搬运

2.注入通道组

可理解为 优先 / 可插队通道组,用于紧急、实时性要求高的信号。

  • 最多支持 4 个通道;
  • 每个通道拥有独立的数据寄存器,不会出现数据覆盖;
  • 可随时打断规则通道的转换,执行优先级更高的任务;
  • 转换完成后无 DMA 请求,通常由 CPU 直接读取。
    **注意:**在ZET6系列芯片中,ADC2没有DMA通道,ADC1和ADC3有DMA通道

五、STM32 ADC 寄存器方式配置与使用

5.1 ADC 时钟配置

STM32的ADC模块的时钟频率不能超过14MHz,因此RCC外设中专门为ADC配置了预分频器(通过RCC->ADCPRE寄存器),用于选择输入时钟的分频系数,具体配置如下:
RCC->ADCPRE 寄存器配置(2位控制位):

  • 00:PCLK2 2分频
  • 01:PCLK2 4分频
  • 10:PCLK2 6分频
  • 11:PCLK2 8分频

5.2 ADC 转换方式配置

ADC的转换方式主要通过ADC_CR1和ADC_CR2两个寄存器的对应位配置,分为扫描模式和连续/单次转换模式,具体如下:

5.2.1 扫描模式(ADC_CR1->SCAN)

  • 开启(置1):多通道转换模式,按顺序对通道组中的所有通道依次扫描、逐个转换;
  • 关闭(置0):单通道转换模式,仅转换通道组中第一个通道。

5.2.2 连续/单次转换(ADC_CR2->CONT)

  • 开启(置1):连续转换模式,转换一轮完成后,无需等待外部触发,自动开始下一轮转换,持续进行;
  • 关闭(置0):单次转换模式,转换一轮完成后停止,需等待下次触发(软件或外部触发)才能启动新的转换。

5.3 数据对齐模式选择

ADC的转换结果存储在16位的DR寄存器中,而ADC的分辨率为12位,因此存在数据对齐问题,通过ADC_CR2->ALIGN寄存器配置,默认采用右对齐模式,具体说明如下:

  • 右对齐(默认):转换结果的低12位有效,高4位补零,是实际使用中最常用的对齐方式;
  • 左对齐:转换结果的高12位有效,低4位补零。
    注意:注入组转换得到的结果包含符号位,若为正数,高位补0;若为负数,高位补1。

5.4 转换时间配置

ADC转换分为采样和模数转换两个阶段:首先使用若干个ADC时钟周期对输入电压进行采样(采样周期可通过寄存器配置),随后经过固定的12.5个ADC时钟周期完成模数转换。

5.4.1 总转换时间计算

总转换时间 = 采样时间 + 12.5个ADC周期
示例:当ADC时钟为14MHz(最大允许频率),假设采样时间配置为1.5个ADC周期,则总转换时间 = 1.5 + 12.5 = 14个ADC周期,对应时间为1μs(14MHz时钟周期为1/14μs,14个周期即1μs)。

5.4.2 采样时间配置寄存器

采样时间通过ADC_SMPR1寄存器进行配置,可根据实际需求选择不同的采样周期,以平衡转换速度和采样精度。

5.5 ADC 编码转换为电压数据

模拟电压经过ADC转换后,会得到一个12位的数字值,直接打印该数字值可读性较差,需将其转换为对应的模拟电压值。
实际设计中,ADC的输入电压范围通常设定为0~3.3V(与STM32的IO口供电电压匹配),由于ADC为12位分辨率,其满量程(所有位为1)对应的数字值为2¹² - 1 = 4095,数字值0对应模拟电压0V。
设转换后的数字值为x,对应的模拟电压为Y,两者满足以下等式(线性关系):
Y = (x / 4095) × 3.3V
通过该等式可将数字值换算为直观的模拟电压。

5.6 ADC 核心寄存器详解与配置

ADC的所有配置均通过对应寄存器实现,以下为核心寄存器的功能及配置要点,结合前文配置需求,明确各寄存器的作用及使用方法:

5.6.1 控制寄存器2(ADC_CR2)关键位

  • CONT:连续转换控制位,对应5.2.2节,控制ADC为连续或单次转换模式;
  • ALIGN:数据对齐方式控制位,对应5.3节,控制转换结果为右对齐或左对齐,默认右对齐;
  • EXTTRIG:外部触发选择位,用于开启或关闭外部触发转换功能;
  • EXTSEL:触发源选择位,当开启外部触发(EXTTRIG置1)时,用于选择触发ADC转换的外部信号源;
  • SWSTART:软件触发控制位,置1时启动一次ADC转换(适用于软件触发模式);
  • CAL:校准控制位,置1时启动ADC校准,校准完成后由硬件自动清零(校准需在ADC转换前执行,确保转换精度);
  • ADON:ADC模块启动位,第一次置1启动ADC模块,再次置1触发一次ADC转换(单次转换模式下)。

5.6.2 规则序列寄存器(ADC_SQR1~3)

规则组通道的配置通过ADC_SQR1、ADC_SQR2、ADC_SQR3三个寄存器实现,具体要点如下:

  • ADC_SQR1->L:规则组通道长度控制位,4位宽度,取值范围0~15,对应规则组包含1~16个通道;
  • 通道编号:STM32 ADC共18个通道,每个通道需用5位控制位表示,三个SQR寄存器共可提供80位(16×5)控制位,刚好覆盖1~16个规则通道的配置(ADC_SQR1_SQ相关位为5位宽度,用于配置具体通道号)。

5.6.3 采样时间寄存器(ADC_SMPR1)

用于配置各通道的采样时间,通过对应位的配置,可选择不同的采样周期(如1.5、7.5、13.5等ADC周期),结合ADC时钟频率,决定采样精度和转换速度(采样周期越长,采样精度越高,转换速度越慢)。

5.7 寄存器方式 ADC 完整配置流程(总结)

结合上述所有配置要点,寄存器方式配置ADC的完整流程如下:

  1. 配置 ADC 时钟 :通过RCC->CFGR的ADCPRE位设置预分频,确保不超过 14MHz;
  • 配置转换模式 :通过ADC_CR1->SCAN设置扫描 / 单通道,ADC_CR2->CONT设置连续 / 单次;
  • 配置数据对齐 :通过ADC_CR2->ALIGN设置右对齐(默认)或左对齐;
  • 配置采样时间 :通过ADC_SMPR1/2设置各通道采样周期;
  • 配置规则组通道 :通过ADC_SQR1~3设置序列长度与通道号;
  • ADC 上电 :置位ADC_CR2->ADON,给 ADC 上电(必须先上电);
  • ADC 校准 :置位ADC_CR2->CAL启动校准,等待CAL自动清零;
  • 启动转换 :软件触发置位SWSTART,或等待外部触发,开始转换;
  • 读取数据 :转换完成后读取DR寄存器,换算成电压。

六、寄存器方式使用ADC读取芯片内部温度与参考电压的实现

(1)整体思路

通过ADC的规则组队列采集多路信号时,为了防止DR中的数据被覆盖。需要打开ADC的DMA功能,每转换完成一路信号,通过DMA传送到内存中,确保数据的准确。

初始化函数:

ADC初始化:

1.时钟配置

2.ADC配置

2.1.工作模式:配置扫描模式

2.2.启用连续转换模式(单曲循环)

2.3.数据对齐,右对齐(默认)

2.4.通道配置

2.4.1.设置通道的采样时间SMPR寄存器 001代表7.5个周期

2.4.2.规则组通道序列配置-规则组通道数量配置--L

2.4.3.将通道号保存到通道序列中--SQR3(倒着排列的)

2.5.触发方式选择--使用软件触发AD转换

DMA初始化:

1.时钟配置

2.数据宽度配置

3.传输方向配置

4.地址自增配置

开始转换函数

1.上电唤醒

2.执行校准,等待校准完成

3.开启DMA(DMA必须在上电后开启,否则不生效)

3.1.设置DMA,源地址、目的地址、数据长度等

4.启动转换

5.等待转换完成

计算函数

1.读取数据后进行计算并输出

(2)实现代码

文件名adc.c

cpp 复制代码
#include "adc.h"
void ADC_Init(void)
{
    // 1.打开时钟
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

    // 2.ADC输入时钟频率设置,主时钟频率6分频-12MHz-10
    RCC->CFGR |= RCC_CFGR_ADCPRE_1;
    RCC->CFGR &= ~RCC_CFGR_ADCPRE_0;

    // 3.ADC工作模式配置
    // 3.1.扫描模式开
    ADC1->CR1 |= ADC_CR1_SCAN;
    // 3.2.连续转换模式开
    ADC1->CR2 |= ADC_CR2_CONT;
    // 3.3.外部触发配置
    ADC1->CR2 |= ADC_CR2_EXTTRIG;
    ADC1->CR2 |= ADC_CR2_EXTSEL;
    // 3.4.数据对齐设置,右对齐(默认)
    ADC1->CR2 &= ~ADC_CR2_ALIGN;
    // 3.5.使用芯片内部传感器,开启对应控制位
    ADC1->CR2 |= ADC_CR2_TSVREFE;

    // 4.转换列表配置,列表长度2
    ADC1->SQR1 &= ~ADC_SQR1_L;
    ADC1->SQR1 |= ADC_SQR1_L_0;
    ADC1->SQR3 &= ~ADC_SQR3_SQ1;
    ADC1->SQR3 &= ~ADC_SQR3_SQ2;
    ADC1->SQR3 |= 16 << 0;
    ADC1->SQR3 |= 17 << 5;

    // 5.采样频率设置,
    /*
        内置温度传感器的采样频率按照手册要求,不能低于17.1us,因此通道16的周期需要设置为239.5;-SMP - 111
        通道17为检测芯片工作电压,采样频率无需过高,也可以采用239.5
    */
    ADC1->SMPR1 |= ADC_SMPR1_SMP16;
    ADC1->SMPR1 |= ADC_SMPR1_SMP17;

    // 6.ADC模块上电
    ADC1->CR2 |= ADC_CR2_ADON;

    // 7.开启DMA(注意,该步骤需要在ADC模块上电后才可操作,否则无效)
    ADC1->CR2 |= ADC_CR2_DMA;
}

void ADC_Start_ON_DMA(uint16_t* data, uint16_t size)
{
    // 1.开启ADC校准,并等待校准完成
    ADC1->CR2 |= ADC_CR2_CAL;
    while(ADC1->CR2 & ADC_CR2_CAL);

    // 2.设置DMA传输信息
    DMA_Truansit((uint32_t)&(ADC1->DR), (uint32_t)data, size);

    // 3.开启ADC转换
    ADC1->CR2 |= ADC_CR2_SWSTART;
}

文件名:dma.c

cpp 复制代码
#include "dma.h"

void DMA_Init(void)
{
    // 1.打开时钟
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    // 2.DMA工作模式配置
    // 2.1.数据宽度设置, 16位数据宽度-01
    DMA1_Channel1->CCR &= ~DMA_CCR1_MSIZE_1;
    DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0;
    DMA1_Channel1->CCR &= ~DMA_CCR1_PSIZE_1;
    DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0;
    // 2.2.数据传输方向设置,从外设读-DIR- 0
    DMA1_Channel1->CCR &= ~DMA_CCR1_DIR;
    // 2.3.地址自增设置,外设地址不自增,内存地址自增
    DMA1_Channel1->CCR &= ~DMA_CCR1_PINC;
    DMA1_Channel1->CCR |= DMA_CCR1_MINC;
    // 2.3.循环模式配置,打开循环模式
    DMA1_Channel1->CCR |= DMA_CCR1_CIRC;

    // // 3.开启DMA通道
    // DMA1_Channel1->CCR |= DMA_CCR1_EN;
}

void DMA_Truansit(uint32_t src, uint32_t dst, uint16_t dataLen)
{
    // 1.暂停DMA通道
    DMA1_Channel1->CCR &= ~DMA_CCR1_EN;

    // 2.设置源地址
    DMA1_Channel1->CPAR = src;

    // 3.设置目的地址
    DMA1_Channel1->CMAR = dst;

    // 4.设置数据长度
    DMA1_Channel1->CNDTR = dataLen;

    // 5.开启DMA通道
    DMA1_Channel1->CCR |= DMA_CCR1_EN;
}

文件名main.c

cpp 复制代码
#include "usart.h"
#include "dma.h"
#include "adc.h"
#include "delay.h"

int main(void)
{
    // data[0]存放通道16读取的温度原始值
    // data[1]存放通道17读取的电压原始值
    uint16_t data[2] = {0};

    float temperature = 0.0f;
    float voltage = 0.0f;

    Usart1_Init();
    DMA_Init();
    ADC_Init();
    ADC_Start_ON_DMA(data, 2);
    printf("test\n");
    while(1)
    {
        temperature = (1.43f - (float)data[0] * 3.3 / 4095.0f) / 0.0043f + 25.0f;
        voltage = (float)data[1] * 3.3 / 4095.0f;
        printf("Temperature = %.2f\nVoltage = %.2f\n", temperature, voltage);
        Delay_nms(1000);
    }
}

七、HAL库实现

(1)基础配置(按实际情况)

(2)ADC与DMA配置

(3)调用HAL库实现业务

cpp 复制代码
int main(void)
{
  /* USER CODE BEGIN 1 */
  uint16_t adc_data[2] = {0};
  float temperature = 0.0f;
  float voltage = 0.0f;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  // 数据校准
  HAL_ADCEx_Calibration_Start(&hadc1);
  // adc开始转换
  HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_data, 2);
  //HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*)adc_data, 2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    temperature = (1.43f - (float)adc_data[0] * 3.3f / 4095.0f) / 0.0043f + 25.0f;
    voltage =  (float)adc_data[1] * 3.3f / 4095.0f;

    printf("temperature = %.2f\nvoltage = %.2f\n", temperature, voltage);

    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
相关推荐
不做无法实现的梦~16 小时前
常见工程分析软件
stm32·嵌入式硬件·算法
国产电子元器件17 小时前
电流检测信号漂移问题分析
单片机·嵌入式硬件
YangWeiminPHD17 小时前
单片机AI边缘计算发展之路:从M0的开局到三足鼎立的智能革命
人工智能·单片机·边缘计算
梁朝辉18 小时前
筛选运算放大器时对于开环增益这一项参数怎么看?
嵌入式硬件·硬件工程
Sean_VIP18 小时前
FreeRTOS项目程序框架介绍(五)
笔记·stm32
zlinear数据采集卡18 小时前
单点接地设计电路深度解析:从理论原理到ZLinear采集卡的低噪声实战
c语言·单片机·嵌入式硬件·fpga开发
嵌入式小站18 小时前
STM32 零基础可移植教程 15:ADC 多通道扫描,读取三路 PWM 的平均电压
stm32·单片机·嵌入式硬件
踏着七彩祥云的小丑19 小时前
嵌入式测试学习第 26 天:SPI通信协议基础、主从模式、速度特点
单片机·嵌入式硬件
湉湉家的小虎子19 小时前
【科普贴】浅谈UFS接口——偏硬件解析
驱动开发·嵌入式硬件·fpga开发·硬件工程
hai31524754319 小时前
# FiveOS V5.0 交付(终极合成器版 · 物理合规修正)
人工智能·stm32·单片机·嵌入式硬件·神经网络