这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 功率监测与控制系统DIY-04INA219测负载并在OLED显示

共4条 1/1 1 跳转至

功率监测与控制系统DIY-04INA219测负载并在OLED显示

助工
2025-06-23 12:05:15     打赏

1. 任务目标

2. INA219 介绍

3. 连接关系

4. INA219 驱动

    4.1 适配层

        4.1.1   函数 ina219_interface_iic_init()

        4.1.2   函数 ina219_interface_iic_deinit()

        4.1.3   函数 ina219_interface_iic_read()

        4.1.4   函数 ina219_interface_iic_write()

        4.1.5   函数 ina219_interface_delay_ms()

        4.1.6   函数 ina219_interface_debug_print()

    4.2 驱动的封装

        4.2.1   函数 ina219_basic_init()

        4.2.2   函数 ina219_basic_deinit()

        4.2.3   函数 ina219_basic_read()

    4.3 应用

        4.3.1   流程

        4.3.2   函数 main()

        4.3.3   函数 screen_02_diy_static_menu()

        4.3.4   函数 ina219_read_filter()

        4.3.5   函数 screen_03_diy_dynamic_info()

5. 实操演示

 

1. 任务目标

 

使用开发板实现对INA219的负载测量5V12V电压电流并显示在OLED

 

2. INA219 介绍

 

INA219 是一款具备 I2C SMBUS 兼容接口的分流器和功率监测计。该器件监测分流器电压降和总线电源电压,转换次数和滤波选项可通过编程设定。可编程校准值与内部乘法器相结合,支持直接读取电流值(单位:安培)。通过附加乘法寄存器可计算功率(单位:瓦)。I2C SMBUS 兼容接口具有 16 个可编程地址。INA219 可在 0V 26V 范围内感测总线中的分压。该器件由 3V 5.5V 单电源供电,芯片的最大功耗为 1mA。该设备可用于服务器、通信设备、笔记本、电源管理、充电器、电焊、电源和测试设备等。

image.png

3. 连接关系

 

1.      主控通过I2C接口连接到 INA219 OLED,共用一个I2C控制器,管脚也共用;

2.      电源的 VCC 接到INA219IN+,经过 INA219 内部电路从 IN- 流出,这一端接到负载(电机)的一端;

3.      电源的 GND INA219 GND 以及负载的 GND 连接;


image.png


4. INA219 驱动

 

这里直接采用 libdriver_ina219 仓库中的代码,结构如下:

1.      驱动核心代码是 src/ 目录下的源文件

2.      适配层在 interface/ 目录下,移植到新的平台只需要适配 driver_ina219_interface_template.c 中的6个函数即可


image.png

4.1 适配层

libdriver_ina219 的代码拷贝到工程中,它的适配层都在源文件 stm32f411_driver_ina219_interface.c 中。


image.png

4.1.1 函数 ina219_interface_iic_init()

 

STM32CubeMX 配置工程,使能 I2C1PB8 作为 SCLPB9 作为 SDA,并且插上 Seeed Base Shield 扩展板,多个 I2C 从器件可以共用一组 I2C 管脚,只需要注意使用不同的 I2C 从器件地址即可。

 

由于 MX_I2C1_Init() 已经初始化了 I2C 管脚,这里留空。


#include <stdarg.h>
#include "driver_ina219_interface.h"
#include "stm32f4xx_hal.h"
#include "i2c.h"

#define hi2cx            hi2c1

/**
 * @brief  interface iic bus init
 * @return status code
 *         - 0 success
 *         - 1 iic init failed
 * @note   none
 */
uint8_t ina219_interface_iic_init(void)
{
    return 0;
}


4.1.2 函数 ina219_interface_iic_deinit()


/**
 * @brief  interface iic bus deinit
 * @return status code
 *         - 0 success
 *         - 1 iic deinit failed
 * @note   none
 */
uint8_t ina219_interface_iic_deinit(void)
{
    HAL_I2C_DeInit(&hi2cx);
    return 0;
}


4.1.3 函数 ina219_interface_iic_read()

 

直接调用 HAL_I2C_Mem_Read() 实现读取功能。


/**
 * @brief      interface iic bus read
 * @param[in]  addr iic device write address
 * @param[in]  reg iic register address
 * @param[out] *buf pointer to a data buffer
 * @param[in]  len length of the data buffer
 * @return     status code
 *             - 0 success
 *             - 1 read failed
 * @note       none
 */
