背景
在项目中使用到了多路电压设置模块,之前一直使用的是ad569 四通道输出芯片,这次则使用到一个之前没有接触过的芯片MCp4728,内核中没有集成驱动,需要自己写,因此用该文档记录驱动移植的一个过车过程。
芯片手册现在地址(不需要积分):https://download.youkuaiyun.com/download/ZYH10140/88498978
该系列总共分为3篇,
第一篇讲解如何在用应用层使用iic驱动直接读写芯片mcp4728;
linux MCP4728 IIC 多路DAC输出芯片驱动(一)-优快云博客
第二篇讲解使用字符驱动,应用层调用open、read、write等读写芯片mcp4728;
linux MCP4728 IIC 多路DAC输出芯片驱动(二)-优快云博客
第三篇则讲解使用iiO驱动模型生成对应的文件,应用层调用cat、echo等读写芯片mcp4728;
linux MCP4728 IIC 多路DAC输出芯片驱动(三)-优快云博客
1.应用程读写iic
I2CADDR:0x60
1.1应用程读iic封装
/**********************
i2c读函数,
参数1:从设备地址,
参数2:寄存器地址,
参数3:读取数据缓冲区,
参数4:读取数据大小
*************************************/
unsigned char _i2c_read(unsigned char device_addr, unsigned char sub_addr, unsigned char *buff, int ByteNo)
{
int fd, ret;
unsigned char buftmp[32];
struct i2c_rdwr_ioctl_data i2c_data;
//----------------------------------
//device_addr >>= 1;
//init
fd = open(i2c_dev, O_RDWR);
if (fd<0)
{
printf("not have /dev/i2c-1 t\r\n");
return -1;
}
i2c_data.nmsgs = 2;
i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs *sizeof(struct i2c_msg));
if (i2c_data.msgs == NULL)
{
printf("malloc error");
close(fd);
return -1;
}
ioctl(fd, I2C_TIMEOUT, 1);
ioctl(fd, I2C_RETRIES, 2);
//write reg
memset(buftmp, 0, 32);
buftmp[0] = sub_addr;
i2c_data.msgs[0].len = 1;
i2c_data.msgs[0].addr = device_addr;
i2c_data.msgs[0].flags = 0; // 0: write 1:read
i2c_data.msgs[0].buf = buftmp; //read data
i2c_data.msgs[1].len = ByteNo;
i2c_data.msgs[1].addr = device_addr;
i2c_data.msgs[1].flags = 1; // 0: write 1:read
i2c_data.msgs[1].buf = buff;
ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data);
if (ret < 0)
{
printf("read data %x %x error\r\n", device_addr, sub_addr);
close(fd);
free(i2c_data.msgs);
return 1;
}
free(i2c_data.msgs);
close(fd);
#if 1
int i;
printf("i2c__read 0x%02x:",buftmp[0]);
for (i = 0; i < ByteNo; i++)
{
printf(" 0x%02x",buff[i]);
}
printf("\n");
#endif
return 0;
}
1.2应用层写iic
/***************************
i2c写函数,
参数1:从设备地址,
参数2:寄存器地址,
参数3:要写入的数据缓冲区,
参数4:写入数据大小
**********************/
unsigned char _i2c_write(unsigned char device_addr, unsigned char sub_addr, unsigned char *buff, int ByteNo)
{
int fd, ret;
unsigned char buftmp[32];
struct i2c_rdwr_ioctl_data i2c_data;
//----------------------------------
//device_addr >>= 1;
//init
fd = open(i2c_dev, O_RDWR);
if (fd < 0)
{
printf("not have /dev/i2c-1\r\n");
return -1;
}
i2c_data.nmsgs = 1;
i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs *sizeof(struct i2c_msg));
if (i2c_data.msgs == NULL)
{
printf("malloc error");
close(fd);
return -1;
}
ioctl(fd, I2C_TIMEOUT, 1);
ioctl(fd, I2C_RETRIES, 2);
memset(buftmp, 0, 32);
buftmp[0] = sub_addr;
memcpy(buftmp + 1, buff, ByteNo);
i2c_data.msgs[0].len = ByteNo + 1;;
i2c_data.msgs[0].addr = device_addr;
i2c_data.msgs[0].flags = 0; // 0: write 1:read
i2c_data.msgs[0].buf = buftmp;
ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data);
if (ret < 0)
{
printf("write reg %x %x error\r\n", device_addr, sub_addr);
close(fd);
free(i2c_data.msgs);
return 1;
}
free(i2c_data.msgs);
close(fd);
#if 1
int i;
printf("i2c_write 0x%02x:",buftmp[0]);
for(i=0; i<ByteNo; i++)
{
printf(" 0x%02x",buftmp[1+i]);
}
printf("\n");
#endif
_alpu_delay_ms(100);
return 0;
}
有了应用层操作iic的接口,那么接下来我们就直接操作芯片;
2.MCP4728寄存器读写
2.1mcp4728芯片简单讲解
下面我们主要讲解快速操作祥光部分,其他的详细讲解见《linux MCP4728 IIC 多路输出芯片驱动(二)》
根据以上藐视,可对单独的通道进行读写,具体指令分析如下:
1st byte:地址和读写状态
2nd byte: 7-3bit:固定,具体查看手册
2-1bit:通道选择
0bit:EEPROM同步
根据上述分析可以得到都每个通道单独的读指令:
第一字节:地址和读写标志;
第二字节:功能和通道选择等;c2 c1 c0 W1 W0固定即可;DAC1 DAC0选择通道;UDAC:0同步的吧eeprom;因此四路的读写给功能码为:0x58 0x5A;0x5C;0x5E
2.2计算公式
outV = (add * valueR)/4096;
valueR: 输入寄存器的值
outV :要输出的电压
即:输出电压和输入值的关系如下:
valueR = 1241*outV;
比如输出1.5v寄存器的值为:1241 * 1.5 = 1861
2.2分析读指令
iic_id:0x60
以A通到为列读指令:0x60 0x58读取三字节,结合上面封装的接口:
输出:0xc0 0x07 0x45 其中0x0745 为1861; 代表为1.5v
2.3分析写指令
iic_id:0x60
以A通到为列读指令:0x60 0x58读取三字节,结合上面封装的接口:
输出:0xc0 0x07 0x45 其中0x0745 为1861; 代表为1.5v
2.4完整代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define i2c_dev "/dev/i2c-5"
/**************************** 延时函数(ms) *******************************************/
void _alpu_delay_ms(unsigned int i)
{
usleep(2000 * i);
}
/**********************
i2c读函数,
参数1:从设备地址,
参数2:寄存器地址,
参数3:读取数据缓冲区,
参数4:读取数据大小
*************************************/
unsigned char _i2c_read(unsigned char device_addr, unsigned char sub_addr, unsigned char *buff, int ByteNo)
{
int fd, ret;
unsigned char buftmp[32];
struct i2c_rdwr_ioctl_data i2c_data;
//----------------------------------
//device_addr >>= 1;
//init
fd = open(i2c_dev, O_RDWR);
if (fd<0)
{
printf("not have /dev/i2c-1 t\r\n");
return -1;
}
i2c_data.nmsgs = 2;
i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs *sizeof(struct i2c_msg));
if (i2c_data.msgs == NULL)
{
printf("malloc error");
close(fd);
return -1;
}
ioctl(fd, I2C_TIMEOUT, 1);
ioctl(fd, I2C_RETRIES, 2);
//write reg
memset(buftmp, 0, 32);
buftmp[0] = sub_addr;
i2c_data.msgs[0].len = 1;
i2c_data.msgs[0].addr = device_addr;
i2c_data.msgs[0].flags = 0; // 0: write 1:read
i2c_data.msgs[0].buf = buftmp; //read data
i2c_data.msgs[1].len = ByteNo;
i2c_data.msgs[1].addr = device_addr;
i2c_data.msgs[1].flags = 1; // 0: write 1:read
i2c_data.msgs[1].buf = buff;
ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data);
if (ret < 0)
{
printf("read data %x %x error\r\n", device_addr, sub_addr);
close(fd);
free(i2c_data.msgs);
return 1;
}
free(i2c_data.msgs);
close(fd);
#if 1
int i;
printf("i2c__read 0x%02x:",buftmp[0]);
for (i = 0; i < ByteNo; i++)
{
printf(" 0x%02x",buff[i]);
}
printf("\n");
#endif
return 0;
}
/***************************
i2c写函数,
参数1:从设备地址,
参数2:寄存器地址,
参数3:要写入的数据缓冲区,
参数4:写入数据大小
**********************/
unsigned char _i2c_write(unsigned char device_addr, unsigned char sub_addr, unsigned char *buff, int ByteNo)
{
int fd, ret;
unsigned char buftmp[32];
struct i2c_rdwr_ioctl_data i2c_data;
//----------------------------------
//device_addr >>= 1;
//init
fd = open(i2c_dev, O_RDWR);
if (fd < 0)
{
printf("not have /dev/i2c-1\r\n");
return -1;
}
i2c_data.nmsgs = 1;
i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs *sizeof(struct i2c_msg));
if (i2c_data.msgs == NULL)
{
printf("malloc error");
close(fd);
return -1;
}
ioctl(fd, I2C_TIMEOUT, 1);
ioctl(fd, I2C_RETRIES, 2);
memset(buftmp, 0, 32);
buftmp[0] = sub_addr;
memcpy(buftmp + 1, buff, ByteNo);
i2c_data.msgs[0].len = ByteNo + 1;;
i2c_data.msgs[0].addr = device_addr;
i2c_data.msgs[0].flags = 0; // 0: write 1:read
i2c_data.msgs[0].buf = buftmp;
ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data);
if (ret < 0)
{
printf("write reg %x %x error\r\n", device_addr, sub_addr);
close(fd);
free(i2c_data.msgs);
return 1;
}
free(i2c_data.msgs);
close(fd);
#if 1
int i;
printf("i2c_write 0x%02x:",buftmp[0]);
for(i=0; i<ByteNo; i++)
{
printf(" 0x%02x",buftmp[1+i]);
}
printf("\n");
#endif
_alpu_delay_ms(100);
return 0;
}
int main()
{
unsigned char buff[512] = {0x00, 0x01, 0x02};
_i2c_read(0x60, 0x58, buff, 3) ;
_i2c_read(0x60, 0x5A, buff, 3) ;
_i2c_read(0x60, 0x5C, buff, 3) ;
_i2c_read(0x60, 0x5E, buff, 3) ;
_alpu_delay_ms(100);
buff[0] = 0x07;
buff[1] = 0x45;
_i2c_write(0x60, 0x58, buff, 2) ;
_i2c_write(0x60, 0x5A, buff, 2) ;
_i2c_write(0x60, 0x5C, buff, 2) ;
_i2c_write(0x60, 0x5E, buff, 2) ;
_alpu_delay_ms(100);
_i2c_read(0x60, 0x5E, buff, 3) ;
return 0;
}
3.总结
该方法能够快速实现功能,不需要掌握能内核、驱动等开发技能。