STM32F3系列 ADC采样单端采样模式(基于LL库)

STM32F3系列 ADC 单端采样(基于LL库)

  • 芯片型号:STM32f303RBT6
  • 开发软件:MDK5 & CubeMX & VS Code

目录

目录

  • [STM32F3系列 ADC 单端采样(基于LL库)](#STM32F3系列 ADC 单端采样(基于LL库))
    • 目录
    • 引言
    • [1 基础知识](#1 基础知识)
      • 1.1ADC转换基本流程
      • [1.2 时钟树](#1.2 时钟树)
      • [1.3 关键参数](#1.3 关键参数)
        • [1.3.1 位数](#1.3.1 位数)
        • [1.3.2 触发信号](#1.3.2 触发信号)
        • [1.3.3 采样时间](#1.3.3 采样时间)
        • [1.3.4 转换时间](#1.3.4 转换时间)
    • [2 CubeMx 配置步骤](#2 CubeMx 配置步骤)
      • [2.1 确定输入通道](#2.1 确定输入通道)
      • [2.2 配置ADC](#2.2 配置ADC)
      • [2.3 输出设置](#2.3 输出设置)
      • [2.4 MD5 设置](#2.4 MD5 设置)
    • [3 程序解读](#3 程序解读)
      • [3.1 ADC初始化](#3.1 ADC初始化)
      • [3.2 校准和启动ADC](#3.2 校准和启动ADC)
      • [3.3 主函数配置](#3.3 主函数配置)
      • [3.4 匿名上位机程序](#3.4 匿名上位机程序)
    • [4 实验波形](#4 实验波形)
    • [5 总结](#5 总结)

引言

STM32F303系列单片机一般具有多个12位逐次逼近型(Successive approximatio)模数转换器(ADC,analog-to-digital converter)。STM32的ADC功能很多:单端采样、差分采样、主从模式、双ADC模式、注入模式等。本文作为学习笔记,记录最简单的单端(single-end)模式.

1 基础知识

1.1ADC转换基本流程

上图是STM32ADC的框图,黑色箭头我自己加的开关。其转换流程大概可以简略为:

  1. 接收到触发信号,摁下开关
  2. 外部电压经过外部电阻(\(R_{AIN}\)),杂散电容(\(C_{parastitic}\))、ADC内部电阻(\(R_{ADC}\)),给采样电容\(C_{ADC}\)充电。
  3. 充电完成,松开开关,转换核心开始将电容上的电压值转为数字信号。
  4. 转换完成,转换结果存入ADC的数据寄存器中。

1.2 时钟树

STM32F3系列的单片机的ADC时钟有两路,一路为同步时钟(黑色),一路为异步时钟(蓝色)。异步时钟除了比同步时钟具有更多的分频选择,其他没有大的区别,其时钟最高频率也是相同的。

但是建议采用同步时钟,使用异步时钟时或许可能转换会出现问题,我也不知道什么原因。

1.3 关键参数

1.3.1 位数

ADC一般可以分为10bit、12bit、16bit等,这个bit就是指的位数。位数的含义就是能把参考电平分为2的多少次方份。

比如:10bit的ADC可以把参考电平分为1024份; 12bit的ADC可以把参考电平分为4096份。若是参考电压为3.3V,则10bitADC可以分辨的最小电压为\(3.3/(2^{10}) = 3.22mV\),12bitADC可以分辨的最小电压为\(3.3/(2^{12}) = 0.806mV\)。

由此可见,ADC位数越大,其分辨率越高,采集到的电压相对越准确。

1.3.2 触发信号

触发信号对于ADC转换流程中的第一步,即告诉ADC什么时候开始转换。触发信号有很多种,常用的有:软件触发、定时器信号触发和外部触发等。

1.3.3 采样时间

采样时间对应ADC转换流程中的第二步,即开关摁下多久,外部信号对\(C_{ADC}\)充电多长时间。一般来说采样时间以ADC时钟周期\(T_{ADC}\)的倍数,如:\(1.5T_{ADC}\)、\(2.5T_{ADC}\)、\(19.5T_{ADC}\)等。一般来说采样时间越长,信号采样越准确,一般是根据外电阻的大小来选择采样时间,具体采样时间选择可以参照下表:

例如:外电阻为5k,外电阻介入2.7k~8.2k之间,则可以选择的采样时间为\(61.5T_{ADC}\)

1.3.4 转换时间

转换时间指的是ADC将制定电压转为为数字信号所用的时间,这个时间一般不可以控制,与ADC的时钟周期有关,时钟周期越长则转换时间越小。

2 CubeMx 配置步骤

2.1 确定输入通道

选取ADC1-IN1通道作为检测通道,选择单端模式Single-ended。

2.2 配置ADC

重要参数介绍:

  • Mode:independence即独立模式;
  • Clock Prescale:选取同步时钟最为ADC的时钟,分频系数为1,即ADC时钟为72M
  • Resolution(分辨率):选取12bit
  • Data Alignment(数据对其):一般选右对齐
  • End of Conversion selection(转换完成信号):这个参数指定了何时ADC触发DMA和中断,有两个参数End of single conversion(EOC) 与 End of sequence of conversion(EOS),即单次转换完成和顺序转换完成,由于我们只有一个通道选择这两个一样的,本次选择EOC。
  • OverRun behavior(覆写行为):若使能这个功能,则在ADC上次数据还没有读取的时候,新的输出产生时,会直接覆写上次数据。
  • Lower Power Auto Wait(低功耗自动等待):用于低功耗的功能。
  • Enable Regular Conversations(使能常规转换组):字面意思,使能常规组转换。
  • Number of Conversion(转换数量):需要转换的信号有几个。
  • External Trigger Conversion Source(外部触发源):触发信号是什么?软件、或定时器等。
  • External Trigger Conversion Edge(外部触发边沿):指定触发类型,上升沿触发、或下降沿触发等
  • Rank 1:
    • Channal(通道号):对应Channel1
    • Sampling Time(采样时间):对应上文的采样时间;
    • Offset Number(通道偏移):指定那个通道需要数据偏移。
    • Offset(数值偏移):即在采集到的数据减去一个数字偏置。
  • ADC_Injected Conversions(ADC注入模式):暂时不需要。
  • Analog Watchdog1~3():看门狗功能,暂时不需要。

2.3 输出设置

使用LL库,剩下的按照常规配置就行。

2.4 MD5 设置

勾选Reset and Run,否则下载程序后单片机不会自动运行,复位后才会运行。

3 程序解读

3.1 ADC初始化

C 复制代码
void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  LL_ADC_InitTypeDef ADC_InitStruct = {0};
  LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
  LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_ADC12);    // 使能ADC时钟

  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);    // 使能GPIOA时钟
  /**ADC1 GPIO Configuration
  PA0   ------> ADC1_IN1
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Common config
  */
  ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;   // 12bit分辨率
  ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;// 数据右对齐
  ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;// 不使用低功耗模式
  LL_ADC_Init(ADC1, &ADC_InitStruct);
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;// 软件触发
  ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;// 不使用扫描模式
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;// 不使用断续模式
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;// 单次触发单次转换
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_LIMITED;// 不使用DMA
  ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN; // 数据覆写使能
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
  ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_SYNC_PCLK_DIV1;// 使能内部时钟1分频作为ADC时钟
  ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_INDEPENDENT;// ADC采取独立模式
  LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);

  /* Enable ADC internal voltage regulator *//*使能ADC内部的稳压器*/
  LL_ADC_EnableInternalRegulator(ADC1);
  /* Delay for ADC internal voltage regulator stabilization. */
  /* Compute number of CPU cycles to wait for, from delay in us. */
  /* Note: Variable divided by 2 to compensate partially */
  /* CPU processing cycles (depends on compilation optimization). */
  /* Note: If system core clock frequency is below 200kHz, wait time */
  /* is only a few CPU processing cycles. */
  uint32_t wait_loop_index;
  wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
  while(wait_loop_index != 0)
  {
    wait_loop_index--;
  }

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_1);    // 设置通道1转换次序为1
  LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_181CYCLES_5);// 采样时间为181.5个ADC周期
  LL_ADC_SetChannelSingleDiff(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SINGLE_ENDED);// 采样模式为单端采样
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

3.2 校准和启动ADC

上述配置完后,使用LL库还需要一些额外的代码,ADC才可以正常工作。

C 复制代码
void Mx_ADC_Start(void)
{
  uint8_t TimeDelta = LL_ADC_DELAY_CALIB_ENABLE_ADC_CYCLES;

  LL_ADC_StartCalibration(ADC1,LL_ADC_SINGLE_ENDED);        // 开始校准
  while(LL_ADC_IsCalibrationOnGoing(ADC1))                  // 等待校准完成
  	;
  LL_ADC_SetCalibrationFactor(ADC1,LL_ADC_SINGLE_ENDED  LL_ADC_GetCalibrationFactor(ADC1,LL_ADC_SINGLE_ENDED));// 将校准向量写  ADC1中

  while(TimeDelta > 0)  // 校准后延时
  {
  	TimeDelta--;
  }
  LL_ADC_Enable(ADC1); // 使能ADC
}

3.3 主函数配置

主函数就比较简单,使用软件触发ADC转换,待ADC转换完成后,使用窗口将转换数据经串口上传到上位机。串口程序用的为匿名上位机。

C 复制代码
while (1)
{
  LL_ADC_REG_StartConversion(ADC1);           // 用软件触发ADC转换
  while(LL_ADC_REG_IsConversionOngoing(ADC1)) // 等待ADC转换完成
  	;
  sent_data(LL_ADC_REG_ReadConversionData12(ADC1),0,0,0);// 使用匿名上机  ADC数据发送到电脑
  while(count)  // 适当延迟
  {
  	count--;
  }
}

3.4 匿名上位机程序

C 复制代码
uint8_t BUFF[30];

void sent_data(uint16_t A,uint16_t B,uint16_t C,uint16_t D)
{
  int i;
  uint8_t sumcheck = 0;
  uint8_t addcheck = 0;
  uint8_t _cnt=0;
  BUFF[_cnt++]=0xAA;//帧头
  BUFF[_cnt++]=0xFF;//目标地址
  BUFF[_cnt++]=0XF1;//功能码
  BUFF[_cnt++]=0x08;//数据长度
  BUFF[_cnt++]=(A&0x00ff);//数据内容,小段模式,低位在前
  BUFF[_cnt++]=(A&0xff00)>>8;//需要将字节进行拆分,调用上面的宏定义即可。
  BUFF[_cnt++]=(B&0x00ff);
  BUFF[_cnt++]=(B&0xff00)>>8;	
  BUFF[_cnt++]=(C&0x00ff);//数据内容,小段模式,低位在前
  BUFF[_cnt++]=(C&0xff00)>>8;//需要将字节进行拆分,调用上面的宏定义即可。
  BUFF[_cnt++]=(D&0x00ff);
  BUFF[_cnt++]=(D&0xff00)>>8;
  //SC和AC的校验直接抄最上面上面简介的即可
  for(i=0;i<BUFF[3]+4;i++) 
  {
  	sumcheck+=BUFF[i];
  	addcheck+=sumcheck;
  }
  BUFF[_cnt++]=sumcheck;	
  BUFF[_cnt++]=addcheck;	

  for(i=0;i<_cnt;i++) 
  {
  	while ((USART1->ISR & 0X40) == 0)
    ; /* 等待上一个字符发送完成 */
  	USART1->TDR=BUFF[i];
  }//串口逐个发送数据
}

4 实验波形

使用函数信号发生器,产生频率为1kHz,幅值为3V的正弦波,经两个2k电阻分压后传入ADC采集通道,电路图如图:

实验波形在匿名上位机上显示如图:

可以看到:

  • 波形为正弦波
  • 最高值 1863 对应 1.501V
    证明ADC采集正确。

5 总结

至此完成了STM32最简单的ADC单端采样,STM32的ADC还有很多其他功能,待之后有时间再记录。本文记录难免有错误,如有错误,欢迎指出。

相关推荐
十月的皮皮9 小时前
stm20260720-从新手 C 到量产 STM32 工程:程序设计推导指南
c语言·开发语言·stm32·stm32cubemx·hal库
开发笔记-阿牛9 小时前
解析一款有意思的产品||CK6159A语音芯片设计的儿童专注力训练机-舒尔特玩具
stm32·单片机·嵌入式硬件·音频
搞全栈小苏11 小时前
SIMetrix_simplis8.4安装教程——附安装包
stm32·单片机·嵌入式硬件·电力电子
qq_5739678212 小时前
STM32控制器
stm32·单片机·嵌入式硬件
某林21213 小时前
履带小车底盘stm32F103RCT6开发
人工智能·stm32·单片机·嵌入式硬件·架构·人机交互·ros2
做萤石二次开发的哈哈15 小时前
萤石 ERTC Windows SDK 包含哪些类型定义?
stm32·单片机·嵌入式硬件
亿道电子Emdoor15 小时前
【Aitium】PCB导出DXF文件出现eDuplicateRecordName报错
stm32·单片机·嵌入式硬件·altium
巴巴媛6661 天前
STM32学习笔记【38.DHT11实验】
笔记·stm32·学习
深圳市泰凌微电子1 天前
HK航顺 HK32F407RGT7替STM32F405RGT6 微控制器32位MCU
stm32·单片机·嵌入式硬件
小心亦新1 天前
STM32学习16--定时器输出比较2直流电机
stm32·嵌入式硬件·学习