gpio模拟I2C的信号发送———基于rk3308b-cc-plus

一、dts文件书写

	gpio_i2c: gpio_i2c {
		status = "okay";
		compatible = "gpio_i2c";
		gpio_sda = <&gpio2 RK_PA2 GPIO_ACTIVE_HIGH>;
		gpio_scl = <&gpio2 RK_PA3 GPIO_ACTIVE_HIGH>;
	};

分别用两个gpio模拟数据线与信号线

二、利用字符设备模拟输入输出

驱动代码

#include
#include
#include
#include
#include
#include
#include 
#include 
#include
#include
#include
#include
#include
#include

#define DELAY_TIME_HXD 100 //设置i2c clk, hxd019大概要求10k左右

static int SDA;					/*SDA的引脚号*/
static int SCL;					/*SDL的引脚号*/
static int majorNumber;
static const char *CLASS_NAME = "i2c_class";
static const char *DEVICE_NAME = "i2c_device";

static struct cdev gpio_dev;
static dev_t ndev;
static int gpio_status;
static char recv_msg[2000];

static struct class *i2c_gpio_class;
static struct device *i2c_gpio_device;

struct i2c_gpio_info {
	int i2c_gpio;			/*gpio号*/
	int gpio_value;			/*电平*/
};

static int i2c_gpio_open(struct inode *node, struct file *file)
{
	printk(KERN_INFO "GPIO init\n");
	return 0;
}

static ssize_t i2c_gpio_read(struct file *file, char *buf, size_t len, loff_t *offset)
{
	int cnt = 0;/*将内核空间的数据copy到用户空间*/
	cnt = copy_to_user(buf, &gpio_status, 1);
	if (cnt == 0) {
		return 0;
	} else {
		printk(KERN_ALERT "ERROR occur when reading!!\n");
		return -EFAULT;
	}
	return 1;
}

/* I2C起始条件 */
void i2c_start(void)
{
	//初始化GPIO口
	gpio_direction_output (SDA, 1); 	//设置SDA方向为输出,设置SDA为高电平
	gpio_direction_output (SCL, 1);    	//设置SCL方向为输出,设置SCL为高电平
	udelay(DELAY_TIME_HXD);                            //延时

	//起始条件
	gpio_set_value(SDA, 0);                 //SCL为高电平时,SDA由高变低
	udelay(DELAY_TIME_HXD);
}

/* I2C终止条件 */
void i2c_stop(void)
{
	gpio_set_value(SCL, 1);
	gpio_direction_output (SDA, 0);
	udelay(DELAY_TIME_HXD);
	gpio_set_value(SDA, 1);             //SCL高电平时,SDA由低变高
}

/*
I2C读取ACK信号(写数据时使用)
返回值 :0表示ACK信号有效;非0表示ACK信号无效
*/
unsigned char i2c_read_ack(void)
{
	unsigned char r;
	gpio_direction_input (SDA);//设置SDA方向为输入
	gpio_set_value(SCL,0);              // SCL变低
	r = gpio_get_value(SDA);                //读取ACK信号
	udelay(DELAY_TIME_HXD);
	gpio_set_value(SCL,1);              // SCL变高
	udelay(DELAY_TIME_HXD);
	return r;
}

/* I2C发出ACK信号(读数据时使用) */
void i2c_send_ack(void)
{
	gpio_set_value(SCL,0);              // SCL变低
	gpio_direction_output(SDA, 0);          //设置SDA方向为输出,发出ACK信号
	printk(KERN_INFO "ACK SDAlow\n");
	udelay(DELAY_TIME_HXD);
	gpio_set_value(SCL,1);              // SCL变高
	udelay(DELAY_TIME_HXD);
}

/* I2C字节写 */
void i2c_write_byte(unsigned char b)
{
	int i;
	gpio_direction_output(SDA, gpio_get_value(SDA));          //设置SDA方向为输出
	for (i=7; i>=0; i--) {
		gpio_set_value(SCL, 0);             // SCL变低
		udelay(DELAY_TIME_HXD);
		gpio_set_value(SDA, b & (1<<i));        //从高位到低位依次准备数据进行发送
		gpio_set_value(SCL, 1);             // SCL变高
		udelay(DELAY_TIME_HXD);
	}
	i2c_read_ack();                 //检查目标设备的ACK信号
}

