Zynq Linux 读写 IIC设备 寄存器 (MAX7500和TCA9534)
目录参考文章一、MAX7500介绍二、TCA9534介绍三、Vivado工程说明四、设备树配置五、测试程序六、测试参考文章linux 上层应用 i2c读写demo程序Xilinx Wiki/Linux/Linux Drivers/Linux I2C DriverXilinx Wiki/Linux/Linux Drivers/Cadence I2C Driver一、MAX7500介绍二、TCA953
参考文章
- linux 上层应用 i2c读写demo程序
- Xilinx Wiki/Linux/Linux Drivers/Linux I2C Driver
- Xilinx Wiki/Linux/Linux Drivers/Cadence I2C Driver
一、MAX7500介绍
MAX7500-MAX7504温度传感器精确测量温度,并提供超温报警/中断/关机输出。 这些器件使用高分辨率、σ -delta、模数转换器(ADC)将温度测量数据转换为数字形式。 通信是通过一个i2c兼容的2线串行接口。 MAX7500/MAX7501/MAX7502集成了一个超时特性,提供了对I2C总线锁定的保护。 MAX7503/MAX7504不包含超时特性。
- 温度寄存器:
通过温度寄存器读取测得的温度。 温度数据格式为 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。
- 配置寄存器:
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;
}
六、测试
-
查看spi总线下设备:
ls /dev/i2c*
上面4个 iic 设备 对应设备树中指定的 i2c 控制器:aliases { i2c0 = &i2c0; i2c1 = &i2c1; i2c2 = &axi_iic_0; i2c3 = &axi_iic_1; };
-
运行测试程序
更多推荐
所有评论(0)