如何使用 MCP4452 的命令和数据位将一行写入 I2C 设备

How do you write a line to an I2C device with a command and data bits for an MCP4452

我正在尝试修改一段最初为 AD5245 设计的代码,以将其用于 MCP4452。两者都是 Raspberry PI 上的 I2C 设备。我试图弄清楚如何根据图像/

中的文档制定写入设备的代码

如何将所有这些合并到类似 AD5245 的示例代码中?

static void writeAD5245(uint8_t i2cMCPAddress, uint8_t mcpdevice, uint8_t value) {
  beginMCP(i2cMCPAddress);
  uint8_t lsb = (uint8_t)(value >> 8);
  uint8_t msb = (uint8_t)value;
  uint16_t payload = (msb << 8) | lsb;
  i2c_smbus_write_word_data(i2cMCPHandle, MCP_WRITE, payload);
  endMCP();
}

完整数据表:MCP4452 Datasheet

编辑以包含完整的 .h 和 .cpp 文件。

mcp4452.h

#include <sys/ioctl.h>
#include <unistd.h>
extern "C" {
    #include <linux/i2c.h>
   #include <linux/i2c-dev.h>
   #include <i2c/smbus.h>
}

#define MCP_ADDRESS     (0x2C)

#define MCP_READ            (0x01)
#define MCP_WRITE           (0x00)

#define NV_WIPER_0      (0x02)//10
#define NV_WIPER_1      (0x03)//11
#define NV_WIPER_2      (0x08)//1000
#define NV_WIPER_3      (0x09)//1001

#define MCP_CHANNEL_0   (0x00)//0
#define MCP_CHANNEL_1   (0x01)//1
#define MCP_CHANNEL_2   (0x06)//6
#define MCP_CHANNEL_3   (0x07)//7

#define MCP_CMD_WRITE   (0x00)//WRITE NEXT BYTE (SET VALUE)
#define MCP_CMD_INC     (0x01)//INCREMENT BY 1
#define MCP_CMD_DEC     (0x02)//DECREMENT BY 1
#define MCP_CMD_READ        (0x03)//READ DATA

#define MCP_RESET           (0xFF)//RESET DEVICE

class MCP4452
{
    protected:
        uint8_t   m_i2cMCPAddress;
        uint8_t   m_MCPbitShift;
        uint8_t  m_MCPChannel;
        uint8_t  m_MCPCommand;
    public:
        MCP4452(uint8_t i2cMCPAddress = MCP_ADDRESS);
        uint8_t setWPOS(uint8_t channel, uint8_t wiper_position, int command, int delay);
    private:
};

mcp4452.cpp:

#include "mcp4452.h"
#include <iostream>
#include <bitset> //CAN BE REMOVED AFTER FUNCTIONING

int i2cMCPHandle;

