【DIY电子作品】电压与电流CC表 基于小安派AiPi-Eye-S1 part2

[复制链接]
查看1843 | 回复10 | 2023-12-12 16:17:09 | 显示全部楼层 |阅读模式

本帖最后由 bzhou830 于 2023-12-12 16:23 编辑

本帖最后由 bzhou830 于 2023-12-12 16:21 编辑

上一篇https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=43771&_dsign=5acd58c5中我们分析了硬件的可行性。然后使用lvgl在小安派s1上做了一个简单的ui界面。这篇我们来研究下INA226这个IC的使用。

1.INA226模块

INA226是一款集成了高精度电流和电压测量功能的数字电流/功率监测芯片。它可以通过I2C接口与微控制器或其他设备通信,用于实时监测和测量电流、电压和功率。从INA226的data sheet中可以看到其典型测量电路如下图,vbus引脚接被测量电路的电源+端,Vin+/Vin-串联在被测试回路中。地址线A0/A1直接接地,因为我们这里的IIC上只使用了一片INA226。

Snipaste_2023-12-12_08-52-26.png

data sheet中还给出了layout的参考方案,这个方案和我们使用的模块布局是比较一致的。

Snipaste_2023-12-12_08-54-57.png

INA226通过I2C接口与主控设备通信,所以我们就需要使用小安派的IO和INA226的IIC时钟线和数据线连接起来。IIC通信部分直接参考数据手册中的时序图即可,在INA226_Init()函数中,写入Configuration Register的数据为0x4527(16次平均,1.1ms,1.1ms,连续测量分流电压和总线电压)想修改的话参考数据手册寄存器定义表格。而写入Calibration Register的数据需要根据实际电路中的采样电阻阻值以及电流分辨率来设置。因为我们使用的INA226模块中使用的采样电阻为R100(0.1欧)。根据数据手册中的公式1和公式2。

因为Shunt Voltage Register的值最大为 0x7FFF,LSB=2.5uV, FSR = 81.92mV。又因为分流电阻阻值为 0.1欧,所以最大电流为 819.2mA,Maximum Expected Current的值不能超过 819.2mA。(避免在使用中出现的超量程情况)

假设 Current_LSB = 0.02mA,则 Maximum Expected Current = 655.36mA,满足上述条件。则: CAL = 0.00512/(0.02*0.1)*1000 = 2560= 0x0A00。 所以写入Calibration Register中的数据为0x0A00。

2. 小安派驱动INA226

前面讲到INA226通过IIC接口和小安派通信。这里为了深入的了解下IIC时序,我们直接采用模拟IIC的方式来对INA226进行驱动,并且使用逻辑分析仪来调试、捕获波形数据来进行深入研究。驱动INA226的代码如下:

bsp_ina226.h

#ifndef __BSP_INA226__
#define __BSP_INA226__


#define READ_ADDR                  0x81  //A0=GND,A1=GND // R=1, W=0
#define WRITE_ADDR                 0x80

#define Config_Reg                 0x00
#define Shunt_V_Reg                0x01
#define Bus_V_Reg                  0x02
#define Power_Reg                  0x03
#define Current_Reg                0x04
#define Calib_Reg                  0x05
#define Mask_En_Reg                0x06
#define Alert_Reg                  0x07
#define Man_ID_Reg                 0xFE  //0x5449
#define ID_Reg                     0xFF  //0x2260

uint16_t INA226_Read2Byte(uint8_t reg_addr);
uint8_t INA226_Write2Byte(uint8_t reg_addr,uint16_t reg_data);
void INA226_Init(void);

#endif

bsp_ina226.c

#include "bflb_gpio.h"
#include "bsp_ina226.h"

struct bflb_device_s *gpio;

#define IIC_SDA_PIN GPIO_PIN_0
#define IIC_SCL_PIN GPIO_PIN_1
#define READ_SDA bflb_gpio_read(gpio, IIC_SDA_PIN)

#define delay_us(val) bflb_mtimer_delay_us(val);
#define delay_ms(val) bflb_mtimer_delay_ms(val);

void SDA_OUT()
{
    bflb_gpio_deinit(gpio, IIC_SDA_PIN);
    bflb_gpio_init(gpio, IIC_SDA_PIN, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
}

void SDA_IN()
{
    bflb_gpio_deinit(gpio, IIC_SDA_PIN);
    bflb_gpio_init(gpio, IIC_SDA_PIN, GPIO_INPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
}

void IIC_SetSDA()
{
    bflb_gpio_set(gpio, IIC_SDA_PIN); // 将pin0设置为高电平
}

void IIC_ResetSDA()
{
    bflb_gpio_reset(gpio, IIC_SDA_PIN); // 将pin0设置为低电平
}

void IIC_setSDAVal(uint8_t val)
{
    if (val)
    {
        bflb_gpio_set(gpio, IIC_SDA_PIN); // 将pin0设置为高电平
    }
    else
    {
        bflb_gpio_reset(gpio, IIC_SDA_PIN); // 将pin0设置为低电平
    }
}

void IIC_SetSCL()
{
    bflb_gpio_set(gpio, IIC_SCL_PIN); // 将pin1设置为高电平
}

void IIC_ResetSCL()
{
    bflb_gpio_reset(gpio, IIC_SCL_PIN); // 将pin1设置为高电平
}


// 初始化IIC
void IIC_Init(void)
{
    gpio = bflb_device_get_by_name("gpio");
    bflb_gpio_init(gpio, IIC_SDA_PIN, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    bflb_gpio_init(gpio, IIC_SCL_PIN, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
}

// 产生IIC起始信号
void IIC_Start(void)
{
    SDA_OUT(); // sda线输出
    IIC_SetSDA();
    IIC_SetSCL();
    delay_us(4);
    IIC_ResetSDA(); // START:when CLK is high, DATA change form high to low
    delay_us(4);
    IIC_ResetSCL(); // 钳住I2C总线,准备发送或接收数据
}
// 产生IIC停止信号
void IIC_Stop(void)
{
    SDA_OUT(); // sda线输出
    IIC_ResetSCL();
    IIC_ResetSDA(); // STOP:when CLK is high DATA change form low to high
    delay_us(4);
    IIC_SetSCL();
    IIC_SetSDA(); // 发送I2C总线结束信号
    delay_us(4);
}
// 等待应答信号到来
// 返回值:1,接收应答失败
//         0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
    uint8_t ucErrTime = 0;
    SDA_IN(); // SDA设置为输入
    IIC_SetSDA();
    delay_us(1);
    IIC_SetSCL();
    delay_us(1);
    while (READ_SDA)
    {
        ucErrTime++;
        if (ucErrTime > 250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_ResetSCL(); // 时钟输出0
    return 0;
}
// 产生ACK应答
void IIC_Ack(void)
{
    IIC_ResetSCL();
    SDA_OUT();
    IIC_ResetSDA();
    delay_us(2);
    IIC_SetSCL();
    delay_us(2);
    IIC_ResetSCL();
}
// 不产生ACK应答
void IIC_NAck(void)
{
    IIC_ResetSCL();
    SDA_OUT();
    IIC_SetSDA();
    delay_us(2);
    IIC_SetSCL();
    delay_us(2);
    IIC_ResetSCL();
}
// IIC发送一个字节
// 返回从机有无应答
// 1,有应答
// 0,无应答
void IIC_Send_Byte(uint8_t txd)
{
    uint8_t t;
    SDA_OUT();
    IIC_ResetSCL(); // 拉低时钟开始数据传输
    for (t = 0; t < 8; t++)
    {
        // IIC_SDA=(txd&0x80)>>7;
        IIC_setSDAVal((txd & 0x80) >> 7);
        txd <<= 1;
        delay_us(2); // 对TEA5767这三个延时都是必须的
        IIC_SetSCL();
        delay_us(2);
        IIC_ResetSCL();
        delay_us(2);
    }
}
// 读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t IIC_Read_Byte(unsigned char ack)
{
    unsigned char i, receive = 0;
    SDA_IN(); // SDA设置为输入
    for (i = 0; i < 8; i++)
    {
        IIC_ResetSCL();
        delay_us(2);
        IIC_SetSCL();
        receive <<= 1;
        if (READ_SDA)
            receive++;
        delay_us(1);
    }
    if (!ack)
        IIC_NAck(); // 发送nACK
    else
        IIC_Ack(); // 发送ACK
    return receive;
}

uint16_t INA226_Read2Byte(uint8_t reg_addr)
{
    uint16_t reg_data = 0;
    uint16_t temp = 0;
    IIC_Start();
    IIC_Send_Byte(WRITE_ADDR);
    if (IIC_Wait_Ack())
        return 0;
    IIC_Send_Byte(reg_addr);
    if (IIC_Wait_Ack())
        return 0;
    IIC_Start();
    IIC_Send_Byte(READ_ADDR);
    if (IIC_Wait_Ack())
        return 0;
    reg_data = IIC_Read_Byte(1);
    reg_data = (reg_data << 8) & 0xFF00;
    temp = IIC_Read_Byte(0);
    IIC_Stop();
    reg_data |= temp;
    return reg_data;
}

uint8_t INA226_Write2Byte(uint8_t reg_addr, uint16_t reg_data)
{
    uint8_t data_high = (uint8_t)((reg_data & 0xFF00) >> 8);
    uint8_t data_low = (uint8_t)reg_data & 0x00FF;
    IIC_Start();
    IIC_Send_Byte(WRITE_ADDR);
    if (IIC_Wait_Ack())
        return 0;
    IIC_Send_Byte(reg_addr);
    if (IIC_Wait_Ack())
        return 0;
    IIC_Send_Byte(data_high);
    if (IIC_Wait_Ack())
        return 0;
    IIC_Send_Byte(data_low);
    if (IIC_Wait_Ack())
        return 0;
    IIC_Stop();
    delay_ms(2);
    return 1;
}

void INA226_Init(void)
{
    IIC_Init();
    INA226_Write2Byte(Config_Reg, 0x4527); // 0100_010_100_100_111 //16次平均,1.1ms,1.1ms,连续测量分流电压和总线电压
    INA226_Write2Byte(Calib_Reg, 0x0A00);
}

烧录代码用逻辑分析仪来看看数据波形,可以从波形上看到INA226和小安派通信正常。 Snipaste_2023-12-11_20-54-41.jpg

!这里再放出来一个通信不正常的图,原因就是输入输出同时设置出现电气干扰的情况下数据错误的情况. Snipaste_2023-12-11_07-14-29.jpg

从串口输出的测量值这时候已经出现: Snipaste_2023-12-11_20-45-42.jpg

至此,我们已经成功的驱动了INA226, 后面讲测量的数据显示在显示器上,并对外形美化美化就完成了。

点个支持再走吧,下期见!

本帖被以下淘专辑推荐:

选择去发光,而不是被照亮
回复

使用道具 举报

爱笑 | 2023-12-12 16:39:38 | 显示全部楼层
优秀~
用心做好保姆工作
回复

使用道具 举报

1084504793 | 2023-12-12 16:46:07 | 显示全部楼层
学到了
回复

使用道具 举报

干簧管 | 2023-12-12 19:03:51 | 显示全部楼层
优秀~
回复

使用道具 举报

iiv | 2023-12-12 19:51:00 | 显示全部楼层
玛丽哥优秀
回复 支持 1 反对 0

使用道具 举报

WangChong | 2023-12-12 20:51:38 | 显示全部楼层
666 玛丽哥
回复

使用道具 举报

liuli | 2023-12-12 22:10:05 | 显示全部楼层
学到了
回复

使用道具 举报

bzhou830 | 2023-12-13 08:39:10 | 显示全部楼层

向七哥学习
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

bzhou830 | 2023-12-13 08:40:46 | 显示全部楼层

一起向七哥学习
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

WT_0213 | 2023-12-13 09:01:43 | 显示全部楼层
学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则