STM32-嵌入式学习笔记06-ADC的使用

写在前面

做为信号类电赛菜鸡弟弟coder选手,ADC简直就是这部分的核心输出(貌似也确实和游戏ADC类似哈哈哈),丰富的配置ADC的配置过51系列ADC(应该算是外设ADC),msp430系列ADC,FPGA系列ADC(必然外设),STM32ADC今天也配置下吧。目前只是最简版本,后面有机会更新,不同模式下的ADC。

开发环境

  1. STM32F103RB系列芯片(蓝桥杯开发板可直接用)
  2. keil5

实现功能

ADC使用单次的连续转换,启用滴答定时器确保ADC的电压值可以在200ms的刷新率下进行刷新,并在TFT屏幕上进行显示

STM32 ADC简介

在这里插入图片描述
对于任何数字系统来说,没有ADC(模拟到数字转换器),外部的电压我们就没办法测量,模数转换、数模转换对于任何的测量电子系统来说都是必不可少的东西。举个不太恰当的例子,这就相当于人要和狗狗交流,人(主控)不懂狗的语言(模拟输入)需要狗叫翻译器(模数转换器),把信息翻译进来(板内处理)后,然后再用狗叫模拟机给叫给它听(数模转换)。
这里我参考书中的一些内容来大致解释下在STM32中ADC大致是怎样的情况。

  1. STM32 拥有 1~3 个 ADC(STM32F101/102 系列只有 1 个 ADC),这些 ADC可以独立使用,也可以使用双重模式(提高采样率)。
  2. STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。
  3. ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。
  4. 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
  5. STM32F103 系列最少都拥有 2 个 ADC。
  6. STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降。
  7. STM32 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。

ADC通道对应关系

在这里插入图片描述
开发指南中的基本把ADC中的所有要配置的东西包含在内了,对于库函数开发来说,配置好具体的结构体,用好库函数,基本这部分也算掌握了。给出ADC初始化结构体大家品一品。

typedef struct
{
    
    
  uint32_t ADC_Mode;                      /*!< Configures the ADC to operate in independent or
                                               dual mode. 
                                               This parameter can be a value of ADC_mode */

  FunctionalState ADC_ScanConvMode;       /*!< Specifies whether the conversion is performed in
                                               Scan (multichannels) or Single (one channel) mode.
                                               This parameter can be set to ENABLE or DISABLE */

  FunctionalState ADC_ContinuousConvMode; /*!< Specifies whether the conversion is performed in
                                               Continuous or Single mode.
                                               This parameter can be set to ENABLE or DISABLE. */

  uint32_t ADC_ExternalTrigConv;          /*!< Defines the external trigger used to start the analog
                                               to digital conversion of regular channels. This parameter
                                               can be a value of @ref ADC_external_trigger_sources_for_regular_channels_conversion */

  uint32_t ADC_DataAlign;                 /*!< Specifies whether the ADC data alignment is left or right.
                                               This parameter can be a value of @ref ADC_data_align */

  uint8_t ADC_NbrOfChannel;               /*!< Specifies the number of ADC channels that will be converted
                                               using the sequencer for regular channel group.
                                               This parameter must range from 1 to 16. */
}ADC_InitTypeDef;

ADC配置函数

void ADC_Config(void)
{
    
    
	ADC_InitTypeDef  ADC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStruct;
	#时钟的使能
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	//配置gpio管脚
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 ;  
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN ; 
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	//配置ADC结构体参数
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//单独工作
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//不使能扫描转换
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//使能连续转换,因为程序简单这里不需要每次测量都单独使能了
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部中断
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据位对齐设置,一般设置右对齐,方便计算
	ADC_InitStructure.ADC_NbrOfChannel = 1;//通道长度,这里就使能了一个,所以为1
	ADC_Init(ADC1, &ADC_InitStructure);//结构体配置完成,使用函数初始化,保存我们的配置

	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);//设置ADC的通道和采样时钟,具体参数见头文件的定义
	

}

ADC配置完毕。

使能 ADC并校准

在设置完了ADC结构体的信息后,我们就使能 AD 转换器,执行复位校准和 AD 校准,这两步
是必须的!不校准将导致结果很不准确。这就和买东西上称前要清零,谁也不想吃亏对吧哈哈哈。

	ADC_Cmd(ADC1, ENABLE);//使能我们使用的ADC1
    //启用ADC1复位校准寄存器,保证ADC能正常使用,先复位 
    ADC_ResetCalibration(ADC1);
    //检查ADC1复位校准寄存器的结束
    while(ADC_GetResetCalibrationStatus(ADC1));
    //开始ADC1校准
    ADC_StartCalibration(ADC1);
    //检查ADC1校准结束
    while(ADC_GetCalibrationStatus(ADC1));
    //启动ADC1软件转换
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

PS上述部分可以直接键入init函数中,也可以自己定义函数调用。

读取 ADC 值

这里比较简单,就使用了读取电压转换值的函数,然后把结果进行了数学转换为真实电压即可。
这里的0XFFF是右对齐除的精度。

v_value=ADC_GetConversionValue(ADC1)*3.30/0xfff;

main.c

下面在加上显示我们的这个ADC的demo就算完了,这里我思路大致就是在TFT屏幕上进行电压值的显示,然后我用了滴答定时器,让ADC的电压值可以在200ms的刷新率下进行刷新。

#include "stm32f10x.h"
#include <stdio.h>
#include "lcd.h"
uint32_t timingdelay = 0;
char ADC_Flag;
void delayms(uint16_t n);
void ADC_Config(void);
float v_value;
uint8_t  buf[20];
int main(void)
{
    
    
	STM3210B_LCD_Init();
	ADC_Config();
	SysTick_Config(SystemCoreClock/1000);
	LCD_SetTextColor(Yellow);
	LCD_SetBackColor(Magenta);
	LCD_Clear(Magenta);
	LCD_DisplayStringLine(Line0, "      ADC-DEMO      ");
	LCD_DisplayStringLine(Line1, "====================");
	LCD_DisplayStringLine(Line3, "                    ");
	while(1)
	{
    
     
			
		 if(ADC_Flag==1)
		 {
    
    
				ADC_Flag = 0;
			 v_value=ADC_GetConversionValue(ADC1)*3.30/0xfff;
			 sprintf(buf,"ADC Value:%.3f",v_value);
			 LCD_DisplayStringLine(Line3,buf);
		 }
			
	}
}
void ADC_Config(void)
{
    
    
	ADC_InitTypeDef  ADC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 ;  
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN ; 
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStructure);

	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);
	ADC_Cmd(ADC1, ENABLE);
  /* Enable ADC1 reset calibration register */   
  ADC_ResetCalibration(ADC1);
	/* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));

  /* Start ADC1 calibration */
  ADC_StartCalibration(ADC1);
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));

  /* Start ADC1 Software Conversion */ 
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

}

void delayms(uint16_t n)
{
    
    
	timingdelay = n;
		while(timingdelay!=0);
}

滴答定时器的模块只需要在 stm32f10x_it 中写上:

void SysTick_Handler(void)
{
    
    
	timingdelay--;
	if(++MS == 200){
    
    
		MS = 0;
		ADC_Flag = 1;
	}
}

记得外部调用的定义变量

extern uint32_t timingdelay;
uint16_t MS=0;
extern char ADC_Flag;

Reference

  1. STM32 开发指南 V1.3 3 正点原子
  2. STM32F10x固件库中文解释 V2.0

猜你喜欢

转载自blog.csdn.net/weixin_41445387/article/details/106876963