MAX14906为符合IEC 61131-2标准的高速、四通道工业数字输出、数字输入器件,可按通道配置为高边(HS)开关、推挽(PP)驱动器、1、3型或2型数字输入。SPI接口具有内置芯片寻址解码器,允许利用具有公共片选(CS)的共享SPI与多个MAX14906器件通信。SPI接口为全局和每通道配置和诊断提供了灵活性,包括电源过压和欠压检测、断线或开路检测、热过载和电流限制报告等。
本应用笔记介绍了一系列功能,为MAX14906编程提供简单且经过验证的解决方案(图1)。它们是用C#编写的,应该很容易移植到任何常见的微控制器上。有关MAX14906引脚、工作模式、SPI命令和控制寄存器的详细信息,请参考MAX14906数据资料。

图1.MAX14906典型应用电路
MAX14906 SPI
MAX14906具有高速SPI串行接口,最大时钟速率为10MHz。SPI 接口遵循时钟极性 CPOL = 0(SCLK 空闲 = 0)和时钟相位 CPHA = 0(上升沿/第一沿对数据进行采样)。命令首先以最高有效位 (MSb) 计时。
MAX14906 SPI支持可寻址SPI,允许使用共享CS信号与多达14906个MAX1直接通信。地址引脚 A0 和 A1 用于配置器件地址。地址位 A0 和 A1 作为 SPI 读写命令的第一和第二位发送。器件监视SPI命令,并在地址与A0和A<>引脚的状态匹配时适当地响应SDO。
MAX14906 SPI支持单周期模式和突发模式。单周期模式一次读取或写入一个寄存器,而突发模式允许在一个SPI周期内读取或写入多个连续寄存器。
单周期SPI命令的长度为16位(8位指令+ 8位数据),禁用CRC,如果启用CRC,则会增加8位,包括5位CRC和3个前导零。命令字节的2 MSB为器件地址位(A1和A0),允许4个MAX14906共享相同的片选(CS)引脚。对于单周期模式,BRST 位设置为 0。设备在SDO上发送的数据报告设备故障情况,如果是写入命令,则报告每个通道的状态,如果是读取命令,则报告寄存器值。单周期读写图如图 2 至图 5 所示。

图2.SPI 单周期写入命令,CRC 禁用。

图3.SPI 单周期写入命令,启用 CRC。

图4.SPI 单周期读取命令,CRC 禁用。

图5.SPI 单周期读取命令,启用 CRC。
突发 SPI 命令使用一个 SPI 周期和一个寄存器地址写入或读取多个连续寄存器,并通过将 BRST 位设置为 1 来启用。
突发写入命令写入 SetOUT 和 SetLED 寄存器,BRST = 1、R/W = 1 和 R[3:0] = 0。该命令包含两个数据字节,一个用于配置 SetOUT 寄存器,另一个用于配置 SetLED 寄存器。如果未启用 CRC,则 SCLK 周期数为 24,如果启用 CRC,则 SCLK 周期数为 32。SPI突发写入图如图6和图7所示。
突发读取命令从地址0x02到0x07(DoiLevel、Interrupt、OvrLdChF、OpnWirChF、ShtVDDChF 和 GlobalErr 寄存器)检索六个连续诊断寄存器的数据,BRST = 1, R/W = 0, R[3:0] = 2。如果未启用 CRC,则 SCLK 周期数为 56,如果启用了 CRC,则 SCLK 周期数为 64。在突发读取期间,SDI数据流中的数据位9至59可以是0或1,但如果启用CRC,这些位用于计算CRC位。SPI突发读取图如图8和图9所示。

图6.SPI 突发写入命令,CRC 禁用。

图7.SPI 突发写入命令,已启用 CRC。

图8.SPI 突发读取命令,CRC 已禁用。

