一、炬力ATT7022B
ATT7022B是一款具有高可靠性、高精度、高稳定性的三相基波/谐波电能计量芯片,在1000:1的动态范围内功率测量精度优于0.1%,电流和电压的有效值测量精度优于0.5%。ATT7022B可以单独计量基波电能,消除谐波对电能计量的负面影响,为电力部门公平计费提供参考依据。其内部集成7通道的16位高精度ADC和24位高速DSP,第七路ADC可用于防窃电,片上集成有温度传感器,通过SPI通讯接口输出三相多功能电表所需的各项电能参数。ATT7022B可以同时给出总有功/无功电能、基波有功/无功电能、谐波有功/无功电能、视在电能等参数。支持软件校表。适用于三相三线和三相四线的多功能电能表。
二、SPI通讯
ATT7022B在实践过程中发现,其与标准的物理SPI口通讯总存在着各种问题。不久前笔者也尝试过使用物理SPI口与ATT7022B通讯,并且成功过,但后来不知为了什么竟然无法获取需要的数据了,也就是说无法通讯了。之后笔者请教了炬力的技术支持工程师,在其帮助下决定使用IO口模拟SPI与之通讯。结果掌握好时序的情况下,与ATT7022B成功通讯。
三、通讯的技术细节
现把使用Philips ARM7 LPC2119 模拟SPI与炬力ATT7022B电力DSP芯片通讯的源码开源如下。
编译器与开发环境: ADS1.2
RTOS: u/COS-II
#include "config.h"
static OS_EVENT *semSpi; // SPI互斥信号量指针
// SPI初始化
uint8 SpiInit( void )
{
PINSEL0 = PINSEL0 & GPIO_CLS_0;
PINSEL1 = PINSEL1 & GPIO_CLS_1;
IO0DIR = ( IO0DIR & ~SCLK ) | SCLK; // 时钟
IO0DIR = IO0DIR & ~MISO; // 输入
IO0DIR = ( IO0DIR & ~MOSI ) | MOSI; // 输出
IO0DIR = ( IO0DIR & ~nCS ) | nCS; // 片选
IO0SET = nCS;
semSpi = OSSemCreate( 1 ); // 建立互斥信号量,防止资源共享冲突
if( semSpi == NULL )
return 1;
else
return 0;
}
// SPI写
uint32 SpiRead( uint8 rCmd )
{
uint8 i, err;
uint32 iRet;
OSSemPend( semSpi, 0, &err );
if( err == OS_NO_ERR )
{
OS_ENTER_CRITICAL(); // 关中断,模拟SPI通讯时不允许打断
IO0SET = nCS;
IO0CLR = SCLK;
IO0CLR = nCS; // 打开SPI
for( i = 0; i < 8; i++ ) // 发送一个字节的数据
{
IO0SET = SCLK;
if( rCmd & 0x80 ) // 高位在前进行发送
{
IO0SET = MOSI;
}
else
{
IO0CLR = MOSI;
}
IO0CLR = SCLK;
rCmd = rCmd << 1;
}
IO0CLR = MOSI;
SpiDelay( 1 );
iRet = 0;
for( i = 0; i < 24; i++ ) // 接收24位的数据
{
iRet = iRet << 1;
IO0SET = SCLK;
if( IO0PIN & MISO )
{
iRet = iRet | 0x01;
}
IO0CLR = SCLK;
}
IO0SET = nCS; // 关闭SPI口
OS_EXIT_CRITICAL(); // 开中断
}
OSSemPost( semSpi ); // SPI使用结束,释放信号量
return iRet;
}
// SPI读
uint32 SpiWrite( uint8 wCmd, uint32 data )
{
uint8 i, err;
uint32 iRet;
OSSemPend( semSpi, 0, &err );
if( err == OS_NO_ERR )
{
OS_ENTER_CRITICAL(); // 关中断,模拟SPI通讯时不允许打断
IO0SET = nCS;
IO0CLR = SCLK;
IO0CLR = nCS; // 打开SPI
wCmd = wCmd | 0x80; // 最高位置位,表示为写操作
for( i = 0; i < 8; i++ ) // 发送一个字节的数据
{
IO0SET = SCLK;
if( wCmd & 0x80 ) // 高位在前进行发送
{
IO0SET = MOSI;
}
else
{
IO0CLR = MOSI;
}
IO0CLR = SCLK;
wCmd = wCmd << 1;
}
IO0CLR = MOSI;
SpiDelay( 1 );
for( i = 0; i < 25; i++ ) // 接收24位的数据
{
IO0SET = SCLK;
if( data & 0x1000000 ) // 高位在前进行发送
{
IO0SET = MOSI;
}
else
{
IO0CLR = MOSI;
}
IO0CLR = SCLK;
data = data << 1;
}
IO0CLR = MOSI;
IO0SET = nCS; // 关闭SPI口
OS_EXIT_CRITICAL(); // 开中断
}
OSSemPost( semSpi ); // SPI使用结束,释放信号量
return iRet;
}
// 延时
void SpiDelay( uint32 t )
{
uint32 i;
while( t )
{
for( i = 0; i < 10; i++ );
--t;
}
}