add: 04_I2C/06_编写APP直接访问EEPROM
143
IMX6ULL/doc_pic/04_I2C/06_编写APP直接访问EEPROM.md
Normal file
@@ -0,0 +1,143 @@
|
||||
## 编写APP直接访问EEPROM
|
||||
|
||||
参考资料:
|
||||
|
||||
* Linux驱动程序: `drivers/i2c/i2c-dev.c`
|
||||
* I2C-Tools-4.2: `https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/`
|
||||
* AT24cxx.pdf
|
||||
|
||||
本节源码:GIT仓库中
|
||||
|
||||
* `doc_and_source_for_drivers\IMX6ULL\source\04_I2C\01_at24c02_test`
|
||||
* `doc_and_source_for_drivers\STM32MP157\source\A7\04_I2C\01_at24c02_test`
|
||||
|
||||
### 1. 硬件连接
|
||||
|
||||
* STM32MP157的I2C模块连接方法
|
||||

|
||||
|
||||
* IMX6ULL的I2C模块连接方法
|
||||

|
||||
|
||||
|
||||
|
||||
### 2. AT24C02访问方法
|
||||
|
||||
#### 2.1 设备地址
|
||||
|
||||
从芯片手册上可以知道,AT24C02的设备地址跟它的A2、A1、A0引脚有关:
|
||||
|
||||

|
||||
|
||||
打开I2C模块的原理图(这2个文件是一样的):
|
||||
|
||||
* `STM32MP157\开发板配套资料\原理图\04_Extend_modules(外设模块)\eeprom.zip\i2c_eeprom_module_v1.0.pdf`
|
||||
* `IMX6ULL\开发板配套资料\原理图\Extend_modules\eeprom.zip\i2c_eeprom_module_v1.0.pdf`
|
||||
* 如下:
|
||||

|
||||
|
||||
从原理图可知,A2A1A0都是0,所以AT24C02的设备地址是:0b1010000,即0x50。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 2.2 写数据
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 2.3 读数据
|
||||
|
||||
可以读1个字节,也可以连续读出多个字节。
|
||||
连续读多个字节时,芯片内部的地址会自动累加。
|
||||
当地址到达存储空间最后一个地址时,会从0开始。
|
||||
|
||||

|
||||
|
||||
### 3. 使用I2C-Tools的函数编程
|
||||
|
||||
|
||||
|
||||
### 4. 编译
|
||||
|
||||
#### 4.1 在Ubuntu设置交叉编译工具链
|
||||
|
||||
* STM32MP157
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||
```
|
||||
|
||||
* IMX6ULL
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.2 使用I2C-Tools的源码
|
||||
|
||||

|
||||
|
||||
#### 4.3 编译
|
||||
|
||||
为IMX6ULL编译时,有如下错误:
|
||||

|
||||
|
||||
这是因为IMX6ULL的工具链自带的include目录中,没有smbus.h。
|
||||
|
||||
需要我们自己提供这个头文件,解决方法:
|
||||
|
||||
* 提供头文件:
|
||||

|
||||
|
||||
* 修改Makefile指定头文件目录
|
||||
|
||||
```shell
|
||||
all:
|
||||
$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 4.4 上机测试
|
||||
|
||||
**以下命令在开发板中执行。**
|
||||
|
||||
* 挂载NFS
|
||||
|
||||
* vmware使用NAT(假设windowsIP为192.168.1.100)
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
|
||||
192.168.1.100:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 复制、执行程序
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# cp /mnt/at24c02_test /bin
|
||||
[root@100ask:~]# at24c02_test 0 w www.100ask.net
|
||||
[root@100ask:~]# at24c02_test 0 r
|
||||
get data: www.100ask.net
|
||||
```
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 886 KiB |
|
After Width: | Height: | Size: 876 KiB |
BIN
IMX6ULL/doc_pic/04_I2C/pic/04_I2C/038_at24c02_device_address.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
IMX6ULL/doc_pic/04_I2C/pic/04_I2C/039_at24c02_sch.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
IMX6ULL/doc_pic/04_I2C/pic/04_I2C/040_at24c02_byte_write.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
IMX6ULL/doc_pic/04_I2C/pic/04_I2C/041_at24c02_byte_read.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
3
IMX6ULL/source/04_I2C/01_at24c02_test/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
all:
|
||||
$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
|
||||
|
||||
102
IMX6ULL/source/04_I2C/01_at24c02_test/at24c02_test.c
Normal file
@@ -0,0 +1,102 @@
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <i2c/smbus.h>
|
||||
#include "i2cbusses.h"
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/* ./at24c02 <i2c_bus_number> w "100ask.taobao.com"
|
||||
* ./at24c02 <i2c_bus_number> r
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned char dev_addr = 0x50;
|
||||
unsigned char mem_addr = 0;
|
||||
unsigned char buf[32];
|
||||
|
||||
int file;
|
||||
char filename[20];
|
||||
unsigned char *str;
|
||||
|
||||
int ret;
|
||||
|
||||
struct timespec req;
|
||||
|
||||
if (argc != 3 && argc != 4)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf("write eeprom: %s <i2c_bus_number> w string\n", argv[0]);
|
||||
printf("read eeprom: %s <i2c_bus_number> r\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
file = open_i2c_dev(argv[1][0]-'0', filename, sizeof(filename), 0);
|
||||
if (file < 0)
|
||||
{
|
||||
printf("can't open %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_slave_addr(file, dev_addr, 1))
|
||||
{
|
||||
printf("can't set_slave_addr\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argv[2][0] == 'w')
|
||||
{
|
||||
// write str: argv[3]
|
||||
str = argv[3];
|
||||
|
||||
req.tv_sec = 0;
|
||||
req.tv_nsec = 20000000; /* 20ms */
|
||||
|
||||
while (*str)
|
||||
{
|
||||
// mem_addr, *str
|
||||
// mem_addr++, str++
|
||||
ret = i2c_smbus_write_byte_data(file, mem_addr, *str);
|
||||
if (ret)
|
||||
{
|
||||
printf("i2c_smbus_write_byte_data err\n");
|
||||
return -1;
|
||||
}
|
||||
// wait tWR(10ms)
|
||||
nanosleep(&req, NULL);
|
||||
|
||||
mem_addr++;
|
||||
str++;
|
||||
}
|
||||
ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // string end char
|
||||
if (ret)
|
||||
{
|
||||
printf("i2c_smbus_write_byte_data err\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// read
|
||||
ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(buf), buf);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("i2c_smbus_read_i2c_block_data err\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[31] = '\0';
|
||||
printf("get data: %s\n", buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
455
IMX6ULL/source/04_I2C/01_at24c02_test/i2cbusses.c
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels.
|
||||
Part of user-space programs to access for I2C
|
||||
devices.
|
||||
Copyright (c) 1999-2003 Frodo Looijaard <frodol@dds.nl> and
|
||||
Mark D. Studebaker <mdsxyz123@yahoo.com>
|
||||
Copyright (C) 2008-2012 Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* For strdup and snprintf */
|
||||
#define _BSD_SOURCE 1 /* for glibc <= 2.19 */
|
||||
#define _DEFAULT_SOURCE 1 /* for glibc >= 2.19 */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h> /* for NAME_MAX */
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <strings.h> /* for strcasecmp() */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include "i2cbusses.h"
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
|
||||
enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
|
||||
|
||||
struct adap_type {
|
||||
const char *funcs;
|
||||
const char* algo;
|
||||
};
|
||||
|
||||
static struct adap_type adap_types[5] = {
|
||||
{ .funcs = "dummy",
|
||||
.algo = "Dummy bus", },
|
||||
{ .funcs = "isa",
|
||||
.algo = "ISA bus", },
|
||||
{ .funcs = "i2c",
|
||||
.algo = "I2C adapter", },
|
||||
{ .funcs = "smbus",
|
||||
.algo = "SMBus adapter", },
|
||||
{ .funcs = "unknown",
|
||||
.algo = "N/A", },
|
||||
};
|
||||
|
||||
static enum adt i2c_get_funcs(int i2cbus)
|
||||
{
|
||||
unsigned long funcs;
|
||||
int file;
|
||||
char filename[20];
|
||||
enum adt ret;
|
||||
|
||||
file = open_i2c_dev(i2cbus, filename, sizeof(filename), 1);
|
||||
if (file < 0)
|
||||
return adt_unknown;
|
||||
|
||||
if (ioctl(file, I2C_FUNCS, &funcs) < 0)
|
||||
ret = adt_unknown;
|
||||
else if (funcs & I2C_FUNC_I2C)
|
||||
ret = adt_i2c;
|
||||
else if (funcs & (I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
ret = adt_smbus;
|
||||
else
|
||||
ret = adt_dummy;
|
||||
|
||||
close(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Remove trailing spaces from a string
|
||||
Return the new string length including the trailing NUL */
|
||||
static int rtrim(char *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--)
|
||||
s[i] = '\0';
|
||||
return i + 2;
|
||||
}
|
||||
|
||||
void free_adapters(struct i2c_adap *adapters)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; adapters[i].name; i++)
|
||||
free(adapters[i].name);
|
||||
free(adapters);
|
||||
}
|
||||
|
||||
/* We allocate space for the adapters in bunches. The last item is a
|
||||
terminator, so here we start with room for 7 adapters, which should
|
||||
be enough in most cases. If not, we allocate more later as needed. */
|
||||
#define BUNCH 8
|
||||
|
||||
/* n must match the size of adapters at calling time */
|
||||
static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n)
|
||||
{
|
||||
struct i2c_adap *new_adapters;
|
||||
|
||||
new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap));
|
||||
if (!new_adapters) {
|
||||
free_adapters(adapters);
|
||||
return NULL;
|
||||
}
|
||||
memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap));
|
||||
|
||||
return new_adapters;
|
||||
}
|
||||
|
||||
struct i2c_adap *gather_i2c_busses(void)
|
||||
{
|
||||
char s[120];
|
||||
struct dirent *de, *dde;
|
||||
DIR *dir, *ddir;
|
||||
FILE *f;
|
||||
char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX];
|
||||
int foundsysfs = 0;
|
||||
int len, count = 0;
|
||||
struct i2c_adap *adapters;
|
||||
|
||||
adapters = calloc(BUNCH, sizeof(struct i2c_adap));
|
||||
if (!adapters)
|
||||
return NULL;
|
||||
|
||||
/* look in /proc/bus/i2c */
|
||||
if ((f = fopen("/proc/bus/i2c", "r"))) {
|
||||
while (fgets(s, 120, f)) {
|
||||
char *algo, *name, *type, *all;
|
||||
int len_algo, len_name, len_type;
|
||||
int i2cbus;
|
||||
|
||||
algo = strrchr(s, '\t');
|
||||
*(algo++) = '\0';
|
||||
len_algo = rtrim(algo);
|
||||
|
||||
name = strrchr(s, '\t');
|
||||
*(name++) = '\0';
|
||||
len_name = rtrim(name);
|
||||
|
||||
type = strrchr(s, '\t');
|
||||
*(type++) = '\0';
|
||||
len_type = rtrim(type);
|
||||
|
||||
sscanf(s, "i2c-%d", &i2cbus);
|
||||
|
||||
if ((count + 1) % BUNCH == 0) {
|
||||
/* We need more space */
|
||||
adapters = more_adapters(adapters, count + 1);
|
||||
if (!adapters)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
all = malloc(len_name + len_type + len_algo);
|
||||
if (all == NULL) {
|
||||
free_adapters(adapters);
|
||||
return NULL;
|
||||
}
|
||||
adapters[count].nr = i2cbus;
|
||||
adapters[count].name = strcpy(all, name);
|
||||
adapters[count].funcs = strcpy(all + len_name, type);
|
||||
adapters[count].algo = strcpy(all + len_name + len_type,
|
||||
algo);
|
||||
count++;
|
||||
}
|
||||
fclose(f);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* look in sysfs */
|
||||
/* First figure out where sysfs was mounted */
|
||||
if ((f = fopen("/proc/mounts", "r")) == NULL) {
|
||||
goto done;
|
||||
}
|
||||
while (fgets(n, NAME_MAX, f)) {
|
||||
sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype);
|
||||
if (strcasecmp(fstype, "sysfs") == 0) {
|
||||
foundsysfs++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
if (! foundsysfs) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Bus numbers in i2c-adapter don't necessarily match those in
|
||||
i2c-dev and what we really care about are the i2c-dev numbers.
|
||||
Unfortunately the names are harder to get in i2c-dev */
|
||||
strcat(sysfs, "/class/i2c-dev");
|
||||
if(!(dir = opendir(sysfs)))
|
||||
goto done;
|
||||
/* go through the busses */
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
if (!strcmp(de->d_name, "."))
|
||||
continue;
|
||||
if (!strcmp(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
/* this should work for kernels 2.6.5 or higher and */
|
||||
/* is preferred because is unambiguous */
|
||||
len = snprintf(n, NAME_MAX, "%s/%s/name", sysfs, de->d_name);
|
||||
if (len >= NAME_MAX) {
|
||||
fprintf(stderr, "%s: path truncated\n", n);
|
||||
continue;
|
||||
}
|
||||
f = fopen(n, "r");
|
||||
/* this seems to work for ISA */
|
||||
if(f == NULL) {
|
||||
len = snprintf(n, NAME_MAX, "%s/%s/device/name", sysfs,
|
||||
de->d_name);
|
||||
if (len >= NAME_MAX) {
|
||||
fprintf(stderr, "%s: path truncated\n", n);
|
||||
continue;
|
||||
}
|
||||
f = fopen(n, "r");
|
||||
}
|
||||
/* non-ISA is much harder */
|
||||
/* and this won't find the correct bus name if a driver
|
||||
has more than one bus */
|
||||
if(f == NULL) {
|
||||
len = snprintf(n, NAME_MAX, "%s/%s/device", sysfs,
|
||||
de->d_name);
|
||||
if (len >= NAME_MAX) {
|
||||
fprintf(stderr, "%s: path truncated\n", n);
|
||||
continue;
|
||||
}
|
||||
if(!(ddir = opendir(n)))
|
||||
continue;
|
||||
while ((dde = readdir(ddir)) != NULL) {
|
||||
if (!strcmp(dde->d_name, "."))
|
||||
continue;
|
||||
if (!strcmp(dde->d_name, ".."))
|
||||
continue;
|
||||
if ((!strncmp(dde->d_name, "i2c-", 4))) {
|
||||
len = snprintf(n, NAME_MAX,
|
||||
"%s/%s/device/%s/name",
|
||||
sysfs, de->d_name,
|
||||
dde->d_name);
|
||||
if (len >= NAME_MAX) {
|
||||
fprintf(stderr,
|
||||
"%s: path truncated\n",
|
||||
n);
|
||||
continue;
|
||||
}
|
||||
if((f = fopen(n, "r")))
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found:
|
||||
if (f != NULL) {
|
||||
int i2cbus;
|
||||
enum adt type;
|
||||
char *px;
|
||||
|
||||
px = fgets(s, 120, f);
|
||||
fclose(f);
|
||||
if (!px) {
|
||||
fprintf(stderr, "%s: read error\n", n);
|
||||
continue;
|
||||
}
|
||||
if ((px = strchr(s, '\n')) != NULL)
|
||||
*px = 0;
|
||||
if (!sscanf(de->d_name, "i2c-%d", &i2cbus))
|
||||
continue;
|
||||
if (!strncmp(s, "ISA ", 4)) {
|
||||
type = adt_isa;
|
||||
} else {
|
||||
/* Attempt to probe for adapter capabilities */
|
||||
type = i2c_get_funcs(i2cbus);
|
||||
}
|
||||
|
||||
if ((count + 1) % BUNCH == 0) {
|
||||
/* We need more space */
|
||||
adapters = more_adapters(adapters, count + 1);
|
||||
if (!adapters)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
adapters[count].nr = i2cbus;
|
||||
adapters[count].name = strdup(s);
|
||||
if (adapters[count].name == NULL) {
|
||||
free_adapters(adapters);
|
||||
return NULL;
|
||||
}
|
||||
adapters[count].funcs = adap_types[type].funcs;
|
||||
adapters[count].algo = adap_types[type].algo;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
done:
|
||||
return adapters;
|
||||
}
|
||||
|
||||
static int lookup_i2c_bus_by_name(const char *bus_name)
|
||||
{
|
||||
struct i2c_adap *adapters;
|
||||
int i, i2cbus = -1;
|
||||
|
||||
adapters = gather_i2c_busses();
|
||||
if (adapters == NULL) {
|
||||
fprintf(stderr, "Error: Out of memory!\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Walk the list of i2c busses, looking for the one with the
|
||||
right name */
|
||||
for (i = 0; adapters[i].name; i++) {
|
||||
if (strcmp(adapters[i].name, bus_name) == 0) {
|
||||
if (i2cbus >= 0) {
|
||||
fprintf(stderr,
|
||||
"Error: I2C bus name is not unique!\n");
|
||||
i2cbus = -4;
|
||||
goto done;
|
||||
}
|
||||
i2cbus = adapters[i].nr;
|
||||
}
|
||||
}
|
||||
|
||||
if (i2cbus == -1)
|
||||
fprintf(stderr, "Error: I2C bus name doesn't match any "
|
||||
"bus present!\n");
|
||||
|
||||
done:
|
||||
free_adapters(adapters);
|
||||
return i2cbus;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an I2CBUS command line argument and return the corresponding
|
||||
* bus number, or a negative value if the bus is invalid.
|
||||
*/
|
||||
int lookup_i2c_bus(const char *i2cbus_arg)
|
||||
{
|
||||
unsigned long i2cbus;
|
||||
char *end;
|
||||
|
||||
i2cbus = strtoul(i2cbus_arg, &end, 0);
|
||||
if (*end || !*i2cbus_arg) {
|
||||
/* Not a number, maybe a name? */
|
||||
return lookup_i2c_bus_by_name(i2cbus_arg);
|
||||
}
|
||||
if (i2cbus > 0xFFFFF) {
|
||||
fprintf(stderr, "Error: I2C bus out of range!\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
return i2cbus;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a CHIP-ADDRESS command line argument and return the corresponding
|
||||
* chip address, or a negative value if the address is invalid.
|
||||
*/
|
||||
int parse_i2c_address(const char *address_arg, int all_addrs)
|
||||
{
|
||||
long address;
|
||||
char *end;
|
||||
long min_addr = 0x08;
|
||||
long max_addr = 0x77;
|
||||
|
||||
address = strtol(address_arg, &end, 0);
|
||||
if (*end || !*address_arg) {
|
||||
fprintf(stderr, "Error: Chip address is not a number!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (all_addrs) {
|
||||
min_addr = 0x00;
|
||||
max_addr = 0x7f;
|
||||
}
|
||||
|
||||
if (address < min_addr || address > max_addr) {
|
||||
fprintf(stderr, "Error: Chip address out of range "
|
||||
"(0x%02lx-0x%02lx)!\n", min_addr, max_addr);
|
||||
return -2;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
|
||||
{
|
||||
int file, len;
|
||||
|
||||
len = snprintf(filename, size, "/dev/i2c/%d", i2cbus);
|
||||
if (len >= (int)size) {
|
||||
fprintf(stderr, "%s: path truncated\n", filename);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
file = open(filename, O_RDWR);
|
||||
|
||||
if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
|
||||
len = snprintf(filename, size, "/dev/i2c-%d", i2cbus);
|
||||
if (len >= (int)size) {
|
||||
fprintf(stderr, "%s: path truncated\n", filename);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
file = open(filename, O_RDWR);
|
||||
}
|
||||
|
||||
if (file < 0 && !quiet) {
|
||||
if (errno == ENOENT) {
|
||||
fprintf(stderr, "Error: Could not open file "
|
||||
"`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
|
||||
i2cbus, i2cbus, strerror(ENOENT));
|
||||
} else {
|
||||
fprintf(stderr, "Error: Could not open file "
|
||||
"`%s': %s\n", filename, strerror(errno));
|
||||
if (errno == EACCES)
|
||||
fprintf(stderr, "Run as root?\n");
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
int set_slave_addr(int file, int address, int force)
|
||||
{
|
||||
/* With force, let the user read from/write to the registers
|
||||
even when a driver is also running */
|
||||
if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
|
||||
fprintf(stderr,
|
||||
"Error: Could not set address to 0x%02x: %s\n",
|
||||
address, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
44
IMX6ULL/source/04_I2C/01_at24c02_test/i2cbusses.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
i2cbusses.h - Part of the i2c-tools package
|
||||
|
||||
Copyright (C) 2004-2010 Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _I2CBUSSES_H
|
||||
#define _I2CBUSSES_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
struct i2c_adap {
|
||||
int nr;
|
||||
char *name;
|
||||
const char *funcs;
|
||||
const char *algo;
|
||||
};
|
||||
|
||||
struct i2c_adap *gather_i2c_busses(void);
|
||||
void free_adapters(struct i2c_adap *adapters);
|
||||
|
||||
int lookup_i2c_bus(const char *i2cbus_arg);
|
||||
int parse_i2c_address(const char *address_arg, int all_addrs);
|
||||
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet);
|
||||
int set_slave_addr(int file, int address, int force);
|
||||
|
||||
#define MISSING_FUNC_FMT "Error: Adapter does not have %s capability\n"
|
||||
|
||||
#endif
|
||||
52
IMX6ULL/source/04_I2C/01_at24c02_test/include/i2c/smbus.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
smbus.h - SMBus level access helper functions
|
||||
|
||||
Copyright (C) 1995-1997 Simon G. Vogl
|
||||
Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
|
||||
Copyright (C) 2012-2017 Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef LIB_I2C_SMBUS_H
|
||||
#define LIB_I2C_SMBUS_H
|
||||
|
||||
#define I2C_API_VERSION 0x100
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
extern __s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||
int size, union i2c_smbus_data *data);
|
||||
|
||||
extern __s32 i2c_smbus_write_quick(int file, __u8 value);
|
||||
extern __s32 i2c_smbus_read_byte(int file);
|
||||
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
|
||||
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
|
||||
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
|
||||
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
|
||||
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
|
||||
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
extern __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
|
||||
extern __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
|
||||
const __u8 *values);
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
|
||||
ask for less than 32 bytes, your code will only work with kernels
|
||||
2.6.23 and later. */
|
||||
extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
|
||||
__u8 *values);
|
||||
extern __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
|
||||
const __u8 *values);
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
extern __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
|
||||
__u8 *values);
|
||||
|
||||
#endif /* LIB_I2C_SMBUS_H */
|
||||
214
IMX6ULL/source/04_I2C/01_at24c02_test/smbus.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
smbus.c - SMBus level access helper functions
|
||||
|
||||
Copyright (C) 1995-1997 Simon G. Vogl
|
||||
Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
|
||||
Copyright (C) 2012-2013 Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <i2c/smbus.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
|
||||
/* Compatibility defines */
|
||||
#ifndef I2C_SMBUS_I2C_BLOCK_BROKEN
|
||||
#define I2C_SMBUS_I2C_BLOCK_BROKEN I2C_SMBUS_I2C_BLOCK_DATA
|
||||
#endif
|
||||
#ifndef I2C_FUNC_SMBUS_PEC
|
||||
#define I2C_FUNC_SMBUS_PEC I2C_FUNC_SMBUS_HWPEC_CALC
|
||||
#endif
|
||||
|
||||
__s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||
int size, union i2c_smbus_data *data)
|
||||
{
|
||||
struct i2c_smbus_ioctl_data args;
|
||||
__s32 err;
|
||||
|
||||
args.read_write = read_write;
|
||||
args.command = command;
|
||||
args.size = size;
|
||||
args.data = data;
|
||||
|
||||
err = ioctl(file, I2C_SMBUS, &args);
|
||||
if (err == -1)
|
||||
err = -errno;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
__s32 i2c_smbus_write_quick(int file, __u8 value)
|
||||
{
|
||||
return i2c_smbus_access(file, value, 0, I2C_SMBUS_QUICK, NULL);
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_read_byte(int file)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0x0FF & data.byte;
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_byte(int file, __u8 value)
|
||||
{
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, value,
|
||||
I2C_SMBUS_BYTE, NULL);
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_read_byte_data(int file, __u8 command)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||
I2C_SMBUS_BYTE_DATA, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0x0FF & data.byte;
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
data.byte = value;
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_BYTE_DATA, &data);
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_read_word_data(int file, __u8 command)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||
I2C_SMBUS_WORD_DATA, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0x0FFFF & data.word;
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
data.word = value;
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_WORD_DATA, &data);
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
data.word = value;
|
||||
if (i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_PROC_CALL, &data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FFFF & data.word;
|
||||
}
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
__s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i, err;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||
I2C_SMBUS_BLOCK_DATA, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 1; i <= data.block[0]; i++)
|
||||
values[i-1] = data.block[i];
|
||||
return data.block[0];
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
|
||||
const __u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i;
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
for (i = 1; i <= length; i++)
|
||||
data.block[i] = values[i-1];
|
||||
data.block[0] = length;
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_BLOCK_DATA, &data);
|
||||
}
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
|
||||
ask for less than 32 bytes, your code will only work with kernels
|
||||
2.6.23 and later. */
|
||||
__s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
|
||||
__u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i, err;
|
||||
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
data.block[0] = length;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||
length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
|
||||
I2C_SMBUS_I2C_BLOCK_DATA, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 1; i <= data.block[0]; i++)
|
||||
values[i-1] = data.block[i];
|
||||
return data.block[0];
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
|
||||
const __u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i;
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
for (i = 1; i <= length; i++)
|
||||
data.block[i] = values[i-1];
|
||||
data.block[0] = length;
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
|
||||
}
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
__s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
|
||||
__u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i, err;
|
||||
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
for (i = 1; i <= length; i++)
|
||||
data.block[i] = values[i-1];
|
||||
data.block[0] = length;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_BLOCK_PROC_CALL, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 1; i <= data.block[0]; i++)
|
||||
values[i-1] = data.block[i];
|
||||
return data.block[0];
|
||||
}
|
||||
@@ -85,8 +85,8 @@ git clone https://e.coding.net/weidongshan/doc_and_source_for_drivers.git
|
||||
* 2021.02.25 发布"I2C系统":
|
||||
|
||||
* 修改:03_SMBus协议,增加了I2C Block Read/Write
|
||||
|
||||
* 新增:05\_无需编写驱动直接访问设备\_I2C-Tools介绍
|
||||
* 新增:05\_无需编写驱动直接访问设备\_I2C-Tools介绍
|
||||
* 新增:06_编写APP直接访问EEPROM
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -221,7 +221,41 @@ Functionality flag: I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
|
||||
|
||||
|
||||
|
||||
#### 2.11 SMBus Block Write - Block Read Process Call
|
||||
#### 2.11 I2C Block Read
|
||||
|
||||
在一般的I2C协议中,也可以连续读出多个字节。
|
||||
它跟`SMBus Block Read`的差别在于设备发出的第1个数据不是长度N,如下图所示:
|
||||
|
||||

