STM32F407ZG 模数转换器ADC & 内部温度传感器 & 光敏传感器

模数转换器ADC

模数转换,顾名思义是引脚上的连续模拟电压转换为内存中存储的数字变量,ADC的工作就是为模拟电路到数字电路搭建桥梁。

简介

STM32F4xx系列一般都有 3 个 ADC ,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。
STM32F4xx系列使用12 位 ADC 是逐次趋近型模数转换器。它具有多达 19 个复用通道,可测量来自 16 个外部 源、两个内部源和 VBAT 通道的信号。这些通道的 A/D 转换可在单次、连续、扫描或不连续采样模式下进行。ADC 的结果存储在一个左对齐或右对齐的 16 位数据寄存器中。
ADC 具有模拟看门狗特性,允许应用检测输入电压是否超过了用户自定义的阈值上限或下限。

ADC特性

ADC引脚

STM32F407ZGT6包含有 3 个 ADC 。 STM32F4 的 ADC 最大的转换速率为 2.4 Mhz ,也就是
转换时间为 1us (在 ADCCLK= 36 M, 采样周期为 3 个 ADC 时钟下得到),不建议让 ADC 的时钟超
过 36 M ,否则将导致结果准确度下降。

ADC框图
ADC转换包含了两种通道,注入通道和规则通道,简单来说,规则通道即普通情况下的转换,注入通道则相当于中断,可以打断规则通道下的转换,在注入通道转换完成后才重新回到规则通道的转换。

多重ADC模式
具体见官方手册。
可实现以下四种模式:
● 注入同时模式
● 规则同时模式
● 交替模式
● 交替触发模式
也可按以下方式组合使用上述模式:
● 注入同时模式 + 规则同时模式
● 规则同时模式 + 交替触发模式

寄存器

状态寄存器 SR

SR
OVR 是溢出标志
比较重要的就是 STRT (规则通道)和 JSTRT (注入通道)两个转换开始的标志,以及 EOC (规则通道)和 JEOC (注入通道)转换结束的标志。
AWD 是模拟看门狗事件发生标志。
上述各位由硬件置1,需要软件清0。其中, EOC 在读取 DR 数据寄存器后也会清0。

控制寄存器 CR1 & CR2

CR1
只介绍一些实验会用到的位。
在这里插入图片描述
在这里插入图片描述

PS:
ADC的分辨率指的是模数转换器所能表示的最大数是多少,即ADC的位数,如果ADC是10位ADC,那么分辨率是2的10次方,即1024的分辨率,如果模拟量是温度,测量范围是0~100度,那么可以把100度分成1024份,每一份你都能感知,当温度有100/1024度的变化时,能测量出来。
ADC的采样率指ADC每秒钟会进行多少次的模拟量转数字量的操作,如10K/s就是说ADC每秒钟,就采集了10K个模拟量,并将模拟量转换为数字量。当采样声时,一般的采样率是44Kbps/s,当采样温度时,几K/s的采样率就够了。

CR2
带 EXT 的都是外部触发相关,具体见官方手册。
比较重要的有:
在这里插入图片描述

通用控制寄存器 CCR

在这里插入图片描述
在这里插入图片描述

ADC 采样时间寄存器 SMPR1 & SMPR2

在这里插入图片描述

内部温度传感器

简介

STM32F4有一个内部的温度传感器,可以用来测量 CPU 及周围的温度 。该温度传感器在内部和 ADC 1 _IN16(STM32F40xx/F41xx 系列)或 ADC1_IN18(STM32F42xx/F43xx系列) 输入通道相连接,此通道把传感器输出的电压转换成数字值。 STM32F4 的内部温度传感器支持的温度范围为: 40~125度。精度为± 1. 5 ℃左右。
STM32F4内部温度传感器的使用很简单,只要设置一下内部 ADC ,并激活其内部温度传感器通道。具体见实验。

光敏传感器

简介

