学习板:STM32F103ZET6
参考:
STM32F103(十八)ADC总结(5W字)
ADC相关的几个实验—内部温度传感器、内部参照电压、光敏电阻
- 一、前言
- 二、内部温度传感器
- 1、原理
- 2、代码实现:
- 3、完整代码
- 1、adc.h代码
- 2、adc.c代码
- 3、main.c
- 4、测试
- 三、内部参考电压
- 1、代码实现
- 2、完整代码:
- 1、adc.h代码
- 2、adc.c代码
- 3、main.c
- 3、测试
- 四、光敏传感器
- 1、原理
- 2、代码实现
- 3、完整代码
- 1、adc.h代码
- 2、adc.c代码
- 3、main.c代码
- 4、测试
一、前言
上一篇博客总结的ADC的相关寄存器、相关库函数以及如何编程,本博把ADC相关的几个功能实验总结一下,作为上一篇博客ADC的复习。
内部温度传感器在ADC1的16通道、内部参考电压在ADC1的17通道,这俩个功能是在芯片内部的,并没有引脚引出,所以不需要配置引脚。
至于光敏电阻,这个是我板子外设自己添加的,因引脚设计而异,之后再详述。
二、内部温度传感器
1、原理
芯片内部温度传感器的温度与电压关系接近为线性,按照线性方程来解,ST官方给了几个重要参数:25摄氏度时传感器电压输出:1.43V、线性方程的斜率为4.3mV/℃=0.0043V/摄氏度。
则有:
所以只需要得到VT即可根据这个公式得到内部温度传感器的温度显示。
2、代码实现:
ADC的相关内容上一博客都总结过了,再一步步看看代码,含义不在赘述。
(1)使能ADC1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
(2)设置ADC1时钟分频
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
(3)重置ADC1
ADC_DeInit(ADC1);
(4)ADC1初始化
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_InitStruct.ADC_NbrOfChannel=1;
ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
ADC_Init(ADC1, &ADC_InitStruct);
(5)内部温度传感器使能
ADC_TempSensorVrefintCmd(ENABLE);
(6)ADC1使能
ADC_Cmd(ADC1,ENABLE);
(7)ADC1复位校准并等待完成
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)) //等待复位校准结束
{
}
(8)ADC1校准并等待校准完成
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1))//等待校准结束
{
}
(9)写一个获取ADC转换结果的函数
传递参数为:通道数、序列的序号、采用周期
u16 adc_getval(u8 ch,u8 num,u8 ADC_SampleTime) //传递通道、序列序号、采样周期
{
ADC_RegularChannelConfig(ADC1, ch, num, ADC_SampleTime);//规则通道设置
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发、并开启转换
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))
{
//转换过程中执行空语句,等待转换完成
}
return ADC_GetConversionValue(ADC1);
}
3、完整代码
1、adc.h代码
//adc.h
#ifndef _ADC_
#define _ADC_
#include "stm32f10x.h"
void adc_Init(void);
u16 adc_getval(u8 ch,u8 num,u8 ADC_SampleTime);
#endif
2、adc.c代码
#include "adc.h"
#include "stm32f10x.h"
#include "usart.h"
void adc_Init(void)
{
ADC_InitTypeDef ADC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_DeInit(ADC1);
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_InitStruct.ADC_NbrOfChannel=1;
ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
ADC_Init(ADC1, &ADC_InitStruct);
ADC_TempSensorVrefintCmd(ENABLE);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)) //等待复位校准结束
{
}
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1))//等待校准结束
{
}
}
u16 adc_getval(u8 ch,u8 num,u8 ADC_SampleTime) //传递通道、序列序号、采样周期
{
ADC_RegularChannelConfig(ADC1, ch, num, ADC_SampleTime);//规则通道设置
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发、并开启转换
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))
{
//转换过程中执行空语句,等待转换完成
}
return ADC_GetConversionValue(ADC1);
}
3、main.c
#include "stm32f10x.h"
#include "delay.h"
#include "adc.h"
#include "usart.h"
u16 a;
float b;
int main(void)
{
delay_init();
adc_Init();
uart_init(115200);
while(1)
{
a=adc_getval(ADC_Channel_16,1,ADC_SampleTime_28Cycles5);\
b=(a*(3.3/4096)-1.43)/0.0043+25;
printf("ADC= %d tsensor= %1.3f ℃ \r\n",a,b);
delay_ms(500);
}
}
4、测试
下载后通过串口监视器察看:
三、内部参考电压
注意一个东西:ADC的参考电压由Vref+、Vref-引脚提供,而内部参考电压与外部电路无关。
1、代码实现
代码与内部温度传感器的代码类似,只有以下不同之处:
2、完整代码:
1、adc.h代码
//adc.h
#ifndef _ADC_
#define _ADC_
#include "stm32f10x.h"
void adc_Init(void);
u16 adc_getval(u8 ch,u8 num,u8 ADC_SampleTime);
#endif
2、adc.c代码
#include "adc.h"
#include "stm32f10x.h"
#include "usart.h"
void adc_Init(void)
{
ADC_InitTypeDef ADC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_DeInit(ADC1);
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_InitStruct.ADC_NbrOfChannel=1;
ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
ADC_Init(ADC1, &ADC_InitStruct);
ADC_TempSensorVrefintCmd(ENABLE);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)) //等待复位校准结束
{
}
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1))//等待校准结束
{
}
}
u16 adc_getval(u8 ch,u8 num,u8 ADC_SampleTime) //传递通道、序列序号、采样周期
{
ADC_RegularChannelConfig(ADC1, ch, num, ADC_SampleTime);//规则通道设置
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发、并开启转换
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))
{
//转换过程中执行空语句,等待转换完成
}
return ADC_GetConversionValue(ADC1);
}
3、main.c
#include "stm32f10x.h"
#include "delay.h"
#include "adc.h"
#include "usart.h"
u16 a;
float b;
int main(void)
{
delay_init();
adc_Init();
uart_init(115200);
while(1)
{
a=adc_getval(ADC_Channel_17,1,ADC_SampleTime_28Cycles5);
b=a*(3.3/4096);
printf("ADC= %d Vrefint= %1.3f V \r\n",a,b);
delay_ms(500);
}
}
3、测试
下载后通过串口监视器察看:
四、光敏传感器
有的板子上面有光敏传感器,这里做一个总结。
1、原理
先分析一下原理图:
首先,我的实验板上光敏传感器接的ADC3通道6
其次分析一下光敏二极管的工作原理:
没有光照射时,二极管对地反接,二极管两端“电阻”无穷大,ADC3通道6的引脚测的是二极管两端的电压,此时为3.3V,当有光照射时,光敏二极管慢慢开始导通,且根据光照强调不同,其导通能力不同,相当于随着光照的增强,二极管的“电阻”在减小,R34会慢慢增大分压,导致二极管两端电压减小。
所以随着光照的增强,ADC3通道6转换的电压值在减小。
2、代码实现
(1)使能ADC3和PF8时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3|RCC_APB2Periph_GPIOF, ENABLE);
(2)设置ADC3时钟分频
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
(3)重置ADC3
ADC_DeInit(ADC3);
(4)ADC3初始化
由于只用到一个通道,故设置规则序列为1
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_InitStruct.ADC_NbrOfChannel=1;
ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
ADC_Init(ADC3, &ADC_InitStruct);
(5)使能ADC3
ADC_Cmd(ADC3,ENABLE);
(6)ADC3复位校准并等待完成
ADC_ResetCalibration(ADC3);
while(ADC_GetResetCalibrationStatus(ADC3)) //等待复位校准结束
{
}
(7)ADC3校准并等待完成
ADC_StartCalibration(ADC3);
while(ADC_GetCalibrationStatus(ADC3))//等待校准结束
{
}
(8)编写获取ADC3转换值函数
u16 adc_getval(u8 ch,u8 num,u8 ADC_SampleTime) //传递通道、序列序号、采样周期
{
ADC_RegularChannelConfig(ADC3, ch, num, ADC_SampleTime);//规则通道设置
ADC_SoftwareStartConvCmd(ADC3,ENABLE);//软件触发、并开启转换
while(!ADC_GetFlagStatus(ADC3,ADC_FLAG_EOC))
{
//转换过程中执行空语句,等待转换完成
}
return ADC_GetConversionValue(ADC3);
}
(9)主函数编写
在while循环中通过循环调用(8)中编写的函数,获取转换值,而转换值为0~4096的值,0对应0V、4096对应3.3V,故需要将这个ADC转换的值映射到0 ~3.3V区间
while(1)
{
a=adc_getval(ADC_Channel_6,1,ADC_SampleTime_28Cycles5);
b=a*(3.3/4096);
printf("ADC= %d Vrefint= %1.3f V \r\n",a,b);
delay_ms(500);
}
3、完整代码
1、adc.h代码
#ifndef _ADC_
#define _ADC_
#include "stm32f10x.h"
void adc_Init(void);
u16 adc_getval(u8 ch,u8 num,u8 ADC_SampleTime);
#endif
2、adc.c代码
#include "adc.h"
#include "stm32f10x.h"
#include "usart.h"
void adc_Init(void)
{
ADC_InitTypeDef ADC_InitStruct;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3|RCC_APB2Periph_GPIOF, ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOF, &GPIO_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_DeInit(ADC3);
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_InitStruct.ADC_NbrOfChannel=1;
ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
ADC_Init(ADC3, &ADC_InitStruct);
ADC_Cmd(ADC3,ENABLE);
ADC_ResetCalibration(ADC3);
while(ADC_GetResetCalibrationStatus(ADC3)) //等待复位校准结束
{
}
ADC_StartCalibration(ADC3);
while(ADC_GetCalibrationStatus(ADC3))//等待校准结束
{
}
}
u16 adc_getval(u8 ch,u8 num,u8 ADC_SampleTime) //传递通道、序列序号、采样周期
{
ADC_RegularChannelConfig(ADC3, ch, num, ADC_SampleTime);//规则通道设置
ADC_SoftwareStartConvCmd(ADC3,ENABLE);//软件触发、并开启转换
while(!ADC_GetFlagStatus(ADC3,ADC_FLAG_EOC))
{
//转换过程中执行空语句,等待转换完成
}
return ADC_GetConversionValue(ADC3);
}
3、main.c代码
#include "stm32f10x.h"
#include "delay.h"
#include "adc.h"
#include "usart.h"
u16 a;
float b;
int main(void)
{
delay_init();
adc_Init();
uart_init(115200);
while(1)
{
a=adc_getval(ADC_Channel_6,1,ADC_SampleTime_28Cycles5);
b=a*(3.3/4096);
printf("ADC= %d Vrefint= %1.3f V \r\n",a,b);
delay_ms(500);
}
}
4、测试
下载代码后通过串口监视器:
实验室光照下:
手摁住光敏传感器:
手电筒照射光敏传感器:
可见,ADC3通道6的确可以捕获到电压值,并且与我们之前分析的一致,强光下二极管导通