• Stm32 ADC学习


    stm32 ADC 简介

    stm32的ADC是 12位逐次逼近型 模拟数字转换器;它包括18个通道,可以用来测量16个外部通道2个内部通道.ADC转换的结果存放在16位数据寄存器(ADC规则数据寄存器,ADC_DR 和 ADC注入数据寄存器,ADC_JDCx)中,这个数据寄存器可以设置对齐方式为左对齐或右对齐.

    ADC通道与GPIO对应表(图片来自整点原子STM32F1开发指南库函数版本)


    规则通道组注入通道组

    注入通道可打断规则通道的转换

    所谓规则通道组和注入通道组其实应该就是通道的分组吧,按照OOP的思想来理解,通道组是一个基类,注入通道组和规则通道组派生自通道组这个基类,通道组这个基类中包含了一个保存各个通道的数组.

    为什么要对通道进行分组呢,这个有待深究,以后再说.


    ADC相关的寄存器

    ADC_CR1

    各个位描述如下图:

    • scan位:
      设置扫描模式,1为使用扫描模式,0则关闭.扫描模式下,有ADC_SQRx或ADC_JSQRx寄存器选中的通道被转换,此时如果设置了EOCIE或JEOCIE,则只有在最后一个通道转换完毕后才会产生EOC或JEOC中断
    • DUALMOD位:
      设置ADC的操作模式,详细的看下面的来自<<stm32中文参考手册>>截图

    ADC_CR2

    各个位描述如下图:

    • ADON位:用于开关AD转换器
    • CONT位:用于设置是否进行连续转换,使用单次转换CONT位必须设置为0.
    • EXTSEL[2:0]:用于选择启动规则转换组转换的外部事件

      如果需要使用软件触发,就将这三个位设置为 111

    ADC采样事件寄存器(ADC_SMPR1和ADC_SMPR2)

    各个位描述如下图:

    这两个寄存器用来设置通道0~17的采样时间,每个通道要占3位.

    对于每个要转换的通道,采样时间尽量长一点,以获得较高的准确度,但是会降低ADC的转换速率.ADC的转换时间可以由以下公式计算:

    Tconv = 采样时间 + 12.5周期

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

    这几个寄存器功能都差不多,不一一详细说明了.

    • L[3:0]:用于存储规则序列的长度

    ADC数据寄存器(ADC_DR)

    这个没什么好说的,用来存放AD转换后的结果

    要注意可以通过ADC_CR2的ALIGN位设置这个寄存器是左对齐还是右对齐

    ADC状态寄存器(ADC_SR)

    没啥好说的,保存了各种状态,看图吧.


    通过库函数配置ADC1通道1进行AD转换

    查看手册可以知道ADC1通道1对应着PA1,如下图



    1.外设使能

    • STM32F103ZET6的ADC通道1在PA1上所以我们先要使能 PORTA的时钟 和 ADC1时钟,然后设置 PA1为模拟输入.

    2.复位ADC1,同时设置ADC1的分频因子

    • 开启了ADC1的时钟后,要复位ADC1,将ADC1的全部寄存器重设位缺省值,然后通过RCC_CFGR设置ADC1的分频因子,并且分频因子要确保ADC1的时钟不超过14MHz!
      //ADC复位 ADC_DeInit();
      RCC_ADCCLKConfig(RCC_PCLK2_DIV6); //设置分配因子的库函数

    3.初始化ADC1参数,设置ADC1的工作模式以及规则序列的相关信息.
    void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);// ADC初始化

    4.使能ADC并校准
    void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState); //使能指定的ADC
    void ADC_ResetCalibration(ADC_TypeDef* ADCx);//复位校准
    while(ADC_GetResetCalibrationStatus(ADCx));//等待复位校准结束
    void ADC_StartCalibration(ADC_TypeDef* ADCx);//执行ADC校准
    while(ADC_GetCalibrationStatus(ADCx));//等待AD校准结束

    5.设置规则序列1里面的通道,读取ADC的值
    void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); //设置规则序列通道以及采样周期
    void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);//允许软件开启ADC转换
    while(!ADC_GetFlagStatus(ADCx,ADC_FLAG_EOC));//等待转换结束
    uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);//获取转 换结果数据

    MDK5中实现代码

    • 配置stm32f103库函数编程环境,我用的是正点原子的那一套,不多记录了;

    • ADC1_Init() 函数

      void ADC1_Init(void)
      /* ---------------------------------------------
          ADC1_IN1 -> PA.1
      --------------------------------------------- */
      void ADC1_Init(void)
      {
          GPIO_InitTypeDef gpioInitStruct;
          ADC_InitTypeDef adcInitStruct;
          //Enable periph clock
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
          //配置ADC的时钟,不要超过14MHz;
          RCC_ADCCLKConfig(RCC_PCLK2_Div6);	
          //Congfig PA.1
          gpioInitStruct.GPIO_Mode = GPIO_Mode_AIN;	//设置PA.1的输入模式为模拟输入
          gpioInitStruct.GPIO_Pin = GPIO_Pin_1;
          gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
          GPIO_Init(GPIOA,&gpioInitStruct);
          //ADC1 复位
          ADC_DeInit(ADC1);
          //initialize adc1
          adcInitStruct.ADC_Mode = ADC_Mode_Independent;	//ADC模式:ADC独立模式
          adcInitStruct.ADC_DataAlign = ADC_DataAlign_Right;	//ADC_DR寄存器的数据对齐方式:右对齐
          adcInitStruct.ADC_ScanConvMode = DISABLE;	//是否允许连续扫描模式:不允许,使用单通道模式
          adcInitStruct.ADC_ContinuousConvMode = DISABLE;	//单次转换模式
          adcInitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//ADC触发方式:不需要外部触发源,由软件触发
          adcInitStruct.ADC_NbrOfChannel = 1;	//进行转换的ACD通道数目
          ADC_Init(ADC1,&adcInitStruct);
          //Enable ADC1
          ADC_Cmd(ADC1,ENABLE);
          //ADC1校准
          ADC_ResetCalibration(ADC1);	//复位校准
          while(ADC_GetResetCalibrationStatus(ADC1));//等待复位校准完成
          ADC_StartCalibration(ADC1); //ACD校准
          while(ADC_GetCalibrationStatus(ADC1));	//等待ADC校准完成
      }
      
    • ADC_GetConvValue()

      u16 ADC_GetConvValue(u8 channel)
      /**
      * 获取指定的通道adc的转换结果
      * @param channel: 指定adc的通道,必须在1~3之间
      */
      u16 ADC_GetConvValue(u8 channel)
      {
          //Config sample channel
          //采样周期尽量设置长一些,以确保精度
          ADC_RegularChannelConfig(ADC1,channel,1,ADC_SampleTime_239Cycles5);
          //使能软件转换
          ADC_SoftwareStartConvCmd(ADC1,ENABLE);
          while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));
          return ADC_GetConversionValue(ADC1);
      }
      
    • ADC_GetAverageConvValue()

      u16 ADC_GetAverageConvValue(u8 channel,u8 times)
      u16 ADC_GetAverageConvValue(u8 channel,u8 times)
      {
          u8 i = 0;
          u32 temp = 0;
          for(;i < times;i++)
          {
              temp += ADC_GetConvValue(channel);
          }
          return (u16)(temp/times);
      }
      
    • ADC_SampleValue2ReadableValue()

      double ADC_SampleValue2ReadableValue(u16 sampleValue)
      /**
          * 描述:将AD转换后存放在ADC_DR寄存器中的值转换为有意义的值,
          * 		 可以通过宏定义 REF 来设置参考电压
          * @param sampleValue : 要转换的值
          * @retval 转换的结果,是一个double类型的浮点数
          */
      double ADC_SampleValue2ReadableValue(u16 sampleValue)
      {
          return (double)sampleValue*((double)REF/4096.0);
      }
      
    • main.c文件代码

      main.c
      #include"ADC.h"
      #include"delay.h"
      #include"usart.h"
      

      void Init(void);
      void Loop(void);

      int main(void)
      {
      Init();
      while(1)
      Loop();
      }

      void Init(void)
      {
      delay_init();
      USART_Debug_Init();
      ADC1_Init();
      USART_WriteLine("系统初始化完成!将开始AD采集.");
      }

      void Loop(void)
      {
      u16 sampledValue;
      sampledValue = ADC_GetAverageConvValue(1,5);
      USART_WriteLine("采样值: %d ; 计算所得电压值: %.2f ",sampledValue,ADC_SampleValue2ReadableValue(sampledValue));
      delay_ms(250);
      }

    • 在main.c中使用的 USART_WriteLine(const char*str,...)函数 是我自己封装的串口函数,也可以使用正点原子 System文件夹 下提供的printf()函数;

    总结库函数配置ADC的步骤

    1.使能需要用到的GPIO 和 ADC的时钟;
    2.配置ADC的时钟分频:
    void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
    2.配置IO口,输入模式为 GPIO_Mode_AIN,模拟输入模式;
    3.调用 ADC_DeInit(); 对ADC进行复位;
    4.调用 ADC_Init();初始化ADC
    5.调用 ADC_Cmd(ADCx,ENABLE);,使能ADC
    6.校准ADC:
    ADC_ResetCalibration(ADC1); //复位校准 while(ADC_GetResetCalibrationStatus(ADC1));//等待复位校准完成 ADC_StartCalibration(ADC1); //ACD校准 while(ADC_GetCalibrationStatus(ADC1)); //等待ADC校准完成
    7.获取AD转换的结果

      </div>
  • 相关阅读:
    Linux远程连接Windows桌面
    Ubuntu telnet
    Linux Source命令及脚本的执行方式解析(转)
    Java图形与文本(18)
    从把3000行代码重构成15行代码谈起
    学习:java原理—反射机制
    BugFix系列---开篇介绍
    回调函数透彻理解Java
    Java回调函数的理解
    Java注解浅谈
  • 原文地址:https://www.cnblogs.com/Laggage/p/10406118.html
Copyright © 2020-2023  润新知