DSP

使用Philips ARM7 LPC2119 通过SPI与炬力ATT7022B电力DSP芯片的通讯

2019-07-13 20:54发布

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