1、介绍
最近准备搞电源,刚好可以拿芯源的开发板做测试用。
2、硬件准备
使用官方提供的CW32F003ExPx StartKit 评估板
• CW32F003ExPx 微控制器(ARM® Cortex®-M0+ 最高主频 48MHz),TSSOP24 封装,20K 字节 FLASH,3K 字节 RAM
• 3 颗 LED:
- 电源指示灯(LED3),用户指示灯(LED1,LED2)
• 三个轻触开关:
- 复位轻触开关(S3),用户轻触开关(S1,S2)
• USB 转串口芯片(CH340N)
• FLASH 芯片(W25Q64JVSSIQ)
• EEPROM 芯片(CW24C02AD)
• 板载接口:
- Mini USB 接口(串口通信,USB 供电)
- 下载器调试接口
- 所有 GPIO 口通过排针引出
• 多种方式供电:USB VBUS 供电,3.3V 供电(LD1117AS33TR),外接 1.65V ~ 5.5V 供电
采用INA226模块作为功率计采集传感器
该 INA226 最高失调电压为 10 微伏 (uV),最大增益误差为 0.1%,精度比业界领先竞争产品高 10 倍。该器件是一款完整的 16 位单芯片解决方案,能够为数字 I2C 接口的电流、电压及电源提供全面可编程测量。INA226 可为服务器、电信设备、计算机、电源管理设备以及测试设备的设计人员带来极大优势,能够充分满足其对精确电源测量的需求。
主要特性与优势
• 具有 I2C 接口的单芯片解决方案支持电流、电压以及电源测量,无需外部多路复用器或 ADC 即可简化电路板设计,应对布局局限性;
• 业界最高精度,具有 10 uV 最大失调电压与 0.1% 最大增益误差,不但可在低电流下实现更高精度,而且还可使用更小的分流电阻器,从而可降低 I*R 损耗;
• 独立可编程转换时间与采样平均技术不但可简化每个系统的速度需求定制,而且还可降低对软件及存储器的需求;
• 420 uA 最大静态电流与 2 uA 最大关断电流支持高效工作,即便 INA226 集成 ADC 与电源多路复用器,功耗也比同类竞争产品的分立式解决方案低 3.5%;
• 140 dB 共模抑制比 (CMRR) 与 36 V 共模电压 (CMV) 可在整个工作范围内确保变化极小或根本无变化的失调电压,从而可简化误差分析。
将开发板和传感器进行连接
3、程序设计
本来准备采用硬件IIC,但是无奈硬件IIC和INA226通信一直未成功,所以最终移植了一个软件IIC。软件IIC基本代码如下:
//delay
static void delay_nns(uint16_t D) //30纳秒ns 根据手册要用到IIC的HS高速模式
{
uint16_t i;
uint32_t M = 0;//720W
for(i = 0;i < D; i++)
for(M=10;M > 0;M--);
}
void delay_nms(uint16_t ms) //毫秒
{
uint16_t i;
uint32_t M = 0;//720W
for(i = 0;i < ms; i++)
for(M=120000;M > 0;M--);
}
void delay_nus(uint16_t us)//微秒
{
uint16_t i;
uint16_t M = 0;//720W
for(i = 0;i < us; i++)
for(M=720;M > 0;M--);
}
//IIC
/****************IIC***************************/
void INA226_SDA_Set_Output()
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pins = INA_SDA_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_Init(INA_SDA_GPIO_PORT, &GPIO_InitStructure);
}
void INA226_SDA_Set_Input()
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pins = INA_SDA_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
GPIO_Init(INA_SDA_GPIO_PORT, &GPIO_InitStructure);
}
void INA226_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pins = INA_SDA_PIN | INA_SCL_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_Init(INA_SDA_GPIO_PORT, &GPIO_InitStructure);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_SET);
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_SET);
delay_nms(5);
}
void INA226_IIC_Start(void)
{
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_SET);
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_SET);
delay_nns(5);
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_RESET);//START:when CLK is high,DATA change form high to low
delay_nns(5);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_RESET);//钳住I2C总线,准备发送或接收数据
delay_nns(5);
}
void INA226_IIC_Stop(void)
{
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_RESET);//STOP:when CLK is high DATA change form low to high
delay_nns(5);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_SET);
delay_nns(5);
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_SET);//发送I2C总线结束信号
delay_nns(5);
}
void INA226_IIC_Ack(void)
{
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_RESET);
delay_nns(5);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_SET);
delay_nns(5);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_RESET);;
delay_nns(5);
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_SET);
}
void INA226_IIC_NAck(void)
{
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_SET);
delay_nns(5);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_SET);
delay_nns(5);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_RESET);;
delay_nns(5);
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_RESET);
}
uint8_t INA226_IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_SET);
delay_nns(5);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_SET);
delay_nns(5);
INA226_SDA_Set_Input();
while(GPIO_ReadPin(INA_SDA_GPIO_PORT, INA_SDA_PIN))
{
ucErrTime++;
if(ucErrTime>250)
{
INA226_IIC_Stop();
return 1;
}
}
INA226_SDA_Set_Output();
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_RESET);//时钟输出0
return 0;
}
void INA226_IIC_Send_Byte(uint8_t txd)
{
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_RESET);;//拉低时钟开始数据传输
for(uint8_t i = 0;i < 8;i++)
{
if(txd&0x80)
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_SET);
else
GPIO_WritePin(INA_SDA_GPIO_PORT, INA_SDA_PIN, GPIO_Pin_RESET);
txd<<=1;
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_SET);
delay_nns(5);
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_RESET);
delay_nns(5);
}
}
uint8_t INA226_IIC_Read_Byte(unsigned char ack)
{
uint8_t TData=0,i;
INA226_SDA_Set_Input();
for(i=0;i<8;i++)
{
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_SET);
delay_nns(5);
TData=TData<<1;
if(GPIO_ReadPin(INA_SDA_GPIO_PORT, INA_SDA_PIN))
{
TData|=0x01;
}
GPIO_WritePin(INA_SCL_GPIO_PORT, INA_SCL_PIN, GPIO_Pin_RESET);;
delay_nns(5);
}
INA226_SDA_Set_Output();
if(!ack)
INA226_IIC_NAck();
else
INA226_IIC_Ack();
return TData;
}
接下来就是INA226的一些配置,配置采集电流和电压,通过电流和电压算出功率。
void INA226_Init(void)
{
INA226_IIC_Init();
delay_nms(10);
INA226_SendData(WRITE_ADDR,0x00,0x4727);//初始化结果采用64次平均值,全功能,连续模式
INA226_SendData(WRITE_ADDR,0x05,0xE800);//(当单位为mA时:0.002R=0xA00;0.01R=0x200);(上限8A)
INA226_Get_ID(WRITE_ADDR);
}
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;
}
uint16_t INA226_GetShuntCurrent(uint8_t addr)
{
uint16_t temp=0;
INA226_SetRegPointer(addr,Current_Reg);
temp = INA226_ReadData(addr);
// if(temp&0x8000) temp = ~(temp - 1);//保留符号留给读数函数处理
return temp;
}
//获取 id
uint16_t INA226_Get_ID(uint8_t addr)
{
uint16_t temp=0;
INA226_SetRegPointer(addr,ID_Reg);
temp = INA226_ReadData(addr);
return (uint16_t)temp;
}
//获取校准值
uint16_t INA226_GET_CAL_REG(uint8_t addr)
{
uint16_t temp=0;
INA226_SetRegPointer(addr,Calib_Reg);
temp = INA226_ReadData(addr);
return temp;
}
//1.25mV/bit
uint16_t INA226_GetVoltage(uint8_t addr)
{
uint16_t temp=0;
INA226_SetRegPointer(addr,Bus_V_Reg);
temp = INA226_ReadData(addr);
return temp;
}
//2.5uV/bit
uint16_t INA226_GetShuntVoltage(uint8_t addr)
{
uint16_t temp=0;
INA226_SetRegPointer(addr,Shunt_V_Reg);
temp = INA226_ReadData(addr);
if(temp&0x8000) temp = ~(temp - 1);
return temp;
}
//2.5mW/bit
uint16_t INA226_Get_Power(uint8_t addr)
{
uint16_t temp=0;
INA226_SetRegPointer(addr,Power_Reg);
temp = INA226_ReadData(addr);
return temp;
}
配置对于的参数,设置电流电压的系数,然后将数据通过串口发送给上位机
其中控制板载LED亮灭,这样可以采集出不同的功率。
while(1)
{
voltage_value = INA226_GetVoltage(WRITE_ADDR)*1.25f*0.001f;
current_value = INA226_GetShuntCurrent(WRITE_ADDR)*0.002f;
power_value = voltage_value * current_value;
power_total_value += power_value / 3600;
sprintf(uart_send, "Current:%.2fmA, Volatage:%.2fV, Power:%.2fmW, PowerTotal:%.3fmW/h\r\n", current_value, voltage_value, current_value * voltage_value, power_total_value);
USART_SendBuf_Polling(DEBUG_USARTx, (uint8_t *)uart_send, strlen(uart_send));
FirmwareDelay(1000000);
GPIO_TogglePin(CW_GPIOA, GPIO_PIN_7);
GPIO_TogglePin(CW_GPIOA, GPIO_PIN_6);
}
4、上机演示
将开发板串口接入PC,打开串口调试助手,可以看到能一直收到开发板的电源状态信息。
视频演示:
|
|