【GD32F427开发板试用】INA226完成电流电压采集

这篇具有很好参考价值的文章主要介绍了【GD32F427开发板试用】INA226完成电流电压采集。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:จุ๊บ冰语

前言

本次有幸参与并通过了极术社区组织的【GD32F427开发板试用】活动,让我对国产兆易创新的GD32处理器有了更深刻的认识。
开发板到手后,先从邮箱中提供的链接https://aijishu.com/a/1060000…下载相关资料,当然我们也可以直接访问兆易创新的官网 资料库:https://www.gd32mcu.com/cn/do…去下载关于GD32F4更加详尽的资料。本次开发板主要使用到的资料为(对资料清单进行了稍详细的说明,希望对其他人有帮助):
表1 资料清单

编号 文件名 说明
1 GD32F427xx Datasheet GD32F427系列MCU数据手册,里面可以看到每个引脚对应的复用功能及复用配置的AFx等信息
2 GD32F4xx系列MCU用户手册 支持GD32F470/GD32F427/GD32F425,这个是一个1000页的庞大pdf文件,对每个内部资源及外设都有详细介绍,包括对应的寄存器等信息
3 GD32F4xx Firmware Library GD32F4xx标准固件库。适用于GD32F4xx系列MCU,与Cortex-M微控制器软件接口标准(CMSIS)兼容。固件库包括程序、数据结构和宏定义,覆盖所有集成外设的特征,并包括了全部相关驱动和示例程序。如果在 GD32F4xx_Demo_Suites 目录下未找到合适的例程代码,可在 GD32F4xx_Firmware_Library\Examples 中查找,总有咱能用到的
4 GD32F4xx Firmware Library User Guide GD32F4xx系列MCU固件库使用指南,大部分的接口都会在这个pdf中说明
5 GD32F4xx系列硬件开发指南 专为基于GD32F4xx系列MCU开发者提供,对GD32F4xx系列产品硬件开发做了总体介绍。提供常用外设接口的电路及设计指导
6 GD32F4xx AddOn 1. GigaDevice.GD32F4xx_Addon.3.0.0.exe Keil4 环境补丁,支持Keil v4.7x; 2. GigaDevice.GD32F4xx_DFP.3.0.0.pack Keil5 在线支持包, 支持 Keil v5.27 及以上版本;我安装的MDK5.34,使用的就是这个pack文件; 3. IAR_GD32F4xx_ADDON.3.0.0.exe IAR 环境补丁,支持 IAR v7.4 以上版本。
7 GD32F4xx_Demo_Suites GD32F4xx系列开发板套件,支持GD32F427V-START、GD32F470V-START、GD32F427R-START、GD32F470i-EVAL和GD32F470Z-EVAL。

其中,咱们到手的开发板原理图就在
GD32F4xx_Demo_Suites_V2.6.1\GD32427R_START_Demo_Suites\Docs\Schematic
路径下,对应的例程在 GD32427R_START_Demo_Suites\Projects 中。 |


设计思路

之前从公众号看到一篇关于1.5V碱性电池充电的文章,再加上家里小孩玩玩具超级费电,这次刚好收到GD32F427开发板,加上之前买的INA226单路电流电压检测模块,就想着做一个数控电池充电工装,当然也可很简单的改为电源数控表头。
首先,放出资料链接:https://mp.weixin.qq.com/s/l4…基础电路集锦,其中第4个电路就是1.5V碱性电池充电电路;

【GD32F427开发板试用】INA226完成电流电压采集

图1 1.5v电池充电电路及分析

这是用LM358制作的一个双路1.5V电池充电器,使用TL431生成2.5V基准,50Ω和100Ω构成分压电路,使LM358的同向端电压为 2.5/150*100=1.666V,当反向端检测的电压达到1.666V时,关断三极管,停止充电。R3100Ω作为限流电阻,保证最大电流不超过60mA。

其实这里还有电池内阻,特别是使用越久电压越低的电池,内阻越大,所以电流不会超过50mA。


硬件设计

1、电路仿真

参考这个电路,我在multisim上也仿真了一个电路,不过我的调流使用的电位器+MOS管实现。

【GD32F427开发板试用】INA226完成电流电压采集

图2 仿真充电电路可行性

2、MOS管实测

实际MOS管选用的FSP740,实测GS电压3.21v开始导通,4.14v可通过500mA电流,MOS管只是轻微发热,如图

【GD32F427开发板试用】INA226完成电流电压采集

【GD32F427开发板试用】INA226完成电流电压采集

图3 MOS管Vgs电压实测: ch1为gs电压电流,ch2是ds电压电流

实际上我们充电时,通过INA226(单路)来采集充电电压电流信息,反馈给gd32,再控制数字电位器调节MOS管Vgs,进而调节电流。够成一个pid反馈环。