光敏传感器是最常见的传感器之一,它的种类繁多,主要有:光电管、光电倍增管、光敏电阻、光敏三极管、太阳能电池、红外线传感器、紫外线传感器、光纤式光电传感器、色彩传感器、 CCD 和 CMOS 图像传感器等。光传感器是目前产量最多、应用最广的传感器之一,它在自动控制和非电量电测技术中占有非常 重要的地位。
光敏传感器是利用光敏元件将光信号转换为电信号的传感器,它的敏感波长在可见光波长附近,包括红外线波长和紫外线波长。光传感器不只局限于对光的探测,它还可以作为探测元件组成其他传感器,对许多非电量进行检测,只要将这些非电量转换为光信号的变化即可。

光敏二极管

光敏二极管也叫光电二极管。光敏二极管与半导体二极管在结构上是类似的 其管芯是一个具有光敏特征的 PN 结,具有单向导电性,因此工作时需加上反向电压。
无光照时,有很 小的饱和反向漏电流,即暗电流,此时光敏二极管截止。当受到光照时 饱和反向漏电流大大增加,形成光电流 它随入射光强度的变化而变化。当光线照射 PN 结时,可以使 PN 结中产生电子一空穴对,使少数载流子的密度增加。这些载流子在反向电压下漂移,使反向电流增加。因此可以利用光照强弱来改变电路中的电流。利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过ADC 读取电压值,判断外部光线的强弱。

实验

ADC配置步骤

  1. 开启 PA 口时钟 和 ADC1 时钟 ,设置 PA 5 为模拟输入。
    特别注意,对于IO 口复用为 ADC 我们要设置模式为模拟输入,而不是复用功能,也不需要调用 GPIO_PinAFConfig 函数(此函数里没有复用为ADC的选项)来设置引脚映射关系。
  2. 设置 ADC 的通用控制寄存器 CCR ,配置 ADC 输入时钟分频,模式为独立模式等 。
    即创建 ADC_CommonInitTypeDef 类型结构体进行初始化。
  3. 初始化 ADC1 参数,设置 ADC1 的转换分辨率, 转换方式,对齐方式, 以及规则序列等 相关信息。 即创建 ADC_InitTypeDef 类型结构体进行初始化。
  4. 开启 AD 转换器。即调用 Cmd 函数。
  5. 设置规则序列1的通道,开启 ADC 转换,在转换完成后即可读取 ADC 的值。
    注意 ADC 测量的电压不应超过3.3V,否则可能会被烧坏。

内部温度传感器实验

配置步骤

  1. 配置ADC1,具体见上一节。其中STM32F407ZG 内部温度传感器设置在 ADC1 的 通道 16 上。
  2. 激活 ADC 的内部通道,通过 ADC_CCR 的 TSVREFE 位( bit23 )设置。即函数:
    ADC_TempSensorVrefintCmd(ENABLE);// 使能内部温度传感器
  3. 读取 ADC 数据寄存器即温度传感器引脚的电压值,再通过如下公式:
    T (℃) = { (Vsense - V25) / Avg_Slope } + 25
    上式中:
    V25 是 Vsense 在 25 度时的数值(典型值为: 0.76 )。
    Avg_Slope 是 温度与 Vsense 曲线的平均斜率(单位为 mv/℃或 uv/℃)(典型值为2.5mV /℃)。
    即可计算得到内部CPU周围的温度。

代码

ADC配置

void adc_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    
    //使能GPIOA和ADC1时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PA5
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //复位ADC1
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, DISABLE);
    
    ADC_TempSensorVrefintCmd(ENABLE); //使能内部温度传感器
    
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //不使用DMA
    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //分频数4
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; //两个采样间隔时间
    ADC_CommonInit(&ADC_CommonInitStructure);
    
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //非连续转换
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    //ADC_InitStructure.ADC_ExternalTrigConv = ; //无外部触发
    ADC_InitStructure.ADC_NbrOfConversion = 1; //一个转换在转换序列中
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //数据12bit
    ADC_InitStructure.ADC_ScanConvMode = DISABLE; //非扫描模式
    ADC_Init(ADC1, &ADC_InitStructure);
    
    ADC_Cmd(ADC1, ENABLE); //开启ADC1
}

读取ADC数据及温度值转换

uint16_t adc_get(uint8_t channel)
{
    //配置规则通道
    ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_480Cycles);
    
    //软件触发
    ADC_SoftwareStartConv(ADC1);
    
    //等待转换完成
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    
    return ADC_GetConversionValue(ADC1);
}

