Stm32_标准库_8_ADC_光敏传感器_测量具体光照强度

ADC简介


测量方式


采用二分法比较数据

IO通道


ADC基本结构及配置路线


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

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

复制代码
光照强度 = 100 - 模拟量 / 40;

代码:

完整朴素代码:

c 复制代码
#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);
		     
	}
}

效果:


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

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

添加代码:

c 复制代码
uint8_t length(uint16_t num){
	  uint8_t length = 0;
	  while(num > 0){
		   num = num / 10;
		   length = length + 1;
	  }
	  return length;
}

完整优化代码1:

c 复制代码
#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

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

所以我写入了一个只清屏某个部分的函数

添加代码:

c 复制代码
/* 
   直接用清屏函数整体刷新会导致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:

c 复制代码
#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);
	}
}

效果:

相关推荐
DuHz20 分钟前
C程序中的数组与指针共生关系
linux·c语言·开发语言·嵌入式硬件·算法
来生硬件工程师27 分钟前
【STM32笔记】:P04 断言的使用
c语言·笔记·stm32·单片机·嵌入式硬件·硬件架构·硬件设计
GilgameshJSS1 小时前
STM32H743-ARM例程22-USB_CDC
arm开发·stm32·嵌入式硬件
江苏学蠡信息科技有限公司1 小时前
STM32中硬件I2C的时钟占空比
stm32·单片机·fpga开发
光子物联单片机1 小时前
STM32G474单片机开发入门(十七)DAC详解及输出电压和正弦波实战
stm32·单片机·嵌入式硬件·mcu
DIY机器人工房1 小时前
【嵌入式面试题】STM32F103C8T6 完整元器件解析 + 面试问题答案
stm32·单片机·面试·嵌入式·面试题·diy机器人工房
日更嵌入式的打工仔2 小时前
嵌入式入门:APP+BSP+HAL 三层分级架构浅析
笔记·单片机·嵌入式硬件·学习
优信电子4 小时前
基于STM32F103驱动AS5600读取磁体旋转的模拟量/角度数据
stm32·单片机·嵌入式硬件·as5600·磁编码器·磁角度传感器
homelook4 小时前
【mqtt参数上云和小程序开发】【第5期】hcsr04超声波测距模块调试完成
嵌入式硬件
点灯小铭4 小时前
基于单片机的PWM三基色LED灯控制器设计与无线调色系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业