STM32 HAL库 内部传感器驱动实现

一、STM32F407 内部传感器概述

STM32F407 微控制器集成了多个内部传感器,其中比较常用的是温度传感器和内部参考电压传感器。这些传感器可以为系统提供重要的环境信息,例如温度传感器可以用于监测芯片的工作温度,避免芯片因过热而损坏;内部参考电压传感器可以用于校准 ADC 测量结果,提高测量精度。

二、硬件连接

由于是内部传感器,不需要额外的硬件连接。但是要使用这些传感器,需要确保芯片的电源和时钟正常工作,并且 ADC(模拟 - 数字转换器)模块正常配置。

三、开发环境搭建

1. 安装 Keil MDK

Keil MDK 是一款广泛使用的 ARM 微控制器开发工具,你可以从官方网站下载并安装。

2. 安装 STM32CubeMX

STM32CubeMX 是 ST 公司提供的一款图形化配置工具,可以帮助你快速配置 STM32 微控制器的外设和时钟。你可以从 ST 官方网站下载并安装。

3. 创建项目

打开 STM32CubeMX,选择 STM32F407 芯片,配置系统时钟和 ADC 模块。在 "Pinout & Configuration" 选项卡中,选择 "ADC",启用 ADC1 并配置相关参数。然后生成 Keil MDK 项目。

四、温度传感器驱动实现

1. 温度传感器原理

STM32F407 的温度传感器的输出电压与温度成线性关系,其公式为

其中,T 是温度,VSENSE是温度传感器的输出电压,25 是在 25°C 时的输出电压,Avg_Slope 是温度传感器的平均斜率。

2. 代码实现

以下是使用 HAL 库实现温度传感器读取的代码示例:

复制代码
#include "main.h"
#include "stm32f4xx_hal.h"

ADC_HandleTypeDef hadc1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);

// 读取温度传感器的值
float Read_Temperature(void)
{
    uint32_t adc_value;
    float voltage;
    float temperature;

    // 启动 ADC 转换
    HAL_ADC_Start(&hadc1);
    // 等待转换完成
    if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
    {
        // 获取转换结果
        adc_value = HAL_ADC_GetValue(&hadc1);
        // 计算电压
        voltage = (float)adc_value * 3.3 / 4096;
        // 计算温度
        temperature = ((voltage - 0.76) / 0.0025) + 25;
    }
    else
    {
        temperature = -1;
    }
    // 停止 ADC 转换
    HAL_ADC_Stop(&hadc1);

    return temperature;
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_ADC1_Init();

    float temperature;

    while (1)
    {
        temperature = Read_Temperature();
        // 这里可以添加代码将温度值发送到串口或其他设备
        HAL_Delay(1000);
    }
}

void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** 初始化 RCC 振荡器 
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 336;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 7;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }
    /** 初始化 RCC 时钟 
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                  |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
        Error_Handler();
    }
}

static void MX_ADC1_Init(void)
{
    ADC_ChannelConfTypeDef sConfig = {0};

    /** 初始化 ADC 
    */
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 1;
    hadc1.Init.DMAContinuousRequests = DISABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
        Error_Handler();
    }
    /** 配置 ADC 通道 
    */
    sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
}

static void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();

    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);

    /*Configure GPIO pins : PD12 PD13 PD14 PD15 */
    GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}

void Error_Handler(void)
{
    while(1)
    {
    }
}
3. 代码解释
  • MX_ADC1_Init 函数:初始化 ADC1 模块,配置时钟、分辨率、转换模式等参数,并将温度传感器通道配置为 ADC 通道。
  • Read_Temperature 函数:启动 ADC 转换,等待转换完成,获取转换结果,计算电压和温度,最后停止 ADC 转换。
  • main 函数:初始化系统时钟、GPIO 和 ADC 模块,然后在循环中不断读取温度传感器的值。

五、内部参考电压传感器驱动实现

1. 内部参考电压传感器原理

STM32F407 的内部参考电压传感器的输出电压是一个固定值,通常为 1.2V。通过读取 ADC 转换结果,可以计算出实际的参考电压值,从而校准 ADC 测量结果。

2. 代码实现
复制代码
#include "main.h"
#include "stm32f4xx_hal.h"

ADC_HandleTypeDef hadc1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);

// 读取内部参考电压传感器的值
float Read_Vrefint(void)
{
    uint32_t adc_value;
    float vrefint;

    // 启动 ADC 转换
    HAL_ADC_Start(&hadc1);
    // 等待转换完成
    if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
    {
        // 获取转换结果
        adc_value = HAL_ADC_GetValue(&hadc1);
        // 计算内部参考电压
        vrefint = (float)(1.2 * 4096) / adc_value;
    }
    else
    {
        vrefint = -1;
    }
    // 停止 ADC 转换
    HAL_ADC_Stop(&hadc1);

    return vrefint;
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_ADC1_Init();

    float vrefint;

    while (1)
    {
        vrefint = Read_Vrefint();
        // 这里可以添加代码将内部参考电压值发送到串口或其他设备
        HAL_Delay(1000);
    }
}

void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** 初始化 RCC 振荡器 
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 336;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 7;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }
    /** 初始化 RCC 时钟 
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                  |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
        Error_Handler();
    }
}

static void MX_ADC1_Init(void)
{
    ADC_ChannelConfTypeDef sConfig = {0};

    /** 初始化 ADC 
    */
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 1;
    hadc1.Init.DMAContinuousRequests = DISABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
        Error_Handler();
    }
    /** 配置 ADC 通道 
    */
    sConfig.Channel = ADC_CHANNEL_VREFINT;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
}

static void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();

    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);

    /*Configure GPIO pins : PD12 PD13 PD14 PD15 */
    GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}

void Error_Handler(void)
{
    while(1)
    {
    }
}
3. 代码解释
  • MX_ADC1_Init 函数:初始化 ADC1 模块,配置时钟、分辨率、转换模式等参数,并将内部参考电压传感器通道配置为 ADC 通道。
  • Read_Vrefint 函数:启动 ADC 转换,等待转换完成,获取转换结果,计算内部参考电压值,最后停止 ADC 转换。
  • main 函数:初始化系统时钟、GPIO 和 ADC 模块,然后在循环中不断读取内部参考电压传感器的值。

六、调试与优化

1. 调试
  • 使用调试工具(如 JTAG 或 SWD)连接开发板,单步执行代码,检查变量的值,确保 ADC 转换结果正确。
  • 使用串口调试助手,将温度和内部参考电压值发送到计算机,实时监测传感器数据。
2. 优化
  • 可以采用多次采样取平均值的方法,提高测量精度。
  • 可以根据实际应用需求,调整 ADC 的采样时间和转换模式,提高系统性能。

七、总结

通过以上步骤,你可以实现 STM32F407 内部温度传感器和内部参考电压传感器的驱动。这些传感器可以为系统提供重要的环境信息,提高系统的稳定性和可靠性。在实际应用中,你可以根据具体需求对代码进行修改和优化。

相关推荐
cjy_Somnr3 小时前
keil5报错显示stm32的SWDIO未连接不能烧录
stm32·单片机·嵌入式硬件
Lay_鑫辰4 小时前
西门子诊断-状态和错误位(“轴”工艺对象 V1...3)
服务器·网络·单片机·嵌入式硬件·自动化
无垠的广袤6 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
雲烟8 小时前
嵌入式设备EMC安规检测参考
网络·单片机·嵌入式硬件
泽虞8 小时前
《STM32单片机开发》p7
笔记·stm32·单片机·嵌入式硬件
田甲8 小时前
【STM32】 数码管驱动
stm32·单片机·嵌入式硬件
up向上up9 小时前
基于51单片机垃圾箱自动分类加料机快递物流分拣器系统设计
单片机·嵌入式硬件·51单片机
纳祥科技18 小时前
Switch快充方案,内置GaN,集成了多个独立芯片
单片机
单片机日志19 小时前
【单片机毕业设计】【mcugc-mcu826】基于单片机的智能风扇系统设计
stm32·单片机·嵌入式硬件·毕业设计·智能家居·课程设计·电子信息
松涛和鸣20 小时前
从零开始理解 C 语言函数指针与回调机制
linux·c语言·开发语言·嵌入式硬件·排序算法