NUCLEO-F411RE RT-Thread 体验 (6) - GCC环境 I2C驱动移植以及i2c-tool的编写

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,&reg,5,w_value);
    rt_thread_delay(10);
    read_regs(i2c_bus,1,&reg,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);
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值