使用STM32标准库测量光敏传感器的具体光照强度

ADC简介


测量方式


采用二分法比较数据

IO通道


ADC基本结构及配置路线


获取数字变量需要用到用到光敏电阻的AO口,AO端口接在PA0引脚即可

测得的模拟数据与实际光照强度之间的关系为

光照强度 = 100 - 模拟量 / 40;

代码:

完整朴素代码:

#include "stm32f10x.h"    // Device header
#include "Delay.h"
#include "OLED.h"

GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;


void AD_Init(void){//初始化AD
	   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//开启ADC1的时钟
	   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIOA的时钟
	   RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADC模块工作时钟 72 / 6 = 12MHZ
	   
	   /*配置GPIO口*/
	   GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	   GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	   GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	   GPIO_Init(GPIOA, &GPIO_InitStruct);
	   
	   /*在规则组列表第一个位置,写入通道0这个通道*/
	   ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	   /*结构体初始化ADC*/
	   ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;//单次转换
	   ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
	   ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发方式,不使用外部触发,即软件触发
	   ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC工作模式为独立模式
	   ADC_InitStruct.ADC_NbrOfChannel = 1;//通道数目
	   ADC_InitStruct.ADC_ScanConvMode = DISABLE;//非扫描
	   ADC_Init(ADC1, &ADC_InitStruct);
		 
		 //开启ADC电源
		 ADC_Cmd(ADC1, ENABLE);
		 
		 /*给ADC校准*/
		 ADC_ResetCalibration(ADC1);//复位校准
		 while(ADC_GetResetCalibrationStatus(ADC1) == SET);//返回ADC1复位校准状态
		 ADC_StartCalibration(ADC1);//开始校准
		 while(ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
}
 
uint16_t AD_Getvailue(void){//获取信息
	   ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发转换
	   while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待转换完成
	   return ADC_GetConversionValue(ADC1);//读取数据
}
 
uint16_t Reality_ADLight(uint16_t ADCnum){//获取光照强度
	  return 100 - ADCnum / 40;
}

 
int main(void){
	 
	OLED_Init();//初始化OLED
	AD_Init();
	while(1){
		    uint16_t num  = AD_Getvailue();
		    uint16_t num1 = Reality_ADLight(num); 
		    
		    OLED_ShowString(1, 1, "ADO:");
        OLED_ShowNum(1, 5, num, 5);
		    OLED_ShowString(2, 1, "LUX:");
		    OLED_ShowNum(2, 5, num1, 3);
		    Delay_ms(300);
		     
	}
}

效果:


此代码的不足之处在于每次写入数字都会提前占据固定位置,这个固定位置在整个过程是不能更改的,十分影响观感

所以添加求数字长度的函数,方便随时捕捉并调正所占空间
添加代码:

uint8_t length(uint16_t num){
	  uint8_t length = 0;
	  while(num > 0){
		   num = num / 10;
		   length = length + 1;
	  }
	  return length;
}

完整优化代码1:

#include "stm32f10x.h"    // Device header
#include "Delay.h"
#include "OLED.h"

GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;


void AD_Init(void){//初始化AD
	   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//开启ADC1的时钟
	   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIOA的时钟
	   RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADC模块工作时钟 72 / 6 = 12MHZ
	   
	   /*配置GPIO口*/
	   GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	   GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	   GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	   GPIO_Init(GPIOA, &GPIO_InitStruct);
	   
	   /*在规则组列表第一个位置,写入通道0这个通道*/
	   ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	   /*结构体初始化ADC*/
	   ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;//单次转换
	   ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
	   ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发方式,不使用外部触发,即软件触发
	   ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC工作模式为独立模式
	   ADC_InitStruct.ADC_NbrOfChannel = 1;//通道数目
	   ADC_InitStruct.ADC_ScanConvMode = DISABLE;//非扫描
	   ADC_Init(ADC1, &ADC_InitStruct);
		 
		 //开启ADC电源
		 ADC_Cmd(ADC1, ENABLE);
		 
		 /*给ADC校准*/
		 ADC_ResetCalibration(ADC1);//复位校准
		 while(ADC_GetResetCalibrationStatus(ADC1) == SET);//返回ADC1复位校准状态
		 ADC_StartCalibration(ADC1);//开始校准
		 while(ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
}
 
uint16_t AD_Getvailue(void){//获取信息
	   ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发转换
	   while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待转换完成
	   return ADC_GetConversionValue(ADC1);//读取数据
}
uint8_t length(uint16_t num){
	  uint8_t length = 0;
	  while(num > 0){
		   num = num / 10;
		   length = length + 1;
	  }
	  return length;
}
uint16_t Reality_ADLight(uint16_t ADCnum){//获取光照强度
	  return 100 - ADCnum / 40;
}

 
int main(void){
	 
	OLED_Init();//初始化OLED
	AD_Init();
	while(1){
		    uint16_t num  = AD_Getvailue();
		    uint16_t num1 = Reality_ADLight(num); 
		    
		    OLED_ShowString(1, 1, "ADO:");
        OLED_ShowNum(1, 5, num, length(num));
		    OLED_ShowString(2, 1, "LUX:");
		    OLED_ShowNum(2, 5, num1, length(num1));
		    Delay_ms(300);
		    OLED_Clear();
	}
}

效果:

写入数据是采用覆盖制,例如上次写入的数据是1234,本次写入的数据是999,那么此时展现的效果为9994,由于ADO取值范围为[0 ~4095],LUX(光照强度)取值范围为[1, 100],所以为了不影响数据的合理性,所以必须要在每次写入新数据时必须要清理一下OLED

但是由于提供的清屏函数每次都是将全部数据清理掉,所以画面刷新也要从新再全部刷新一次所以整体画面会不连贯

所以我写入了一个只清屏某个部分的函数
添加代码:

/* 
   直接用清屏函数整体刷新会导致OLED画面不连贯
   清除行函数:保留本行字符串,清除本行剩余部分
   row:清除的具体行
   len:不希望被清除的字符串长度
*/

void OLED_LoactionClear(uint8_t row, uint8_t len)
{  
	uint8_t i, j;
	for (j = row * 2 - 2; j < row * 2; j++)
	{
		OLED_SetCursor(j, len * 8);
		for(i = len * 8; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

放入位置

需要将其copy到OLED.c文件下,并在OLED.h文件内声明一下


具体函数使用方法:

OLED_LoactionClear(uint8_t row, uint8_t len);
此函数有两个参数:

其中row指你想要进行清屏操作的具体行,OLED上一共能显示4行
其中len代表row行从左到右len长度区间的字符串将会被保留,row行剩余其他数据将全被清除

完整优化代码2:

#include "stm32f10x.h"    // Device header
#include "Delay.h"
#include "OLED.h"

GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;


void AD_Init(void){//初始化AD
	   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//开启ADC1的时钟
	   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIOA的时钟
	   RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADC模块工作时钟 72 / 6 = 12MHZ
	   
	   /*配置GPIO口*/
	   GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	   GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	   GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	   GPIO_Init(GPIOA, &GPIO_InitStruct);
	   
	   /*在规则组列表第一个位置,写入通道0这个通道*/
	   ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	   /*结构体初始化ADC*/
	   ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;//单次转换
	   ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
	   ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发方式,不使用外部触发,即软件触发
	   ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC工作模式为独立模式
	   ADC_InitStruct.ADC_NbrOfChannel = 1;//通道数目
	   ADC_InitStruct.ADC_ScanConvMode = DISABLE;//非扫描
	   ADC_Init(ADC1, &ADC_InitStruct);
		 
		 //开启ADC电源
		 ADC_Cmd(ADC1, ENABLE);
		 
		 /*给ADC校准*/
		 ADC_ResetCalibration(ADC1);//复位校准
		 while(ADC_GetResetCalibrationStatus(ADC1) == SET);//返回ADC1复位校准状态
		 ADC_StartCalibration(ADC1);//开始校准
		 while(ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
}
 
uint16_t AD_Getvailue(void){//获取信息
	   ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发转换
	   while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待转换完成
	   return ADC_GetConversionValue(ADC1);//读取数据
}
uint8_t length(uint16_t num){
	  uint8_t length = 0;
	  while(num > 0){
		   num = num / 10;
		   length = length + 1;
	  }
	  return length;
}
uint16_t Reality_ADLight(uint16_t ADCnum){//获取光照强度
	  return 100 - ADCnum / 40;
}

 
int main(void){
	 
	OLED_Init();//初始化OLED
	AD_Init();
	while(1){
		    uint16_t num  = AD_Getvailue();
		    uint16_t num1 = Reality_ADLight(num); 
		    
		    OLED_ShowString(1, 1, "ADO:");
		    OLED_LoactionClear(1, length(num) + 3);//"ADO:"长度为3所以要加3
        OLED_ShowNum(1, 5, num, length(num));
		    OLED_ShowString(2, 1, "LUX:");
		    OLED_LoactionClear(2, length(num1) + 3);
		    OLED_ShowNum(2, 5, num1, length(num1));
		    Delay_ms(300);
	}
}

效果:

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32标准库测量光敏传感器的具体光照强度

发表评论