应用笔记 4246

怎样使用MAXQ3180微控制器的串行外设接口(SPI)

By: Ben Smith

摘要 : MAXQ3180微控制器是电表多相模拟前端。它具备现代多功能电表的所有功能。MAXQ3180通过串行外设互联(SPI™)总线将其读数传送给主机微控制器。本应用笔记介绍怎样实现这一接口,演示实例代码以帮助设计人员实现这一通信机制。

SPI简介

串行外设接口(SPI)是器件间总线协议,实现芯片间的快速、同步、全双工通信。由一个主机驱动同步时钟,选择对哪些从机寻址。每个SPI外设含有一个移位寄存器和控制电路,使被寻址的串行外设接口SPI外设能够同时发送和接收数据。

图1. SPI从机示意图
图1. SPI从机示意图

SPI通信采用了四种不同的电路:
  • SCLK:所有器件使用的同步时钟。主机驱动该时钟,从机接收时钟。注意,SCLK可以被选通,不需要在SPI操作之间进行驱动。
  • MOSI:主机出,从机入。这是主机在SPI总线上驱动所有从机的主要数据线。只有所选的从机同步来自MOSI的数据。
  • MISO:主机入,从机出。这是所选从机向主机发送时驱动的主要数据线。只有所选的从机可以驱动该电路。实际上,这是SPI总线安排中允许从机驱动的唯一电路。
  • SSEL:该信号在每一从机上都不同。当有效(通常为低电平)时,所选从机必须驱动MISO。
对于这一讨论,需要特别说明的是,SPI外设同时发送和接收。最简单的理解是主机总是发送一个字节,接收一个字节。

有些SPI外设牺牲速率以模拟半双工工作。MAXQ3180微控制器没有采用这一方式,它是真正的全双工SPI从机。

本应用笔记的其他部分介绍怎样连接并成功使用SPI总线上的MAXQ3180。

MAXQ3180通信简介

对于主机,MAXQ3180看起来象一个存储器阵列,同时含有RAM和ROM。这是因为MAXQ3180中的ROM固件读取RAM的工作参数,将结果放到RAM中。因此,配置MAXQ3180和对RAM进行块写入一样简单。

有些MAXQ3180 “存储”位置触发器件内部操作,“随时”计算电表测量结果。向这些位置写入的是“nop”。对RAM和虚拟ROM位置特殊功能和目的的讨论已经超出了本文档的范围。此处最重要的是微控制器的确只有两种SPI通信操作:读和写。

MAXQ3180中的每一次操作以主机发送两个字节开始,它含有命令(例如,读或者写)、要访问的地址、访问的字节数。如前所述,每个SPI外设对接收到的每个字节返回一个字节。因此,MAXQ3180在接收到第一个命令字节后返回0xC1,第二个命令字节后返回0xC2。该协议显示在下面的图2中。

图2. 主机向MAXQ3180读写数据
图2. 主机向MAXQ3180读写数据

如果主机读取一个或者多个字节,它必须发送空字节。记住,主机不能接收来自从机的任何信息,除非它发送某些信息:发送一个字节以得到一个字节。但是接收一条命令后,MAXQ3180要计算结果,当主机发送空字节时,它可能还没有准备好。出于这一原因,MAXQ3180总是在发送数据之前发送零或者NAK字符等多个字节(0x4E或者ASCII 'N'),随后是一个ACK字符(0x41,或者ASCII 'A')。

如果主机写入一个或者多个字节,发送命令后,它立即发送要写入的数据。MAXQ3180为每一个数据字节返回ACK (0x41)。然后,它返回NAK (0x4E),直到写周期完成,随后返回最终ACK。

注意,最终ACK之后,MAXQ3180立即准备开始下一操作;它不需要进行任何其他等待。它甚至不需要触发SSEL以开始下一操作。MAXQ3180知道第一次操作已经完成,准备进行下一操作。

不论什么原因,如果需要复位主机和MAXQ3180之间的通信(例如,如果通信是异步的),从第一个命令字节重新启动通信之前,主机只需要等待200ms。200ms延时指示MAXQ3180,主机放弃了前面的操作。

命令字节

命令字节告诉MAXQ3180:
  1. 申请的操作是READ还是WRITE
  2. 操作的长度
  3. RAM中要改动的地址(或者要读取的虚拟ROM地址)
图3. 命令字节结构
图3. 命令字节结构

第一个命令字节(图3)告诉MAXQ3180,所申请的操作是READ还是WRITE,以及操作的长度。命令字节结构如下:

Length Code Data Length
0b00 1 byte
0b01 2 bytes
0b10 4 bytes
0b11 8 bytes

命令字节1的其他部分和所有的命令字节2提供要访问的RAM字节的地址(或者一样的虚拟ROM功能)。

主机软件设计