/* I2C字节读 */
unsigned char i2c_read_byte(void)
{
	int i;
	unsigned char r = 0;
	gpio_direction_input(SDA);           //设置SDA方向为输入
	for (i=7; i>=0; i--) {
		gpio_set_value(SCL, 0);         // SCL变低
		udelay(DELAY_TIME_HXD);
		r = (r <<1) | gpio_get_value(SDA);      //从高位到低位依次准备数据进行读取
		gpio_set_value(SCL, 1);         // SCL变高
		udelay(DELAY_TIME_HXD);
	}
	i2c_send_ack();                 //向目标设备发送ACK信号
	return r;
}

/*
I2C读操作
addr:目标设备地址
buf:读缓冲区
len:读入字节的长度
*/
void i2c_read(unsigned char addr, unsigned char* buf, int len)
{
	int i;
	unsigned char t;
	i2c_start();                        //起始条件,开始数据通信
	//发送地址和数据读写方向
	t = (addr << 1) | 1;                    //低位为1,表示读数据
	i2c_write_byte(t);
	//读入数据
	for (i=0; i<len; i++)
		buf[i] = i2c_read_byte();
	i2c_stop();                     //终止条件,结束数据通信
}
/*
I2C写操作
addr:目标设备地址
buf:写缓冲区
len:写入字节的长度
*/
void i2c_write (unsigned char addr, unsigned char* buf, int len)
{
	int i;
	unsigned char t;
	i2c_start();                        //起始条件,开始数据通信
	//发送地址和数据读写方向
	t = (addr << 1) | 0;                    //低位为0,表示写数据
	i2c_write_byte(t);
	//写入数据
	for (i=0; i<len; i++)
		i2c_write_byte(buf[i]);
	i2c_stop();                     //终止条件,结束数据通信
}

static ssize_t i2c_gpio_write(struct file *file, const char *buf, size_t len, loff_t *offset)
{
	/*将用户空间的数据copy到内核空间*/
	unsigned char addr = 'h';
	char real_msg[2000];

	int cnt = copy_from_user(recv_msg, buf, len);
	strncpy(real_msg, recv_msg + 1, strlen(recv_msg) - 1);
	if (cnt == 0) {
		/*首先对recv_msg拆分,分为目的和传输内容*/
		if (recv_msg[0] == '1') {
			//printk(KERN_INFO "I2C read! The length of recv_msg is %d\n", strlen(recv_msg));
			i2c_read(addr, real_msg, strlen(real_msg));

		} else if (recv_msg[0] == '0') {
			printk(KERN_INFO "I2C write!\n");
			i2c_write(addr, real_msg, strlen(real_msg));
		}
	} else {
		printk(KERN_ALERT "ERROR occur when writing!!\n");
		return -EFAULT;
	}
	return len;
}
static int i2c_gpio_release(struct inode *node, struct file *file)
{
	printk(KERN_INFO "Release!!\n");
	return 0;
}

static struct file_operations file_oprts = {
	.open = i2c_gpio_open,
	.read = i2c_gpio_read,
	.write = i2c_gpio_write,
	.release = i2c_gpio_release,
};

static int i2c_gpio_probe(struct platform_device *pdev)
{
	/*先把两个引脚request到!*/
	int gpio;											/*gpio号*/
	struct i2c_gpio_info i2c_info;						/*结构体准备*/
	struct device_node *i2c_gpio_node = pdev->dev.of_node;	/*设备节点*/

	printk(KERN_INFO "I2C GPIO Program Probe.\n");

	/*先申请模拟的SDA线*/
	gpio = of_get_named_gpio(i2c_gpio_node, "gpio_sda", 0);
	if (!gpio_is_valid(gpio)) {
		dev_err(&pdev->dev, "gpio_sda: %d is invalid\n", gpio);
        return -ENODEV;
	}
	if(gpio_request(gpio, "gpio_sda")) {
		dev_err(&pdev->dev, "gpio_sda: %d request failed!\n", gpio);
        gpio_free(gpio);
        return -ENODEV;
	}

	SDA = gpio;
	i2c_info.i2c_gpio = gpio;
	i2c_info.gpio_value = 1;
	gpio_direction_output(i2c_info.i2c_gpio, i2c_info.gpio_value);
	printk(KERN_INFO "I2C SDA request successfully.\n");

	/*再申请模拟的SCL线*/
	gpio = of_get_named_gpio(i2c_gpio_node, "gpio_scl", 0);
	if (!gpio_is_valid(gpio)) {
		dev_err(&pdev->dev, "gpio_scl: %d is invalid\n", gpio);
        return -ENODEV;
	}
	if(gpio_request(gpio, "gpio_scl")) {
		dev_err(&pdev->dev, "gpio_scl: %d request failed!\n", gpio);
        gpio_free(gpio);
        return -ENODEV;
	}

	SCL = gpio;
	i2c_info.i2c_gpio = gpio;
	i2c_info.gpio_value = 1;
	gpio_direction_output(i2c_info.i2c_gpio, i2c_info.gpio_value);
	printk(KERN_INFO "I2C SCL request successfully.\n");
	return 0;
}

static struct of_device_id i2c_match_table[] = {
	{ .compatible = "gpio_i2c",},
	{},
};

static struct platform_driver i2c_gpio_driver = {
	.driver = {
		.name = "gpio_i2c",
		.owner = THIS_MODULE,
		.of_match_table = i2c_match_table,
	},
	.probe = i2c_gpio_probe,

};

static int cdev_create(void)
{
	printk(KERN_INFO "chev adding-----\n");
	cdev_init(&gpio_dev, &file_oprts);
	if (alloc_chrdev_region(&ndev, 0, 1, DEVICE_NAME) < 0) {
		printk(KERN_ALERT "Register failed!!\r\n");
		return -1;
	}
	if (cdev_add(&gpio_dev, ndev, 1) < 0) {
		printk("add wrong!\n");
		return -1;
	}
	majorNumber = MAJOR(ndev);
	printk(KERN_ALERT "Registe success,major number is %d\r\n", majorNumber);

	/*以CLASS_NAME创建一个class结构,这个动作将会在/sys/class目录创建一个名为CLASS_NAME的目录*/
	i2c_gpio_class = class_create(THIS_MODULE, CLASS_NAME);
	if (IS_ERR(i2c_gpio_class)) {
		unregister_chrdev_region(ndev, 1);
		return PTR_ERR(i2c_gpio_class);
	}

	/*以DEVICE_NAME为名,参考/sys/class/CLASS_NAME在/dev目录下创led建一个设备:/dev/DEVICE_NAME*/
	i2c_gpio_device = device_create(i2c_gpio_class, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
	if (IS_ERR(i2c_gpio_device)) {
		class_destroy(i2c_gpio_device);
		unregister_chrdev_region(ndev, 1);
		return PTR_ERR(i2c_gpio_device);
	}
	printk(KERN_ALERT "i2c_gpio device init success!!\r\n");
	return 0;
}

static int i2c_gpio_init(void)
{
	platform_driver_register(&i2c_gpio_driver);

	if (cdev_create() != 0)
	{
		printk(KERN_INFO "cdev create failed.\n");
		return -1;
	}
	return 0;
}
module_init(i2c_gpio_init);

static void i2c_gpio_exit(void)
{
	device_destroy(i2c_gpio_class, MKDEV(majorNumber, 0));
	class_unregister(i2c_gpio_class);
	class_destroy(i2c_gpio_class);
	unregister_chrdev_region(ndev, 1);
	platform_driver_unregister(&i2c_gpio_driver);
}
module_exit(i2c_gpio_exit);

MODULE_LICENSE("GPL");

应用层代码

#include 
#include 
#include 
#include 
#include 
#include 
static char buf[256] = {1};
int main(int argc, char *argv[])
{
	int fd = open("/dev/i2c_device", O_RDWR);

	if (fd < 0) {
		perror("Open file failed!!!\r\n");
		return -1;
	}
	printf("Please input:\n");
	scanf("%s", buf);
	
	int ret = write(fd, buf, strlen(buf));
	if (ret < 0)
		perror("Failed to write!!");
	
	close(fd);
	return 0;
}

三、示波器观察

分别让两个引脚连接到示波器上,然后采取边缘触发模式,运行应用层代码,输入你想传输的信息,观察得到的波形即可~

四、仍存在的问题

1、会在正常输入后还有其他波形,目前未找到原因
2、不支持从机发送信号,所以read部分,开始读取从机信号时,SDA会一直保持高电平

你可能感兴趣的:(嵌入式学习,单片机,嵌入式硬件)