内核下gpio模拟i2c驱动修改

Linux内核很多驱动都使用到I2C子系统。EEPROM、RTC,电池,tp等。

inux内核的i2c-gpio是使用GPIO模拟I2C协议的驱动,在内核中已经实现了,我们要做的只需要配置2根GPIO即可。

GPIO模拟I2C协议的驱动位于drivers/i2c/busses目录。驱动名称为“i2c-gpio”,驱动文件为drivers/i2c/busses/i2c-gpio.c

1. GPIO支持要先保证是选上的

2.  先配置内核

Device Drivers->
    I2C support  --->
        I2C Hardware Bus support  --->
            <*> GPIO-based bitbanging I2C 

3. 修改内核文件

static struct i2c_gpio_platform_data da850_gpio_i2c_pdata = {
	.sda_pin	= DA850_GPIO_I2C_SDA,   //通用的gpio引脚
	.scl_pin    = DA850_GPIO_I2C_SCL,   //通用的gpio引脚
	.udelay		= 10,			/* 50 KHz */
	.timeout 	= 100,
};

static struct platform_device da850_gpio_i2c = {
	.name		= "i2c-gpio",
	.id		    = 2,    //生成 i2c-gpio.2   
	.dev		= {
		.platform_data	= &da850_gpio_i2c_pdata,
	},
};

平台初始化

static __init void da850_evm_init(void)
{
	..........
    ..........

	/*
	 * Though bootloader takes care to set emif clock at allowed
	 * possible rate. Kernel needs to reconfigure this rate to
	 * support platforms requiring fixed emif clock rate.
	 */
	ret = da850_set_emif_clk_rate();
	if (ret)
		pr_warning("da850_evm_init: Failed to set rate of pll0_sysclk3/emif clock: %d\n",
				ret);

	ret = da850_register_edma(da850_edma_rsv);
	if (ret)
		pr_warning("da850_evm_init: edma registration failed: %d\n",
				ret);

	ret = davinci_cfg_reg_list(da850_i2c0_pins);
	if (ret)
		pr_warning("da850_evm_init: i2c0 mux setup failed: %d\n",
				ret);


	platform_device_register(&da850_gpio_i2c);   //注册平台设备

	ret = da8xx_register_i2c(0, &da850_evm_i2c_pdata);
	if (ret) {
	    pr_warning("da850_evm_init: i2c0 mux setup failed: %d\n",
		    ret);
	    return;
	}
    .........
	i2c_register_board_info(2, da850_gpio_i2c_devices,     //gpio-i2c
                        ARRAY_SIZE(da850_gpio_i2c_devices));
    ........
}

i2c_board_info :这是个枚举类型的数组,把要加入的GPIO加入到这个数组里面。

static struct i2c_board_info __initdata da850_gpio_i2c_devices[] = {
	{                
		I2C_BOARD_INFO("bq20z75", 0x0b),
    },
};

这样就实现了gpio-i2c功能了,但是在使用前需要先确保当期使用的io口是作为GPIO使用的,我就踩了这个坑。

对于引脚配置成GPIO过程

OMAP-L138 C6000 DSP+ARM Processor Technical Reference Manual (Rev.A).pdf 244页有相应的管脚配置说明

引脚配置复用的位置在\linux-3.3\arch\arm\mach-davinci\da850.c的 结构体static const struct mux_config da850_pins[] = {}

static const struct mux_config da850_pins[] = {
    #ifdef CONFIG_DAVINCI_MUX
    ..............
    MUX_CFG(DA850, EMA_A_22,    10,     4,    15,    1,    false)
    MUX_CFG(DA850, EMA_A_20,   10,     12,    15,    1,    false)

    ...............
}

参数说明:  MUX_CFG(DA850, EMA_A_22,    4,    24,    15,    2,    false)

MUX_CFG :这个宏在arch/arm/mach-davinci/mux.h定义

#define MUX_CFG(soc, desc, muxreg, mode_offset, mode_mask, mux_mode, dbg)

DA850: 平台

EMA_A_22: 管脚描述

