开发中经常与i2c打交道,芯片中自带的硬件i2c控制器使用起来并不是很灵活,而且要研究半天的寄存器。所以干脆搞一个软件模拟gpio的通用代码,移植起来也方便,使用灵活
具体代码如下:
#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*100) #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(1); i2c_dat(0); DELAY(1); } /* * sends a stop bit via I2C rountine. * */ static void i2c_stop_bit(void) { i2c_clk(0); i2c_dat(1); DELAY(1); i2c_clk(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); if (nack == 0) return 1; return 0; } /* * 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 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 * */ unsigned char gpio_i2c_read(unsigned char devaddress, unsigned char address) { int rxdata; si2c_init(); i2c_start_bit(); i2c_send_byte((unsigned char)((devaddress<<1)|0)); i2c_receive_ack(); i2c_send_byte(address); i2c_receive_ack(); i2c_start_bit(); i2c_send_byte((unsigned char)((devaddress<<1)|1)); i2c_receive_ack(); rxdata = i2c_receive_byte(); i2c_send_ack(); i2c_stop_bit(); si2c_exit(); 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 * */ void gpio_i2c_write(unsigned char devaddress, unsigned char address, unsigned char data) { si2c_init(); i2c_start_bit(); i2c_send_byte((unsigned char)((devaddress<<1)|0)); i2c_receive_ack(); i2c_send_byte(address); i2c_receive_ack(); i2c_send_byte(data); i2c_receive_ack(); i2c_stop_bit(); si2c_exit(); } 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的功能了