HPMicro MCU片上ADC使用指南

HPMicro MCU片上ADC使用指南

1. 引言

ADC,全称为Analog-to-Digital Converter,即模数转换器,是一种将模拟信号转换为数字信号的电子设备。ADC的主要作用是在存在外部和内部噪声的情况下,将未知的模拟信号转换为数字信号,以便于计算机或其他数字系统进行处理和分析。

2. MCU片上ADC具有的主要特点

  • 采样时间设置
  • 分辨率设置
  • 内部通道采样(温度传感器)
  • 外部通道采样
  • 软件触发/硬件触发
  • 看门狗监测
  • 中断机制
  • 寄存器、DMA存储转换结果
  • 多样的转换模式

3. ADC转换模式的介绍

为了能够提供给应用逻辑在使用上的便利性,一般MCU片上ADC都定义了单次模式(one-shot mode)、循环模式(cycle mode)、单次扫描模式(one-shot scan mode)、循环扫描模式(cycle scan mode)、单次间断模式(one-shot interrupted mode)、循环间断模式(cycle interrupted mode):

单通道:

  • 单次模式:ADC执行一个指定通道的一次转换,转换完成后,ADC自动停止工作
  • 循环模式:ADC执行一个指定通道的循环转换,完成一次转换后,ADC自动进行下一次转换

多通道:

  • 单次扫描模式:ADC执行一个序列的一次转换,转换完成后,ADC自动停止工作
  • 循环扫描模式:ADC执行一个序列的循环转换,完成一次转换后,ADC自动进行下一次序列转换
  • 单次间断模式:ADC执行一个序列的一次转换,每个通道转换完成后,ADC会等待下次触发到达后,再启动转换,直到整个序列的通道转换全部完成, ADC自动停止工作
  • 循环间断模式:ADC执行一个序列的循环转换,每个通道转换完成后,ADC会等待下次触发到达后,再启动转换,整个序列的通道转换全部完成后,ADC自动进行下一次序列转换

4. HPMicro MCU片上ADC具有的特点

  • 16位逐次逼近型ADC

  • 16位分辨率下最大采样率为2MSPS

  • 输入信号类型

    • 单端输入
    • 差分输入(早期型号不支持)
  • 外部通道采样

    • 最多支持16个外部通道输入
  • 分辨率设置

    • 支持设置为16位、12位、10位、8位
  • 采样时间设置

    • 支持设置灵活的采样时间
  • 读取转换模式(oneshot mode)

    • 读取直接触发转换
  • 周期转换模式 (period mode)

    • 按照定时器周期转换
  • 序列转换模式 (sequence mode)

    • 由软件或硬件触发启动序列
    • 仅支持一组序列且序列长度可达16
    • 支持序列单次转换或序列循环转换
    • 支持序列内间断转换
  • 抢占转换模式 (preemption mode)

    • 由软件或硬件触发启动序列
    • 支持十二组序列且每组序列长度可达4
  • 寄存器、DMA存储转换结果

    • 读取模式、周期模式只能通过寄存器读取转换结果
    • 序列模式、抢占模式只能通过从DMA写入的memory中读取转换结果
  • 支持多个ADC同步工作

    • 通过互联管理器(trigmux)输入同源触发信号,可以同步触发多个ADC同时启动转换
  • 中断机制

    • 支持看门狗阈值超限中断
    • 支持序列转换模式中任意通道转换完成中断
    • 支持序列转换模式中所有通道转换完成中断
    • 支持抢占转换模式中任意通道转换完成中断
  • 看门狗监测

    • 每个通道都可设置独立的阈值以及超限中断

    注:

    • HPMicro的早期产品也有12位ADC,除了分辨率的差异,其余主要特性与16位ADC相同(请查阅手册了解具体信息)

    • HPMicro的早期型号中的16位ADC仅支持单端输入(请查阅手册了解具体信息)

5. 如何在转换模式上将HPMicro MCU片上ADC与其他MCU片上ADC进行对应?

根据HPMicro MCU片上ADC的特性与其他MCU片上ADC的主要特点进行对比,可以发现主要特点基本都已涵盖,下面关于ADC的转换模式,做进一步地介绍:

  • ADC单通道单次模式:可以使用HPMicro MCU片上ADC的读取转换模式、序列转换模式、抢占转换模式

  • ADC单通道循环模式:可以使用HPMicro MCU片上ADC的序列转换模式、抢占转换模式

  • ADC多通道单次扫描模式:可以使用HPMicro MCU片上ADC的序列转换模式、抢占转换模式

  • ADC多通道循环扫描模式:可使使用HPMicro MCU片上ADC的序列转换模式、抢占转换模式

  • ADC多通道单次间断模式:可以使用HPMicro MCU片上ADC的序列转换模式

  • ADC多通道循环间断模式:可使使用HPMicro MCU片上ADC的序列转换模式

    注:

    • 由此看出HPMicro MCU片上的ADC的序列转换模可以覆盖其他MCU片上ADC的所有转换模式

6. 如何通过HPM SDK来使用HPMicro MCU片上的ADC的序列模式?

通过上述介绍,我们已经了解了其他MCU以及HPMicro MCU片上的ADC的的特点后,接下来以序列模式为例,做进一步介绍:

  • ADC管脚初始化

    • 设置指定管脚IO PAD的FUNC_CTL寄存器的BIT8(IOC_PAD_FUNC_CTL_ANALOG_MASK),用于隔离指定管脚所复用的数字功能

      以HPM6E00为例,PF07既有数字功能的复用, 也有模拟功能的复用:

      因此对于此管脚需要使用模拟功能时,就需要隔离数字功能,程序设置如下:

      void init_adc16_pins(void)
      {
          HPM_IOC->PAD[IOC_PAD_PF07].FUNC_CTL = IOC_PAD_FUNC_CTL_ANALOG_MASK;
      }
  • ADC时钟初始化

    • ADC时钟结构,HPMicro MCU的所有系列的用户手册上的功能时钟章节中都有时钟结构框图,如下图所示:

      注:

      • 目前HPMicro MCU片上ADC的时钟结构都具有两级,第一级为BUS时钟或者ANAn时钟,第二级为ADCn时钟

      • BUS为AHB或AXI,由MCU的设计决定,具体可通过查阅SYSCTL寄存器中的ADCCLK的MUX选择位来确认

    • 设置ADC的输入时钟, 用于配置BUS时钟或者ANAn时钟

    示例:以HPM6E00为例,选择200MHz的BUS时钟作为ADC0的输入时钟

    a) 设置PLL1输出400MHz, 再设置CLK_TOP_AHB时钟来源于PLL1CLK0的2分频输出, 因此得到200MHz的时钟

    pllctlv2_init_pll_with_freq(HPM_PLLCTLV2, pllctlv2_pll1, 400000000);                /* PLL1 400MHz */
    pllctlv2_set_postdiv(HPM_PLLCTLV2, pllctlv2_pll1, pllctlv2_clk0, pllctlv2_div_1p0); /* PLL1CLK0 400MHz */
    clock_set_source_divider(clock_ahb, clk_src_pll1_clk0, 2);                          /* AHB 200MHz */

    b) 选择AHB时钟作为ADC0的输入时钟

    clock_set_adc_source(clock_adc0, clk_adc_src_ahb0);
  • ADC初始化

    ADC初始化分为两部分,模拟部分和数字部分, 其中模拟部分为通用属性,而数字部分分为四种模式:读取模式、周期模式、序列模式、抢占模式

    • 设置ADC通用属性

      a) 通用设置结构体定义

      /** @brief ADC16 common configuration struct. */
      typedef struct {
          uint8_t res;            /* 用于设置分辨率,可以选择16位,12位,10位,8位 */
          uint8_t conv_mode;      /* 用于设置工作模式,可以选择读取模式、周期模式、序列模式、抢占模式 */
          uint32_t adc_clk_div;   /* 用于设置ADC输入时钟分频系数,  最大16分频 */
          bool port3_realtime;    /* 用于设置多模式下的抢占使能 */
          bool wait_dis;          /* 用于在使用读取模式时,设置是否禁用阻塞方式 */
          bool sel_sync_ahb;      /* 用于标记ADC的输入时钟与BUS时钟是否相同 */
          bool adc_ahb_en;        /* 用于设置DMA访问AHB总线的使能,仅对序列模式和抢占模式中DMA需要访问memory时有效 */
      } adc16_config_t;

      注:

      • 多模式指的是对于同一个ADC,可以同时请求ADC进行读取转换、周期转换、序列转换、抢占转换

      • 多模式抢占中,抢占模式抢占其余模式(读取转换、周期转换、序列转换),而其他模式不能抢占抢占模式

      • 多模式抢占中,抢占只能发生在ADC的采样阶段,当ADC进入转换阶段,则不能发生抢占

      b) 示例:设置ADC分辨率为16位,设置序列模式进行转换,标记输入时钟与BUS相同,使能DMA访问AHB总线

      adc16_config_t cfg;
      
      /* initialize an ADC instance */
      adc16_get_default_config(&cfg);
      
      cfg.res            = adc16_res_16_bits;         /* 设置分辨率为16位 */
      cfg.conv_mode      = adc16_conv_mode_sequence;  /* 设置位序列转换模式 */
      cfg.adc_clk_div    = adc16_clock_divider_4;     /* 设置分频为4 */
      cfg.sel_sync_ahb   = true;                      /* 标记ADC输入时钟为BUS时钟相同 */
      cfg.adc_ahb_en = true;                          /* 使能DMA访问AHB总线 */
      
      /* adc16 initialization */
      if (adc16_init(BOARD_APP_ADC16_BASE, &cfg) == status_success) {
          /* enable irq */
          intc_m_enable_irq_with_priority(BOARD_APP_ADC16_IRQn, 1); 
          return status_success;
      } else {
          printf("%s initialization failed!\n", BOARD_APP_ADC16_NAME);
          return status_fail;
      }
    • 设置ADC序列转换属性

      a) 定义序列转换模式所使用的通道,例如:

      uint8_t seq_adc_channel[] = {BOARD_APP_ADC16_CH_1};

      注:

      • 如果需要实现多通道,可在数组中添加通道号对应的宏定义

      b) 定义序列转换模式存储转换结果所使用的buffer,例如:

      ATTR_PLACE_AT_NONCACHEABLE_WITH_ALIGNMENT(ADC_SOC_DMA_ADDR_ALIGNMENT) uint32_t seq_buff[APP_ADC16_SEQ_DMA_BUFF_LEN_IN_4BYTES];

      注:

      • 定义序列转换模式的memory时,必须声明以ADC_SOC_DMA_ADDR_ALIGNMENT所定义的长度对齐

      • 定义序列buffer的长度时,不得超过最大长度ADC_SOC_SEQ_MAX_DMA_BUFF_LEN_IN_4BYTES

      c) 通道属性
      通道设置结构体定义,如下:

      /** @brief ADC16 channel configuration struct. */
      typedef struct {
          uint8_t ch;                 /* 用于设置通道号 */
          uint16_t thshdh;            /* 用于设置看门狗监测阈值上限 */
          uint16_t thshdl;            /* 用于设置看门狗监测阈值下限 */
          bool wdog_int_en;           /* 用于设置看门狗监测中断使能 */
          uint8_t sample_cycle_shift; /* 用于设置采样时间左移位数 */
          uint32_t sample_cycle;      /* 用于设置采样时间,以ADC输入时钟分频后的时钟周期位单位 */
      } adc16_channel_config_t;

      示例:不启用看门狗阈值监测,仅设置通道号和采样周期

      adc16_channel_config_t ch_cfg;
      
      /* get a default channel config */
      adc16_get_channel_default_config(&ch_cfg);
      
      /* initialize an ADC channel */
      ch_cfg.sample_cycle = APP_ADC16_CH_SAMPLE_CYCLE;
      
      for (uint32_t i = 0; i < sizeof(seq_adc_channel); i++) {
          ch_cfg.ch = seq_adc_channel[i];
          adc16_init_channel(BOARD_APP_ADC16_BASE, &ch_cfg);
      }

      注:

      • 通过adc16_get_channel_default_config(&ch_cfg) 获取默认的配置,默认配置中禁用了看门狗监测功能

      示例: 启用看门狗阈值监测, 设置监测通道电压超过VREF/2时,产生中断

      adc16_channel_config_t ch_cfg;
      
      /* get a default channel config */
      adc16_get_channel_default_config(&ch_cfg);
      
      /* initialize an ADC channel */
      ch_cfg.sample_cycle = APP_ADC16_CH_SAMPLE_CYCLE;
      ch_cfg.thshdh       = 0x7ffff;
      ch_cfg.thshdl       = 0;
      ch_cfg.wdog_int_en  = true;
      
      for (uint32_t i = 0; i < sizeof(seq_adc_channel); i++) {
          ch_cfg.ch           = seq_adc_channel[i];
          adc16_init_channel(BOARD_APP_ADC16_BASE, &ch_cfg);
      }

      注:

      • 根据Datasheet, VREF为ADC外部参考电压,取值为2.0~3.6V

      d) 序列属性
      序列模式队列设置结构体定义,如下:

      /** @brief ADC16 queue configuration struct for the sequence mode. */
          typedef struct {
          bool seq_int_en;        /* 用于设置序列模式指定队列成员的通道完成转换后是否产生中断 */
          uint8_t ch;               /* 用于指定序列模式队列成员的通道号 */
      } adc16_seq_queue_config_t;

      序列设置结构体定义,如下:

      /** @brief ADC16 configuration struct for the sequence mode. */
      typedef struct {
          adc16_seq_queue_config_t queue[ADC_SOC_SEQ_MAX_LEN];  /* 用于设置序列模式队列成员的通道号和中断使能 */
          bool restart_en;                                      /* 用于设置序列模式完成一次序列转换后是否自动重新开始 */
          bool cont_en;                                         /* 用于设置是否使能多通道的循环转换 */
          bool sw_trig_en;                                      /* 用于设置是否使能软件触发 */
          bool hw_trig_en;                                      /* 用于设置是否使能硬件触发 */
          uint8_t seq_len;                                      /* 用于设置序列长度,最大长度为16 */
      } adc16_seq_config_t;

      e) 序列模式中的DMA
      DMA设置结构体定义,如下:

      /** @brief ADC16 DMA configuration struct. */
      typedef struct {  
          uint32_t *start_addr;           /* 用于设置序列buffer的起始地址 */
          uint32_t buff_len_in_4bytes;    /* 用于设置序列buffer的长度,以4字节为单位 */
          uint32_t stop_pos;              /* 用于设置DMA停止写入位置,DMA写入指针指向设置的offset时,暂停工作(此位置不写入) */
          bool stop_en;                   /* 用于设置是否使能DMA在停止位置处停止写入 */
      } adc16_dma_config_t;

      注:

      • 当使用HPM6750时,如果序列buffer的地址在DLM范围内,则需要通过将地址转换为AXI地址,具体请参考SDK中ADC相关sample
      • 当设置stop_en为true时,stop_pos的设置才有效
      • DMA写入数据时,会按照所设置的序列长度顺序循环写入序列buffer中

      f) 序列模式中DMA所使用的数据结构
      数据结构定义,如下:

      /** @brief ADC16 DMA configuration struct for the sequence mode. */
      #if defined(ADC_SOC_IP_VERSION) && (ADC_SOC_IP_VERSION < 2)
      typedef struct {
          uint32_t result    :16;     /* 用于存放16位ADC转换结果 */
          uint32_t seq_num   :4;      /* 用于表示在序列中的编号,一个序列最大长度为16,因此编号取值为0~15 */
          uint32_t           :4;
          uint32_t adc_ch    :5;      /* 用于表示ADC的通道号 */
          uint32_t           :2;
          uint32_t cycle_bit :1;      /* 用于表示序列每轮完成后的翻转指示位(0和1之间翻转变化) */
      } adc16_seq_dma_data_t;
      #else
      typedef struct {
          uint32_t result    :16;
          uint32_t seq_num   :4;
          uint32_t adc_ch    :5;
          uint32_t           :6;
          uint32_t cycle_bit :1;
      } adc16_seq_dma_data_t;
      #endif

      g)触发源(硬件或软件)

      • 硬件触发:通过硬件触发源,经过互联管理器(trigmux)到达ADC,触发ADC启动序列模式转换

      • 软件触发:通过调用如下API, 即可完成一次软件触发ADC转换

      hpm_stat_t adc16_trigger_seq_by_sw(ADC16_Type *ptr);

      注:

      • 在无法确保相邻两次触发的间隔大于ADC采样/转换的周期时,应用逻辑需要判断API的返回值,以此来确认是否产生了software conflict
    • 设置互联管理器
      通过调用如下API来设置互联管理器

      /* uint8_t input:  用于设置互联管理器的输入 */
      /* uint8_t output: 用于设置互联管理器的输出 */
      void init_trigger_mux(TRGM_Type *ptr, uint8_t input, uint8_t output);

      示例: 以HPM6750为例,设置以PWM0的通道8的信号作为输入,ADC3的序列触发作为输出

      init_trigger_mux(HPM_TRGM0, HPM_TRGM0_INPUT_SRC_PWM0_CH8REF, TRGM_TRGOCFG_ADC3_STRGI);

      注:

      • 互联管理器的输入源可以选择来自于PWM、GPTMR、GPIO等,而输出需要根据具体ADC实例来选择TRGM_TRGOCFG_ADCn_STRGI
    • 设置硬件触发源
      具体指的是对于所选择的互联管理器的外设进行初始化(此处不做详解)

    • 中断事件
      ADC中断事件枚举类型定义如下:

      /** @brief  Define ADC16 irq events. */
      typedef enum {
          /** This mask indicates that a trigger conversion is complete. */
          adc16_event_trig_complete       = ADC16_INT_STS_TRIG_CMPT_MASK,
      
          /** This mask indicates that a conflict caused by software-triggered conversions. */
          adc16_event_trig_sw_conflict    = ADC16_INT_STS_TRIG_SW_CFLCT_MASK,
      
          /** This mask indicates that a conflict caused by hardware-triggered conversions. */
          adc16_event_trig_hw_conflict    = ADC16_INT_STS_TRIG_HW_CFLCT_MASK,
      
          /** This mask indicates that a conflict caused when bus reading from different channels. */
          adc16_event_read_conflict       = ADC16_INT_STS_READ_CFLCT_MASK,
      
          /** This mask indicates that a conflict caused by sequence-triggered conversions. */
          adc16_event_seq_sw_conflict     = ADC16_INT_STS_SEQ_SW_CFLCT_MASK,
      
          /** This mask indicates that a conflict caused by hardware-triggered conversions. */
          adc16_event_seq_hw_conflict     = ADC16_INT_STS_SEQ_HW_CFLCT_MASK,
      
          /** This mask indicates that DMA is stopped currently. */
          adc16_event_seq_dma_abort       = ADC16_INT_STS_SEQ_DMAABT_MASK,
      
          /** This mask indicates that all of the configured conversion(s) in a queue is(are) complete. */
          adc16_event_seq_full_complete   = ADC16_INT_STS_SEQ_CMPT_MASK,
      
          /** This mask indicates that one of the configured conversion(s) in a queue is complete. */
          adc16_event_seq_single_complete = ADC16_INT_STS_SEQ_CVC_MASK,
      
          /** This mask indicates that DMA FIFO is full currently. */
          adc16_event_dma_fifo_full       = ADC16_INT_STS_DMA_FIFO_FULL_MASK
      } adc16_irq_event_t;    

      此处,仅讨论与序列模式相关的中断事件,根据定义:

      • 当需要在序列模式的单个通道转换完成时产生中断,使能adc16_event_seq_single_complete事件中断即可
      • 当需要在序列模式的多个(所有)通道转换完成时产生中断,可以使能adc16_event_seq_full_complete事件中断,也可以仅使能最后一个通道的转换完成事件中断
    • 中断服务

      • 判断序列模式任意通道转换完成中断是否产生: 通过读取中断状态寄存器中的SEQ_CVC位来确定
      • 判断通道看门狗监测中断是否产生: 通过读取中断状态寄存器中(一个或多个)通道所对应的掩码来确定
      • 看门狗监测中断产生后,需要关闭中断,由应用逻辑处理后(或异常消除后),再确定是否开启相关中断,否则中断会持续产生

      示例: 中断服务

      SDK_DECLARE_EXT_ISR_M(BOARD_APP_ADC16_IRQn, isr_adc16)
      void isr_adc16(void)
      {
          uint32_t status;
      
          status = adc16_get_status_flags(BOARD_APP_ADC16_BASE);
      
          /* Clear status */
          adc16_clear_status_flags(BOARD_APP_ADC16_BASE, status);
      
          if (ADC16_INT_STS_SEQ_CVC_GET(status)) {
              /* Set flag to read memory data */
              seq_complete_flag = 1;
          }
      
          if (ADC16_INT_STS_TRIG_CMPT_GET(status)) {
              /* Set flag to read memory data */
              trig_complete_flag = 1;
          }
      
          if (ADC16_INT_STS_WDOG_GET(status) & APP_ADC16_CH_WDOG_EVENT) {
              adc16_disable_interrupts(BOARD_APP_ADC16_BASE, APP_ADC16_CH_WDOG_EVENT);
              res_out_of_thr_flag = ADC16_INT_STS_WDOG_GET(status) & APP_ADC16_CH_WDOG_EVENT;
          }
      }

      注:

      • 上述代码中,APP_ADC16_CH_WDOG_EVENT为初始化时,所定义的(一个或多个)通道对应的掩码(具体可查阅手册中相关寄存器描述)
    • 序列转换结果和看门狗监测结果处理
      a) 示例:序列模式中,对转换结果进行处理

      hpm_stat_t process_seq_data(uint32_t *buff, int32_t start_pos, uint32_t len)
      {
           adc16_seq_dma_data_t *dma_data = (adc16_seq_dma_data_t *)buff;
      
           if (ADC16_IS_SEQ_DMA_BUFF_LEN_INVLAID(len)) {
               return status_invalid_argument;
           }
      
           current_cycle_bit = !current_cycle_bit;
      
           for (uint32_t i = start_pos; i < start_pos + len; i++) {
               printf("Sequence Mode - %s - ", BOARD_APP_ADC16_NAME);
               printf("Cycle Bit: %02d - ",   dma_data[i].cycle_bit);
               printf("Sequence Number:%02d - ", dma_data[i].seq_num);
               printf("ADC Channel: %02d - ",  dma_data[i].adc_ch);
               printf("Result: 0x%04x\n", dma_data[i].result);
      
               if (dma_data[i].cycle_bit != current_cycle_bit) {
                   printf("Error: Cycle bit is not expected value[%d]!\n", current_cycle_bit);
                   while (1) {
      
                   }
               }
          }
      
          return status_success;
      }

      b) 示例: 看门狗监测处理

      void channel_result_out_of_threshold_handler(void)
      {
           adc16_channel_threshold_t threshold;
      
           if (res_out_of_thr_flag & APP_ADC16_CH_WDOG_EVENT) {
               adc16_get_channel_threshold(BOARD_APP_ADC16_BASE, ADC16_SOC_TEMP_CH_NUM, &threshold);
               printf(“Warning - %s [channel %02d] - Sample voltage is out of the thresholds between 0x%04x and 0x%04x !\n”, BOARD_APP_ADC16_NAME, ADC16_SOC_TEMP_CH_NUM, threshold.thshdl, threshold.thshdh);
               res_out_of_thr_flag = 0;
               adc16_enable_interrupts(BOARD_APP_ADC16_BASE, APP_ADC16_CH_WDOG_EVENT);
           }
      }

7. 如何设置HPMicro MCU片上ADC的序列模式的相关属性来对应到其他MCU所具有的转换模式?

  • 定义ADC序列数组中定义通道

    uint8_t seq_adc_channel[] = {BOARD_APP_ADC16_CH_1, BOARD_APP_ADC16_CH_2, BOARD_APP_ADC16_CH_3};
  • 各种模式对应序列设置的差异

    模式 seq_len设置值 restart_en设置值 cont_en设置值
    单通道单次模式 1 false x
    单通道连续模式 1 true x
    多通道单次扫描模式 sizeof(seq_adc_channel) false true
    多通道连续扫描模式 sizeof(seq_adc_channel) true true
    多通道单次间断模式 sizeof(seq_adc_channel) false false
    多通道连续间断模式 sizeof(seq_adc_channel) true false

    注:

    • 通道定义可以是一个或多个,但是不能超过序列所支持的最大长度

    • x表示不用设置(设置无效)

    示例:多通道循环扫描模式

    需要设置序列长度, 且需要使能序列自动重新开始,还需要使能自动循环转换,如下:

    /* Set a sequence config */
    seq_cfg.seq_len    = sizeof(seq_adc_channel);
    seq_cfg.restart_en = true;
    seq_cfg.cont_en    = true;

8. 关于HPMicro MCU片上ADC的性能评估

请查阅《HPM系列MCU 高精度ADC之性能评估与测试指南》, 相关连接如下:

https://www.hpmicro.com/Public/Uploads/uploadfile/files/20240620/HPMxilieMCUgaojingduADCxingnengpingguyuceshizhinan7f.pdf

0
0

订阅

发表回复 0

Your email address will not be published. Required fields are marked *