虽然MAXQ3180含有一个硬件SPI控制器,ROM固件中的软件程序还是要处理每一消息字节。出于这一原因,连续字节之间需要有延时。在当前的MAXQ3180型号中,这一延时不得小于100µs才能实现可靠的工作。请参考图4图5

图4. 读取MAXQ3180的流程图
图4. 读取MAXQ3180的流程图

图5. 写入MAXQ3180的流程图
图5. 写入MAXQ3180的流程图

代码清单

提供代码以实现具有内置SPI主机的MAXQ2000微控制器和MAXQ3180的接口。其他微控制器用户需要提供自己的SPI原语,还可能要修改高层子程序。

在下面的清单中,dly_us子程序使程序线程停止执行几个微秒。定义SPI_TIMEOUT常数以提供比字符超时时间更长的参数。

在高层子程序中,采用ENUM按名称来选择寄存器。在其他参数中,它提供register_lookup_table阵列的索引,含有每个MAXQ3180寄存器的寄存器长度。请参考图6图7图8

unsigned char Send_SPI(unsigned char x)
{
  unsigned char y = 0;
  int z;
  int error = 0;
  SPICN = 3; /* MSTSM, SPIEN */
  z = 0; while ((SPICN_bit.STBY) && (++z < SPI_TIMEOUT));
  if (z == SPI_TIMEOUT) error = 1;
  SPICN_bit.SPIC = 0; /* Clear transfer complete flag */
  SPIB = x;
  z = 0; while ((!SPICN_bit.SPIC) && (++z < SPI_TIMEOUT));
  if (z == SPI_TIMEOUT) error = 1;
  y = SPIB;
  SPICN_bit.SPIC = 0;
  dly_us(100);
  if (error) return 0;
  return y;
}
图6. Send_SPI原代码

long Read_AFE(enum METER_REGISTER_RECORD reg, uint16 reg_addr)
{
  extern unsigned char record[8];
  unsigned long x = 0;
  unsigned char i, regadd, command_code = 0;
  for(i=0; i<8; i++) record[i] = 0;
  switch(register_lookup_table[reg].register_length)
  {
  case 2: command_code |= 0x10; break;
  case 4: command_code |= 0x20; break;
  case 8: command_code |= 0x30; break;
  }
  command_code |= reg_addr >> 8;
  regadd = reg_addr & 0xff;

  /* Disable SPI to reset it */
  SPICN_bit.SPIEN = 0;
  for(x=0; x<300; x++);
  SPICN_bit.SPIEN = 1;

  SPI_SELECT_0;
  i = 0;
  while((Send_SPI(command_code)!= 0xC1)&&(++i < SPI_COMMAND_RETRIES))
    spi_comm_timeout();

  x = 0xffffffff;
  if (i == SPI_COMMAND_RETRIES) goto spierror;
  Send_SPI(regadd);
  i = 0; while((Send_SPI(0) != 'A') && (++i < SPI_RETRIES));
  if (i == SPI_RETRIES) goto spierror;
  x = 0;
  for(i=0; i<register_lookup_table[reg].register_length; i++)
  {
    record[i] = Send_SPI(0);
    x |= ((uint32)record[i]) << (i * 8);
  }

spierror:
  SPI_DESELECT_0;
  return (int32)x;
}
图7. ReadAFE (SPI_Read)子程序代码

void Write_AFE(enum METER_REGISTER_RECORD reg, uint16 reg_addr, uint32 data)
{
  uint8 i, regadd, command_code = 0x80;
  int x;
  switch(register_lookup_table[reg].register_length)
  {
  case 2: command_code |= 0x10; break;
  case 4: command_code |= 0x20; break;
  case 8: command_code |= 0x30; break;
  }
  command_code |= reg_addr >> 8;
  regadd = reg_addr & 0xff;

  /* Disable SPI hardware to reset it */
  SPICN_bit.SPIEN = 0;
  for(x=0; x<300; x++);
  SPICN_bit.SPIEN = 1;

  SPI_SELECT_0;
  i = 0;
  while((Send_SPI(command_code)!=0xC1)&&(++i < SPI_COMMAND_RETRIES))
    spi_comm_timeout();
  if (i == SPI_COMMAND_RETRIES) goto spierror;
  Send_SPI(regadd);
  for(i=0; i<register_lookup_table[reg].register_length; i++)
    Send_SPI((data >> (i * 8)) & 0xff);
  i = 0; while((Send_SPI(0) != 'A') && (++i < SPI_RETRIES));

spierror:
  SPI_DESELECT_0;
}
图8. Write_AFE (SPI_Write)子程序代码
下一步
EE-Mail 订阅EE-Mail,接收关于您感兴趣的新文档的自动通知。
© , Maxim Integrated Products, Inc.
The content on this webpage is protected by copyright laws of the United States and of foreign countries. For requests to copy this content, contact us.
APP 4246:
应用笔记 4246,AN4246, AN 4246, APP4246, Appnote4246, Appnote 4246