当前位置:   article > 正文

STM32第十四课(ADC,HAL)_hal_adc_pollforconversion(&hadc1,10)

hal_adc_pollforconversion(&hadc1,10)

STM32F407ZGT6 包含有 3 个 ADC。 STM32F4 的 ADC 最大的转换速率为 2.4Mhz,也就是
转换时间为 0.41us(在 ADCCLK=36M,采样周期为 3 个 ADC 时钟下得到),不要让 ADC 的时
钟超过 36M,

STM32F4 的 ADC 在单次转换模式下,只执行一次转换,该模式可以通过 ADC_CR2 寄存
器的 ADON 位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入
通道),这时 CONT 位为 0。

一旦所选择的通道转换完成,转换结果将被存在 ADC_DR 寄存器中,EOC(转换结束)标志将被置位,如果设置了 EOCIE,则会产生中断。然后 ADC 将停止,直到下次启动。

ADC_CR1 的 SCAN 位,该位用于设置扫描模式,在扫描模式下,由 ADC_SQRx 或 ADC_JSQRx 寄存器选中的通道被转换。如果设置了 EOCIE 或 JEOCIE,只在最后一个通道转换完毕后才会产生 EOC 或 JEOC 中断。

ADC_CR1[25:24]用于设置 ADC 的分辨率,
00–12位,15个ADCCLK,
01–10位,13个ADCCLK,
10–8位,11个ADCCLK,
11–6位,9个ADCCLK,

ADC_CR2,ADON 位用于开关 AD 转换器。而 CONT 位用于设置是否进行连续转换,我们使用单次转换,所以 CONT 位必须为 0。 ALIGN 用于设置数据对齐,我们使用右对齐,该位设置为 0。
EXTEN[1:0]用于规则通道的外部触发使能设置,
00–disable,
01–posedge trigger,
10–negedge trigger,
11–both edge trigger,

ADC_CR2的 SWSTART 位用于开始规则通道的转换,我们每次转换(单次转换模式下)都需要向该位写1。

ADC 通用控制寄存器(ADC_CCR),TSVREFE 位是内部温度传感器和 Vrefint 通道使能位,
ADCPRE[1:0]用于设置ADC 输入时钟分频, 00~11 分别对应 2/4/6/8 分频,
ADC时钟(ADCCLK)来自 APB2, APB2频率一般是 84Mhz,所以我们一般设置 ADCPRE=01,即 4 分频,这样得到 ADCCLK 频率为 21Mhz。
MULTI[4:0]用于多重 ADC 模式选择,

ADC 采样时间寄存器(ADC_SMPR1 和 ADC_SMPR2),
ADC 的转换时间可以由以下公式计算:
Tcovn=采样时间+12 个周期
例如,
当 ADCCLK=21Mhz 的时候,并设置 3 个周期的采样时间,
则得到: Tcovn=3+12=15 个周期=0.71us。

ADC 规则序列寄存器(ADC_SQR1~3) ,
L[3: 0]用于存储规则序列的长度,我们这里只用了 1 个,所以设置这几个位的值为 0。
SQx则存储了转换序列中第 x次转换时,使用哪个通道的编号(0~18)。

ADC 规则数据寄存器(ADC_DR)。注入通道的转换结果被保存在 ADC_JDRx 里面。

ADC 状态寄存器(ADC_SR), EOC 位,我们通过判断该位来决定是否此次规则通道的 AD
转换已经完成, 如果该位位 1,则表示转换完成了, 就可以从 ADC_DR 中读取转换结果,否则
等待转换完成。

++++++++++++++++++++++++++++++++++++++
库函数分布在 stm32f4xx_adc.c 文件和 stm32f4xx_adc.h 文件中。

开启 PA 口时钟和 ADC1 时钟,设置 PA5 为模拟输入。
STM32F407ZGT6 的 ADC1 通道 5 在 PA5 上,所以,我们先要使能 GPIOA 的时钟,然后设置 PA5 为模拟输入。
同时我们要把 PA5 复用为 ADC,所以我们要使能 ADC1 时钟。
对于 IO 口复用为 ADC 我们要设置模式为模拟输入,而不是复用功能,不需要调用GPIO_PinAFConfig 函数来设置引脚映射关系。

初始化 GPIOA5 为模拟输入,关键代码为:

GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_5; //PA5
GPIO_Initure.Mode=GPIO_MODE_ANALOG; //模拟输入
GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉

HAL_GPIO_Init(GPIOA,&GPIO_Initure);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

初始化 ADC,设置 ADC 时钟分频系数,分辨率,模式,扫描方式,对齐方式等信息。
HAL 库中, 初始化 ADC 是通过函数 HAL_ADC_Init 来实现的,该函数声明为:

HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc);
  • 1

入口参数 hadc,为 ADC_HandleTypeDef 结构体指针类型,这是ADC的句柄。结构体定义为:

typedef struct
{
	ADC_TypeDef *Instance; //ADC1/ ADC2/ ADC3
	ADC_InitTypeDef Init; //初始化结构体变量
	__IO uint32_t NbrOfCurrentConversionRank; //当前转换序列
	DMA_HandleTypeDef *DMA_Handle; //DMA 方式使用
	HAL_LockTypeDef Lock;
	__IO HAL_ADC_StateTypeDef State;
	__IO uint32_t ErrorCode;
}ADC_HandleTypeDef;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

第二个成员变量 Init 含义,它是结构体ADC_InitTypeDef 类型,结构体 ADC_InitTypeDef 定义为:

typedef struct
{
	uint32_t ClockPrescaler;//分频系数 2/4/6/8 分频 ADC_CLOCK_SYNC_PCLK_DIV4
	uint32_t Resolution; //分辨率 12/10/8/6 位: ADC_RESOLUTION_12B
	uint32_t DataAlign; //对齐方式:左对齐还是右对齐: ADC_DATAALIGN_RIGHT
	uint32_t ScanConvMode; //扫描模式 DISABLE
	uint32_t EOCSelection; //EOC 标志是否设置 DISABLE
	uint32_t ContinuousConvMode;//开启连续转换模式或者单次转换模式 DISABLE
	uint32_t DMAContinuousRequests;//开启 DMA 请求连续模式或者单独模式 DISABLE
	uint32_t NbrOfConversion; //规则序列中有多少个转换 1
	uint32_t DiscontinuousConvMode;//不连续采样模式 DISABLE
	uint32_t NbrOfDiscConversion;//不连续采样通道数 0
	uint32_t ExternalTrigConv; //外部触发方式 ADC_SOFTWARE_START
	uint32_t ExternalTrigConvEdge;//外部触发边沿
}ADC_InitTypeDef;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

HAL 库同样提供了 ADC 的 MSP 初始化函数,一般情况下,时钟使能和 GPIO 初始化都会放在 MSP 初始化函数中。函数声明为:

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc);
  • 1

+++++++++++++++++++++++++++++++++++++++++++++++
开启 AD 转换器。
在设置完了以上信息后,我们就开启 AD 转换器了(通过 ADC_CR2 寄存器控制)。

HAL_ADC_Start(&ADC1_Handler); //开启 ADC
  • 1

配置通道, 读取通道 ADC 值。
接下来我们要做的就是设置规则序列 1 里面的通道,然后启动 ADC 转换。在转换结束后,读取转换结果值值就是了。设置规则序列通道以及采样周期的函数是

HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef* hadc,
												ADC_ChannelConfTypeDef* sConfig);
  • 1
  • 2

第二个入口参数 sConfig,它是 ADC_ChannelConfTypeDef 结构体指针类型,结构体定义如下:

typedef struct
{
	uint32_t Channel; //ADC 通道
	uint32_t Rank; //规则通道中的第几个转换
	uint32_t SamplingTime; //采样时间
	uint32_t Offset; //备用,暂未用到
}ADC_ChannelConfTypeDef;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Channel 用来设置 ADC 通道,
Rank 用来设置要配置的通道是规则序列中的第几个转换,
SamplingTime 用来设置采样时间。

使用实例为:

ADC1_ChanConf.Channel= ADC_CHANNEL_5; //通道 5
ADC1_ChanConf.Rank=1; //第 1 个序列,序列 1
ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_480CYCLES; //采样时间
ADC1_ChanConf.Offset=0;
HAL_ADC_ConfigChannel(&ADC1_Handler,&ADC1_ChanConf); //通道配置
  • 1
  • 2
  • 3
  • 4
  • 5

配置好通道并且使能 ADC 后,接下来就是读取 ADC 值。这里我们采取的是查询方式读取,
所 以 我 们 还 要 等 待 上 一 次 转 换 结 束 。
HAL 库 提 供 了 专 用 函 数HAL_ADC_PollForConversion,函数定义为:

HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc,
															uint32_t Timeout);
  • 1
  • 2

等待上一次转换结束之后,接下来就是读取 ADC 值,函数为:

uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
  • 1

++++++++++++++++++++++++++++++++++++++++++++++
实例,
cubemx中配置PA5为ADC1_IN5。
然后配置ADC1的相关参数。

添加功能函数。

uint16_t Get_Adc(uint32_t ch)
{
    HAL_ADC_Start(&hadc1); //开启 ADC
    HAL_ADC_PollForConversion(&hadc1,10); //轮询转换
    return (uint16_t)HAL_ADC_GetValue(&hadc1);//返回最近一次 ADC1 规则组的转换结果
}

uint16_t Get_Adc_Average(uint32_t ch,uint8_t times)
{
    uint32_t temp_val=0;
    uint8_t t;
    
    for(t=0;t<times;t++)
    {
        temp_val+=Get_Adc(ch);
        HAL_Delay(5);

    }
    
    return temp_val/times;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在main中添加任务代码。

		adcx = Get_Adc_Average(ADC_CHANNEL_5,20);//获取通道 5 的转换值, 20 次取平均
        LCD_ShowxNum(134,130,adcx,4,32,0); //显示 ADCC 采样后的原始值
        
        temp = (float)adcx*(3.3/4096); //获取计算后的带小数的实际电压值,比如 3.1111
        adcx=temp; //赋值整数部分给 adcx 变量,因为 adcx 为 u16 整型       
        LCD_ShowxNum(134,170,adcx,1,32,0); //显示电压值的整数部分
        
        temp-=adcx; //把已经显示的整数部分去掉,留下小数部分,比如 3.1111-3=0.1111
        temp*=1000;//小数部分乘以 1000,例如: 0.1111 就转换为 111.1,保留三位小数。
        LCD_ShowxNum(150,210,temp,3,32,0X80); //显示小数部分
        
        
        pin_value = HAL_GPIO_ReadPin(led1_GPIO_Port, led1_Pin);
        HAL_GPIO_WritePin(led1_GPIO_Port ,led1_Pin ,!pin_value);
      
        HAL_Delay(250);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

++++++++++++++++++++++++++++++++++++++++++++++
STM32F4 有一个内部的温度传感器,在内部和 ADC1_IN16(STM32F40xx/F41xx 系列) 输入通道相连接,此通道把传感器输出的电压转换成数字值。 温度范围为: -40~125 度。精度为±1.5℃左右。
只要设置一下内部 ADC,并激活其内部温度传感器通道就差不多了。通过 ADC_CCR 的 TSVREFE 位(bit23)设置。设置该位为 1 则启用内部温度传感器。内部温度传感器固定的连接在 ADC1 的通道 16 上,设置好 ADC1 之后只要读取通道 16 的值,就是温度传感器返回来的电压值了。

在 HAL 库中开启内部温度传感器,只需要将 ADC 通道改为 ADC_CHANNEL_TEMPSENSOR 即可,

+++++++++++++++++++++++++++++++++++++++++++++
实战,
在cubemx中,配置ADC1,勾选temp sensor。
配置ADC1,EOC flag at single channel conversion,
配置rank,
rank1为之前的ADC1_IN5,
rank2为temp sensor。

添加功能函数。

void Get_Adc1(uint16_t buffer[2])
{
    HAL_ADC_Start(&hadc1); //开启 ADC
    HAL_ADC_PollForConversion(&hadc1,10); //轮询转换
    buffer[0] = (uint16_t)HAL_ADC_GetValue(&hadc1);//返回最近一次 ADC1 规则组的转换结果
    HAL_ADC_Start(&hadc1); //开启 ADC
    HAL_ADC_PollForConversion(&hadc1,10); //轮询转换
    buffer[1] = (uint16_t)HAL_ADC_GetValue(&hadc1);//返回最近一次 ADC1 规则组的转换结果
    
}

void Get_Adc1_Average(uint16_t buffer_in[2],uint16_t buffer_out[2], uint8_t times)
{
    uint32_t temp_val[2]={0,0};
    uint8_t t;
    
    for(t=0;t<times;t++)
    {
        Get_Adc1(buffer_in);
        
        temp_val[0] += buffer_in[0];
        temp_val[1] += buffer_in[1];
        HAL_Delay(5);
    }
    
    buffer_out[0] = temp_val[0] / times;
    buffer_out[1] = temp_val[1] / times;
}

short Get_Temprate(uint16_t temp_origin)
{
    uint32_t adcx;
    short result;
    double temperate;
    adcx=temp_origin;
    //读取内部温度传感器通道,10 次取平均
    temperate=(float)adcx*(3.3/4096); //电压值
    temperate=(temperate-0.76)/0.0025 + 25; //转换为温度值
    temperate*=100; //扩大 100 倍.
    result=temperate;
    return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

这里打开了2个ADC1的通道,所以cubemx使能了scanmode。
如果是使用POLL方式,
每次读取ADC,要重复start-poll-read,从而获取各个通道的最近一次转换值。

在main中,添加任务代码。

		Get_Adc_Average(adc_buffer,adc_avg_buffer, 20);//获取通道 5 的转换值, 20 次取平均
		
        adcx = adc_avg_buffer[0];
        LCD_ShowxNum(134,130,adcx,4,32,0); //显示 ADCC 采样后的原始值
        adcx = adc_avg_buffer[1];
        LCD_ShowxNum(300,130,adcx,4,32,0); //显示 ADCC 采样后的原始值
      
        adcx = adc_avg_buffer[0];
        temp = ((float)adcx)*(3.3/4096); //获取计算后的带小数的实际电压值,比如 3.1111
        adcx=temp; //赋值整数部分给 adcx 变量,因为 adcx 为 u16 整型       
        LCD_ShowxNum(134,170,adcx,1,32,0); //显示电压值的整数部分
        
        
        temp-=adcx; //把已经显示的整数部分去掉,留下小数部分,比如 3.1111-3=0.1111
        temp*=1000;//小数部分乘以 1000,例如: 0.1111 就转换为 111.1,保留三位小数。
        LCD_ShowxNum(166,170,temp,3,32,0X80); //显示小数部分
        
        adcx = adc_avg_buffer[1];
        temp_sensor=Get_Temprate(adcx); //得到温度值
        if(temp_sensor < 0){
            LCD_ShowString(134,210,32,32,32,1,(uint8_t*)"-"); //显示负号
        
        }
        else{
            LCD_ShowString(134,210,32,32,32,1,(uint8_t*)"+"); //显示号
        }
        LCD_ShowxNum(166,210,temp_sensor/100,2,32,0); //显示电压值的整数部分
        LCD_ShowxNum(230,210,temp_sensor%100,2,32,0); //显示小数部分
      
        pin_value = HAL_GPIO_ReadPin(led1_GPIO_Port, led1_Pin);
        HAL_GPIO_WritePin(led1_GPIO_Port ,led1_Pin ,!pin_value);
      
        HAL_Delay(250);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

++++++++++++++++++++++++++++++++++++++++++++++++
STM32F4 开发板板载了一个光敏二极管(光敏电阻),作为光敏传感器, 无光照时,有很小的饱和反向漏电流,即暗电流,此时光敏二极管截止。当受到光照时,饱和反向漏电流大大增加,形成光电流,它随入射光强度的变化而变化。
利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过 ADC 读取电压值,判断外部光线的强弱。
利用 ADC3 的通道 5(PF7)来读取光敏二极管电压的变化,

+++++++++++++++++++++++++++++++++++++++++++++++++
实战,
在cubemx中,配置ADC3,
配置ADC3,EOC flag at single channel conversion,
配置rank,
rank1为ADC3_IN5,

添加功能函数。

void Get_Adc3(uint16_t buffer[2])
{
    HAL_ADC_Start(&hadc3); //开启 ADC
    HAL_ADC_PollForConversion(&hadc3,10); //轮询转换
    buffer[0] = (uint16_t)HAL_ADC_GetValue(&hadc3);//返回最近一次 ADC3 规则组的转换结果
    
//    HAL_ADC_Start(&hadc3); //开启 ADC
//    HAL_ADC_PollForConversion(&hadc3,10); //轮询转换
//    buffer[1] = (uint16_t)HAL_ADC_GetValue(&hadc3);//返回最近一次 ADC3 规则组的转换结果
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在main中添加任务代码。

		adcx = adc3_avg_buffer[0];
        if(adcx > 4000)
            adcx = 4000;
        adcx = (100 - (adcx/40));
        LCD_ShowxNum(134,242,adcx,2,32,0); //
  • 1
  • 2
  • 3
  • 4
  • 5
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号