【STM32】ADC采集光敏数据(不看库函数手册进行配置)

51 篇文章 4 订阅
15 篇文章 1 订阅

前言

1、STM32库函数源代码说明

2、GPIO配置

3、ADC初始化

4、ADC 复位校准

5、采集数据

6、实验现象

7、完整代码


前言


在写一个工程的某一个功能的时候,我们都会对照库函数手册进行对该模块功能的配置,但是,有的时候,中文版的库函数手册在翻译过来的时候会有翻译上的出入,所以为了工程的可行性、进度性,我们可以不通过库函数手册,用另一种方法进行配置,请耐心往下看,完整的代码会在最后写出!

1、STM32库函数源代码说明

1.1首先我们先新建两个.c和.h新的文件,

 我们用的是ADC1通道3,(这里不进行解释),所以我们要把PA3的引脚配置成模拟模式,因为连接ADC的所有通过I/O引脚,都必须为模拟输入模式。

那在模拟输入之前,我们要开GPIO A组的时钟,那我们开时钟的时候,怎么不通过手册去开时钟呢?

我们的规则是:先查看.h,(查看该功能的.h文件),比如RCC,我要使能时钟,那么就要先找stm32f10x_rcc.h()

就在该模块的“+”号这里打开,找到rcc.h,

 那打开rcc.h,之后我们怎么看文件呢?注意,因为rcc比较特殊,因为rcc里面没有结构体,使能rcc没有结构体,打开.h后,直接倒着看,拉到文件最下面的文字,最下面存放的是rcc相关函数的声明。

找到这个,因为我们用的是APB2,我们就直接复制过去到工程, 

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

2、然后右键单击RCC_APB2PeriphClockCmd,跳转过去,它里面会有一个函数调用说明。

 点击后,进入到rcc.c文件看到其函数说明。

解释:brief:表示的是说明,摘要的意思,在这里相当于函数说明;

           param:参数的意思,说的是,你在这里用的函数的是哪个形参;

 

 那我们用的是GPIO A口的时钟,所以我们按照上面的图片的里面的 RCC_APB2Periph_GPIOA,我们直接复制下来,替换掉即可,

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

 这样就完成一个函数的编写,使能GPIO A,下一步就到GPIO结构体,

2、GPIO配置

(RCC没有结构体,记住,因为他不需要使用结构体初始化,所以我们可以直接调用结构体初始化),但是GPIO是有结构体的,需要初始化结构体,查看的方法不一样,当然见面的步骤是一样的,先是找到gpio.h文件,这时候,注意,不需要像rcc一样拉到最下面,gpio的结构体是在上面的(因为rcc特殊)

往下滑,可以看到很多参数的宏定义:

 

 再往下就可以看到结构体(截图到的是GPIO的结构体)

 备注:一旦设计到结构体的,就按照这个方法来,从上往下看,不涉及到结构体的,就拉到最下面就可以了。

接下来,我们找到结构体类型,复制到工程

这样我们就可以直接进行配置

 注意:找到这些结构体之后,这些填充的参数也是要在你找到结构体的上面或下面就可以找到,比如,第一个要我们填充的就是GPIO的模式,是要模拟输入的,

 剩下的两个不再说明,目前完整的如下

void GZ_Init(void)//用的是PA3引脚,ADC1通道3
{
 GPIO_InitTypeDef gz;
	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  gz.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	gz.GPIO_Pin = GPIO_Pin_3;
	gz.GPIO_Speed = GPIO_Speed_50MHz;
 
}

这时候,当你结束初始化结构体,还要对函数进行初始化,方法是拉到函数的最下面进行查找,

 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

我们的操作是右键单击跳到 GPIO_Init的gpio.c文件,

圈的解释是: where x can be (A..G)to select the GPIO peripheral. 其中x可以是(A.g)来选择GPIO外围设备。然后取地址就可以完成这一步的操作。

 GPIO_Init( GPIOA, &gz);//根据初始化的参数进行配置

到这里我们的引脚已经完成了模拟输入。

这里漏了一步,我们ADC设置的时候要设置预分频值,在rcc.h文件最底下可以找到,

 可以分6/8分频都可以,我们通右键点击跳转过去就可以知道该填充的值;

RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72/6=12Mhz,不能超过14MHZ

到此,我们完成RCC的配置之后,我们是要用ADC功能的,这时候就要初始化ADC功能。

3、ADC初始化

前面的步骤和上面GPIO一样,加相关的功能的.h文件,adc.h

ADC结构体如下:

 对结构体初始化前,我们要把结构体成员复制过来 ADC_InitTypeDef

定义好之后,就可以得出其结构体的成员,如上。

得出ADC结构体如下:

