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


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

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

    目录

    引言

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

    1 基础知识

    1.1ADC转换基本流程

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

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

    1.2 时钟树

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

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

    1.3 关键参数

    1.3.1 位数

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

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

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

    1.3.2 触发信号

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

    1.3.3 采样时间

    采样时间对应ADC转换流程中的第二步,即开关摁下多久,外部信号对CADC充电多长时间。一般来说采样时间以ADC时钟周期TADC的倍数,如:1.5TADC2.5TADC19.5TADC等。一般来说采样时间越长,信号采样越准确,一般是根据外电阻的大小来选择采样时间,具体采样时间选择可以参照下表:
    image
    例如:外电阻为5k,外电阻介入2.7k~8.2k之间,则可以选择的采样时间为61.5TADC

    1.3.4 转换时间

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

    2 CubeMx 配置步骤

    2.1 确定输入通道

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

    2.2 配置ADC

    image
    重要参数介绍:

    • 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 输出设置

    image

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

    2.4 MD5 设置

    image

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

    3 程序解读

    3.1 ADC初始化

    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才可以正常工作。

    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转换完成后,使用窗口将转换数据经串口上传到上位机。串口程序用的为匿名上位机。

    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 匿名上位机程序

    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;i3]+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采集通道,电路图如图:
    image
    实验波形在匿名上位机上显示如图:
    image
    可以看到:

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

    5 总结

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

  • 相关阅读:
    搭建Spark开发环境(第二弹)
    关于英文BALL的一些地道说法和用法,你知多少?
    【Unity入门】鼠标输入和键盘输入
    一篇超级最全的python基础篇(新手必看!)
    环境土壤物理模型HYDRUS建模方法与多案例应用
    力扣刷题(代码回忆录)——动态规划
    10分钟构建前后端分离后台管理系统(renren-fast、renren-vue)
    朝阳药品数据分析案例
    vue视频播放功能
    无重复字符的最长子串(python3)
  • 原文地址:https://www.cnblogs.com/muheandrabbit/p/17783979.html