uint8_t ina219_interface_iic_read(uint8_t addr, uint8_t reg, uint8_t *buf, uint16_t len)
{
    HAL_StatusTypeDef ret;

    ret = HAL_I2C_Mem_Read(&hi2cx, addr, reg, 1, buf, len, HAL_MAX_DELAY);
    if (HAL_OK != ret) {
        return 1;
    }

    return 0;
}


4.1.4 函数 ina219_interface_iic_write()

 

直接调用 HAL_I2C_Mem_Write() 实现写寄存器功能。


/**
 * @brief     interface iic bus write
 * @param[in] addr iic device write address
 * @param[in] reg iic register address
 * @param[in] *buf pointer to a data buffer
 * @param[in] len length of the data buffer
 * @return    status code
 *            - 0 success
 *            - 1 write failed
 * @note      none
 */
uint8_t ina219_interface_iic_write(uint8_t addr, uint8_t reg, uint8_t *buf, uint16_t len)
{
    HAL_StatusTypeDef ret;

    ret = HAL_I2C_Mem_Write(&hi2cx, addr, reg, 1, buf, len, HAL_MAX_DELAY);
    if (HAL_OK != ret) {
        return 1;
    }

    return 0;
}



4.1.5 函数 ina219_interface_delay_ms()

 

直接调用 HAL_Delay() 实现延时功能。

/**
 * @brief     interface delay ms
 * @param[in] ms time
 * @note      none
 */
void ina219_interface_delay_ms(uint32_t ms)
{
    HAL_Delay(ms);
}


4.1.6 函数 ina219_interface_debug_print()

 

由于把标注输入输出重定向到串口,即 printf() 可以直接打印字符串,所以这里屏蔽了 UART 硬件操作。


/**
 * @brief     interface print format data
 * @param[in] fmt format data
 * @note      none
 */
void ina219_interface_debug_print(const char *const fmt, ...)
{
    va_list args;

    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}


4.2 驱动的封装

 

在文件 driver_ina219_basic.c 文件中封装 libdriver_ina219 ,实现用户自定义的3个函数,实现 INA219 设备的初始化、反初始化、数据读取。


 

4.2.1 函数 ina219_basic_init()

 

在此文件中定义了一个全局变量  ina219_handle_t gs_handle,保存了INA219设备的I2C 从器件地址,适配层API实现,以及设备的状态变量。

 

        此函数线把适配层函数挂接到 gs_handle

        调用 ina219_set_addr_pin() 设置I2C地址

        调用 ina219_set_resistance() 设置电流传感器的分流电阻值

        调用 ina219_init() 初始化I2C硬件接口,读 INA219_REG_CONF 寄存器,并进行软件复位

        调用 ina219_set_bus_voltage_range() 设置 INA219 电流传感器的总线电压测量范围

        调用 ina219_set_bus_voltage_adc_mode() 设置 INA219 电流传感器的总线电压ADC采样模式,即ADC采样精度和转换时间

        调用 ina219_set_shunt_voltage_adc_mode() 设置 INA219 电流传感器的分流电阻电压ADC采样模式

        调用 ina219_set_mode() 设置 INA219 电流传感器的工作模式,例如连续测量分流电压和总线电压

        调用 ina219_set_pga() 设置 INA219 电流传感器的可编程增益放大器PGA参数,例如 ±320mV 量程

        调用 ina219_calculate_clibration() ina219_set_calibration() INA219 电流传感器进行校准值计算和配置


#include "driver_ina219_basic.h"

static ina219_handle_t gs_handle;        /**< ina219 handle */

/**
 * @brief     basic example init
 * @param[in] addr_pin iic address pin
 * @param[in] r reference resistor value
 * @return    status code
 *            - 0 success
 *            - 1 init failed
 * @note      none
 */