|
||||
|
||||
I2C-tools中的函数:i2c_smbus_read_i2c_block_data()。
|
||||
|
||||
先发出`Command Code`(它一般表示芯片内部的寄存器地址),再发出1个字节的`Byte Conut`(表示后续要发出的数据字节数),最后发出全部数据。
|
||||
|
||||
```shell
|
||||
Functionality flag: I2C_FUNC_SMBUS_READ_I2C_BLOCK
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 2.12 I2C Block Write
|
||||
|
||||
在一般的I2C协议中,也可以连续发出多个字节。
|
||||
它跟`SMBus Block Write`的差别在于发出的第1个数据不是长度N,如下图所示:
|
||||
|
||||

|
||||
|
||||
I2C-tools中的函数:i2c_smbus_write_i2c_block_data()。
|
||||
|
||||
先发出`Command Code`(它一般表示芯片内部的寄存器地址),再发出1个字节的`Byte Conut`(表示后续要发出的数据字节数),最后发出全部数据。
|
||||
|
||||
```shell
|
||||
Functionality flag: I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 2.13 SMBus Block Write - Block Read Process Call
|
||||
|
||||
先写一块数据,再读一块数据。
|
||||
|
||||
@@ -231,7 +265,7 @@ Functionality flag: I2C_FUNC_SMBUS_BLOCK_PROC_CALL
|
||||
|
||||
|
||||
|
||||
#### 2.12 Packet Error Checking (PEC)
|
||||
#### 2.14 Packet Error Checking (PEC)
|
||||
|
||||
PEC是一种错误校验码,如果使用PEC,那么在P信号之前,数据发送方要发送一个字节的PEC码(它是CRC-8码)。
|
||||
|
||||
|
||||
143
STM32MP157/doc_pic/04_I2C/06_编写APP直接访问EEPROM.md
Normal file
@@ -0,0 +1,143 @@
|
||||
## 编写APP直接访问EEPROM
|
||||
|
||||
参考资料:
|
||||
|
||||
* Linux驱动程序: `drivers/i2c/i2c-dev.c`
|
||||
* I2C-Tools-4.2: `https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/`
|
||||
* AT24cxx.pdf
|
||||
|
||||
本节源码:GIT仓库中
|
||||
|
||||
* `doc_and_source_for_drivers\IMX6ULL\source\04_I2C\01_at24c02_test`
|
||||
* `doc_and_source_for_drivers\STM32MP157\source\A7\04_I2C\01_at24c02_test`
|
||||
|
||||
### 1. 硬件连接
|
||||
|
||||
* STM32MP157的I2C模块连接方法
|
||||

