赞
踩
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);
初始化 ADC,设置 ADC 时钟分频系数,分辨率,模式,扫描方式,对齐方式等信息。
HAL 库中, 初始化 ADC 是通过函数 HAL_ADC_Init 来实现的,该函数声明为:
HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc);
入口参数 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;
第二个成员变量 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;
HAL 库同样提供了 ADC 的 MSP 初始化函数,一般情况下,时钟使能和 GPIO 初始化都会放在 MSP 初始化函数中。函数声明为:
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc);
+++++++++++++++++++++++++++++++++++++++++++++++
开启 AD 转换器。
在设置完了以上信息后,我们就开启 AD 转换器了(通过 ADC_CR2 寄存器控制)。
HAL_ADC_Start(&ADC1_Handler); //开启 ADC
配置通道, 读取通道 ADC 值。
接下来我们要做的就是设置规则序列 1 里面的通道,然后启动 ADC 转换。在转换结束后,读取转换结果值值就是了。设置规则序列通道以及采样周期的函数是
HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef* hadc,
ADC_ChannelConfTypeDef* sConfig);
第二个入口参数 sConfig,它是 ADC_ChannelConfTypeDef 结构体指针类型,结构体定义如下:
typedef struct
{
uint32_t Channel; //ADC 通道
uint32_t Rank; //规则通道中的第几个转换
uint32_t SamplingTime; //采样时间
uint32_t Offset; //备用,暂未用到
}ADC_ChannelConfTypeDef;
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); //通道配置
配置好通道并且使能 ADC 后,接下来就是读取 ADC 值。这里我们采取的是查询方式读取,
所 以 我 们 还 要 等 待 上 一 次 转 换 结 束 。
HAL 库 提 供 了 专 用 函 数HAL_ADC_PollForConversion,函数定义为:
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc,
uint32_t Timeout);
等待上一次转换结束之后,接下来就是读取 ADC 值,函数为:
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
++++++++++++++++++++++++++++++++++++++++++++++
实例,
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; }
在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);
++++++++++++++++++++++++++++++++++++++++++++++
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; }
这里打开了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);
++++++++++++++++++++++++++++++++++++++++++++++++
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 规则组的转换结果
}
在main中添加任务代码。
adcx = adc3_avg_buffer[0];
if(adcx > 4000)
adcx = 4000;
adcx = (100 - (adcx/40));
LCD_ShowxNum(134,242,adcx,2,32,0); //
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。