⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gpio_i2c.c

📁 利用I/O口模拟I2C总线
💻 C
字号:
/*    
 *   extdrv/interface/gpio_i2c/gpio_i2c.c
 */

#include <linux/module.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>

#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>

#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>

#include "gpio_i2c.h" 

static void __iomem *gpio_map_addr;
unsigned int GPIO_BASE_MMAP;

/* GPIO1_1 */
#define SCL             (1 << 0)

/* GPIO1_0 */
#define SDA             (1 << 1)

#define BIT0   0x01
#define BIT1   0x02

#define GPIO_1_BASE 0x101e5000     //Hi3510 决定的,gpio1的基址

#define GPIO_1_DIR (GPIO_BASE_MMAP + 0x400)//0x4000:GPIO_DIR(方向控制寄存器)的偏移地址

#define GPIO_I2C_SDA_REG (GPIO_BASE_MMAP + 0x04)//gpio1 GPIO_DATA的bit[0]
#define GPIO_I2C_SCL_REG (GPIO_BASE_MMAP + 0x08)//gpio1 GPIO_DATA的bit[1]
#define GPIO_I2C_SCLSDA_REG (GPIO_BASE_MMAP + 0x0c)

#define HW_REG(reg) *((volatile unsigned int *)(reg))//取地址里的内容
#define DELAY(us)       time_delay_us(us)

/* 
 * I2C by GPIO simulated  clear 0 routine.
 * @param whichline: GPIO control line
 */
static void i2c_clr(unsigned char whichline)
{
	unsigned char regvalue;
	
	if(whichline == SCL)
	{
		regvalue = HW_REG(GPIO_1_DIR);
		regvalue |= BIT1;             
		HW_REG(GPIO_1_DIR) = regvalue;
		
		HW_REG(GPIO_I2C_SCL_REG) = 0;
		return;
	}
	else if(whichline == SDA)
	{
		regvalue = HW_REG(GPIO_1_DIR);
		regvalue |= BIT0;
		HW_REG(GPIO_1_DIR) = regvalue;
		
		HW_REG(GPIO_I2C_SDA_REG) = 0;
		return;
	}
	else if(whichline == (SDA|SCL))
	{
		regvalue = HW_REG(GPIO_1_DIR);
		regvalue |= BIT0 | BIT1;
		HW_REG(GPIO_1_DIR) = regvalue;
		
		HW_REG(GPIO_I2C_SCLSDA_REG) = 0;
		return;
	}
	else
	{
		printk("Error input.\n");
		return;
	}
	
}

/* 
 * I2C by GPIO simulated set 1 routine.
 * @param whichline: GPIO control line
 */
static void  i2c_set(unsigned char whichline)
{
	unsigned char regvalue;
	
	if(whichline == SCL)
	{
		regvalue = HW_REG(GPIO_1_DIR);
		regvalue |= BIT1;
		HW_REG(GPIO_1_DIR) = regvalue;
		
		HW_REG(GPIO_I2C_SCL_REG) = BIT1;
		return;
	}
	else if(whichline == SDA)
	{
		regvalue = HW_REG(GPIO_1_DIR);
		regvalue |= BIT0;
		HW_REG(GPIO_1_DIR) = regvalue;
		
		HW_REG(GPIO_I2C_SDA_REG) = BIT0;
		return;
	}
	else if(whichline == (SDA|SCL))
	{
		regvalue = HW_REG(GPIO_1_DIR);
		regvalue |= BIT0 | BIT1;
		HW_REG(GPIO_1_DIR) = regvalue;
		
		HW_REG(GPIO_I2C_SCLSDA_REG) = BIT0 | BIT1;
		return;
	}
	else
	{
		printk("Error input.\n");
		return;
	}
}

/*
 *  delays for a specified number of micro seconds rountine.
 *
 *  @param usec: number of micro seconds to pause for
 *
 */
void time_delay_us(unsigned int usec)
{
	int i,j;
	
	for(i=0;i<usec * 5;i++)
	{
		for(j=0;j<47;j++)
		{;}
	}
}