static void beginMCP(uint8_t i2cMCPAddress) {
  i2cMCPHandle = open("/dev/i2c-1", O_RDWR);
  if(i2cMCPHandle < 0)
  {
    fprintf(stderr, "Error while opening the i2c-2 device! Error: %s\n", strerror(errno));
    exit(1);
  }
  if(ioctl(i2cMCPHandle, I2C_SLAVE, i2cMCPAddress) < 0)
  {
    fprintf(stderr, "Error while configuring the slave address %d. Error: %s\n", i2cMCPAddress, strerror(errno));
    exit(1);
  }
}
static void endMCP(void) {
    close(i2cMCPHandle);
}
static void resetMCP(uint8_t i2cMCPAddress, int delay) {
    beginMCP(i2cMCPAddress);
   i2c_smbus_write_word_data(i2cMCPHandle, MCP_WRITE, MCP_RESET);
   endMCP();
}
static void setPOS(uint8_t i2cMCPAddress, uint8_t mcpchannel, uint8_t position) {
    beginMCP(i2cMCPAddress);
    uint16_t payload = (mcpchannel << 12) | (MCP_CMD_WRITE << 10) | (position);
    std::cout << "WRITE PAYLOAD: " << std::bitset<16>(payload) <<std::endl;
    i2c_smbus_write_word_data(i2cMCPHandle, MCP_WRITE, payload);
    endMCP();
}
static void incPOS(uint8_t i2cMCPAddress, uint8_t mcpchannel) {
    beginMCP(i2cMCPAddress);
    uint16_t payload = (mcpchannel << 12) | (MCP_CMD_INC << 10);
    std::cout << "INC PAYLOAD: " << std::bitset<16>(payload) <<std::endl;
    i2c_smbus_write_word_data(i2cMCPHandle, MCP_WRITE, payload);
    endMCP();
}
static void decPOS(uint8_t i2cMCPAddress, uint8_t mcpchannel) {
    beginMCP(i2cMCPAddress);
    uint16_t payload = (mcpchannel << 12) | (MCP_CMD_DEC << 10);
    std::cout << "DEC PAYLOAD: " << std::bitset<16>(payload) <<std::endl;
    i2c_smbus_write_word_data(i2cMCPHandle, MCP_WRITE, payload);
    endMCP();
}
static uint16_t readMCP(uint8_t i2cMCPAddress) {
    beginMCP(i2cMCPAddress);
    uint16_t res = i2c_smbus_read_word_data(i2cMCPHandle, MCP_READ);
    std::cout << "BYTES READ: " << std::bitset<8>(res >> 8) << std::endl;
    endMCP();
    return res;
}
MCP4452::MCP4452(uint8_t i2cMCPAddress) 
{
    m_i2cMCPAddress = i2cMCPAddress;
    m_MCPChannel = MCP_CHANNEL_0;
    m_MCPbitShift = 8;
    m_MCPCommand = MCP_CMD_WRITE;
}
uint8_t MCP4452::setWPOS(uint8_t channel, uint8_t wiper_position, int command, int delay) {
  if (wiper_position > 255 || wiper_position < 0)
  {
    std::cout << "Wiper Pos Error" <<std::endl;
    return -2;
  }
  if (channel!=0 && channel!=1 && channel!=2 && channel!=3) {
    std::cout << "Channel Error" <<std::endl;
    return -2;
  }
  switch (channel) {
    case 0:
        m_MCPChannel=MCP_CHANNEL_0;
        //m_MCPChannel= NV_WIPER_0;
        break;
    case 1:
        m_MCPChannel=MCP_CHANNEL_1;
        //m_MCPChannel= NV_WIPER_1;
        break;
    case 2:
        m_MCPChannel=MCP_CHANNEL_2;
        //m_MCPChannel= NV_WIPER_2;
        break;
    case 3:
        m_MCPChannel=MCP_CHANNEL_3;
        //m_MCPChannel= NV_WIPER_3;
        break;
  }
  std::cout << "m_i2cMCPAddress: " << std::bitset<8>(m_i2cMCPAddress) << "  ON CHANNEL: " << std::bitset<4>(m_MCPChannel) << " USING m_MCPCommand: " << std::bitset<8>(m_MCPCommand) << std::endl;
  int w = -1;
  if (command==0) {//WRITE WIPER POSITION
      setPOS(m_i2cMCPAddress, m_MCPChannel, wiper_position);
      usleep(delay);
      // Read the conversion results
      w = readMCP(m_i2cMCPAddress) >> m_MCPbitShift;
      std::cout << "FIRST READ VALUE: " << w << std::endl;
      usleep(delay);
      if (int(w)!=int(wiper_position)) {
            w = -1;
            int counter=0;
            while (int(w) != int(wiper_position) && counter<10) {
                counter++;
                w = -1;
                setPOS(m_i2cMCPAddress, m_MCPChannel, wiper_position);
                usleep(delay);
                w = readMCP(m_i2cMCPAddress) >> m_MCPbitShift;
                std::cout << "SECOND READ VALUE: " << w << std::endl;
            }
        }
    } else if (command==1) {//INCREMENT WIPER BY 1
        incPOS(m_i2cMCPAddress, m_MCPChannel);
        usleep(delay);
        w = readMCP(m_i2cMCPAddress) >> m_MCPbitShift;
       std::cout << "INC READ VALUE: " << w << std::endl;
    } else if (command==2) {//DECREMENT WIPER BY 1
        decPOS(m_i2cMCPAddress, m_MCPChannel);
        usleep(delay);
        w = readMCP(m_i2cMCPAddress) >> m_MCPbitShift;
       std::cout << "DEC READ VALUE: " << w << std::endl;
     } else if (command==3) {//RESET DEVICE
        resetMCP(m_i2cMCPAddress, delay);
        usleep(delay);
        w = readMCP(m_i2cMCPAddress) >> m_MCPbitShift;
        std::cout << "WIPER RESET " << w << std::endl;
    } else {
        std::cout << "INVALID COMMAND RECEIVED" << std::endl;
    }
  return w;
}