3、电路参数分析

设想模块最终可对1.2v镍氢电池,1.5v碱性电池,保证控制mos管的控制电压始终处于大部分都可覆盖到,且能保证控制电压可以关断mos管实现停止充电。实际电路中,图2的R3,R4都使用的微型电位器503,即50k。

先假设充电电池电压1.2v(实际电压约为0.9~1.4v)和1.5v(电池电压约为1.3~1.7v)档,即需要在0.9~1.7v范围内实现控制mos管,电位器(4路10k并联成2.5k)上分压范围为0.9+4.14=5.04v到1.7+3.21=4.91v,实际1.2和1.5v电池充电电流都<100mA,电压取4.2v到5v即可,此时R3电阻分压为4.2v,通过电流为(0.8/2500=0.00032A),R3=4/0.00032=12500欧,R4电阻为12-5=7/0.00032=21875欧,
即: R3: 12.5K, R4: 21.875K

软件设计

好了,硬件分析好了,开始写代码,主要四个部分:
1,INA226驱动,I2C接口;
2,AD8403驱动,spi接口;
3,反馈PID控制,调节充电电流;
4,输出系统。目前是通过usb虚拟串口上报的。

软件基于例程中 cdc_acm (路径为:GD32F4xx_Firmware_Library\Examples\USB\USB_Device\cdc_acm )编写,可以通过USB接口虚拟串口上报数据,便于在上位机上把数据绘制成曲线。

1、INA226驱动代码

在这里向大家推荐一个app,『半导小芯』,可以很方便的查看芯片资料,同类替换,国产化型号等。INA226是具有警报功能的 36V、16 位、超精密 12C 输出电流/电压/功率监控器,IN+/IN-可以通过接不同阻值的采样电阻来采样不同档位的电流,VBUS直接接IN-测量电压为36v,如果加上分压电路可扩展采样电压范围,所以是一个非常适合数控表头的芯片。

【GD32F427开发板试用】INA226完成电流电压采集

【GD32F427开发板试用】INA226完成电流电压采集

图4 INA226模块

我买的模块,将A0/A1都焊接到VCC,此时模块地址为:

【GD32F427开发板试用】INA226完成电流电压采集

地址为 100 0101,即0x45,代码中按这个地址访问I2C即可。

【GD32F427开发板试用】INA226完成电流电压采集

图5  INA226典型电路

和GD32的连接引脚为PB7/PB8,PB7为I2C0_SDA,PB8为I2C0_SCL,并通过2K电阻上拉到3.3V。

【GD32F427开发板试用】INA226完成电流电压采集

软件代码参考https://www.cnblogs.com/zjx12…
.h文件

#define     I2C0_IN226_ADDRESS7         0x45  //A0;A1 -> VCC


#define     CFG_REG             0x00        //
#define     SV_REG              0x01        //分流电压, 此处分流电阻为 0.1欧
#define     BV_REG              0x02        //总线电压
#define     PWR_REG             0x03        //电源功率
#define     CUR_REG             0x04        //电流
#define     CAL_REG             0x05        //校准,设定满量程范围以及电流和功率测数的 
//#define     ONFF_REG          0x06        //屏蔽 使能 警报配置和转换准备就绪
//#define     AL_REG            0x07        //包含与所选警报功能相比较的限定值
#define     INA226_GET_ADDR     0XFF        //包含唯一的芯片标识号

#define     CFG_RESET          (3<<14)

//===使能过压
#define SHUNT_OVER_VOLTAGE_ENABLE                       (1<<15)
//===不使能过压
#define SHUNT_OVER_VOLTAGE_DISABLE                     ~(1<<15)
    //===使能欠压
#define SHUNT_UNDER_VOLTAGE_ENABLE                      (1<<14)
//===不使能欠压
#define SHUNT_UNDER_VOLTAGE_DISABLE                    ~(1<<14)

//===使能过压
#define BUS_OVER_VOLTAGE_ENABLE                         (1<<13)
//===不使能过压
#define BUS_OVER_VOLTAGE_DISABLE                       ~(1<<13)
//===使能欠压
#define BUS_UNDER_VOLTAGE_ENABLE                        (1<<12)
//===不使能欠压
#define BUS_UNDER_VOLTAGE_DISABLE                      ~(1<<12)

//===使能功率阈值
#define POWER_OVER_LIMIT_ENABLE                         (1<<11)
//===不使能功率阈值
#define POWER_OVER_LIMIT_DISABLE                        (1<<11)

//===使能转换准备好标志
#define CONVERSION_READY_ENABLE                         (1<<10)
//===不使能转换准备好标志
#define CONVERSION_READY_DISABLE                        (1<<10)

