您的位置:首页 > 其它

gpio模拟I2C

2013-06-13 13:57 274 查看
开发中经常与i2c打交道,芯片中自带的硬件i2c控制器使用起来并不是很灵活,而且要研究半天的寄存器。所以干脆搞一个软件模拟gpio的通用代码,移植起来也方便,使用灵活

2013-7-8更新代码:添加自旋锁,i2c速率调整

具体代码如下:

#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/version.h>
#include <linux/kernel.h>   
#include <linux/fs.h>       
#include <linux/types.h>    
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/rt2880/rt_mmap.h>
#include <asm/system.h>   

#include <asm/io.h>

#include "gpio_i2c.h" 
#include "../ralink_gpio.h"

#define DELAY(u)		udelay(u*10)
//#define DELAY(u)		mdelay(u)
static spinlock_t i2c_lock = SPIN_LOCK_UNLOCKED;

#define RT2860REG(addr) (*(volatile u32 *)(addr))

static void gpio_i2c_set_bit(int id, int onoff)
{	
	int tmp;

	if(id<=21&&id>=0)
	{
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
		tmp |= (1<<(id));
		*(volatile u32 *)(RALINK_REG_PIODIR) = tmp;

		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
		tmp = (tmp&~(1<<(id)))|(onoff<<(id));
		*(volatile u32 *)(RALINK_REG_PIODATA) = tmp;
	}
	else if(id>=22&&id<=27)
	{
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));
		tmp |= (1<<(id-22));
		*(volatile u32 *)(RALINK_REG_PIO2722DIR) = tmp;

		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DATA));
		tmp = (tmp&~(1<<(id-22)))|(onoff<<(id-22));
		*(volatile u32 *)(RALINK_REG_PIO2722DATA) = tmp;
	}
	else
	{
		printk("####HU#### %s id %d invalid \r\n",__FUNCTION__,id);
	}
}

static int gpio_i2c_get_bit(int id)
{	
	int tmp;

	if(id<=21&&id>=0)
	{
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
		tmp &= ~(1<<(id));
		*(volatile u32 *)(RALINK_REG_PIODIR) = tmp;

		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
		return (tmp>>id&0x01);
	}
	else if(id>=22&&id<=27)
	{
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));
		tmp &= ~(1<<(id-22));
		*(volatile u32 *)(RALINK_REG_PIO2722DIR) = tmp;

		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DATA));
		return (tmp>>(id-22)&0x01);
	}
	else
	{
		printk("####HU#### %s id %d invalid \r\n",__FUNCTION__,id);
	}
}

static void i2c_clk(int onoff)
{	
	gpio_i2c_set_bit(2,onoff);
	
}

static void i2c_dat(int onoff)
{	
	gpio_i2c_set_bit(1,onoff);
	
}

 
static unsigned char i2c_data_read(void)
{
//	unsigned char tmp;
	
//	tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
//	tmp &= ~(1<<(1));
//	*(volatile u32 *)(RALINK_REG_PIODIR) = tmp;
//		
//	//DELAY(1);
//		
//	tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
//	if((tmp&(1<<1)) != 0)
//		return 1;
//	else
//		return 0;

	return gpio_i2c_get_bit(1);
}

/*
 * sends a start bit via I2C rountine.
 *
 */
static void i2c_start_bit(void)
{
        DELAY(1);
        i2c_clk(1);
		i2c_dat(1);
        DELAY(3);
        i2c_dat(0);
        DELAY(1);
}

/*
 * sends a stop bit via I2C rountine.
 *
 */
static void i2c_stop_bit(void)
{
      
		i2c_clk(0);
		i2c_dat(0);
		DELAY(1);
		i2c_clk(1);
        DELAY(1);
		i2c_dat(1);
}

/*
 * sends a character over I2C rountine.
 *
 * @param  c: character to send
 *
 */
static void i2c_send_byte(unsigned char c)
{
    int i;
    local_irq_disable();
	//i2c_clk(1);
    for (i=0; i<8; i++)
    {
        i2c_clk(0);
		if (c & (1<<(7-i)))
            i2c_dat(1);
        else
            i2c_dat(0);
		
        DELAY(1);
		
		i2c_clk(1);
		DELAY(1);
    }
   // i2c_dat(1);
    local_irq_enable();
}