adc1_gz.ADC_ContinuousConvMode = 是否连续模式,我们启动为连续模式
    adc1_gz.ADC_DataAlign = 数据对齐方式,左还是右对齐,我们选择右对齐
    adc1_gz.ADC_ExternalTrigConv = 是否为外部触发,我们选择软件触发模式
    adc1_gz.ADC_Mode = 独立模式还是双模式,我们选择双模式
    adc1_gz.ADC_NbrOfChannel = 你需要装换的通道数量
    adc1_gz.ADC_ScanConvMode = 选择单通道还是多通道,因为本次是光敏电阻功能,就需要单通道就好了。

接下来需要我们对结构体成员参数进行填充,在这之前,我们不知道要填充什么样的参数值,我们可以对照右边的英文进行翻译即可得出填充的信息。

 完整的ADC结构体成员配置参数如下:

	adc1_gz.ADC_ContinuousConvMode = DISABLE;//单次转换
	adc1_gz.ADC_DataAlign =  ADC_DataAlign_Right;//数据右对齐
	adc1_gz.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//选软件启动
	adc1_gz.ADC_Mode = ADC_Mode_Independent;//独立模式
	adc1_gz.ADC_NbrOfChannel = 1;//设置转换的通道数量
	adc1_gz.ADC_ScanConvMode = DISABLE;//配置为单通道模式

 完成以后在adc.h文件拉到最下面,找到初始化函数,对其进行初始化即可,然后再开使能时钟就可以了,因为时钟操作能GPIO一样,我们就或“|”,GPIO时钟。

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);

 初始化完之后,ADC就需要进行先校准才可以正常采集数据(这个工程在这里不解释,可以看往期文章或者百度)

4、ADC 复位校准

 注意:1、建议在每次上电后执行一次校准;

            2、启动校准前,ADC必须处于关机状态超过至少两个ADC时钟周期。

好的,因为这里他说了,要通过设置ADC_CR2寄存器的CAL位进行启动。所以我们就要看CAL寄存器,

CAL在位2,

 由此可以知道,写1才开始校准,0结束。

 接下来,对校准函数初始化,一样的步骤,回到adc.h文件,找到一下四个校准函数;这四个函数都要用到,

void ADC_ResetCalibration(ADC_TypeDef* ADCx);

解释:重置指定的ADC的校准寄存器


FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);

解释:获取ADC重置校准寄存器状态


void ADC_StartCalibration(ADC_TypeDef* ADCx);

解释:开始指定ADC的校准程序


FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);

解释:获取指定的ADC的校准状态

写到这里,你会觉得ADC很麻烦。确实麻烦,但是只要你配置完一个ADC,后面的ADC只需要改变通道的引脚就可以了,所以只能是是,前期麻烦了点,后期就简单多了。

好的,接下来,我们就对校准器进行编写改造,为了节省时间,小编已对其校准器部分写好并解释如下:

开始的时候

FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);

这是一个flag的状态,那就是要有返回值,一般是成功set或者失败reset,这个操作只需要右键点击ADC_GetResetCalibrationStatus跳转过去就可以查看得出来,这里就不多加啰嗦。

那么复位就是等待它为1,那就用while语句,如果是1,那就取反就可以了,因为如果是1,复位成功返回的是set,set=成功=1;1取反,就是跳出,就是不需要再等了。(这样说能理解吧!)

整理如下:

  ADC_ResetCalibration(ADC1);//复位校准器
  while ( ADC_GetResetCalibrationStatus(ADC1));//复位校准器状态
	
  ADC_StartCalibration(ADC1);//开始校准
  while(ADC_GetCalibrationStatus(ADC1));//等待校准

初始化完成,接下来就需要进行采集数据,并将采集到的数据进行转换。

5、采集数据

这里我们结合上述的内容,我们可以知道,我们采集的是PA3连接ADC通道3通过的转换数据。

上述,我们已经不预分频处理好了,接下来,我们就要对其注入组和规则组进行设置(注入和规则相当于抢占和响应优先级)。转换结束之后,EOC位会被置1,因为我们选择的是单次转换模式

简单而言,如果我们选择的是规则通道,那EOC置1,选择的是规则通道,那么JEOC置1.

如果是单次转换模式,如果你采用了规则通道,数据转换完后,EOC位会被置1;单次转换模式下,采用的是注入模式,数据转换完成后,JEOC置1.

 所以通道不一样,等待的标志位不一样,开始的时候,我们不确定使用的是注入还是规则通道,那应该用什么方法去确定呢?这个怎么说呢,意思是注入还是规则,由你自己决定。那怎么决定呢,实际上,它是将规则通道和注入通道分别封装成了一个函数,你要用哪个,调用哪个通道去采集就好了。

 因为我们这个光敏的工程,只需要一个通道,设置成什么通道由你决定。(我们这次用规则通道)

一样的步骤,stm32f10x_adc.h,去找到规则通道

 然后我们新建一个光照通道采集的函数如下:

然后步骤一样,单击右键跳转进入就可以看到参数配置,

 

 四个参数分别是:设置为ADC几号?  ADC1;

 uint8_t ADC_Channel   设置为通道几号?  通道13;

uint8_t Rank  设置的是转换的通道数量顺序1-16,就是第几个开始转换,转换的顺序;

uint8_t ADC_SampleTime  设置的是转换的时间;(这些参数值的配置都可以早stm32f10x_adc.c找到)

//单次转换,需要我们调用函数
void get_gz_val(void)
{
   ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_41Cycles5);//规则通道配置函数
}

接着,我们之前初始化的时候是选择软件启动,所以就要调用使能指定的ADC软件转换启动功能的函数,那就是我们回到_adc.c去找这个函数,这个就不复制了,自己看到这里,就会找了。

好的,接下来,我们选择的规则组转换完之后会置1,因为我们不可能直接返回数据,要等转换完以后,(等状态位 置1,说明数据才转换完成,这样才能返回数据,当然转换也是需要时间的)

在获取前,要检查ADC标志位是否置1;那就要去找标志位函数,就是获取完成的标志位,才能够返回想要的数据。

while(!(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==SET));

读取数据:

返回最近一次ADC规则组的转换数据。

这里注意,因为它是无符号的返回值,所以我们要改成u16,不然程序会报错,把void get_gz_val

改成 u16 get_gz_val即可。这样就返回了。完整如下:

//单次转换,需要我们调用函数
u16 get_gz_val(void)
{
   ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_41Cycles5);//规则通道配置函数
	 ADC_SoftwareStartConvCmd(ADC1,ENABLE);//启动软件
	
	while(!(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==SET));//等待EOC位置1
	return ADC_GetConversionValue(ADC1);//返回ADC1通道采集到的数据
}

PS:记得函数声明哦,别写着写着忘记声明了。接下来在main函数里面进行初始化调用就可以进行测试了。最后,计算光照亮度还有一个公式:

光照=100-(获取到的数据()/4096*100);

6、实验现象

7、完整代码

gz.c

#include "gz.h"


void GZ_Init(void)//用的是PA3引脚,ADC1通道3
{
  GPIO_InitTypeDef gz;
	ADC_InitTypeDef adc1_gz;
	
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72/6=12Mhz,不能超过14MHZ

  gz.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	gz.GPIO_Pin = GPIO_Pin_3;
	gz.GPIO_Speed = GPIO_Speed_50MHz;
	  GPIO_Init( GPIOA, &gz);
	
	adc1_gz.ADC_ContinuousConvMode = DISABLE;//单次转换
	adc1_gz.ADC_DataAlign =  ADC_DataAlign_Right;//数据右对齐
	adc1_gz.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//选软件启动
	adc1_gz.ADC_Mode = ADC_Mode_Independent;//独立模式
	adc1_gz.ADC_NbrOfChannel = 1;//设置转换的通道数量
	adc1_gz.ADC_ScanConvMode = DISABLE;//配置为单通道模式
	
	ADC_Init(ADC1, &adc1_gz);//初始化
  ADC_Cmd(ADC1, ENABLE); //使能时钟
	

	ADC_ResetCalibration(ADC1);//复位校准器
  while ( ADC_GetResetCalibrationStatus(ADC1));//复位校准器状态
	
  ADC_StartCalibration(ADC1);//开始校准
  while(ADC_GetCalibrationStatus(ADC1));//等待校准
}



//单次转换,需要我们调用函数
u16 get_gz_val(void)
{
   ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_41Cycles5);//规则通道配置函数
	 ADC_SoftwareStartConvCmd(ADC1,ENABLE);//启动软件
	
	while(!(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==SET));//等待EOC位置1
	return ADC_GetConversionValue(ADC1);//返回ADC1通道采集到的数据
}

gz.h

#ifndef _GZ_H_
#define _GZ_H_

#include "stm32f10x.h"
#include "io_bit.h"

void GZ_Init(void);
u16 get_gz_val(void);
#endif

mian.c(关键部分,因为还有其他功能,太多太乱,不完全复制了)

	

while(1)
	{
		  Delay_ms(1000);
	    Gz = 0;
		  liangdu = 100-(Get_Gz_Data(3)/4096.0*100);
		  printf("光照强度:%02d%%\r\n",liangdu);
}

总结:再不看手册的时候,可以按照这样的方法来,当然习惯了手册的话,这个就是你的辅助方法了,希望我的方法能对初学的入门的朋友有所帮助,哪里写的不好的话,大家可以指正!谢谢,最后,大家可以稍微关注我一下,谢谢!

  • 22
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丘比特惩罚陆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值