//===报警端口低有效
#define    ALERT_POLARITY_ACTIVE_LOW                    ~(1<<1)
//===报警端口搞有效
#define    ALERT_POLARITY_ACTIVE_HIGH                    (1<<1)

//===报警端口锁存使能
#define     ALERT_LATCH_ENABLE                            (1)
//===报警端口锁存不使能
#define     ALERT_LATCH_DISABLE                           0xFFFE

//---------------------------------------  Configuration Register (00h) (Read/Write)
//===配置平均转换的次数
#define    AVG_MODE_MASK                        ~(7<<9)
#define    AVG_MODE_1                            (0<<9)
#define    AVG_MODE_4                            (1<<9)
#define    AVG_MODE_16                           (2<<9)
#define    AVG_MODE_64                           (3<<9)
#define    AVG_MODE_128                          (4<<9)
#define    AVG_MODE_256                          (5<<9)
#define    AVG_MODE_512                          (6<<9)
#define    AVG_MODE_1024                         (7<<9)

//===配置总线电压转换的时间
#define     BUS_VOLTAGE_CONVERSIOM_TIME_MASK             ~(7<<6)
#define     BUS_VOLTAGE_CONVERSIOM_TIME_140_US            (0<<6)
#define     BUS_VOLTAGE_CONVERSIOM_TIME_204_US            (1<<6)
#define     BUS_VOLTAGE_CONVERSIOM_TIME_332_US            (2<<6)
#define     BUS_VOLTAGE_CONVERSIOM_TIME_588_US            (3<<6)
#define     BUS_VOLTAGE_CONVERSIOM_TIME_1100_US           (4<<6)
#define     BUS_VOLTAGE_CONVERSIOM_TIME_2116_US           (5<<6)
#define     BUS_VOLTAGE_CONVERSIOM_TIME_4156_US           (6<<6)
#define     BUS_VOLTAGE_CONVERSIOM_TIME_8244_US           (7<<6)

//===配置采样电压转换的时间
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_MASK            ~(7<<3)
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_140_US           (0<<3)
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_204_US           (1<<3)
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_332_US           (2<<3)
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_588_US           (3<<3)
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_1100_US          (4<<3)
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_2116_US          (5<<3)
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_4156_US          (6<<3)
#define     SHUNT_VOLTAGE_CONVERSIOM_TIME_8244_US          (7<<3)

//===配置操作模式
#define     OPERATING_MODE_MASK                           ~(7<<0)
#define     OPERATING_MODE_POWER_DOWN_1                    (0<<0)
#define     OPERATING_MODE_SHUNT_VOLTAGE_TRIG              (1<<0)
#define     OPERATING_MODE_BUS_VOLTAGE_TRIG                (2<<0)
#define     OPERATING_MODE_SHUNT_BUS_VOLTAGE_TRIG          (3<<0)
#define     OPERATING_MODE_POWER_DOWN_2                    (4<<0)
#define     OPERATING_MODE_SHUNT_VOLTAGE_CONT              (5<<0)
#define     OPERATING_MODE_BUS_VOLTAGE_CONT                (6<<0)
#define     OPERATING_MODE_SHUNT_BUS_VOLTAGE_CONT          (7<<0)
//----------------------------------------------------------------------------



//===总线电压量程每BIT对应的电压值,单位是毫伏
#define INA226_RANG_BUS_VOLTAGE_MV_BIT                  1.25f
//===采样电阻上电压量程每BIT对应的电压值,单位是微伏
#define INA226_RANG_SHUNT_VOLTAGE_UV_BIT                2.5f
//===采样电阻的大小,单位是毫欧
#define INA226_SAMPLE_RES_MR                            1
//===INA226的电流最大采集量程,这个和校准寄存器有关
#define INA226_RANG_CURRENT_MA_MAX                      15000
//===INA226的电流量程每BIT对应电流值,单位是微安安
#define INA226_RANG_CURRENT_UA_BIT_X1                   (UINT16_T)( INA226_RANG_CURRENT_MA_MAX*1000/(1<<15))
//===校准寄存器的值
#define INA226_CALIB_REG_DEFAULT_X1                     (UINT16_T)( 5120*1000/(INA226_RANG_CURRENT_UA_BIT_X1*INA226_SAMPLE_RES_MR) )
//===
#define INA226_RANG_CURRENT_UA_BIT_X2                   (UINT16_T)( INA226_RANG_CURRENT_UA_BIT_X1*2 )
//===
#define INA226_CALIB_REG_DEFAULT_X2                     (UINT16_T)( INA226_CALIB_REG_DEFAULT_X1*2 )

//===结构体定义
typedef struct _INA226_HandlerType                      INA226_HandlerType;
//===指针结构体定义
typedef struct _INA226_HandlerType                    * pINA226_HandlerType;


