NUCLEO-F411RE RT-Thread 体验 (6) - GCC环境 I2C驱动移植以及i2c-tool的编写
1、I2C驱动移植
RT-Rhread这里用的是软件模拟i2c,stm32的驱动里并没有找到硬件i2c的驱动,但是在GD32里面却有硬件i2c的驱动,有兴趣的小伙伴可以根据gd32的代码写一份stm32硬件I2c的驱动。
rtconfig.h里添加i2c的配置
Makefile里添加i2c的编译
这样修改后,输入list_device,会列出i2c1设备。
2、应用层的使用
在PB6 PB7 上挂了一颗at24c02,我们用msh cmd写几个数据进去,然后读出来。
应用层最重要的是发送函数的封装,如果在kernel下搞过i2c的设备驱动,rt_i2c_transfer函数这个形式并不陌生。我这里封装了读函数,写函数,多寄存器,多数据的读写方式。
3 、完整测试代码
#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
#define I2C_BUS_NAME "i2c1" /* EEPROM连接的I2C总线设备名称 */
#define I2C_TEST_ADDR 0x50 /* 从机地址 */
#define I2C_MAX_DATA_LENGTH 128
static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C总线设备句柄 */
/* I2C 写寄存器 */
static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint16_t reg_len,rt_uint8_t *reg,
rt_uint16_t len, rt_uint8_t *data)
{
rt_uint8_t buf[I2C_MAX_DATA_LENGTH] = {0};
rt_uint8_t *p = buf;
struct rt_i2c_msg msgs;
memcpy(p,reg,reg_len);
p += reg_len;
memcpy(p,data,len);
msgs.addr = I2C_TEST_ADDR;
msgs.flags = RT_I2C_WR;
msgs.buf = buf;
msgs.len = reg_len + len;
/* 调用I2C设备接口传输数据 */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
/* 读I2C寄存器数据 */
static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint16_t reg_len,
rt_uint8_t *reg, rt_uint16_t len, rt_uint8_t *buf)
{
struct rt_i2c_msg msgs[2];
msgs[0].addr = I2C_TEST_ADDR;
msgs[0].flags = RT_I2C_WR;
msgs[0].buf = reg;
msgs[0].len = reg_len;
msgs[1].addr = I2C_TEST_ADDR;
msgs[1].flags = RT_I2C_RD;
msgs[1].buf = buf;
msgs[1].len = len;
/* 调用I2C设备接口传输数据 */
if (rt_i2c_transfer(bus, msgs, 2) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
static void i2c_sample(int argc, char *argv[])
{
rt_uint8_t reg = 0x00;
rt_uint8_t r_value[5] = {0};
rt_uint8_t w_value[5] = {0x98,0x97,0x99,0xA0,0x45};
char name[RT_NAME_MAX];
if (argc == 2)
{
rt_strncpy(name, argv[1], RT_NAME_MAX);
}
else
{
rt_strncpy(name, I2C_BUS_NAME, RT_NAME_MAX);
}
/* 查找I2C总线设备,获取I2C总线设备句柄 */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
if (i2c_bus == RT_NULL)
{
rt_kprintf("can't find %s device!\n", name);
}
write_reg(i2c_bus,1,®,5,w_value);
rt_thread_delay(10);
read_regs(i2c_bus,1,®,5,r_value);
for(int i=0;i<5;i++)
{
rt_kprintf("get data[%d] == 0x%02x\n",i,r_value[i]);
}
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(i2c_sample, i2c test sample);
4、i2cdetect 工具
封装i2cdetect工具,用来查找总线的设备,比如总线上挂载一颗at24c02,那么使用i2cdetect后现象如图:
i2cdetect代码
void i2c_detect_show_usage(void)
{
rt_kprintf("Usage:\r\n");
rt_kprintf(" i2cdetect (-h | --help)\r\n");
rt_kprintf(" i2cdetect (-f | --find ) find devices in i2c bus \r\n");
}
static int i2c_send_detect_msg(struct rt_i2c_bus_device *bus,rt_uint8_t device_addr)
{
struct rt_i2c_msg msgs;
msgs.addr = device_addr;
msgs.flags = RT_I2C_WR;
msgs.buf = NULL;
msgs.len = 0;
/* 调用I2C设备接口传输数据 */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
rt_uint8_t show_i2c_detect_result(rt_uint8_t value)
{
rt_int8_t result = 0;
rt_uint8_t j = 0;
rt_int8_t name[8] = {0};
rt_sprintf(name,"%s%d","i2c",value);
/* 查找I2C总线设备,获取I2C总线设备句柄 */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
if (i2c_bus == RT_NULL)
{
rt_kprintf("can't find %s device!\n", name);
return -1;
}
for(j = 0;j < 128; j++)
{
if((j % 16) == 0)
{
rt_kprintf("\r\n");
}
result = i2c_send_detect_msg(i2c_bus,j);
if(result == 0)
{
rt_kprintf(" %X ",j);//%X 十六进制输出,大写;%x 小写
}
else
{
rt_kprintf(" -- ");
}
}
rt_kprintf("\r\n");
return 0;
}
int i2cdetect(int argc ,char *argv[])
{
rt_uint8_t bus_index;
int c;
int longindex = 0;
const char short_options[] = "hf:";
const struct option long_options[] =
{
{"help", 0, NULL, 'h'},
{"find", 1, NULL, 'f'},
{NULL, 0, NULL, 0},
};
if (argc == 1)
{
/* goto the help */
i2c_detect_show_usage();
return 0;
}
/* init 0 */
optind = 0;
do
{
/* parse the args */
c = getopt_long(argc, argv, short_options, long_options, &longindex);
if (c == 0x3f)
{
i2c_detect_show_usage();
return -1;
}
switch (c)
{
case 'h':
i2c_detect_show_usage();
break;
case 'f':
bus_index = strtol(optarg,NULL,10);
if (bus_index >= 0)
{
show_i2c_detect_result(bus_index);
}
else
{
i2c_detect_show_usage();
}
break;
default:break;
}
}while (c != -1);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(i2cdetect, i2c device detect);
5、i2cset 命令
比如 往0x50 设备的0x00地址写一个字节数据 0x08,那么命令如下:
i2cset -f 1 0x50 0x00 0x08 b
比如 往0x50 设备的0x00地址写一个half word 数据 0x08,0x07,那么命令如下:
i2cset -f 1 0x50 0x00 0x08 0x07 w
i2cset命令代码:
void i2c_set_show_usage(void)
{
rt_kprintf("Usage:\r\n");
rt_kprintf(" i2cset (-h | --help)\r\n");
rt_kprintf(" i2cset (-f | --find ) i2cset -f busNUM deviceaddr regaddr data\n");
rt_kprintf(" i2cset (-f | --find ) write a byte : i2cset -f 1 0x50 0x00 0x01 b \n");
rt_kprintf(" i2cset (-f | --find ) write a word : i2cset -f 1 0x50 0x00 0x01 0x02 w\n");
}
int i2cset(int argc,char *argv[])
{
char *end;
int value;
int find = 0,version = 0;
int opt;
int longindex = 0;
int address,daddress;
rt_int8_t name[8] = {0};
rt_uint8_t data[128] = {0};
rt_uint8_t wite_num = 1;
const char short_options[] = "hf:";
const struct option long_options[] =
{
{"help", 0, NULL, 'h'},
{"find", 1, NULL, 'f'},
{NULL, 0, NULL, 0},
};
/* init 0 */
optind = 0;
do
{
/* parse the args */
opt = getopt_long(argc, argv, short_options, long_options, &longindex);
if (opt == 0x3f)
{
i2c_set_show_usage();
return -1;
}
switch (opt)
{
case 'h':
i2c_set_show_usage();
return 0;
case 'f':
find = strtol(optarg,NULL,10);
if (find <= 0)
{
i2c_set_show_usage();
return -1;
}
break;
default:break;
}
}while (opt != -1);
if (argc < optind + 3)
{
/* goto the help */
i2c_set_show_usage();
return 0;
}
rt_sprintf(name,"%s%d","i2c",find);
/* 查找I2C总线设备,获取I2C总线设备句柄 */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
if (i2c_bus == RT_NULL)
{
rt_kprintf("can't find %s device!\n", name);
return -1;
}
address = strtol(argv[optind],&end,0);
if (*end || address < 0 || address > 0xff)
{
rt_kprintf("Error: Data address invalid!\n");
i2c_set_show_usage();
return -1;
}
daddress = strtol(argv[optind+1],NULL,0);
if (daddress <0 || address > 0xff)
{
rt_kprintf("Error: Data address invalid!\n");
i2c_set_show_usage();
return -1;
}
int data_num = argc - optind - 2 - 1;
for (int i=0;i<data_num;i++)
{
data[i] = strtol(argv[optind + 2 + i],NULL,0);
if (data[i] > 0xff)
{
rt_kprintf("Error: Data address invalid!\n");
i2c_set_show_usage();
return -1;
}
}
if (!strncmp(argv[argc-1],"b",1))
{
wite_num = 1;
}
else if (!strncmp(argv[argc-1],"w",1))
{
wite_num = 2;
}
else
{
wite_num = 1;
}
if (data_num != wite_num)
{
rt_kprintf("Error: Data Number Wrong\n");
i2c_set_show_usage();
return -1;
}
value = write_reg(i2c_bus,address,1,&daddress,wite_num,data);
if (value == RT_EOK)
{
if (wite_num == 1)
{
rt_kprintf("Write Device: 0x%02x Reg: 0x%02x Data: 0x%02x Success\n",address,daddress,data[0]);
}
else
{
rt_kprintf("Write Device: 0x%02x Reg: 0x%02x Data: 0x%02x 0x%02x Success\n",address,daddress,data[0],data[1]);
}
}
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(i2cset, i2c set value to device);
6、i2cget命令
从设备中读取数据,
比如从i2c1总线上的0x50设备中的0x00地址读取一个字节数据
i2cget -f 1 0x50 0x00 b
比如从i2c1总线上的0x50设备中的0x00地址读取half-word数据
i2cget -f 1 0x50 0x00 w
i2cget 命令代码
void i2c_get_show_usage(void)
{
rt_kprintf("Usage:\r\n");
rt_kprintf(" i2cget (-h | --help)\r\n");
rt_kprintf(" i2cget (-f | --find ) i2cset -f busNUM deviceaddr regaddr \n");
rt_kprintf(" i2cget (-f | --find ) read a byte : i2cget -f 1 0x50 0x00 b \n");
rt_kprintf(" i2cget (-f | --find ) read a word : i2cget -f 1 0x50 0x00 w \n");
}
int i2cget(int argc ,char *argv[])
{
char *end;
int value;
int find = 0,version = 0;
int opt;
int longindex = 0;
rt_uint8_t address,daddress;
rt_int8_t name[8] = {0};
rt_uint8_t data[2] = {0};
rt_uint8_t wite_num = 1;
const char short_options[] = "hf:";
const struct option long_options[] =
{
{"help", 0, NULL, 'h'},
{"find", 1, NULL, 'f'},
{NULL, 0, NULL, 0},
};
/* init 0 */
optind = 0;
do
{
/* parse the args */
opt = getopt_long(argc, argv, short_options, long_options, &longindex);
if (opt == 0x3f)
{
i2c_get_show_usage();
return -1;
}
switch (opt)
{
case 'h':
i2c_get_show_usage();
return 0;
case 'f':
find = strtol(optarg,NULL,10);
if (find <= 0)
{
i2c_get_show_usage();
return -1;
}
break;
default:break;
}
}while (opt != -1);
if (argc < optind + 3)
{
/* goto the help */
i2c_get_show_usage();
return 0;
}
rt_sprintf(name,"%s%d","i2c",find);
/* 查找I2C总线设备,获取I2C总线设备句柄 */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
if (i2c_bus == RT_NULL)
{
rt_kprintf("can't find %s device!\n", name);
return -1;
}
address = strtol(argv[optind],&end,0);
if (*end || address < 0 || address > 0xff)
{
rt_kprintf("Error: Data address invalid!\n");
i2c_get_show_usage();
return -1;
}
daddress = strtol(argv[optind+1],NULL,0);
if (daddress <0 || address > 0xff)
{
rt_kprintf("Error: Data address invalid!\n");
i2c_get_show_usage();
return -1;
}
if (!strncmp(argv[argc-1],"b",1))
{
wite_num = 1;
}
else if (!strncmp(argv[argc-1],"w",1))
{
wite_num = 2;
}
else
{
wite_num = 1;
}
value = read_regs(i2c_bus,address,1,&daddress,wite_num,data);
if (value == RT_EOK)
{
if (wite_num == 1)
{
rt_kprintf("Write Device: 0x%02x Reg: 0x%02x Data: 0x%02x Success\n",address,daddress,data[0]);
}
else
{
rt_kprintf("Write Device: 0x%02x Reg: 0x%02x Data: 0x%02x 0x%02x Success\n",address,daddress,data[0],data[1]);
}
}
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(i2cget, i2c get value from device);