EDIT2:测试有效负载数据和结果

0000 0000 0000 0000<<CHANNEL 1 SET TO 0--WORKS
0000 0000 1111 1111<<CHANNEL 1 SET TO 255--WORKS
0000 0100 0000 0000<<CHANNEL 1 INCREMENT--DOES NOT WORK
0000 1000 0000 0000<<CHANNEL 1 DECREMENT--DOES NOT WORK

0111 0000 0000 0000<<CHANNEL 4 SET TO 0--DOES NOT WORK
0111 0000 1111 1111<<CHANNEL 4 SET TO 255--DOES NOT WORK
0111 0100 0000 0000<<CHANNEL 4 INCREMENT--WORKS
0111 1000 0000 0000<<CHANNEL 4 DECREMENT--WORKS

您的代码未正确实现 I²C 写入。

如在 datasheet and also other tutorials 中所见,对于使用 I²C 总线,重要的是对于写入,您发送包含数据的 third 字节(适用于单个字节写)在写命令期间。

因此,

  • 字节 1 - 控制字节包括 设备地址 R/W 位
  • 字节 2 - 命令字节包括数据地址命令
  • 字节 3 - 数据字节 写入命令 期间预期 。

据我了解,<i2c/smbus.h> 驱动程序会为您整理格式,您只需提供 命令字节 数据字节写入时

因此,您的方法 setPOS 变为:

static void setPOS(uint8_t i2cMCPAddress, uint8_t mcpchannel, uint8_t position) {
    beginMCP(i2cMCPAddress);
    uint8_t command = mcpchannel << 4;          // Set the data address
    command = command | (MCP_CMD_WRITE) << 2;   // Set the command type
    
    std::cout << "WRITE COMMAND: " << std::bitset<8>(command) <<std::endl;
    std::cout << "WRITE DATA: " << std::bitset<8>(position) <<std::endl;

    // Changed from write *word* to write *byte* data
    i2c_smbus_write_byte_data(i2cMCPHandle, command, position);
    endMCP();
}

来自以下测试调用:

setPOS(MCP_ADDRESS, MCP_CHANNEL_0, 0xFF);
setPOS(MCP_ADDRESS, MCP_CHANNEL_1, 0xFF);

给出以下输出:

WRITE COMMAND: 00000000
WRITE DATA: 11111111
WRITE COMMAND: 00010000
WRITE DATA: 11111111

见上例here

我知道这就是您遇到问题的原因

Odd thing is that now I am able to write a value (0-255) to the devices first wiper and I can increment/decrement (+1/-1) wipers 2-4, but I cannot write a value to wipers 2-4 and I cannot increment/decrement wiper 1.

请注意,从数据表中我确实看到 MSB 设置在 p53 上:

‘1000 00d’b - Write Next Byte (Third Byte) to Volatile Wiper 0 Register.

上面的例子我没有设置。您应该验证是否应该设置它。