//IO操作
#define     I2C_SCK                        GPIO_PIN_8 //PC13
#define     I2C_SDA                        GPIO_PIN_7 //PC2 //RES

#define IIC_SCL_H       gpio_bit_set(  GPIOB, I2C_SCK) //SCL H
#define IIC_SCL_L       gpio_bit_reset(GPIOB, I2C_SCK) //SCL H

#define IIC_SDA_H       gpio_bit_set(  GPIOB, I2C_SDA) //SDA_H
#define IIC_SDA_L       gpio_bit_reset(GPIOB, I2C_SDA) //SDA_L

#define READ_SDA        gpio_input_bit_get(GPIOB, I2C_SDA)  //输入SDA
#define SDA_READ        gpio_mode_set( GPIOB, GPIO_MODE_INPUT,  GPIO_PUPD_NONE, I2C_SDA );
#define SDA_WRITE       gpio_mode_set( GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SDA );




void        INA226_Init(void);

void        INA226_SendData(uint8_t addr,uint8_t reg,uint16_t data);
uint16_t     INA226_ReadData(uint8_t addr);

void        INA226_SetRegPointer(uint8_t addr,uint8_t reg);
long        INA226_GetShunt_Current(uint8_t addr);
uint16_t    INA226_Get_ID(uint8_t addr);
uint16_t    INA226_GET_CAL_REG(uint8_t addr);
uint32_t    INA226_GetBusVoltage(uint8_t addr);
uint32_t    INA226_GetShuntVoltage(uint8_t addr);
uint16_t    INA226_Get_Power(uint8_t addr);
void        Get_Shunt_voltage(float *Voltage);
void        Get_Shunt_Current(float *Current);

.c文件

void INA226_IIC_Init(void)
{
    rcu_periph_clock_enable(RCU_GPIOB);
    
    /* set SPI0_CS as GPIO*/
    gpio_mode_set(          GPIOB, GPIO_MODE_OUTPUT,    GPIO_PUPD_PULLUP,   I2C_SCK | I2C_SDA );
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP,       GPIO_OSPEED_50MHZ,  I2C_SCK | I2C_SDA );
        
    IIC_SDA_H;
    IIC_SCL_H;  
    
    delay_nms(5);
}
  
void INA226_Init(void)
{
    INA226_IIC_Init();
    delay_nms(10);
//    INA226_SendData(INA226_ADDR1, CFG_REG, 0xC000);

//    HAL_Delay(10);
    //uint16_t cfg_reg_value = 0x4000|(3<<9)|(4<<6)|(4<<3)|7;
//    INA226_SendData(INA226_ADDR1, CFG_REG, 0x484f);
//    INA226_SendData(INA226_ADDR1, CAL_REG, 0x0012);
    INA226_SendData(INA226_ADDR1, CFG_REG, 
         AVG_MODE_64 | BUS_VOLTAGE_CONVERSIOM_TIME_2116_US | OPERATING_MODE_SHUNT_BUS_VOLTAGE_CONT );
    INA226_SendData(INA226_ADDR1, CAL_REG, 0x800);//A00
}

void INA226_SendData(uint8_t addr, uint8_t reg, uint16_t data)
{
    uint8_t temp = 0;
    INA226_IIC_Start();
    INA226_IIC_Send_Byte(addr);
    INA226_IIC_Wait_Ack();
 
    INA226_IIC_Send_Byte(reg);
    INA226_IIC_Wait_Ack();
    
    temp = (uint8_t)(data>>8);
    INA226_IIC_Send_Byte(temp);
    INA226_IIC_Wait_Ack();
 
    temp = (uint8_t)(data&0x00FF);
    INA226_IIC_Send_Byte(temp);
    INA226_IIC_Wait_Ack();
    
    INA226_IIC_Stop();
}


void INA226_SetRegPointer(uint8_t addr,uint8_t reg)
{
    INA226_IIC_Start();
 
    INA226_IIC_Send_Byte(addr);
    INA226_IIC_Wait_Ack();
 
    INA226_IIC_Send_Byte(reg);
    INA226_IIC_Wait_Ack();
 
    INA226_IIC_Stop();
}


uint16_t INA226_ReadData(uint8_t addr)
{
    uint16_t temp=0;
    INA226_IIC_Start();
 
    INA226_IIC_Send_Byte(addr+1);
    INA226_IIC_Wait_Ack();
    
    temp = INA226_IIC_Read_Byte(1);
    temp<<=8;    
    temp |= INA226_IIC_Read_Byte(0);
    
    INA226_IIC_Stop();
    return temp;
}





