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);
}
}
效果: