参考文章

  1. linux 上层应用 i2c读写demo程序
  2. Xilinx Wiki/Linux/Linux Drivers/Linux I2C Driver
  3. Xilinx Wiki/Linux/Linux Drivers/Cadence I2C Driver

一、MAX7500介绍

MAX7500-MAX7504温度传感器精确测量温度,并提供超温报警/中断/关机输出。 这些器件使用高分辨率、σ -delta、模数转换器(ADC)将温度测量数据转换为数字形式。 通信是通过一个i2c兼容的2线串行接口。 MAX7500/MAX7501/MAX7502集成了一个超时特性,提供了对I2C总线锁定的保护。 MAX7503/MAX7504不包含超时特性。
在这里插入图片描述

  1. 温度寄存器:
    在这里插入图片描述
    通过温度寄存器读取测得的温度。 温度数据格式为 9 位二进制补码,寄存器以 2 个字节读出:高字节和低字节。
  • 位 D15 是符号位。 当位 D15 为 1 时,温度读数为负。 当位 D15 为零时,温度读数为正。
  • 位 D14–D7 包含温度数据,LSB 代表 0.5°C,MSB 代表 64°C(见表 3)。 MSB 首先传输。
  • 低字节的最后 7 位,即位 D6-D0,无关紧要。 读取温度寄存器时,必须忽略位 D6–D0。
  • 当测得的温度大于+127.5°C 时,温度寄存器中存储的值被剪裁为7F8h。 当测得的温度低于-64°C 时,温度寄存器中的值被剪裁为BF8h。
    在这里插入图片描述
  1. 配置寄存器:
    在这里插入图片描述
    8 位配置寄存器设置故障队列、OS 极性、关机控制以及 OS 输出是在比较器还是中断模式下工作。 写入配置寄存器时,将位 D7、D6 和 D5 设置为零。 见表 5。
    • 位 D4 和 D3,故障队列位,确定触发 OS 条件所需的故障数量。 请参见表 6。队列中设置的故障数量必须发生才能使 OS 输出跳闸。 故障队列可防止操作系统在嘈杂环境中误跳闸。
    • 将位 D2(OS 极性位)设置为零以强制 OS 输出低电平有效。 将位 D2 设置为 1 以将 OS 输出极性设置为高电平有效。 OS在任何情况下都是开漏输出,需要上拉电阻输出高电压。 见图 4。
    • 将比较器/中断位 D1 设置为零以在比较器模式下运行过热关断模块。 在比较器模式下,当温度升高到 TOS 值以上时,OS 被断言。 当温度低于 THYST 值时,OS 被取消断言。
    • 对于正常操作,将位 D0(关闭位)设置为零。 将位 D0 设置为 1 可关闭 MAX7500–MAX7504 内部模块,将电源电流降至 3μA。 只要关闭位被设置,I2C 接口就会保持活动状态。 TOS、THYST 和配置寄存器在关断期间仍可写入和读取。

二、TCA9534介绍

TCA9534是一个16引脚器件,提供8位通用并行输入输出(I/O)扩展,用于两线双向I2C总线(或SMBus)协议。 该设备可以在1.65 V到5.5 V的电源电压范围内运行,这允许与多种设备一起使用。 设备支持100 khz(标准模式)和400 khz(快速模式)时钟频率。 当开关、传感器、按钮、led、风扇和其他类似设备需要额外的I/O时,像TCA9534这样的I/O扩展器提供了一个简单的解决方案。

  • I2C to Parallel Port Expander
  • 5-V Tolerant I/O Ports
  • 400-kHz Fast I2C Bus

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、Vivado工程说明

TCA9534 连接到 PS 端 IIC_0 上:
MAX7500 连接到 AXI_IIC_0 上:
在这里插入图片描述
在这里插入图片描述

四、设备树配置

指定 IIC 设备 编号, 其他内容不需要修改:

aliases {
	i2c0 = &i2c0;
	i2c1 = &i2c1;
	i2c2 = &axi_iic_0;
	i2c3 = &axi_iic_1;
};
	

RTC 设备 配置如下,可参考 ZYNQ:Linux添加I2C-RTC驱动

&i2c1 {
	clock-frequency = <400000>;
	status = "okay";
	
	// 添加 RTC 设备节点, @68 代表设备地址
    rtc: rtc-ds1338@68 {
		// 根据 drivers/rtc/rtc-ds1307.c 中的 compatible 表找到的。
		compatible = "dallas,ds1338"; 
		// 设备地址
		reg = <0x68>; // PDF P13 : 1101 000 R/nW , 0x68+ R/nW 
    };
};

五、测试程序

#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>


#define I2C_EXP_IO_FILE_NAME 	"/dev/i2c-0"
#define I2C_RTC_FILE_NAME 		"/dev/i2c-1"
#define I2C_TEMP_FILE_NAME 		"/dev/i2c-2"
#define I2C_FAN_FILE_NAME 		"/dev/i2c-3"

#define LED_ON	1
#define LED_OFF	0

#define RTC_I2C_ADDRESS 		0x68//1101000+R/W
#define EXT_IO_I2C_ADDRESS		0x20
#define TEMP_1_I2C_ADDRESS		0x48
#define TEMP_2_I2C_ADDRESS		0x49
#define TEMP_3_I2C_ADDRESS		0x4A
#define TEMP_4_I2C_ADDRESS		0x4B
#define FAN_I2C_ADDRESS			0x00

#define TCA9534_INPUT_PORT_REG 				0x00 //Input Port Register
#define TCA9534_OUTPUT_PORT_REG 			0x01 //Output Port Register
#define TCA9534_POLARITY_INVERSION_REG 		0x02 //Polarity Inversion Register
#define TCA9534_CONFIGURATION_REG 			0x03 //Configuration Register

#define CFG_EN_5V0_OUTPUT_BIT				(0x01)
#define CFG_LED_RUN_OUTPUT_BIT				(0x01 << 1)
#define CFG_LED_PWR_OUTPUT_BIT				(0x01 << 2)

#define CFG_EN_5V0_ON_BIT					(0x01)
#define CFG_LED_RUN_ON_BIT					(0x01 << 1)
#define CFG_LED_PWR_ON_BIT					(0x01 << 2)

#define MAX7500_TEMPERATURE_REG 			0x00
#define MAX7500_CONFIGURATION_REG 			0x01


int set_i2c_register(int file, unsigned char addr, unsigned char reg, unsigned char* value, unsigned char len)
{
    unsigned char outbuf[len + 1];
    struct i2c_rdwr_ioctl_data packets;
    struct i2c_msg messages[1];
 
    messages[0].addr	= addr;
    messages[0].flags	= 0;
    messages[0].len		= sizeof(outbuf);
    messages[0].buf		= outbuf;
 
    /* The first byte indicates which register we'll write */
    outbuf[0] = reg;
 
    /* 
     * The second byte indicates the value to write.  Note that for many
     * devices, we can write multiple, sequential registers at once by
     * simply making outbuf bigger.
     */
	for(int i = 0; i < len; i++)
	{
	    outbuf[i + 1] = value[i];
	}
 
    /* Transfer the i2c packets to the kernel and verify it worked */
    packets.msgs  = messages;
    packets.nmsgs = 1;
    if(ioctl(file, I2C_RDWR, &packets) < 0) {
        perror("Unable to send data");
        return -1;
    }
 
    return 0;
}
 
 
int get_i2c_register(int file, unsigned char addr, unsigned char reg, unsigned char *val, unsigned char len) 
{
    unsigned char outbuf;
    //unsigned char inbuf[len];
    struct i2c_rdwr_ioctl_data packets;
    struct i2c_msg messages[2];
 
    /*
     * In order to read a register, we first do a "dummy write" by writing
     * 0 bytes to the register we want to read from.  This is similar to
     * the packet in set_i2c_register, except it's 1 byte rather than 2.
     */
    outbuf = reg;
    messages[0].addr  = addr;
    messages[0].flags = 0;
    messages[0].len   = sizeof(outbuf);
    messages[0].buf   = &outbuf;
 
    /* The data will get returned in this structure */
    messages[1].addr  = addr;
    messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
    messages[1].len   = len;
    messages[1].buf   = val;
 
    /* Send the request to the kernel and get the result back */
    packets.msgs      = messages;
    packets.nmsgs     = 2;
    if(ioctl(file, I2C_RDWR, &packets) < 0) {
        perror("Unable to send data");
        return -1;
    }
    //*val = inbuf;
 
    return 0;
}


int iic_open(char* iic_path)
{
	int file = 0;
	// Open a connection to the I2C userspace control file.
	if ((file = open(iic_path, O_RDWR)) < 0) {
		perror("Unable to open i2c control file");
		exit(-1);
	}
	return file;
}



void iic_close(int file)
{
	close(file);
}


int set_iic_led_run(int on_off)
{
	int ret = 0;
	int fd = 0;
	unsigned char value = 0;
	unsigned char tmp = 0;
	fd = iic_open(I2C_EXP_IO_FILE_NAME);
	if(fd < 0)
		return -1;
	 
	ret = get_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_CONFIGURATION_REG, &value, sizeof(value));
	if(ret < 0)
		return -1;
	if(value & CFG_LED_RUN_OUTPUT_BIT){
		tmp = value & ~CFG_LED_RUN_OUTPUT_BIT;
		ret = set_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_CONFIGURATION_REG, &tmp, sizeof(tmp));//设置为输出
		if(ret < 0)
			return -1;
	}

	ret = get_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_OUTPUT_PORT_REG, &value, sizeof(value));
	if(ret < 0)
		return -1;
	if(on_off){//LED ON
		if(0 == (value & CFG_LED_RUN_ON_BIT)){
			tmp = value | CFG_LED_RUN_ON_BIT;
			ret = set_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_OUTPUT_PORT_REG, &tmp , sizeof(tmp));//设置为ON
			if(ret < 0)
				return -1;
		}
	}else{//LED OFF
		if(value & CFG_LED_RUN_ON_BIT){
			tmp = value & ~CFG_LED_RUN_ON_BIT;
			ret = set_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_OUTPUT_PORT_REG, &tmp , sizeof(tmp));//设置为OFF
			if(ret < 0)
				return -1;
		}
	}

	iic_close(fd);
	
	return 0;
}

int set_iic_led_pwr(int on_off)
{
	int ret = 0;
	int fd = 0;
	unsigned char value = 0;
	unsigned char tmp = 0;
	fd = iic_open(I2C_EXP_IO_FILE_NAME);
	if(fd < 0)
		return -1;
	 
	ret = get_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_CONFIGURATION_REG, &value, sizeof(value));
	if(ret < 0)
		return -1;
	if(value & CFG_LED_PWR_OUTPUT_BIT){
		tmp = value & ~CFG_LED_PWR_OUTPUT_BIT;
		ret = set_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_CONFIGURATION_REG, &tmp, sizeof(tmp));//设置为输出
		if(ret < 0)
			return -1;
	}

	ret = get_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_OUTPUT_PORT_REG, &value, sizeof(value));
	if(ret < 0)
		return -1;
	if(on_off){//LED ON
		if(0 == (value & CFG_LED_PWR_ON_BIT)){
			tmp = value | CFG_LED_PWR_ON_BIT;
			ret = set_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_OUTPUT_PORT_REG, &tmp , sizeof(tmp));//设置为ON
			if(ret < 0)
				return -1;
		}
	}else{//LED OFF
		if(value & CFG_LED_PWR_ON_BIT){
			tmp = value & ~CFG_LED_PWR_ON_BIT;
			ret = set_i2c_register(fd, EXT_IO_I2C_ADDRESS, TCA9534_OUTPUT_PORT_REG, &tmp , sizeof(tmp));//设置为OFF
			if(ret < 0)
				return -1;
		}
	}

	iic_close(fd);

	return 0;
}

float get_temp_value(unsigned char slave_addr)
{
	int ret = 0;
	int fd = 0;
	unsigned char value[2] = {0};
	unsigned short tmp = 0;
	float temp = 0;
	fd = iic_open(I2C_TEMP_FILE_NAME);
	if(fd < 0)
		return -1;
		
	ret = get_i2c_register(fd, slave_addr, MAX7500_TEMPERATURE_REG, value, sizeof(value));
	if(ret < 0)
		return -1;
		
	tmp = value[0] << 8 | value[1];
	tmp = tmp >> 7;
	for(int i = 0; i < 8; i++)
	{
		temp += ((tmp >> i) & 0x01) * (0.5 * (0x01 << i));
	}
//	printf("temp = %f\n", temp);

	if(tmp & 0x8000){//负数

	}else{//正数

	}

	iic_close(fd);
	
	return temp;
}


float get_device_temp_max()
{
	float temp = 0;
	float tmp[4] = {0};
	
	tmp[0] = get_temp_value(TEMP_1_I2C_ADDRESS);
	tmp[1] = get_temp_value(TEMP_2_I2C_ADDRESS);
	tmp[2] = get_temp_value(TEMP_3_I2C_ADDRESS);
	tmp[3] = get_temp_value(TEMP_4_I2C_ADDRESS);

	for(int i = 0; i < 4; i++){
		printf("\ttemp[%d] = %f C\n", i, tmp[i]);
	}

	temp = tmp[0];

	for(int i = 0; i < 3; i++)
	{
		temp = temp >= tmp[i+1] ? temp : tmp[i+1];
	}
	
	return temp;
}

int main()
{
    int ret;   
    
    printf("LED_IIC Control:\n");
	ret = set_iic_led_pwr(LED_ON);
	if(0 == ret)
		printf("\tLed_Power is ON\n");
	else{
		printf("Led_Power Set Failed!\n");
		return -1;
	}
	ret = set_iic_led_run(LED_ON);
	if(0 == ret)
		printf("\tLed_Run is ON\n");
	else{
		printf("Led_Run Set Failed!\n");
		return -1;
	}

	printf("Temp Sensor(MAX7500):\n");
	get_device_temp_max();
	
	return 0;
}



六、测试

  1. 查看spi总线下设备:

    ls /dev/i2c*
    

    在这里插入图片描述
    上面4个 iic 设备 对应设备树中指定的 i2c 控制器:

    aliases {
    	i2c0 = &i2c0;
    	i2c1 = &i2c1;
    	i2c2 = &axi_iic_0;
    	i2c3 = &axi_iic_1;
    };
    
  2. 运行测试程序
    在这里插入图片描述
    在这里插入图片描述

Logo

更多推荐