|
||||
|
||||
* IMX6ULL的I2C模块连接方法
|
||||

|
||||
|
||||
|
||||
|
||||
### 2. AT24C02访问方法
|
||||
|
||||
#### 2.1 设备地址
|
||||
|
||||
从芯片手册上可以知道,AT24C02的设备地址跟它的A2、A1、A0引脚有关:
|
||||
|
||||

|
||||
|
||||
打开I2C模块的原理图(这2个文件是一样的):
|
||||
|
||||
* `STM32MP157\开发板配套资料\原理图\04_Extend_modules(外设模块)\eeprom.zip\i2c_eeprom_module_v1.0.pdf`
|
||||
* `IMX6ULL\开发板配套资料\原理图\Extend_modules\eeprom.zip\i2c_eeprom_module_v1.0.pdf`
|
||||
* 如下:
|
||||

|
||||
|
||||
从原理图可知,A2A1A0都是0,所以AT24C02的设备地址是:0b1010000,即0x50。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 2.2 写数据
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 2.3 读数据
|
||||
|
||||
可以读1个字节,也可以连续读出多个字节。
|
||||
连续读多个字节时,芯片内部的地址会自动累加。
|
||||
当地址到达存储空间最后一个地址时,会从0开始。
|
||||
|
||||

|
||||
|
||||
### 3. 使用I2C-Tools的函数编程
|
||||
|
||||
|
||||
|
||||
### 4. 编译
|
||||
|
||||
#### 4.1 在Ubuntu设置交叉编译工具链
|
||||
|
||||
* STM32MP157
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||
```
|
||||
|
||||
* IMX6ULL
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.2 使用I2C-Tools的源码
|
||||
|
||||

|
||||
|
||||
#### 4.3 编译
|
||||
|
||||
为IMX6ULL编译时,有如下错误:
|
||||

|
||||
|
||||
这是因为IMX6ULL的工具链自带的include目录中,没有smbus.h。
|
||||
|
||||
需要我们自己提供这个头文件,解决方法:
|
||||
|
||||
* 提供头文件:
|
||||

|
||||
|
||||
* 修改Makefile指定头文件目录
|
||||
|
||||
```shell
|
||||
all:
|
||||
$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 4.4 上机测试
|
||||
|
||||
**以下命令在开发板中执行。**
|
||||
|
||||
* 挂载NFS
|
||||
|
||||
* vmware使用NAT(假设windowsIP为192.168.1.100)
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
|
||||
192.168.1.100:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 复制、执行程序
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# cp /mnt/at24c02_test /bin
|
||||
[root@100ask:~]# at24c02_test 0 w www.100ask.net
|
||||
[root@100ask:~]# at24c02_test 0 r
|
||||
get data: www.100ask.net
|
||||
```
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 886 KiB |
|
After Width: | Height: | Size: 876 KiB |
|
After Width: | Height: | Size: 24 KiB |
BIN
STM32MP157/doc_pic/04_I2C/pic/04_I2C/039_at24c02_sch.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
STM32MP157/doc_pic/04_I2C/pic/04_I2C/040_at24c02_byte_write.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
STM32MP157/doc_pic/04_I2C/pic/04_I2C/041_at24c02_byte_read.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
3
STM32MP157/source/A7/04_I2C/01_at24c02_test/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
all:
|
||||
$(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
|
||||
|
||||
102
STM32MP157/source/A7/04_I2C/01_at24c02_test/at24c02_test.c
Normal file
@@ -0,0 +1,102 @@
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <i2c/smbus.h>
|
||||
#include "i2cbusses.h"
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/* ./at24c02 <i2c_bus_number> w "100ask.taobao.com"
|
||||
* ./at24c02 <i2c_bus_number> r
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned char dev_addr = 0x50;
|
||||
unsigned char mem_addr = 0;
|
||||
unsigned char buf[32];
|
||||
|
||||
int file;
|
||||
char filename[20];
|
||||
unsigned char *str;
|
||||
|
||||
int ret;
|
||||
|
||||
struct timespec req;
|
||||
|
||||
if (argc != 3 && argc != 4)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf("write eeprom: %s <i2c_bus_number> w string\n", argv[0]);
|
||||
printf("read eeprom: %s <i2c_bus_number> r\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
file = open_i2c_dev(argv[1][0]-'0', filename, sizeof(filename), 0);
|
||||
if (file < 0)
|
||||
{
|
||||
printf("can't open %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_slave_addr(file, dev_addr, 1))
|
||||
{
|
||||
printf("can't set_slave_addr\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argv[2][0] == 'w')
|
||||
{
|
||||
// write str: argv[3]
|
||||
str = argv[3];
|
||||
|
||||
req.tv_sec = 0;
|
||||
req.tv_nsec = 20000000; /* 20ms */
|
||||
|
||||
while (*str)
|
||||
{
|
||||
// mem_addr, *str
|
||||
// mem_addr++, str++
|
||||
ret = i2c_smbus_write_byte_data(file, mem_addr, *str);
|
||||
if (ret)
|
||||
{
|
||||
printf("i2c_smbus_write_byte_data err\n");
|
||||
return -1;
|
||||
}
|
||||
// wait tWR(10ms)
|
||||
nanosleep(&req, NULL);
|
||||
|
||||
mem_addr++;
|
||||
str++;
|
||||
}
|
||||
ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // string end char
|
||||
if (ret)
|
||||
{
|
||||
printf("i2c_smbus_write_byte_data err\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// read
|
||||
ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(buf), buf);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("i2c_smbus_read_i2c_block_data err\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[31] = '\0';
|
||||
printf("get data: %s\n", buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
455
STM32MP157/source/A7/04_I2C/01_at24c02_test/i2cbusses.c
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels.
|
||||
Part of user-space programs to access for I2C
|
||||
devices.
|
||||
Copyright (c) 1999-2003 Frodo Looijaard <frodol@dds.nl> and
|
||||
Mark D. Studebaker <mdsxyz123@yahoo.com>
|
||||
Copyright (C) 2008-2012 Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* For strdup and snprintf */
|
||||
#define _BSD_SOURCE 1 /* for glibc <= 2.19 */
|
||||
#define _DEFAULT_SOURCE 1 /* for glibc >= 2.19 */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h> /* for NAME_MAX */
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <strings.h> /* for strcasecmp() */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include "i2cbusses.h"
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
|
||||
enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
|
||||
|
||||
struct adap_type {
|
||||
const char *funcs;
|
||||
const char* algo;
|
||||
};
|
||||
|
||||
static struct adap_type adap_types[5] = {
|
||||
{ .funcs = "dummy",
|
||||
.algo = "Dummy bus", },
|
||||
{ .funcs = "isa",
|
||||
.algo = "ISA bus", },
|
||||
{ .funcs = "i2c",
|
||||
.algo = "I2C adapter", },
|
||||
{ .funcs = "smbus",
|
||||
.algo = "SMBus adapter", },
|
||||
{ .funcs = "unknown",
|
||||
.algo = "N/A", },
|
||||
};
|
||||
|
||||
static enum adt i2c_get_funcs(int i2cbus)
|
||||
{
|
||||
unsigned long funcs;
|
||||
int file;
|
||||
char filename[20];
|
||||
enum adt ret;
|
||||
|
||||
file = open_i2c_dev(i2cbus, filename, sizeof(filename), 1);
|
||||
if (file < 0)
|
||||
return adt_unknown;
|
||||
|
||||
if (ioctl(file, I2C_FUNCS, &funcs) < 0)
|
||||
ret = adt_unknown;
|
||||
else if (funcs & I2C_FUNC_I2C)
|
||||
ret = adt_i2c;
|
||||
else if (funcs & (I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
ret = adt_smbus;
|
||||
else
|
||||
ret = adt_dummy;
|
||||
|
||||
close(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Remove trailing spaces from a string
|
||||
Return the new string length including the trailing NUL */
|
||||
static int rtrim(char *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--)
|
||||
s[i] = '\0';
|
||||
return i + 2;
|
||||
}
|
||||
|
||||
void free_adapters(struct i2c_adap *adapters)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; adapters[i].name; i++)
|
||||
free(adapters[i].name);
|
||||
free(adapters);
|
||||
}
|
||||
|
||||
/* We allocate space for the adapters in bunches. The last item is a
|
||||
terminator, so here we start with room for 7 adapters, which should
|
||||
be enough in most cases. If not, we allocate more later as needed. */
|
||||
#define BUNCH 8
|
||||
|
||||
/* n must match the size of adapters at calling time */
|
||||
static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n)
|
||||
{
|
||||
struct i2c_adap *new_adapters;
|
||||
|
||||
new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap));
|
||||
if (!new_adapters) {
|
||||
free_adapters(adapters);
|
||||
return NULL;
|
||||
}
|
||||
memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap));
|
||||
|
||||
return new_adapters;
|
||||
}
|
||||
|
||||
struct i2c_adap *gather_i2c_busses(void)
|
||||
{
|
||||
char s[120];
|
||||
struct dirent *de, *dde;
|
||||
DIR *dir, *ddir;
|
||||
FILE *f;
|
||||
char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX];
|
||||
int foundsysfs = 0;
|
||||
int len, count = 0;
|
||||
struct i2c_adap *adapters;
|
||||
|
||||
adapters = calloc(BUNCH, sizeof(struct i2c_adap));
|
||||
if (!adapters)
|
||||
return NULL;
|
||||
|
||||
/* look in /proc/bus/i2c */
|
||||
if ((f = fopen("/proc/bus/i2c", "r"))) {
|
||||
while (fgets(s, 120, f)) {
|
||||
char *algo, *name, *type, *all;
|
||||
int len_algo, len_name, len_type;
|
||||
int i2cbus;
|
||||
|
||||
algo = strrchr(s, '\t');
|
||||
*(algo++) = '\0';
|
||||
len_algo = rtrim(algo);
|
||||
|
||||
name = strrchr(s, '\t');
|
||||
*(name++) = '\0';
|
||||
len_name = rtrim(name);
|
||||
|
||||
type = strrchr(s, '\t');
|
||||
*(type++) = '\0';
|
||||
len_type = rtrim(type);
|
||||
|
||||
sscanf(s, "i2c-%d", &i2cbus);
|
||||
|
||||
if ((count + 1) % BUNCH == 0) {
|
||||
/* We need more space */
|
||||
adapters = more_adapters(adapters, count + 1);
|
||||
if (!adapters)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
all = malloc(len_name + len_type + len_algo);
|
||||
if (all == NULL) {
|
||||
free_adapters(adapters);
|
||||
return NULL;
|
||||
}
|
||||
adapters[count].nr = i2cbus;
|
||||
adapters[count].name = strcpy(all, name);
|
||||
adapters[count].funcs = strcpy(all + len_name, type);
|
||||
adapters[count].algo = strcpy(all + len_name + len_type,
|
||||
algo);
|
||||
count++;
|
||||
}
|
||||
fclose(f);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* look in sysfs */
|
||||
/* First figure out where sysfs was mounted */
|
||||
if ((f = fopen("/proc/mounts", "r")) == NULL) {
|
||||
goto done;
|
||||
}
|
||||
while (fgets(n, NAME_MAX, f)) {
|
||||
sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype);
|
||||
if (strcasecmp(fstype, "sysfs") == 0) {
|
||||
foundsysfs++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
if (! foundsysfs) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Bus numbers in i2c-adapter don't necessarily match those in
|
||||
i2c-dev and what we really care about are the i2c-dev numbers.
|
||||
Unfortunately the names are harder to get in i2c-dev */
|
||||
strcat(sysfs, "/class/i2c-dev");
|
||||
if(!(dir = opendir(sysfs)))
|
||||
goto done;
|
||||
/* go through the busses */
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
if (!strcmp(de->d_name, "."))
|
||||
continue;
|
||||
if (!strcmp(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
/* this should work for kernels 2.6.5 or higher and */
|
||||
/* is preferred because is unambiguous */
|
||||
len = snprintf(n, NAME_MAX, "%s/%s/name", sysfs, de->d_name);
|
||||
if (len >= NAME_MAX) {
|
||||
fprintf(stderr, "%s: path truncated\n", n);
|
||||
continue;
|
||||
}
|
||||
f = fopen(n, "r");
|
||||
/* this seems to work for ISA */
|
||||
if(f == NULL) {
|
||||
len = snprintf(n, NAME_MAX, "%s/%s/device/name", sysfs,
|
||||
de->d_name);
|
||||
if (len >= NAME_MAX) {
|
||||
fprintf(stderr, "%s: path truncated\n", n);
|
||||
continue;
|
||||
}
|
||||
f = fopen(n, "r");
|
||||
}
|
||||
/* non-ISA is much harder */
|
||||
/* and this won't find the correct bus name if a driver
|
||||
has more than one bus */
|
||||
if(f == NULL) {
|
||||
len = snprintf(n, NAME_MAX, "%s/%s/device", sysfs,
|
||||
de->d_name);
|
||||
if (len >= NAME_MAX) {
|
||||
fprintf(stderr, "%s: path truncated\n", n);
|
||||
continue;
|
||||
}
|
||||
if(!(ddir = opendir(n)))
|
||||
continue;
|
||||
while ((dde = readdir(ddir)) != NULL) {
|
||||
if (!strcmp(dde->d_name, "."))
|
||||
continue;
|
||||
if (!strcmp(dde->d_name, ".."))
|
||||
continue;
|
||||
if ((!strncmp(dde->d_name, "i2c-", 4))) {
|
||||
len = snprintf(n, NAME_MAX,
|
||||
"%s/%s/device/%s/name",
|
||||
sysfs, de->d_name,
|
||||
dde->d_name);
|
||||
if (len >= NAME_MAX) {
|
||||
fprintf(stderr,
|
||||
"%s: path truncated\n",
|
||||
n);
|
||||
continue;
|
||||
}
|
||||
if((f = fopen(n, "r")))
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found:
|
||||
if (f != NULL) {
|
||||
int i2cbus;
|
||||
enum adt type;
|
||||
char *px;
|
||||
|
||||
px = fgets(s, 120, f);
|
||||
fclose(f);
|
||||
if (!px) {
|
||||
fprintf(stderr, "%s: read error\n", n);
|
||||
continue;
|
||||
}
|
||||
if ((px = strchr(s, '\n')) != NULL)
|
||||
*px = 0;
|
||||
if (!sscanf(de->d_name, "i2c-%d", &i2cbus))
|
||||
continue;
|
||||
if (!strncmp(s, "ISA ", 4)) {
|
||||
type = adt_isa;
|
||||
} else {
|
||||
/* Attempt to probe for adapter capabilities */
|
||||
type = i2c_get_funcs(i2cbus);
|
||||
}
|
||||
|
||||
if ((count + 1) % BUNCH == 0) {
|
||||
/* We need more space */
|
||||
adapters = more_adapters(adapters, count + 1);
|
||||
if (!adapters)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
adapters[count].nr = i2cbus;
|
||||
adapters[count].name = strdup(s);
|
||||
if (adapters[count].name == NULL) {
|
||||
free_adapters(adapters);
|
||||
return NULL;
|
||||
}
|
||||
adapters[count].funcs = adap_types[type].funcs;
|
||||
adapters[count].algo = adap_types[type].algo;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
done:
|
||||
return adapters;
|
||||
}
|
||||
|
||||
static int lookup_i2c_bus_by_name(const char *bus_name)
|
||||
{
|
||||
struct i2c_adap *adapters;
|
||||
int i, i2cbus = -1;
|
||||
|
||||
adapters = gather_i2c_busses();
|
||||
if (adapters == NULL) {
|
||||
fprintf(stderr, "Error: Out of memory!\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Walk the list of i2c busses, looking for the one with the
|
||||
right name */
|
||||
for (i = 0; adapters[i].name; i++) {
|
||||
if (strcmp(adapters[i].name, bus_name) == 0) {
|
||||
if (i2cbus >= 0) {
|
||||
fprintf(stderr,
|
||||
"Error: I2C bus name is not unique!\n");
|
||||
i2cbus = -4;
|
||||
goto done;
|
||||
}
|
||||
i2cbus = adapters[i].nr;
|
||||
}
|
||||
}
|
||||
|
||||
if (i2cbus == -1)
|
||||
fprintf(stderr, "Error: I2C bus name doesn't match any "
|
||||
"bus present!\n");
|
||||
|
||||
done:
|
||||
free_adapters(adapters);
|
||||
return i2cbus;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an I2CBUS command line argument and return the corresponding
|
||||
* bus number, or a negative value if the bus is invalid.
|
||||
*/
|
||||
int lookup_i2c_bus(const char *i2cbus_arg)
|
||||
{
|
||||
unsigned long i2cbus;
|
||||
char *end;
|
||||
|
||||
i2cbus = strtoul(i2cbus_arg, &end, 0);
|
||||
if (*end || !*i2cbus_arg) {
|
||||
/* Not a number, maybe a name? */
|
||||
return lookup_i2c_bus_by_name(i2cbus_arg);
|
||||
}
|
||||
if (i2cbus > 0xFFFFF) {
|
||||
fprintf(stderr, "Error: I2C bus out of range!\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
return i2cbus;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a CHIP-ADDRESS command line argument and return the corresponding
|
||||
* chip address, or a negative value if the address is invalid.
|
||||
*/
|
||||
int parse_i2c_address(const char *address_arg, int all_addrs)
|
||||
{
|
||||
long address;
|
||||
char *end;
|
||||
long min_addr = 0x08;
|
||||
long max_addr = 0x77;
|
||||
|
||||
address = strtol(address_arg, &end, 0);
|
||||
if (*end || !*address_arg) {
|
||||
fprintf(stderr, "Error: Chip address is not a number!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (all_addrs) {
|
||||
min_addr = 0x00;
|
||||
max_addr = 0x7f;
|
||||
}
|
||||
|
||||
if (address < min_addr || address > max_addr) {
|
||||
fprintf(stderr, "Error: Chip address out of range "
|
||||
"(0x%02lx-0x%02lx)!\n", min_addr, max_addr);
|
||||
return -2;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
|
||||
{
|
||||
int file, len;
|
||||
|
||||
len = snprintf(filename, size, "/dev/i2c/%d", i2cbus);
|
||||
if (len >= (int)size) {
|
||||
fprintf(stderr, "%s: path truncated\n", filename);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
file = open(filename, O_RDWR);
|
||||
|
||||
if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
|
||||
len = snprintf(filename, size, "/dev/i2c-%d", i2cbus);
|
||||
if (len >= (int)size) {
|
||||
fprintf(stderr, "%s: path truncated\n", filename);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
file = open(filename, O_RDWR);
|
||||
}
|
||||
|
||||
if (file < 0 && !quiet) {
|
||||
if (errno == ENOENT) {
|
||||
fprintf(stderr, "Error: Could not open file "
|
||||
"`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
|
||||
i2cbus, i2cbus, strerror(ENOENT));
|
||||
} else {
|
||||
fprintf(stderr, "Error: Could not open file "
|
||||
"`%s': %s\n", filename, strerror(errno));
|
||||
if (errno == EACCES)
|
||||
fprintf(stderr, "Run as root?\n");
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
int set_slave_addr(int file, int address, int force)
|
||||
{
|
||||
/* With force, let the user read from/write to the registers
|
||||
even when a driver is also running */
|
||||
if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
|
||||
fprintf(stderr,
|
||||
"Error: Could not set address to 0x%02x: %s\n",
|
||||
address, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
44
STM32MP157/source/A7/04_I2C/01_at24c02_test/i2cbusses.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
i2cbusses.h - Part of the i2c-tools package
|
||||
|
||||
Copyright (C) 2004-2010 Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _I2CBUSSES_H
|
||||
#define _I2CBUSSES_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
struct i2c_adap {
|
||||
int nr;
|
||||
char *name;
|
||||
const char *funcs;
|
||||
const char *algo;
|
||||
};
|
||||
|
||||
struct i2c_adap *gather_i2c_busses(void);
|
||||
void free_adapters(struct i2c_adap *adapters);
|
||||
|
||||
int lookup_i2c_bus(const char *i2cbus_arg);
|
||||
int parse_i2c_address(const char *address_arg, int all_addrs);
|
||||
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet);
|
||||
int set_slave_addr(int file, int address, int force);
|
||||
|
||||
#define MISSING_FUNC_FMT "Error: Adapter does not have %s capability\n"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
smbus.h - SMBus level access helper functions
|
||||
|
||||
Copyright (C) 1995-1997 Simon G. Vogl
|
||||
Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
|
||||
Copyright (C) 2012-2017 Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef LIB_I2C_SMBUS_H
|
||||
#define LIB_I2C_SMBUS_H
|
||||
|
||||
#define I2C_API_VERSION 0x100
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
extern __s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||
int size, union i2c_smbus_data *data);
|
||||
|
||||
extern __s32 i2c_smbus_write_quick(int file, __u8 value);
|
||||
extern __s32 i2c_smbus_read_byte(int file);
|
||||
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
|
||||
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
|
||||
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
|
||||
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
|
||||
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
|
||||
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
extern __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
|
||||
extern __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
|
||||
const __u8 *values);
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
|
||||
ask for less than 32 bytes, your code will only work with kernels
|
||||
2.6.23 and later. */
|
||||
extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
|
||||
__u8 *values);
|
||||
extern __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
|
||||
const __u8 *values);
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
extern __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
|
||||
__u8 *values);
|
||||
|
||||
#endif /* LIB_I2C_SMBUS_H */
|
||||
214
STM32MP157/source/A7/04_I2C/01_at24c02_test/smbus.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
smbus.c - SMBus level access helper functions
|
||||
|
||||
Copyright (C) 1995-1997 Simon G. Vogl
|
||||
Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
|
||||
Copyright (C) 2012-2013 Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <i2c/smbus.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
|
||||
/* Compatibility defines */
|
||||
#ifndef I2C_SMBUS_I2C_BLOCK_BROKEN
|
||||
#define I2C_SMBUS_I2C_BLOCK_BROKEN I2C_SMBUS_I2C_BLOCK_DATA
|
||||
#endif
|
||||
#ifndef I2C_FUNC_SMBUS_PEC
|
||||
#define I2C_FUNC_SMBUS_PEC I2C_FUNC_SMBUS_HWPEC_CALC
|
||||
#endif
|
||||
|
||||
__s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||
int size, union i2c_smbus_data *data)
|
||||
{
|
||||
struct i2c_smbus_ioctl_data args;
|
||||
__s32 err;
|
||||
|
||||
args.read_write = read_write;
|
||||
args.command = command;
|
||||
args.size = size;
|
||||
args.data = data;
|
||||
|
||||
err = ioctl(file, I2C_SMBUS, &args);
|
||||
if (err == -1)
|
||||
err = -errno;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
__s32 i2c_smbus_write_quick(int file, __u8 value)
|
||||
{
|
||||
return i2c_smbus_access(file, value, 0, I2C_SMBUS_QUICK, NULL);
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_read_byte(int file)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0x0FF & data.byte;
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_byte(int file, __u8 value)
|
||||
{
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, value,
|
||||
I2C_SMBUS_BYTE, NULL);
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_read_byte_data(int file, __u8 command)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||
I2C_SMBUS_BYTE_DATA, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0x0FF & data.byte;
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
data.byte = value;
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_BYTE_DATA, &data);
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_read_word_data(int file, __u8 command)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||
I2C_SMBUS_WORD_DATA, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0x0FFFF & data.word;
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
data.word = value;
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_WORD_DATA, &data);
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
data.word = value;
|
||||
if (i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_PROC_CALL, &data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FFFF & data.word;
|
||||
}
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
__s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i, err;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||
I2C_SMBUS_BLOCK_DATA, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 1; i <= data.block[0]; i++)
|
||||
values[i-1] = data.block[i];
|
||||
return data.block[0];
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
|
||||
const __u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i;
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
for (i = 1; i <= length; i++)
|
||||
data.block[i] = values[i-1];
|
||||
data.block[0] = length;
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_BLOCK_DATA, &data);
|
||||
}
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
|
||||
ask for less than 32 bytes, your code will only work with kernels
|
||||
2.6.23 and later. */
|
||||
__s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
|
||||
__u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i, err;
|
||||
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
data.block[0] = length;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
|
||||
length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
|
||||
I2C_SMBUS_I2C_BLOCK_DATA, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 1; i <= data.block[0]; i++)
|
||||
values[i-1] = data.block[i];
|
||||
return data.block[0];
|
||||
}
|
||||
|
||||
__s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
|
||||
const __u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i;
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
for (i = 1; i <= length; i++)
|
||||
data.block[i] = values[i-1];
|
||||
data.block[0] = length;
|
||||
return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
|
||||
}
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
__s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
|
||||
__u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int i, err;
|
||||
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
for (i = 1; i <= length; i++)
|
||||
data.block[i] = values[i-1];
|
||||
data.block[0] = length;
|
||||
|
||||
err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_BLOCK_PROC_CALL, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 1; i <= data.block[0]; i++)
|
||||
values[i-1] = data.block[i];
|
||||
return data.block[0];
|
||||
}
|
||||