图9.SPI 突发读取命令,已启用 CRC。
有关SPI命令的更多详细信息以及寄存器表和指令,请参考MAX14906数据资料。
MAX14906 - 代码应用示例
MAX14906设计用于支持终端设备的工业应用,如可编程逻辑控制器(PLC),需要可配置的数字输入(1/3型或2型)或数字输出(高边开关或推挽式驱动器)。典型应用电路支持4通道组隔离,采用单个MAX14483数字隔离器,如图1所示。
源代码
本应用笔记提供了C#源代码示例,主要提供驱动器功能,用于访问MAX14906中的多个寄存器,以实现配置、控制和诊断功能。所有软件均使用MAX14906评估板进行实现和测试。本应用笔记中的功能用于MAX14906评估板软件,用于处理MAX14906 SPI的基本读写命令。设计用于与MAX2232评估板硬件上的FT14906微控制器配合使用。本文档不包括USB通信和低级FT2232功能。本文档中也未包含图形界面功能。
客户应仅将本文档中的功能用作参考,并根据其应用程序中的微控制器和硬件实现设计自己的固件/软件。
单周期写入
public void WriteRegister(int index)
{
if (index < 0 || index >= registers.Count) { return; }
byte register = 0x00;
byte[] txBuffer = new byte[2];
byte[] rxBuffer = new byte[2];
// Register Byte
register = 0x01;// Write
register |= (byte)(index << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
txBuffer[0] = register;
// Data Byte
txBuffer[1] = registers[index].Value;
SPIWriteRead(txBuffer, ref rxBuffer, 2);
// Store Fault Data
SDOFaults.SHTVDD = ((rxBuffer[0] & 0x20) >> 5);
SDOFaults.AbvVDD = ((rxBuffer[0] & 0x10) >> 4);
SDOFaults.OWOffF = ((rxBuffer[0] & 0x08) >> 3);
SDOFaults.OvrCurr = ((rxBuffer[0] & 0x04) >> 2);
SDOFaults.OvldF = ((rxBuffer[0] & 0x02) >> 1);
SDOFaults.GLOBLF = (rxBuffer[0] & 0x01);
SDOFaults.ChannelFaults = rxBuffer[1];
// If successful, clean up
registers[index].Modified = false;
}
单周期读取
public void ReadRegister(int index)
{
if (index < 0 || index >= registers.Count) { return; }
FTDI.FT_STATUS ftStatus;
byte register = 0x00;
byte[] txBuffer = new byte[2];
byte[] rxBuffer = new byte[2];
// Register Byte
register = 0x00;// Read
register |= (byte)(index << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
txBuffer[0] = register;
// Data Byte
txBuffer[1] = 0x00;
ftStatus = SPIWriteRead(txBuffer, ref rxBuffer, 2);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
registers[index].Value = rxBuffer[1];
}
// Store Fault Data
SDOFaults.SHTVDD = ((rxBuffer[0] & 0x20) >> 5);
SDOFaults.AbvVDD = ((rxBuffer[0] & 0x10) >> 4);
SDOFaults.OWOffF = ((rxBuffer[0] & 0x08) >> 3);
SDOFaults.OvrCurr = ((rxBuffer[0] & 0x04) >> 2);
SDOFaults.OvldF = ((rxBuffer[0] & 0x02) >> 1);
SDOFaults.GLOBLF = (rxBuffer[0] & 0x01);
//SDOFaults.ChannelFaults = rxBuffer[1];// Not Valid, bc on read this is register data
// If successful, clean up
registers[index].Modified = false;
}
突发写入
public void BurstWriteRegisters(int startIndex, int endIndex)
{
// Typically always 0 and 1 only
if (endIndex < startIndex) { return; }
if (startIndex < 0) { return; }
if (endIndex >= registers.Count) { return; }
int byteCount = (endIndex - startIndex) + 2;
byte register = 0x00;
byte[] txBuffer = new byte[byteCount];
byte[] rxBuffer = new byte[byteCount];
// Register Byte
register = 0x01;// Write
register |= (byte)(startIndex << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
register |= (byte)(0x01 << 5);// Burst
txBuffer[0] = register;
// Data Byte
for (int x = 0; x < (byteCount - 1); x++)
{
txBuffer[1 + x] = registers[startIndex + x].Value;
registers[startIndex + x].Modified = false;
}
SPIWriteRead(txBuffer, ref rxBuffer, byteCount);
// Store Fault Data
SDOFaults.SHTVDD = ((rxBuffer[0] & 0x20) >> 5);
SDOFaults.AbvVDD = ((rxBuffer[0] & 0x10) >> 4);
SDOFaults.OWOffF = ((rxBuffer[0] & 0x08) >> 3);
SDOFaults.OvrCurr = ((rxBuffer[0] & 0x04) >> 2);
SDOFaults.OvldF = ((rxBuffer[0] & 0x02) >> 1);
SDOFaults.GLOBLF = (rxBuffer[0] & 0x01);
SDOFaults.ChannelFaults = rxBuffer[1];
}
突发读取
public void BurstReadRegisters(int startIndex, int endIndex)
{
if (endIndex < startIndex) { return; }
if (startIndex < 0) { return; }
if (endIndex >= registers.Count) { return; }
FTDI.FT_STATUS ftStatus;
int byteCount = (endIndex - startIndex) + 2;
byte register = 0x00;
byte[] txBuffer = new byte[byteCount];
byte[] rxBuffer = new byte[byteCount];
// Register Byte
register = 0x00;// Read
register |= (byte)(startIndex << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
register |= (byte)(0x01 << 5);// Burst
txBuffer[0] = register;
// Data Byte
for (int x = 0; x < (byteCount - 1); x++)
{
txBuffer[1 + x] = 0x00;
}
ftStatus = SPIWriteRead(txBuffer, ref rxBuffer, byteCount);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (int x = 0; x < (byteCount - 1); x++)
{
registers[startIndex + x].Value = rxBuffer[1 + x];
registers[startIndex + x].Modified = false;
}
}
// Store Fault Data
SDOFaults.SHTVDD = ((rxBuffer[0] & 0x20) >> 5);
SDOFaults.AbvVDD = ((rxBuffer[0] & 0x10) >> 4);
SDOFaults.OWOffF = ((rxBuffer[0] & 0x08) >> 3);
SDOFaults.OvrCurr = ((rxBuffer[0] & 0x04) >> 2);
SDOFaults.OvldF = ((rxBuffer[0] & 0x02) >> 1);
SDOFaults.GLOBLF = (rxBuffer[0] & 0x01);
}
CRC 计算
private CRC8 crc8 = new CRC8(0x15, 0x1F);
class CRC8
{
private readonly byte POLY = 0x00;
private readonly byte START_VALUE = 0x00;
public CRC8(byte poly, byte startValue = 0x00)
{
this.POLY = poly;
this.START_VALUE = startValue;
}
public byte ComputeChecksum3MSB(byte[] bytes)
{
byte remainder = START_VALUE;
byte min = 0;
for (int bite = 0; bite < bytes.Length; ++bite)
{
// For MAX14906 it does 8/16bits plus the 3 "0" MSBs of check byte
if (bite == (bytes.Length - 1)) { min = 5; }
for (byte bit = 8; bit > min; --bit)
{
remainder = (((bytes[bite] >> (bit - 1) & 0x01) ^ ((remainder >> 4) & 0x01)) > 0) ? (byte)((remainder << 1) ^ POLY) : (byte)(remainder << 1);
}
}
return (byte)(remainder & 0x1F);
}
}
为每个通道配置操作模式的示例
public void SendModeAndSetting(int channel, int mode, int setting)
{
if (channel < 1 || channel > 4) { return; }
if (mode < 0 || mode > 2) { return; }
if (((mode == 1) && (setting < 0 || setting > 4)) || ((mode == 0) && (setting < 0 || setting > 2)) || (mode == 2 && setting != 0)) { return; }
byte register = 0x00;
byte data = 0x00;
byte[] txBuffer = new byte[2];
byte[] rxBuffer = new byte[2];
if (mode == 0 || mode == 2)// Input or Low-leakage
{
////////////////////////////
// Reg 0x00:
///////////////////////////
// Register Byte
register = 0x01;// Write
register |= (0x00 << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
txBuffer[0] = register;
// Data Byte
// Set current bit mode from FTDI
byte temp = 0x00;
// Remember bit logic for FTDI input/output inverted from MAX14906 Reg 0x00
temp = (byte)((~FTDI_ADBUS_MODE & 0x80) >> 7);// DIO1
data |= (byte)(temp << 4);
temp = (byte)((~FTDI_ACBUS_MODE & 0x01));// DIO2
data |= (byte)(temp << 5);
temp = (byte)((~FTDI_ACBUS_MODE & 0x02) >> 1);// DIO3
data |= (byte)(temp << 6);
temp = (byte)((~FTDI_ACBUS_MODE & 0x04) >> 2);// DIO4
data |= (byte)(temp << 7);
// New Mode to Set
temp = (byte)(0x01 << (channel + 3));
data = (mode == 0 || mode == 2) ? (byte)(temp | data) : (byte)(~temp & data);
txBuffer[1] = data;
SPIWriteRead(txBuffer, ref rxBuffer, 2);
////////////////////////////
// Reg 0x0C:
///////////////////////////
// Register Byte
register = 0x00;// Read
register |= (0x0C << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
txBuffer[0] = register;
// Get Current Data from Reg 0x0C
SPIWriteRead(txBuffer, ref rxBuffer, 2);
temp = rxBuffer[1];
// Register Byte
register = 0x01;// Write
register |= (0x0C << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
txBuffer[0] = register;
// Data Byte
data = (setting == 1) ? (byte)(temp | 0x80) : (byte)(temp & ~0x80);// Type 2 IEC Mode, Note this forces all DI to be in this mode
txBuffer[1] = data;
SPIWriteRead(txBuffer, ref rxBuffer, 2);
////////////////////////////
// Reg 0x0D:
///////////////////////////
// Register Byte
register = 0x01;// Write
register |= (0x0D << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
txBuffer[0] = register;
// Data Byte
data = (byte)(DO_Settings & ~(0x03 << (channel - 1) * 2));// Whether any setting (Normal, IEC 2, or Low-leakage) zero out both bits
if (mode == 2)// Low-leakage
{
data |= (byte)(0x02 << (channel - 1) * 2);// For Low-leakage DI must set PP
}
DO_Settings = data;
txBuffer[1] = data;
SPIWriteRead(txBuffer, ref rxBuffer, 2);
}
else// Output
{
////////////////////////////
// Reg 0x00:
///////////////////////////
// Register Byte
register = 0x01;// Write
register |= (0x00 << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
txBuffer[0] = register;
// Data Byte
// Set current bit mode from FTDI
byte temp = 0x00;
// Remember bit logic for FTDI input/output inverted from MAX14906 Reg 0x00
temp = (byte)((~FTDI_ADBUS_MODE & 0x80) >> 7);// DIO1
data |= (byte)(temp << 4);
temp = (byte)((~FTDI_ACBUS_MODE & 0x01));// DIO2
data |= (byte)(temp << 5);
temp = (byte)((~FTDI_ACBUS_MODE & 0x02) >> 1);// DIO3
data |= (byte)(temp << 6);
temp = (byte)((~FTDI_ACBUS_MODE & 0x04) >> 2);// DIO4
data |= (byte)(temp << 7);
// New Mode to Set
temp = (byte)(0x01 << (channel + 3));
data = (mode == 0) ? (byte)(temp | data) : (byte)(~temp & data);
txBuffer[1] = data;
SPIWriteRead(txBuffer, ref rxBuffer, 2);
////////////////////////////
// Reg 0x0D:
///////////////////////////
// Register Byte
register = 0x01;// Write
register |= (0x0D << 1);// Register Address
register |= (byte)(Address << 6);// Chip Address
txBuffer[0] = register;
// Data Byte
data = (byte)(DO_Settings & ~(0x03 << (channel - 1) * 2));// Whether any setting (HS, HS 2x, PP clamp, PP low) zero out both bits
data |= (byte)(setting << (channel - 1) * 2);
DO_Settings = data;// Update settings
txBuffer[1] = data;
SPIWriteRead(txBuffer, ref rxBuffer, 2);
}
// Store Fault Data
SDOFaults.SHTVDD = ((rxBuffer[0] & 0x20) >> 5);
SDOFaults.AbvVDD = ((rxBuffer[0] & 0x10) >> 4);
SDOFaults.OWOffF = ((rxBuffer[0] & 0x08) >> 3);
SDOFaults.OvrCurr = ((rxBuffer[0] & 0x04) >> 2);
SDOFaults.OvldF = ((rxBuffer[0] & 0x02) >> 1);
SDOFaults.GLOBLF = (rxBuffer[0] & 0x01);
SDOFaults.ChannelFaults = rxBuffer[1];
}
结论
本应用笔记介绍了如何对MAX14906进行编程,以监测输入、驱动输出和诊断故障条件。该代码使用MAX14906EVKIT#进行测试。工程师可以使用本应用笔记中提到的C代码示例,快速轻松地实现常用微控制器和MAX14906之间的接口。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !