一、炬力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; } }
|