uint16_t adc_get_average(uint8_t channel, uint8_t times)
{
    uint32_t temp = 0;
    uint8_t t;
    for(t = 0; t < times; t++)
    {
        temp += adc_get(channel);
        delay_ms(5);
    }
    return temp / times;
}

short adc_get_temperature(void)
{
    uint32_t adc_dr;
    short result;
    double temperature;
    adc_dr = adc_get_average(ADC_Channel_16, 20);
    temperature = (double)adc_dr / 4096 * 3.3; //电压值
    temperature = (temperature - 0.76) / 0.0025 + 25; //转换为温度值
    result = temperature *= 100; //扩大100倍
    return result;
}

main.c

int main(void)
{
    uint16_t adc_dr;
    double temp;
    float temperature;
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置系统中断优先级分组2
    delay_init(168); //延时初始化
    usart_init(); //串口初始化波特率为115200
    LED_Init(); //初始化与LED连接的硬件接口
    adc_init();
    while(1)
    {
        adc_dr = adc_get(ADC_Channel_16);
        printf("ADC初始值:%u\n\r", adc_dr);
        printf("\n\r");
        temp = (double)adc_dr/4096*3.3;
        printf("电压值:%f\n\r", temp);
        printf("\n\r");
        temperature = (double)adc_get_temperature()/100;
        printf("温度值:%f\n\r", temperature);
        printf("\n\r");
        printf("\n\r");
        LED0 = !LED0;
        delay_ms(500);
	}
}

实验结果

温度

光敏传感器

实验过程也就是配置ADC3读取通道5的数据,即可转换光敏电阻的电压值。开发板原理图如下:
原理图

代码

ADC3相关
void adc3_init(void)
{
    //GPIOF的配置在light_sensor.c
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    
    //使能GPIOF和ADC3时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
   
    //复位ADC3
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, ENABLE);
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3, DISABLE);
    
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //不使用DMA
    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //分频数4
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; //采样时间
    ADC_CommonInit(&ADC_CommonInitStructure);
    
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //非连续转换
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    //ADC_InitStructure.ADC_ExternalTrigConv = ; //无外部触发
    ADC_InitStructure.ADC_NbrOfConversion = 1; //一个转换在转换序列中
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //数据12bit
    ADC_InitStructure.ADC_ScanConvMode = DISABLE; //非扫描模式
    ADC_Init(ADC3, &ADC_InitStructure);
    
    ADC_Cmd(ADC3, ENABLE); //开启ADC3
}

uint16_t adc3_get(uint8_t channel)
{
    //配置规则通道
    ADC_RegularChannelConfig(ADC3, channel, 1, ADC_SampleTime_480Cycles);
    
    //软件触发
    ADC_SoftwareStartConv(ADC3);
    
    //等待转换完成
    while(!ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC));
    
    return ADC_GetConversionValue(ADC3);
}

light_sensor.c
void light_sensor_init(void)
{    
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    //GPIO_InitStructure.GPIO_OType = ;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    //GPIO_InitStructure.GPIO_Speed = ;
    GPIO_Init(GPIOF, &GPIO_InitStructure);
    
    adc3_init();
}

uint8_t light_lenser_get_value(void)
{
    uint32_t adc_dr = 0;
    uint8_t t;
    for(t = 0; t < 10; t++)
    {
        adc_dr += adc3_get(ADC_Channel_5);
        delay_ms(5);
    }
    adc_dr /= 10;
    
    //这两行参考正点原子的写法,但我没想通这么处理的原因。。
    if(adc_dr > 4000)adc_dr = 4000;
    return (uint8_t)(100 - adc_dr/40);
}

main.c
int main(void)
{
    uint8_t lensor;
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置系统中断优先级分组2
    delay_init(168); //延时初始化
    usart_init(); //串口初始化波特率为115200
    LED_Init(); //初始化与LED连接的硬件接口
    light_sensor_init();
    while(1)
    {
        lensor = light_lenser_get_value();
        printf("亮度值(0-100):%u\n\r", lensor);
        printf("\n\r");
        LED0 = !LED0;
        delay_ms(250);
	}
}

实验结果

正常情况下在75~80,用手机手电筒照射可以到97左右,不同程度遮挡也会使值变小。

在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值