/* 
 * I2C by GPIO simulated  read data routine.
 * @return value: a bit for read 
 */
 
static unsigned char i2c_data_read(void)
{
	unsigned char regvalue;
	
	regvalue = HW_REG(GPIO_1_DIR);
	regvalue &= ~BIT0;              //bit0清0,使SDA的方向为读入
	HW_REG(GPIO_1_DIR) = regvalue;
	DELAY(1);
		
	regvalue = HW_REG(GPIO_I2C_SDA_REG);//读SDA,bit0
	if((regvalue & BIT0) != 0)
		return 1;
	else
		return 0;
}

/*
 * sends a start bit via I2C rountine.
 */
static void i2c_start_bit(void)
{
     DELAY(1);
     i2c_set(SDA | SCL);
     DELAY(1);
     i2c_clr(SDA);//SCL 为高时SDA由高变低
     DELAY(1);
}

/*
 * sends a stop bit via I2C rountine.
 */
static void i2c_stop_bit(void)
{
        /* clock the ack */
        DELAY(1);
        i2c_set(SCL);
        DELAY(1);
        i2c_clr(SCL);  

        /* actual stop bit */
        DELAY(1);
        i2c_clr(SDA);
        DELAY(1);
        i2c_set(SCL);
        DELAY(1);
        i2c_set(SDA);//SCL为高时SDA由低变高
        DELAY(1);
}

/*
 * sends a character over I2C rountine.
 * @param  c: character to send
 */
static void i2c_send_byte(unsigned char c)
{
    int i;

    for (i=0; i<8; i++)
    {
        DELAY(1);
        i2c_clr(SCL);
        DELAY(1);

        if (c & (1<<(7-i)))//相应位为1
            i2c_set(SDA);
        else               //相应位为0
            i2c_clr(SDA);

        DELAY(1);
        i2c_set(SCL);
        DELAY(1);
        i2c_clr(SCL);
    }

    DELAY(1);
    i2c_set(SDA);//释放对SDA的控制权//////////
    DELAY(1); ////////////////////////////
}

/*  receives a character from I2C rountine.
 *  @return value: character received
 */
static unsigned char i2c_receive_byte(void)
{
    int j=0;
    int i;
    unsigned char regvalue;

    i2c_set(SDA);

    for (i=0; i<8; i++)
    {
        DELAY(1);
        i2c_clr(SCL);
        DELAY(1);
        i2c_set(SCL);
        
        regvalue = HW_REG(GPIO_1_DIR);
        regvalue &= ~BIT0;            
        HW_REG(GPIO_1_DIR) = regvalue;//与i2c_data_read函数重复
        DELAY(1);                     
        
        if (i2c_data_read())
            j+=(1<<(7-i));

        DELAY(1);
        i2c_clr(SCL);
    }

    DELAY(1);
    i2c_clr(SDA);///////////////////
    DELAY(1);////////////////////

    return j;
}

/*  receives an acknowledge from I2C rountine.
 *
 *  @return value: 0--Ack received; 1--Nack received 接收到高电平表示从设备接收成功。
 *          
 */
static int i2c_receive_ack(void)
{
    int nack;
    unsigned char regvalue;
                                                                                                                                                                 
    DELAY(1);    
    regvalue = HW_REG(GPIO_1_DIR);
    regvalue &= ~BIT0;
    HW_REG(GPIO_1_DIR) = regvalue;
    
    DELAY(1);
    i2c_clr(SCL);
    DELAY(1);
    i2c_set(SCL);
    DELAY(1);    

    nack = i2c_data_read();

    DELAY(1);
    i2c_clr(SCL);
    DELAY(1);
    i2c_set(SDA);///////////////
    DELAY(1);    /////////////////////

    if (nack == 0)//1:Ack, 0:Nack
        return 1; 

    return 0;
}

/* 
 * sends an acknowledge over I2C rountine.
 * 发送高电平
 */