//-------------------------------------------------------------
//获取电流
long INA226_GetShunt_Current(uint8_t addr)
{
    long temp=0;    
    INA226_SetRegPointer(addr, CUR_REG);
    temp = INA226_ReadData(addr);
    if(temp&0x8000)    
    {
        temp = ~(temp - 1);    
        temp = (uint16_t)temp - 2 * (uint16_t)temp;
    }
    return temp;
}
 
//获取 id
uint16_t  INA226_Get_ID(uint8_t addr)
{
    uint32_t temp=0;
    INA226_SetRegPointer(addr, INA226_GET_ADDR);
    temp = INA226_ReadData(addr);
    return (uint16_t)temp;
}
 
//获取、设置 校准值  Calibration Register (05h) (Read/Write
//
uint16_t INA226_GET_CAL_REG(uint8_t addr)
{    
    uint32_t temp=0;
    INA226_SetRegPointer(addr, CAL_REG);
    temp = INA226_ReadData(addr);
    return (uint16_t)temp;
}
 
//1.25mV/bit
//Full-scale range = 40.96 V (decimal = 7FFF); LSB = 1.25 mV.
uint32_t INA226_GetBusVoltage(uint8_t addr)
{
    uint32_t temp=0;
    INA226_SetRegPointer(addr, BV_REG);
    temp = INA226_ReadData(addr);
    return (uint32_t)temp;
}
 
 
 
//2.5uV/bit,感觉这个值是测不准的,所以下面改成2.2了
uint32_t INA226_GetShuntVoltage(uint8_t addr)
{
    uint32_t temp=0;
    INA226_SetRegPointer(addr, SV_REG);
    temp = INA226_ReadData(addr);
    if(temp&0x8000)    //负数判断
        temp = ~(temp - 1);    
    return (uint32_t)temp;    
}
 
 
uint16_t INA226_Get_Power(uint8_t addr)
{
    int16_t temp=0;
    INA226_SetRegPointer(addr, PWR_REG);
    temp = INA226_ReadData(addr);
    return (uint16_t)temp;
}
 
 
void GetVoltage(float *Voltage)//mV
{
    Voltage[0] = INA226_GetBusVoltage(INA226_ADDR1)*INA226_RANG_BUS_VOLTAGE_MV_BIT;
}
 
 
void Get_Shunt_voltage(float *Voltage)//uV
{
    Voltage[0] = (INA226_GetShuntVoltage(INA226_ADDR1)*INA226_RANG_SHUNT_VOLTAGE_UV_BIT);//这里原来乘的系数是2.5
}
 
 
void Get_Shunt_Current(float *Current)//mA
{
    Current[0] = (INA226_GetShunt_Current(INA226_ADDR1)* 2.5f);
}
 
 
void GetPower()//W
{
    GetVoltage(&INA226_data.voltageVal);                //mV
    Get_Shunt_voltage(&INA226_data.Shunt_voltage);      //uV
    Get_Shunt_Current(&INA226_data.Shunt_Current);      //mA
    
    INA226_data.powerVal=INA226_data.voltageVal*0.001f * INA226_data.Shunt_Current*0.001f;
}

2、AD8403 4路10K,256级数字电位器

【GD32F427开发板试用】INA226完成电流电压采集

图6 AD8403ARU引脚图

和GD32相连的引脚为:CS:PB12;CLK:PB13;SDI:PB15;SDO:PB14;SHDN上拉;RS上拉或接GD32的RESET引脚。

/***********************************************************************************
函 数 名: Write_AD8403
功    能:
参    数:channal - AD8403通道
          send_data - 写入值
返    回: 无
**********************************************************************************/
void Write_AD8403( uint8 channal, uint8 send_data)    
{   
  SPI_cs_low(); 
  
    BSP_SPI_Write(channal);
    BSP_SPI_Write(send_data);
 
    SPI_cs_high();
}

3、反馈调节

采集端:INA226,构成电流、电压采样输入;
控制端:4路数字电位器驱动MOS管;
以其中一路来说,控制环路如下:

【GD32F427开发板试用】INA226完成电流电压采集

图7 反馈环路示意图

这里电压电流不会跳变,所以采样频率不用太快,保证电流电压不会过调即可。我是用的一个100ms的定时器处理,数据上报也是在此处理的。
PID三个系数,一般简单系统使用PI即可,P即比例系数,控制每次变化量的大小,I是积分系数,修正稳态误差。此处P不能太大,太大容易引起振荡,在此就是电流忽大忽小,我们能容忍电流缓升,但是不敢过流(过流会引起电池过热,甚至爆炸),基于这些分析,暂时设置参数为: P= 1;I=0.1; (此处后期还要慢慢优化,找最合适的系数)
代码为:

.h文件

#define   MAX_AD8403    255

//模式枚舉
enum PID_MODE
{
    PID_POSITION = 0,
    PID_DELTA
};

typedef struct
{
    //PID運算模式
    uint8_t mode;
    //PID 三個基本參數
    float Kp;
    float Ki;
    float Kd;
    float max_out; //PID最大輸出
    float max_iout; //PID最大積分輸出
    float set; //PID目標值
    float fdb; //PID當前值
    float out; //三項疊加輸出
    float Pout; //比例項輸出
    float Iout; //積分項輸出
    float Dout; //微分項輸出
    //微分項最近三個值 0最新 1上一次 2上上次
    float Dbuf[3];
    //誤差項最近三個值 0最新 1上一次 2上上次
    float error[3];
} pid_type_def;


extern  float         g_setTemp;
extern  pid_type_def  stPID;
extern  float         kPID[3] ;
extern  int           g_pwm_val;


void  PID_init(pid_type_def *pid, uint8_t mode, const float PID[3], float max_out, float max_iout);
float PID_calc(pid_type_def *pid, float ref, float set);
void  PID_clear(pid_type_def *pid);
float LimitMax(float input, float max);


.c文件
void Timer_config(void)
{
    timer_parameter_struct timer_initpara;

    rcu_periph_clock_enable(RCU_TIMER1);
    
    
    rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
    timer_deinit(TIMER1);

    //当 prescaler = 9999; period = 16799;  CPU频率168MHz,每次定时器中断1s(10000*16800 = 168,000,000)
    //当 prescaler = 99; period = 1679;  CPU频率168MHz,每次定时器中断1ms(1000*1680 = 168,000)
    /* TIMER1 configuration */
    timer_initpara.prescaler         = 999;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 167999;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER1, &timer_initpara);


    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER1);
    /* clear channel 0 interrupt bit */
    timer_interrupt_flag_clear(TIMER1, TIMER_INT_CH3);
    /* channel 0 interrupt enable */
    timer_interrupt_enable(TIMER1,TIMER_INT_CH3);
    
    /* auto-reload preload enable */
    timer_enable(TIMER1);
    
    nvic_irq_enable(TIMER1_IRQn, 1, 1);
}

void TIMER1_IRQHandler(void)
{
    float   current1= 0; 
     float   pidRes1 = 0;
     static  int  Res_val=0;
  
    //每个定时器中断 10ms
    if(SET == timer_interrupt_flag_get( TIMER1,TIMER_INT_CH3)){
        /* clear channel 0 interrupt bit */
        timer_interrupt_flag_clear( TIMER1,TIMER_INT_CH3);
            
    Get_Shunt_Current( &current1 );      
    pidRes1 = PID_calc(  &stPID, temperature, g_setTemp );
    
    Res_val = pidRes1 ;
    
    if( Res_val> MAX_AD8403)  Res_val= MAX_AD8403;
    if( Res_val< 0 )  Res_val= 0;
    
Write_AD8403( Ch1, Res_val );

    Usb_printf("curr:%0.1f,Res:%d\n", temperature, Res_val );
  }
}

//參數    功能
//    *pid    傳入要初始化的PID結構體指針
//    mode    PID運行的模式,增量式還是位置式PID,此處我們定義一個枚舉變量用於設置模式
//    PID[3]    傳入一個數組,用於作為三個基本參數P、I、D的初始值
//    max_out        PID總輸出的限幅,防止整體輸出過大,傳入一個正數,限制範圍為[-max_out,+max_out]
//    max_iout    積分項輸出的限幅,因為系統剛啟動時與目標誤差較大,累計誤差計算輸出會很大,影響系統穩定性,
//                所以對累計誤差進行限幅,傳入一個正數,限制範圍為[-max_iout,+max_iout]                 
void PID_init(pid_type_def *pid, uint8_t mode, const float PID[3], float max_out, float max_iout)
{
    if (pid == NULL || PID == NULL){
        return;
    }
    pid->mode = mode;
    pid->Kp = PID[0];
    pid->Ki = PID[1];
    pid->Kd = PID[2];
    pid->max_out = max_out;
    pid->max_iout = max_iout;
    pid->Dbuf[0] = pid->Dbuf[1] = pid->Dbuf[2] = 0.0f;
    pid->error[0] = pid->error[1] = pid->error[2] = pid->Pout = pid->Iout = pid->Dout = pid->out = 0.0f;
}

float LimitMax( float input, float  max) 
{
  if (input > max) { 
    return max; 
  } else if (input < -max){ 
    return -max; 
  }else{
    return input; 
  } 
}