/*  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;

    local_irq_disable();

	i2c_data_read();
    for (i=0; i<8; i++)
    {
        i2c_clk(0);
		DELAY(1);

		i2c_clk(1);
		
		i2c_data_read();
        if (i2c_data_read())
            j+=(1<<(7-i));
		
        DELAY(1);
		
		
    }
    local_irq_enable();
   // i2c_dat(0);
   // 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;
    
    i2c_data_read();
    i2c_clk(0);
    DELAY(1);
	i2c_clk(1);
	
	//i2c_data_read();
	nack = i2c_data_read();
    DELAY(1);

	i2c_dat(1);

    return nack;
}

/* 
 * sends an acknowledge over I2C rountine.
 *
 */
static void i2c_send_ack(void)
{
    i2c_clk(0);
	i2c_dat(0);
    DELAY(1);
	
    i2c_clk(1);
	DELAY(1);
	
}

static void i2c_send_nack(void)
{
    i2c_clk(0);
	i2c_dat(1);
    DELAY(1);
	
    i2c_clk(1);
	DELAY(1);
	
}

static void si2c_init()
{
	RT2860REG(RALINK_REG_GPIOMODE)=RT2860REG(RALINK_REG_GPIOMODE)|(1<<0);
}

static void si2c_exit()
{
	
}

/*  
 *  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
 * 
 */
int gpio_i2c_read(unsigned char devaddress, unsigned char address)
{
	unsigned long flags;
    int rxdata=-1;
	spin_lock_irqsave(&i2c_lock,flags);
	//spin_lock(&i2c_lock);
	
	si2c_init();
	
    i2c_start_bit();
    i2c_send_byte((unsigned char)((devaddress<<1)|0));
    if(i2c_receive_ack()==1)
	{
		goto EXIT;
	}
    i2c_send_byte(address);
    i2c_receive_ack();   
    i2c_start_bit();
    i2c_send_byte((unsigned char)((devaddress<<1)|1));
	if(i2c_receive_ack())
	{
		goto EXIT;
	}
    rxdata = i2c_receive_byte();
    i2c_send_nack();
    i2c_stop_bit();
	
	si2c_exit();

EXIT:
	spin_unlock_irqrestore(&i2c_lock,flags);
	//spin_unlock(&i2c_lock);
    return rxdata;
}
EXPORT_SYMBOL(gpio_i2c_read);

/*
 *  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
 *
 */

int gpio_i2c_write(unsigned char devaddress, unsigned char address, unsigned char data)
{
	int ret = -1;
	unsigned long flags;
	
	//spin_lock_irqsave(&i2c_lock,flags);
	spin_lock(&i2c_lock);
	
	si2c_init();
	
    i2c_start_bit();
    i2c_send_byte((unsigned char)((devaddress<<1)|0));
    if(i2c_receive_ack()==1)
	{
		goto EXIT;
	}
    i2c_send_byte(address);
    if(i2c_receive_ack()==1)
	{
		goto EXIT;
	}
    i2c_send_byte(data); 
   	if(i2c_receive_ack()==1)
	{
		goto EXIT;
	}
    i2c_stop_bit();

	ret=0;
   	si2c_exit();
EXIT:
	//spin_unlock_irqrestore(&i2c_lock,flags);
	spin_unlock(&i2c_lock);
	return ret;
}
EXPORT_SYMBOL(gpio_i2c_write);

/*
 * initializes I2C interface routine.
 *
 * @return value:0--success; 1--error.
 *
 */
static int __init gpio_i2c_init(void)
{

}

static void __exit gpio_i2c_exit(void)
{
	
}

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");
MODULE_VERSION("HI_VERSION=" OSDRV_MODULE_VERSION_STRING);




gpio_i2c_set_bit与gpio_i2c_get_bit设置gpio高低的,DELAY宏为延时方法,理论只要实现这几个值就能够实现标准的gpio_i2c的功能了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: