前言
机缘巧合之下接触了这两款三相电力计量芯片,在使用之初并没有太多的思考他们的后缀CU和EU,自以为和别的芯片一样只是后缀不同可以互相替代。在后来不断的查找资料翻阅数据手册才发现这两款芯片的引脚定义有差异,供电电压也有差异,网络上能够找到的与之相关的资料也是很少,故作此篇与众君共享。
一、两款芯片的异同
1.相同点
两款芯片都可以用于三相三线或三相四线电能测量(包括电流、电压、功率、功率因数等),都使用SPI和外部MCU进行通讯,封装都是LQFP44。
2.不同点
ATT7022EU的芯片供电电压是3.3V,ATT7022CU的芯片供电电压是5V,这会导致SPI通讯时需要注意到电平转换的问题,例如外部MCU的SPI电平是3.3V,那么他和ATT7022EU通讯是没有问题的,但是用他和ATT7022CU进行SPI通讯那么就会出现问题了,此时就需要用到一个电平转换装置将电平统一。
其次EU需求的是5.5296MHz的晶振,SPI通讯速率最大为10Mbps,而CU需求的是24.576MHz的晶振,晶振使用不当也会导致SPI通讯失败。
最后他们的引脚号也是有些不同的,主要集中在 11、21、22、30、31、32、33号引脚。
图1. ATT7022EU引脚封装图
图2. ATT7022CU引脚封装图
二、实际电路应用
1.参考设计
这里主要用到了STM32F103C8T6最小系统板和ATT7022CU做的这个三相电能计量模块。
2.读程序设计
#include "ATT7022.h"
#include "delay.h"
#include "stdio.h"
#include "stm32f10x_spi.h"
static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT;
void ATT7022C_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2 , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // PB12 推挽
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_12);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //设置为软件模式有什么作用?NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
}
void ATT7022Reset_Hard(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_8);
Delay_ms(20);
GPIO_SetBits(GPIOA,GPIO_Pin_8);
Delay_ms(20);
}
/*******************************************************************************************************
** 函数: SPI2_ReadWrite, SPI发送一个字节,同时接收一个字节
**------------------------------------------------------------------------------------------------------
** 参数: dat 发送数据
** 返回: 接收数据
*******************************************************************************************************/
unsigned char SPI2_ReadWrite(const unsigned char dat)
{
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
{
if ((SPITimeout--) == 0)
return 0;
}
SPI2->DR = dat;
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
{
if ((SPITimeout--) == 0)
return 1;
}
return SPI2->DR;
}
/*------------------------------------ATT7022---------------------------------------*/
// ATT7022校准数据
static Energy_t s_tEmt = {0};
struct ReviseTypeDef RTS ={0};
// ATT7022校准数据
static Revise_t s_tRts ={0xDC,{1.51,1.51,1.67},{33.5,34.3,34.3}};
/*******************************************************************************************************
** 函数: Att7022GpioInit, ATT7022 GPIO初始化
**------------------------------------------------------------------------------------------------------
** 参数: NULL
** 返回: NULL
********************************************************************************************************/
void ATT7022GpioInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//------------------------------------------------------------------------------------------------
// 配置IO和时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 ; // RESET复位
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ; // CS使能
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/*******************************************************************************************************
** 函数: ReadData_ATT7022, 读参数寄存器
**------------------------------------------------------------------------------------------------------
** 参数: addr:被读数据的地址(最高位为0)
** 返回: temp:读出数据(24位)
********************************************************************************************************/
unsigned long AttReadData(unsigned char addr)
{
unsigned long temp=0;
ATTCS=0;
ATT_SPI_ReadWrite(addr&0x7f);
Delay_us(20); // SPI时钟频率过高,读数据时需要等待2us
temp=(temp<<8)|ATT_SPI_ReadWrite(0);
temp=(temp<<8)|ATT_SPI_ReadWrite(0);
temp=(temp<<8)|ATT_SPI_ReadWrite(0);
ATTCS=1;
Delay_us(20);
return temp;
}
/*******************************************************************************************************
** 函数: WriteData_ATT7022, 写入命令或校表数据
**------------------------------------------------------------------------------------------------------
** 参数: addr:被写数据的地址 dat 被写的数据
** 返回: void
********************************************************************************************************/
void WriteData_ATT7022(unsigned char addr,unsigned long dat)
{
ATTCS=0;
ATT_SPI_ReadWrite(addr|0x80);
ATT_SPI_ReadWrite(dat>>16);
ATT_SPI_ReadWrite(dat>>8);
ATT_SPI_ReadWrite(dat>>0);
ATTCS=1;
}
/*******************************************************************************************************
** 函数: AttVarInit, ATT7022寄存器配置
**------------------------------------------------------------------------------------------------------
** 参数: NOLL
** 返回: NOLL
********************************************************************************************************/
void ATT7022VarInit(void)
{
WriteData_ATT7022( 0xC3,0x000000); // 清校表数据
WriteData_ATT7022( 0xC9,0x00005A); // 打开校准数据写
WriteData_ATT7022( 0x01,0xB97E); // 模式配置寄存器
WriteData_ATT7022( 0x03,0xF804); // EMU 单元配置寄存器
WriteData_ATT7022( 0x31,0x3427); // 模拟模块使能寄存器
WriteData_ATT7022( 0x6D,0xFF00); // Vrefgain 的补偿曲线系数
WriteData_ATT7022( 0x6E,0x0DB8); // Vrefgain 的补偿曲线系数
WriteData_ATT7022( 0x6F,0xD1DA); // Vrefgain 的补偿曲线系数
WriteData_ATT7022( 0x16,0x0000); // 无功相位校正寄存器
WriteData_ATT7022( 0x1E,HFconst); // HFConst高频脉冲参数校准
WriteData_ATT7022( 0xC9,0x0001); // 关闭校准数据写
// printf("%x\n\n",(u32)AttReadData(0)); // 测试指令 打印 7122A0 芯片工作 SPI通信正常
}
void Att7022Init(void)
{
ATT7022C_INIT();
ATT7022GpioInit();
// ATT7022VarInit();
}
/*******************************************************************************************************
** 函数: AttGetVolue
**------------------------------------------------------------------------------------------------------
** 参数: addr 地址
** 返回: 有符号数据
********************************************************************************************************/
long AttGetVolue( unsigned char Value)
{
long temp;
temp=AttReadData(Value);
if(AttReadData(0X2d)!=temp) return -1;
if(temp&0x800000) temp = 0x1000000 - temp ;
return temp;
}
/*******************************************************************************************************
** 函数: AttGetUnVolue
**------------------------------------------------------------------------------------------------------
** 参数: addr 地址
** 返回: 无符号数据
********************************************************************************************************/
long AttGetUnVolue( unsigned char Value)
{
long temp;
temp=AttReadData(Value);
if(AttReadData(0X2d)!=temp) return -1;
return temp;
}
/*******************************************************************************************************
** 函数: AttGetAllVolue, 获取ATT7022UE内部电能计量值
**------------------------------------------------------------------------------------------------------
** 参数: Energy_t *dat
** 返回: NONE
********************************************************************************************************/
void AttGetAllVolue( void )
{
//-----------------------------------------------------------------------------------------------
// 单相用电器电能参数采集
s_tEmt.dMU [ItemA] = AttGetVolue(VOLTAGE+ItemA)/UKrmsA; // A相电压
s_tEmt.dMI [ItemA] = AttGetVolue(CURRENT+ItemA)/IKrmsA; // A相电流
s_tEmt.dMP [ItemA] = AttGetVolue(POWERS+ItemA)*KCoefficient; // A相有功功率
s_tEmt.dMQ [ItemA] = AttGetVolue(QPOWERS+ItemA)*KCoefficient; // A相无功功率
s_tEmt.dMPF[ItemA] = AttGetVolue(PF+ItemA)/8388608.0; // A相功率因数
s_tEmt.dwMPoEpt[ItemA] = AttGetUnVolue(EP+ItemA); // A相有功电能
s_tEmt.dwMQoEpt[ItemA] = AttGetUnVolue(EQ+ItemA); // A相无功电能
//-----------------------------------------------------------------------------------------------
// 总电能参数采集
s_tEmt.dMF = AttGetVolue(FREQUENCY)/8191.0; // 总频率
s_tEmt.dMP [ItemALL] = AttGetVolue(POWERS+ItemALL)*KCoefficient*2; // 总有功功率
s_tEmt.dMQ [ItemALL] = AttGetVolue(QPOWERS+ItemALL)*KCoefficient*2; // 总无功功率
s_tEmt.dMPF[ItemALL] = AttGetVolue(PF+ItemALL)/8388608.0; // 总功率因数
s_tEmt.dwMPoEpt[ItemALL] = AttGetUnVolue(EP+ItemALL); // 总有功电能
s_tEmt.dwMQoEpt[ItemALL] = AttGetUnVolue(EQ+ItemALL); // 总无功电能
}
void Appear_U_Data (void){ printf("电压:%0.1fV\n",s_tEmt.dMU[ItemA]);}
void Appear_I_Data (void){ printf("电流:%0.3fA\n",s_tEmt.dMI[ItemA]);}
void Appear_P_Data (void){ printf("有功功率:%0.1fW\n", s_tEmt.dMP[ItemA]-0.3);}
void Appear_Q_Data (void){ printf( "无功功率:%0.1fVar\n", s_tEmt.dMQ[ItemA]);}
void Appear_F_Data (void){ printf("频率:%0.1fHz\n",s_tEmt.dMF);}
void PutAllData(void)
{
printf("-----------------电源线参数-----------------------\n");
Appear_U_Data(); // 串口显示 电压数据
Appear_I_Data(); // 串口显示 电流数据
Appear_P_Data(); // 串口显示 有功功率数据
Appear_Q_Data(); // 串口显示 无功功率数据
Appear_F_Data(); // 串口显示 频率
printf("\n\n");
}
总结
网络上的ATT7022EU资料相对多一些,不过在看懂了数据手册后,两者之间的异同也就了解了,使用起来也就大同小异。