static void i2c_send_ack(void)
{
    DELAY(1);
    i2c_clr(SCL);
    DELAY(1);
    i2c_set(SDA);//发送高电平
    DELAY(1);
    i2c_set(SCL);
    DELAY(1);
    i2c_clr(SCL);
    DELAY(1);
    i2c_clr(SDA);
    DELAY(1);
}

/*  
 *  read data from the I2C bus by GPIO simulated of a device rountine.
 *  @param  devaddress:  address of the device
 *  @param  address: address of register within device
 *   
 *  @return value: data from the device readed
 * 
 */

unsigned char gpio_i2c_read(unsigned char devaddress, unsigned char address)
{
    int rxdata;

    i2c_start_bit();
    i2c_send_byte((unsigned char)(devaddress));
    i2c_receive_ack();
    i2c_send_byte(address);
    i2c_receive_ack();   
    i2c_start_bit();
    i2c_send_byte((unsigned char)(devaddress) | 1);
    i2c_receive_ack();
    rxdata = i2c_receive_byte();
    i2c_send_ack();
    i2c_stop_bit();

    return rxdata;
}

/*  
 *  read data from the I2C bus by GPIO simulated of a digital camera device rountine.
 *
 *  @param  devaddress:  address of the device
 *  @param  address: address of register within device
 *  
 */
 
unsigned char gpio_sccb_read(unsigned char devaddress, unsigned char address)
{
    int rxdata;

    i2c_start_bit();
    i2c_send_byte((unsigned char)(devaddress));
    i2c_receive_ack();
    i2c_send_byte(address);
    i2c_receive_ack();
    i2c_stop_bit();
    i2c_start_bit();
    i2c_send_byte((unsigned char)(devaddress) | 1);
    i2c_receive_ack();
    rxdata = i2c_receive_byte();
    i2c_send_ack();
    i2c_stop_bit();

    return rxdata;
}


/*
 *  writes data to a device on the I2C bus rountine. 
 *
 *  @param  devaddress:  address of the device
 *  @param  address: address of register within device
 *  @param  data:   data for write to device
 *
 */

void gpio_i2c_write(unsigned char devaddress, unsigned char address, unsigned char data)
{
    i2c_start_bit();
    i2c_send_byte((unsigned char)(devaddress));
    i2c_receive_ack();
    i2c_send_byte(address);
    i2c_receive_ack();
    i2c_send_byte(data);
    i2c_stop_bit();
}

static unsigned int  gpioinitialized =0;
/*
 * initializes I2C interface routine.
 *
 * @return value:0--success; 1--error.
 *
 */
static int __init gpio_i2c_init(void)
{
   if(gpioinitialized == 0)
    {
        gpio_map_addr = (unsigned int*)ioremap(GPIO_1_BASE,PAGE_SIZE);//将I/O内存空间映射到核心虚拟地址空间,PAGE_SIZE:每一页内存的大小,在asm/page.h中定义。
        if(gpio_map_addr<0)
        {
            printk("NO mem\n");
            return -EFAULT;
        }
        GPIO_BASE_MMAP = (unsigned int)gpio_map_addr;
		i2c_clr(SCL | SDA);
		printk(KERN_INFO "GPIO control for I2C Driver init successful.\n");		
		gpioinitialized =1;
		
        return 0;
    }
    else
    {
        printk("GPIO control for I2C has been initialized.\n");
        return 0;
    }
}

static void __exit gpio_i2c_exit(void)
{
    iounmap((void *)gpio_map_addr);
    gpioinitialized =0;
}

module_init(gpio_i2c_init);
module_exit(gpio_i2c_exit);

#ifdef MODULE
#include <linux/compile.h>
#endif

MODULE_INFO(build, UTS_VERSION);
MODULE_LICENSE("GPL");

EXPORT_SYMBOL(gpio_i2c_read);
EXPORT_SYMBOL(gpio_sccb_read);
EXPORT_SYMBOL(gpio_i2c_write);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -