樹莓派 HATs ︰ I2C 拾遺《二》

學習者切莫怕閱讀!有時直須對照文字,程式設計構想自然顯現也☆

SMBus Protocol Summary
======================
The following is a summary of the SMBus protocol. It applies to all revisions of the protocol (1.0, 1.1, and 2.0).
Certain protocol features which are not supported by this package are briefly described at the end of this document.

Some adapters understand only the SMBus (System Management Bus) protocol, which is a subset from the I2C protocol. Fortunately, many devices use only the same subset, which makes it possible to put them on an SMBus.

If you write a driver for some I2C device, please try to use the SMBus commands if at all possible (if the device uses only that subset of the I2C protocol). This makes it possible to use the device driver on both SMBus adapters and I2C adapters (the SMBus command set is automatically translated to I2C on I2C adapters, but plain I2C commands can not be handled at all on most pure SMBus adapters).

Below is a list of SMBus protocol operations, and the functions executing them. Note that the names used in the SMBus protocol specifications usually don’t match these function names. For some of the operations which pass a single data byte, the functions using SMBus protocol operation names execute a different protocol operation entirely.

Each transaction type corresponds to a functionality flag. Before calling a transaction function, a device driver should always check (just once) for the corresponding functionality flag to ensure that the underlying I2C adapter supports the transaction in question. See <file:Documentation/i2c/functionality> for the details.

Key to symbols
==============

S (1 bit) : Start bit
P (1 bit) : Stop bit
Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.
A, NA (1 bit) : Accept and reverse accept bit.
Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to get a 10 bit I2C address.
Comm (8 bits): Command byte, a data byte which often selects a register on the device.
Data (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh for 16 bit data.
Count (8 bits): A data byte containing the length of a block operation.

[..]: Data sent by I2C device, as opposed to data sent by the host adapter.

SMBus Quick Command
===================

This sends a single bit to the device, at the place of the Rd/Wr bit.

A Addr Rd/Wr [A] P

Functionality flag: I2C_FUNC_SMBUS_QUICK

 

SMBus Receive Byte: i2c_smbus_read_byte()
==========================================

This reads a single byte from a device, without specifying a device register. Some devices are so simple that this interface is enough; for others, it is a shorthand if you want to read the same register as in the previous SMBus command.

S Addr Rd [A] [Data] NA P

Functionality flag: I2C_FUNC_SMBUS_READ_BYTE

 

SMBus Send Byte: i2c_smbus_write_byte()
========================================

This operation is the reverse of Receive Byte: it sends a single byte to a device. See Receive Byte for more information.

S Addr Wr [A] Data [A] P

Functionality flag: I2C_FUNC_SMBUS_WRITE_BYTE

 

SMBus Read Byte: i2c_smbus_read_byte_data()
============================================

This reads a single byte from a device, from a designated register.
The register is specified through the Comm byte.

S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P

Functionality flag: I2C_FUNC_SMBUS_READ_BYTE_DATA

 

SMBus Read Word: i2c_smbus_read_word_data()
============================================

This operation is very like Read Byte; again, data is read from a device, from a designated register that is specified through the Comm byte. But this time, the data is a complete word (16 bits).

S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P

Functionality flag: I2C_FUNC_SMBUS_READ_WORD_DATA

Note the convenience function i2c_smbus_read_word_swapped is available for reads where the two data bytes are the other way around (not SMBus compliant, but very popular.)

 

SMBus Write Byte: i2c_smbus_write_byte_data()
==============================================

This writes a single byte to a device, to a designated register. The register is specified through the Comm byte. This is the opposite of the Read Byte operation.

S Addr Wr [A] Comm [A] Data [A] P

Functionality flag: I2C_FUNC_SMBUS_WRITE_BYTE_DATA

 

SMBus Write Word: i2c_smbus_write_word_data()
==============================================

This is the opposite of the Read Word operation. 16 bits of data is written to a device, to the designated register that is specified through the Comm byte.

S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P

Functionality flag: I2C_FUNC_SMBUS_WRITE_WORD_DATA

Note the convenience function i2c_smbus_write_word_swapped is available for writes where the two data bytes are the other way around (not SMBus compliant, but very popular.)

……

 

CAT24C32 Data Sheet

cat24c32-w1

 

cat24c32-w2

 

cat24c32-r1

 

cat24c32-2

 

 

eeprog-0.7.6-tear12.tar.gz

/***************************************************************************
copyright : (C) by 2003-2004 Stefano Barbato
email : stefano@codesink.org

Copyright (C) 2011 by Kris Rusocki <kszysiu@gmail.com>
– support for user-defined write cycle time

Id: 24cXX.c,v 1.5 2004/02/29 11:05:28 tat Exp
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include “24cXX.h”

static int i2c_write_1b(struct eeprom *e, __u8 buf)
{
int r;
// we must simulate a plain I2C byte write with SMBus functions
r = i2c_smbus_write_byte(e->fd, buf);
if(r < 0)
fprintf(stderr, “Error i2c_write_1b: %s\n”, strerror(errno));
usleep(10);
return r;
}

 

static int i2c_write_2b(struct eeprom *e, __u8 buf[2])
{
int r;
// we must simulate a plain I2C byte write with SMBus functions
r = i2c_smbus_write_byte_data(e->fd, buf[0], buf[1]);
if(r < 0)
fprintf(stderr, “Error i2c_write_2b: %s\n”, strerror(errno));
usleep(10);
return r;
}

 

static int i2c_write_3b(struct eeprom *e, __u8 buf[3])
{
int r;
// we must simulate a plain I2C byte write with SMBus functions
// the __u16 data field will be byte swapped by the SMBus protocol
r = i2c_smbus_write_word_data(e->fd, buf[0], buf[2] << 8 | buf[1]);
if(r < 0)
fprintf(stderr, “Error i2c_write_3b: %s\n”, strerror(errno));
usleep(10);
return r;
}

#define CHECK_I2C_FUNC( var, label ) \
do { if(0 == (var & label)) { \
fprintf(stderr, “\nError: ” \
#label ” function is required. Program halted.\n\n”); \
exit(1); } \
} while(0);

 

int eeprom_open(char *dev_fqn, int addr, int type, int write_cycle_time, struct eeprom* e)
{
int funcs, fd, r;
e->fd = e->addr = 0;
e->dev = 0;

fd = open(dev_fqn, O_RDWR);
if(fd <= 0)
{
fprintf(stderr, “Error eeprom_open: %s\n”, strerror(errno));
return -1;
}

// get funcs list
if((r = ioctl(fd, I2C_FUNCS, &funcs) < 0))
{
fprintf(stderr, “Error eeprom_open: %s\n”, strerror(errno));
return -1;
}
// check for req funcs
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_BYTE );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_BYTE );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_BYTE_DATA );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_BYTE_DATA );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_WORD_DATA );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_WORD_DATA );

// set working device
if( ( r = ioctl(fd, I2C_SLAVE, addr)) < 0)
{
fprintf(stderr, “Error eeprom_open: %s\n”, strerror(errno));
return -1;
}
e->fd = fd;
e->addr = addr;
e->dev = dev_fqn;
e->type = type;
e->write_cycle_time = write_cycle_time;
return 0;
}

 

int eeprom_close(struct eeprom *e)
{
close(e->fd);
e->fd = -1;
e->dev = 0;
e->type = EEPROM_TYPE_UNKNOWN;
return 0;
}

 

#if 0
int eeprom_24c32_write_byte(struct eeprom *e, __u16 mem_addr, __u8 data)
{
__u8 buf[3] = { (mem_addr >> 8) & 0x00ff, mem_addr & 0x00ff, data };
return i2c_write_3b(e, buf);
}

 

int eeprom_24c32_read_current_byte(struct eeprom* e)
{
ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
return i2c_smbus_read_byte(e->fd);
}

 

int eeprom_24c32_read_byte(struct eeprom* e, __u16 mem_addr)
{
int r;
ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
__u8 buf[2] = { (mem_addr >> 8) & 0x0ff, mem_addr & 0x0ff };
r = i2c_write_2b(e, buf);
if (r < 0)
return r;
r = i2c_smbus_read_byte(e->fd);
return r;
}
#endif

 

int eeprom_read_current_byte(struct eeprom* e)
{
ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
return i2c_smbus_read_byte(e->fd);
}

 

int eeprom_read_byte(struct eeprom* e, __u16 mem_addr)
{
int r;
ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
if(e->type == EEPROM_TYPE_8BIT_ADDR)
{
__u8 buf = mem_addr & 0x0ff;
r = i2c_write_1b(e, buf);
} else if(e->type == EEPROM_TYPE_16BIT_ADDR) {
__u8 buf[2] = { (mem_addr >> 8) & 0x0ff, mem_addr & 0x0ff };
r = i2c_write_2b(e, buf);
} else {
fprintf(stderr, “ERR: unknown eeprom type\n”);
return -1;
}
if (r < 0)
return r;
r = i2c_smbus_read_byte(e->fd);
return r;
}

 

int eeprom_write_byte(struct eeprom *e, __u16 mem_addr, __u8 data)
{
int ret;

if(e->type == EEPROM_TYPE_8BIT_ADDR) {
__u8 buf[2] = { mem_addr & 0x00ff, data };
ret = i2c_write_2b(e, buf);
if (ret == 0 && e->write_cycle_time != 0) {
usleep(1000 * e->write_cycle_time);
}
return ret;
} else if(e->type == EEPROM_TYPE_16BIT_ADDR) {
__u8 buf[3] =
{ (mem_addr >> 8) & 0x00ff, mem_addr & 0x00ff, data };
ret = i2c_write_3b(e, buf);
if (ret == 0 && e->write_cycle_time != 0) {
usleep(1000 * e->write_cycle_time);
}
return ret;
}
fprintf(stderr, “ERR: unknown eeprom type\n”);
return -1;
}