4: 第4组pinmux寄存器(引脚复用寄存器的名称,展开为 PINMUX4 )

24: 在寄存器PINMUX4中的bit24到bit27位,相应位的偏移值

15: 就是pinmux相应位全为1的值(掩码值)

2 : mux_mode(设置这个复用寄存器的GPIO引脚功能)

现在需要将da850.c使用为EMA_A的默认的引脚复用为GPIO引脚,查看管脚配置pdf说明是否支持,

//需要注意将使用该寄存器复用功能的地方全注释,不然编译会报错

da850.c , include/mach/mux.h , board-da850-evm.c

  管脚描述改为当前的修改后的描述,我当前修改描述为GPIO4_4

 

 

///

管脚配置完成修改完成后对内核修改验证方式

用i2cdetect检测有几组i2c总线在系统上,输入:./i2cdetect  -l

用i2cdetect检测挂载在i2c总线上器件,

i2cdetect -r -y 2(检测i2c-1上的挂载情况)

 

由上图可知 0x0b地址有挂载。而这几个分别是0x0b(bq20z75)

i2cset -f -y 1 0x0b 0x09 0x3f (设置i2c-1上0x0b 器件的0x09 寄存器值为0x3f)

i2cget -f -y 1 0x0b 0x09     (读取i2c-1上0x0b 器件的0x09     寄存器值)

 

i2cdetect -r -y 2 这个命令能确定i2c-2总线上那个地址是挂载设备的

此方式验证i2c功能的从设备地址是否正确,如果写入的从设备地址不是实际地址则写入时是不生效的

i2c功能数据读写

//test.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include <string.h>

unsigned char i2c_read(unsigned char device_addr, unsigned char sub_addr, unsigned char *buff, int ByteNo)
{
    int fd, ret, i;
    unsigned char buftmp[32];
    struct i2c_rdwr_ioctl_data i2c_data;
    const char *i2c_dev = "/dev/i2c-2";

    device_addr >>= 1;
    //init
    fd = open(i2c_dev, O_RDWR);
    if (fd<0)
    {
        printf("not have /dev/i2c-1 t\r\n");
        return -1;
    }

    i2c_data.nmsgs = 2;
    i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs *sizeof(struct i2c_msg));
    if (i2c_data.msgs == NULL)
    {
        printf("malloc error");
        close(fd);
        return -1;
    }

    ioctl(fd, I2C_TIMEOUT, 1);
    ioctl(fd, I2C_RETRIES, 2);
    写i2c寄存器直接写,读i2c寄存器之前需要先向要读取的寄存器写,我这个只有读操作

    //write reg
    memset(buftmp, 0, 32);
    buftmp[0] = sub_addr;
    i2c_data.msgs[0].len = 1;
    i2c_data.msgs[0].addr = device_addr;
    i2c_data.msgs[0].flags = 0;     // 0: write 1:read
    i2c_data.msgs[0].buf = buftmp;
    //read data
    i2c_data.msgs[1].len = ByteNo;
    i2c_data.msgs[1].addr = device_addr;
    i2c_data.msgs[1].flags = 1;     // 0: write 1:read
    i2c_data.msgs[1].buf = buff;


    ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data);
    if (ret < 0)
    {
        printf("read data %x %x error\r\n", device_addr, sub_addr);
        close(fd);
        free(i2c_data.msgs);
        return 1;
    }
    free(i2c_data.msgs);
    close(fd);

    printf("i2c_read from: 0x%02x:\t",buftmp[0]);

    //读取的是2byte数据,先低8位,后高8位,拼接起来就是实际数据,根据需要对数据进行操作
    int data = buff[1] *256 + buff[0];  
    printf(" %d\n",data);
    for (i = 0; i < ByteNo; i++)
    {
        printf(" 0x%02x",buff[i]);
    }
    printf("\n");
    return 0;
}

int main(void)  
{
    unsigned char buf[10]= {0};
    i2c_read(0x16, 0x16, buf, 2);     //10 is on charging, 50 is not charging
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值