如何使用 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.
上面的例子我没有设置。您应该验证是否应该设置它。
我正在尝试修改一段最初为 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.
上面的例子我没有设置。您应该验证是否应该设置它。