uint8_t ina219_basic_init(ina219_address_t addr_pin, double r)
{
    uint8_t res;
    uint16_t calibration;
    
    /* link interface function */
    DRIVER_INA219_LINK_INIT(&gs_handle, ina219_handle_t);
    DRIVER_INA219_LINK_IIC_INIT(&gs_handle, ina219_interface_iic_init);
    DRIVER_INA219_LINK_IIC_DEINIT(&gs_handle, ina219_interface_iic_deinit);
    DRIVER_INA219_LINK_IIC_READ(&gs_handle, ina219_interface_iic_read);
    DRIVER_INA219_LINK_IIC_WRITE(&gs_handle, ina219_interface_iic_write);
    DRIVER_INA219_LINK_DELAY_MS(&gs_handle, ina219_interface_delay_ms);
    DRIVER_INA219_LINK_DEBUG_PRINT(&gs_handle, ina219_interface_debug_print);
    
    /* set addr pin */
    res = ina219_set_addr_pin(&gs_handle, addr_pin);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: set addr pin failed.\n");
       
        return 1;
    }

    /* set the r */
    res = ina219_set_resistance(&gs_handle, r);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: set resistance failed.\n");
       
        return 1;
    }
    
    /* init */
    res = ina219_init(&gs_handle);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: init failed.\n");
       
        return 1;
    }
    
    /* set bus voltage range */
    res = ina219_set_bus_voltage_range(&gs_handle, INA219_BASIC_DEFAULT_BUS_VOLTAGE_RANGE);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: set bus voltage range failed.\n");
        (void)ina219_deinit(&gs_handle);
        
        return 1;
    }
    
    /* set bus voltage adc mode */
    res = ina219_set_bus_voltage_adc_mode(&gs_handle, INA219_BASIC_DEFAULT_BUS_VOLTAGE_ADC_MODE);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: set bus voltage adc mode failed.\n");
        (void)ina219_deinit(&gs_handle);
        
        return 1;
    }

    /* set shunt voltage adc mode */
    res = ina219_set_shunt_voltage_adc_mode(&gs_handle, INA219_BASIC_DEFAULT_SHUNT_VOLTAGE_ADC_MODE);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: set shunt voltage adc mode failed.\n");
        (void)ina219_deinit(&gs_handle);
        
        return 1;
    }
    
    /* set shunt bus voltage continuous */
    res = ina219_set_mode(&gs_handle, INA219_MODE_SHUNT_BUS_VOLTAGE_CONTINUOUS);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: set mode failed.\n");
        (void)ina219_deinit(&gs_handle);
        
        return 1;
    }
    
    /* set pga */
    res = ina219_set_pga(&gs_handle, INA219_BASIC_DEFAULT_PGA);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: set pga failed.\n");
        (void)ina219_deinit(&gs_handle);
        
        return 1;
    }
    
    /* calculate calibration */
    res = ina219_calculate_calibration(&gs_handle, (uint16_t *)&calibration);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: calculate calibration failed.\n");
        (void)ina219_deinit(&gs_handle);
        
        return 1;
    }
    
    /* set calibration */
    res = ina219_set_calibration(&gs_handle, calibration);
    if (res != 0)
    {
        ina219_interface_debug_print("ina219: set calibration failed.\n");
        (void)ina219_deinit(&gs_handle);
        
        return 1;
    }
    
    return 0;
}


4.2.2 函数 ina219_basic_deinit()

调用 ina219_deinit() 函数关闭传感器(通过全局变量 gs_handle)


/**
 * @brief  basic example deinit
 * @return status code
 *         - 0 success
 *         - 1 deinit failed
 * @note   none
 */
uint8_t ina219_basic_deinit(void)
{
    uint8_t res;
    
    res = ina219_deinit(&gs_handle);
    if (res != 0)
    {
        return 1;
    }
    
    return 0;
}


4.2.3 函数 ina219_basic_read()

 

这段代码用于读取 INA219 电流、功率检测芯片数据的函数,通过调用三个子函数分别读取总线电压mV 、电流 mA 和功率 mW 的数据。


/**
 * @brief      basic example read
 * @param[out] *mV pointer to a mV buffer
 * @param[out] *mA pointer to a mA buffer
 * @param[out] *mW pointer to a mW buffer
 * @return     status code
 *             - 0 success
 *             - 1 read failed
 * @note       none
 */
uint8_t ina219_basic_read(float *mV, float *mA, float *mW)
{
    uint8_t res;
    int16_t s_raw;
    uint16_t u_raw;
    
    /* read bus voltage */
    res = ina219_read_bus_voltage(&gs_handle, (uint16_t *)&u_raw, mV);
    if (res != 0)
    {
        return 1;
    }
    
    /* read current */
    res = ina219_read_current(&gs_handle, (int16_t *)&s_raw, mA);
    if (res != 0)
    {
        return 1;
    }
    
    /* read power */
    res = ina219_read_power(&gs_handle, (uint16_t *)&u_raw, mW);
    if (res != 0)
    {
        return 1;
    }
    
    return 0;
}