float PID_calc(pid_type_def *pid, float ref, float set)
{
    //判斷傳入的PID指針不為空
    if (pid == NULL)    {
        return 0.0f;
    }
    //存放過去兩次計算的誤差值
    pid->error[2] = pid->error[1];
    pid->error[1] = pid->error[0];
    //設定目標值和當前值到結構體成員
    pid->set = set;
    pid->fdb = ref;
    //計算最新的誤差值
    pid->error[0] = set - ref;
    
    //判斷PID設置的模式
    if (pid->mode == PID_POSITION)
    {
        //位置式PID
        //比例項計算輸出
        pid->Pout = pid->Kp * pid->error[0];
        //積分項計算輸出
        pid->Iout += pid->Ki * pid->error[0];
        //存放過去兩次計算的微分誤差值
        pid->Dbuf[2] = pid->Dbuf[1];
        pid->Dbuf[1] = pid->Dbuf[0];
        //當前誤差的微分用本次誤差减去上一次誤差來計算
        pid->Dbuf[0] = (pid->error[0] - pid->error[1]);
        //微分項輸出
        pid->Dout = pid->Kd * pid->Dbuf[0];
        
        //對積分項進行限幅        
        if(pid->Iout > 0)
          pid->Iout = LimitMax(pid->Iout, pid->max_iout);        
        if(pid->Iout > 0)
          pid->Iout = LimitMax(pid->Iout, pid->max_iout*-1);
        
        //疊加三個輸出到總輸出
        pid->out        =       pid->Pout + pid->Iout + pid->Dout;
        
        //對總輸出進行限幅
        if(pid->out > 0)
          pid->out = LimitMax(pid->out, pid->max_out);
        
        if(pid->out < 0)
          pid->out = LimitMax(pid->out, pid->max_out*-1);
    }    
    else if (pid->mode == PID_DELTA)   {
        //增量式PID
        //以本次誤差與上次誤差的差值作為比例項的輸入帶入計算
        pid->Pout = pid->Kp * (pid->error[0] - pid->error[1]);
        //以本次誤差作為積分項帶入計算
        pid->Iout = pid->Ki * pid->error[0];
        //迭代微分項的數組
        pid->Dbuf[2] = pid->Dbuf[1];
        pid->Dbuf[1] = pid->Dbuf[0];
        //以本次誤差與上次誤差的差值减去上次誤差與上上次誤差的差值作為微分項的輸入帶入計算
        pid->Dbuf[0] = (pid->error[0] - 2.0f * pid->error[1] + pid->error[2]);
        pid->Dout = pid->Kd * pid->Dbuf[0];
        
        //疊加三個項的輸出作為總輸出
        pid->out        +=      pid->Pout + pid->Iout + pid->Dout;
        
        //對總輸出做一個先限幅
        if(pid->out > 0)
          pid->out = LimitMax(pid->out, pid->max_out);
        
        if(pid->out < 0)
          pid->out = LimitMax(pid->out, pid->max_out*-1);
    }
    return pid->out;
}

void PID_clear(pid_type_def *pid)
{
    if (pid == NULL){
        return;
    }
    //當前誤差清零
    pid->error[0] = pid->error[1] = pid->error[2] = 0.0f;
    //微分項清零
    pid->Dbuf[0] = pid->Dbuf[1] = pid->Dbuf[2] = 0.0f;
    //輸出清零
    pid->out = pid->Pout = pid->Iout = pid->Dout = 0.0f;
    //目標值和當前值清零
    pid->fdb = pid->set = 0.0f;
}



main函数中完成PID初始化

pid_type_def  stPID;
float  kPID[3] = {
  1,
  0.1,
  0.0
};

  PID_init( &stPID, PID_DELTA, kPID, MAX_AD8403, MAX_AD8403);//PID_DELTA

4、数据上报

在定时器中上报电压、电流,1S上报一次。 放上 usb_printf 函数的代码。

//usb虚拟串口,printf 函数
//确保一次发送数据不超USB_USART_REC_LEN字节
void usb_printf(char* fmt,...)  
{      
    uint16_t i,j;
    
    va_list ap;
    va_start(ap,fmt);
    vsprintf((char*)USART_PRINTF_Buffer, fmt, ap);
    va_end(ap);
    i=strlen((const char*)USART_PRINTF_Buffer);//此次发送数据的长度
    
    //发送数据
    usbd_ep_send( &cdc_acm, CDC_DATA_IN_EP, USART_PRINTF_Buffer, i );
}

实物展示

最终实物如下,请不要嫌弃拙劣的焊接技巧,毕竟咱就是个程序猿_

【GD32F427开发板试用】INA226完成电流电压采集

【GD32F427开发板试用】INA226完成电流电压采集文章来源地址https://www.toymoban.com/news/detail-433797.html

到了这里,关于【GD32F427开发板试用】INA226完成电流电压采集的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GD32F303固件库开发

    芯片和开发板都可免费申请,暂时只能以公司名义免费申请,填下下面表格即可。 https://www.wjx.top/vm/wFGhGPF.aspx 使用GDLINK、jlink、串口下载程序到GD芯片。 https://blog.csdn.net/qq_24312945/article/details/124325797 芯片读保护以后,flash将不可以从外部读取,这样可以防止别人读取或者盗取

    2023年04月17日
    浏览(45)
  • 调试记录 单片机GD32F103C8T6(兆易创新) 程序烧写完成但是没有现象 (自己做的板子)

    CPU内核:ARM  Cortex-M3 CPU最大主频:108MHz 工作电压范围:2.6V~3.6V 程序存储容量:64KB 程序存储器类型:FLASH RAM, 总容量:20KB GPIO端口数量:37 最高主频可达108MHz, 资料链接:1473490296871.pdf (szlcsc.com) 1.  烧写最简单的 跑马灯程序,但是板子上的LED灯没有反应。 2.  检查单片机

    2024年02月06日
    浏览(45)
  • GD32F303高级定时器输出互补PWM-开发笔记

    ◼ 总通道数:4; ◼ 计数器宽度:16位; ◼ 时钟源可选:内部时钟,内部触发,外部输入,外部触发; ◼ 多种计数模式:向上计数,向下计数和中央计数; ◼ 正交编码器接口:被用来追踪运动和分辨旋转方向和位置; ◼ 霍尔传感器接口:用来做三相电机控制; ◼ 可编程

    2024年02月09日
    浏览(61)
  • 单片机GD32F303RCT6 (Macos环境)开发 (二十)—— 光感芯片veml7700的使用

    1、veml有7个寄存器,每个十六位,见图。 00是config寄存器, 01 02 是中断设置的阈值 03是节能模式的设置 04 是得到的光的亮度值 05是得到的data of whole WHITE 06是中断设置值。 2、我们只测试得到光的亮度值,所以veml寄存器设置如下: 设置gain,integration time ,power save mode ,interrup

    2024年02月04日
    浏览(62)
  • [GD32F4]基于GD32固件库移植cherryusb[STM32F4]

    [GD32F4]基于GD32固件库移植cherryusb[STM32F4] 使用开发板是淘宝买的不知名开发板,没什么好说的,具体的型号是GD32F450VET6。 使用的cherryusb版本是0.9.0版本。 使用的GD32官方固件库版本是:GD32F4xx_Firmware_Library_V3.0.4 cherryusb最牛的地方在于抛弃掉所有的依赖,只需要知道芯片的usb中断

    2024年02月06日
    浏览(49)
  • GD32F4移植STM32F4

    近期在项目中采用了GD32F407VET6替换原项目中的STM32F407VET6,网传GD的兼容性很好,之前也用F1系统的替换了一下,按照CSND各位大佬的经验一步步改进了代码,测试直接通过,现在也一直在项目中实际应用了,一直没有出问题。 所以这SMT时,嘉立创没有STM的货果断换成了GD,可换时

    2024年02月16日
    浏览(77)
  • GD32F4(9):GD32f4出现上电不工作,必须按复位程序才能跑起来

    绘制一个gd32450的pcb板子,结果烧录程序后发生下面事情: 上电程序不能正常启动或者偶尔可以正常启动一次,很随机。 当上电后程序不启动的时候,我再按一下mcu的reset按键,程序就能正常启动了。 当我debug调试的时候,回回都能正常启动,根本定位不到问题 首先在板子里

    2023年04月08日
    浏览(63)
  • 【硬件设计】INA282电流采集电路

    电路原理图和PCB提取方式(立创EDA文件): 关注微信公众号:码上芯路人 私信:硬件设计 电流采集电路是一种用于检测电路中电流的电路。它通常由电流传感器、信号调理电路和模数转换器(ADC)组成。其主要功能是将输入电流转换为电压信号,并对信号进行放大、滤波和

    2024年02月12日
    浏览(89)
  • GD32F103输入捕获

    GD32F103输入捕获程序,经过多次测试,终于完成了。本程序将TIMER2_CH2通道映射到PB0引脚,捕获PB0引脚低电平脉冲时间宽度。PB0是一个按钮,第1次按下采集一个值保存到TIMER2_CountValue1中,第2次按下采集一个值保存到TIMER2_CountValue2中,然后计算其低电平时间宽度。 网上也有人写

    2024年02月14日
    浏览(41)
  • 【GD32F303】星空派介绍

    一、开发板介绍 星空派(GD)开发板是由旗点科技推出的一款GD32开发板,板载GD32F303ZET6芯片,可直接替代STM32F103和GD32F103系列。 支持RT-Thread操作系统等,支持WiFi、4G、loRa等物联通信接口。板载Flash、eeprom等,支持3.2寸的TFT - LCD屏幕。所有IO口均引出,可完整地进行外设开发,

    2024年02月16日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包