如何解决如何使用 MCP4452 的命令和数据位将一行写入 I2C 设备
我正在尝试修改最初为 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_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,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,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,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,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,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 和其他用于处理 I²C 总线的 tutorials 中所见,重要的是对于写入,您发送包含数据的第三字节(适用写入命令期间的单个字节写入)。
因此,
- 字节 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。
我很清楚这就是导致您问题的原因
奇怪的是,现在我可以将值 (0-255) 写入设备第一个雨刷器,并且我可以增加/减少 (+1/-1) 雨刷器 2-4,但我无法向其写入值雨刷器 2-4,我不能增加/减少雨刷器 1。
请注意,从数据表中我确实看到在 p53 上设置了 MSB:
‘1000 00d’b - 将下一个字节(第三个字节)写入易失性抽头 0 寄存器。
在上面的例子中我没有设置它。您应该验证是否应该设置/不设置。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。