4.3 应用

 

        这里用电流源给电机供电,并把 INA219 串接到电路中,Nucleo-F411RE 开发板通过I2C接口与 INA219 通信,读取电压、电流、功率值。然后通过OLED屏幕显示读取的三个数值出来,同时串口打印出来。

 

        为了读取数据的问题,连续读取10个组数据,去掉最大值、最小值,然后对8组数据求平均值。

 

        注意,INA219 的两个拨码开关都拨到左侧,配置其I2C器件地址为 0x40.


4.3.1 流程


image.png

4.3.2 函数 main()

 

函数main() 即流程图的实现。


/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */
    uint8_t ret_val = 0;

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_TIM1_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */

    printf("\r\n");
    printf("\t\t EEPW_2025_DIY1_Task3_INA219 \r\n");
    printf("\t\t Build: %s %s \r\n", __DATE__, __TIME__);
    printf("\r\n");

    HAL_TIM_Base_Start_IT(&htim1);

    OLED_Init();
    OLED_CLS();

    screen_00_welcome_english();
    HAL_Delay(1000 * 3);
    screen_01_welcome_chinese();
    HAL_Delay(1000 * 3);
    screen_02_diy_static_menu();
    HAL_Delay(1000 * 5);

    // 注意拨码开关都拨到 GND,此时 INA219 默认是 I2C 地址 0x40
    ret_val = ina219_basic_init(INA219_ADDRESS_0, 0.01); // 3629mV, 5.48V, 17.33mW
    if (0 != ret_val) {
        printf("ina219_basic_init() failed");
    }

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    while (1) {

        ret_val = ina219_read_and_filter(&mV, &mA, &mW);
        if (0 != ret_val) {
            printf("ina219_read_and_filter() failed");
        } else {
            // printf("%.2f mV \t %.2f mA \t %.2f mW\r\n", mV, mA, mW);
            printf("mV_mA_mW: %.2f, %.2f, %.2f \r\n", mV, mA, mW);

            // 把电压、电流、功率数值显示在 OLED 屏幕上
            screen_03_diy_dynamic_info(mV, mA, mW);
        }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
        HAL_Delay(500);
    }
  /* USER CODE END 3 */
}



4.3.3 函数 screen_02_diy_static_menu()

 

此函数绘制了功率监测的主界面。

注意此函数中第一次显示的电压、电流和功率值是占位数值,不是从INA219读取的真实数值。


#define CURRENT_ROW     2
#define VOLTAGE_ROW     4
#define POWER___ROW     6

#define CURRENT_COL     40
#define VOLTAGE_COL     40
#define POWER___COL     40

#define CURRENT_LEN     60
#define VOLTAGE_LEN     60
#define POWER___LEN     60


void screen_02_diy_static_menu(void)
{
    uint16_t x = 0, y = 0;

    OLED_CLS();

    // DIY 功率监测与控制
    OLED_ShowStr(x, y, "DIY", ASCII_FONT_8X16);

    x += 3*8;
    OLED_ShowCN(x, y, 10); // 功

    x += 16;
    OLED_ShowCN(x, y, 11); // 率

    x += 16;
    OLED_ShowCN(x, y, 12); // 监

    x += 16;
    OLED_ShowCN(x, y, 13); // 测

#if 0
    x += 16;
    OLED_ShowCN(x, y, 21); // 与

    x += 16;
    OLED_ShowCN(x, y, 14); // 系

    x += 16;
    OLED_ShowCN(x, y, 15); // 统
#endif

    x += 16;
    OLED_ShowCN(x, y, 16); // 控

    x += 16;
    OLED_ShowCN(x, y, 17); // 制

    // 电流  mA
    y = CURRENT_ROW;
    x = 0;

    OLED_ShowCN(x, y, 18); // 电
    x += 16;
    OLED_ShowCN(x, y, 20); // 流

    x = CURRENT_COL + CURRENT_LEN;
    OLED_ShowStr(x, y, "mA", ASCII_FONT_8X16);

#ifdef DEBUG_DISPLAY_VALUE
    // 电流 123
    OLED_ShowStr(CURRENT_COL, y, "123", ASCII_FONT_8X16);
#endif

    // 电压   V
    y = VOLTAGE_ROW;
    x = 0;

    OLED_ShowCN(x, y, 18); // 电
    x += 16;
    OLED_ShowCN(x, y, 19); // 压

    x = VOLTAGE_COL + VOLTAGE_LEN;
    OLED_ShowStr(x, y, "mV", ASCII_FONT_8X16);

#ifdef DEBUG_DISPLAY_VALUE
    // 电压 5.43
    OLED_ShowStr(VOLTAGE_COL, y, "5.43", ASCII_FONT_8X16);
#endif
    // 功率  mW
    y = POWER___ROW;
    x = 0;

    OLED_ShowCN(x, y, 10); // 功
    x += 16;
    OLED_ShowCN(x, y, 11); // 率

    x = POWER___COL + POWER___LEN;
    OLED_ShowStr(x, y, "mW", ASCII_FONT_8X16);

#ifdef DEBUG_DISPLAY_VALUE
    // 功率 123mA * 5.43V = 643.5mW
    OLED_ShowStr(POWER___COL, y, "643.5", ASCII_FONT_8X16);
#endif
}



4.3.4 函数 ina219_read_filter()

 

此函数连续读取10次数值,然后去掉最大值、最小值,对8组数值求平均。


static uint8_t ina219_read_and_filter(float* vol, float* cur, float* power)
{
    uint8_t ret = 0;
    float mVol = 0.0f, mCur = 0.0f, mPower = 0.0f;
    float sumVol = 0.0f, sumCur = 0.0f, sumPower = 0.0f;
    float maxVol = 0.0f, maxCur = 0.0f, maxPower = 0.0f;
    float minVol = 0.0f, minCur = 0.0f, minPower = 0.0f;

    for (int i = 0; i < 10; i++) {
        ret += ina219_basic_read(&mVol, &mCur, &mPower);

        sumVol += mVol;
        sumCur += mCur;
        sumPower += mPower;

        if (mVol > maxVol) {
            maxVol = mVol;
        }
        if (mCur > maxCur) {
            maxCur = mCur;
        }
        if (mPower > maxPower) {
            maxPower = mPower;
        }

        if (mVol < minVol) {
            minVol = mVol;
        }
        if (mCur < minCur) {
            minCur = mCur;
        }
        if (mPower < minPower) {
            minPower = mPower;
        }

        HAL_Delay(5);
    }

    if (ret != 0) {
        return 1;
    }

    // remove max and min
    sumVol -= (maxVol + minVol);
    sumCur -= (maxCur + minCur);
    sumPower -= (maxPower + minPower);

    *vol = sumVol / 8.0f;
    *cur = sumCur / 8.0f;
    *power = sumPower / 8.0f;
    return 0;
}


4.3.5 函数 screen_03_diy_dynamic_info()

 

此函数把从INA219读取的电压、电流和功率数值,更新在OLED屏幕上,注意只更新屏幕上指定一个数值区域,并没有全屏刷新。

/**
 * @brief 在屏幕指定的三个位置分别显示电压、电流、功率
 * 所有数据均只显示小数点后两位
 *
 * @param vol 电压,mV
 * @param cur 电流,mA
 * @param power 功率,mW
 */
void screen_03_diy_dynamic_info(float vol, float cur, float power)
{
    char str_vol[10] = { 0 };
    char str_cur[10] = { 0 };
    char str_power[10] = { 0 };

    sprintf(str_vol, "%.2f", vol);
    sprintf(str_cur, "%.2f", cur);
    sprintf(str_power, "%.2f", power);

    OLED_ShowStr(VOLTAGE_COL, VOLTAGE_ROW, (uint8_t *)str_vol, ASCII_FONT_8X16);
    OLED_ShowStr(CURRENT_COL, CURRENT_ROW, (uint8_t *)str_cur, ASCII_FONT_8X16);
    OLED_ShowStr(POWER___COL, POWER___ROW, (uint8_t *)str_power, ASCII_FONT_8X16);
}


https://www.bilibili.com/video/BV1m9KPzWEpj/?vd_source=8f2bbf56b70c541bec2ea0b9f102ebee




关键词: INA219    

菜鸟
2025-06-24 00:44:37     打赏
2楼

楼主,连接图用什么软件画的?好清新!


菜鸟
2025-06-24 16:37:17     打赏
3楼

很简介明了,受教了



专家
2025-06-25 08:47:02     打赏
4楼

谢谢楼主分享


共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]