📄 at24c02.c
字号:
/*
* s3c2440 i2c
* Author: liu xiao ming <lxm650@163.com>
* * 本程序以字符设备驱动的方式读写AT24C02,相对于linux标准的i2c驱动要简单得多, * 对于大多数嵌入式应用是比较合适的。读者也应该可以很容易的修改成适合自己需要的驱动。 * * This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/interrupt.h> #include <linux/delay.h>#include <asm/hardware/clock.h>
#include <asm/io.h>#include <asm/uaccess.h> #include <asm/hardware.h> #include <asm/arch-s3c2410/regs-gpio.h>#include <asm/plat-s3c/regs-iic.h>#undef DEBUG
#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk(__FUNCTION__);printk("(%d): ",__LINE__);printk(x);}
#else
#define DPRINTK(x...) (void)(0)
#endif
#define VERSION_STRING "at24c02 driver for s3c2440"
#define DEVICE_NAME "s3c2440_i2c"
#define S3C2440I2C_MAJOR 240 /* 预设的i2c主设备号 */#define CMD S3C2410_IICCON_ACKEN | S3C2410_IICCON_TXDIV_16 | \ S3C2410_IICCON_IRQEN | S3C2410_IICCON_SCALE(0x0F)#define S3C2440_SLAVE_ADDR 0x10#define SLV_ADDR 0xA0#define I2C_REGS_BASE 0x54000000
static int s3c2440i2c_major = S3C2440I2C_MAJOR;volatile int get_ack;
struct s3c2440i2c_dev {
struct cdev cdev; /* cdev结构体 */
// struct semaphore lock; /* 信号量 */
};
struct s3c2440i2c_dev s3c2440i2c_dev;struct s3c2440i2c_dev *s3c2440i2c_devp; /* 设备结构体指针 */
static void __iomem *base_addr; /* 物理地址映射成虚拟地址后的基地址指针 */static struct clk *i2c_clock; /* 时钟 */void inline clear_pending(void){ unsigned long tmp; tmp = ioread32(base_addr + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; iowrite32(tmp, base_addr + S3C2410_IICCON);}/* * at24c02写函数 * 注意:write_data[0]表示要写入的地址 * write_data[1]表示要写入的数据 * 每次只能写一个字节 **/
static ssize_t at24c02_write(struct file *file, const char *buffer, size_t count, loff_t * ppos){
char write_data[2]; get_ack = 0;
if(count != sizeof(write_data)){
/* 输入数据大小错误 */ DPRINTK("the size of input data must be %d\n", sizeof(write_data));
return 0;
}
copy_from_user(&write_data, buffer, count); iowrite32(SLV_ADDR, base_addr + S3C2410_IICDS); // 写从器件地址 iowrite32(0xF0, base_addr + S3C2410_IICSTAT); // 设置主发送模式并产生起始条件 while(get_ack == 0); // 等待传送完成 get_ack = 0; iowrite32(write_data[0], base_addr + S3C2410_IICDS); // 写要写入字节的地址 clear_pending(); // 清除中断pending位 while(get_ack == 0); // 等待传送完成 get_ack = 0; iowrite32(write_data[1], base_addr + S3C2410_IICDS); // 写数据 clear_pending(); // 清除中断pending位 while(get_ack == 0); // 等待传送完成 get_ack = 0; iowrite32(0xD0, base_addr + S3C2410_IICSTAT); // 停止主发送 clear_pending(); // 清除中断pending位 msleep(1); // 等待停止位起作用 DPRINTK("write data = %d\n", write_data[1]); return count - 1;
}
/* * at24c02的读函数 * 注意:形参count与原来含义不同,不再表示读出的字节数,而是表示要读的数据地址 * 每次只能读一个字节 **/
static ssize_t at24c02_read(struct file *filp, char *buffer, size_t count, loff_t *ppos){
char read_data; unsigned char read_addr; read_addr = count; // 注意count表示要读的地址! get_ack = 0; iowrite32(SLV_ADDR, base_addr + S3C2410_IICDS); // 写从器件地址 iowrite32(0xF0, base_addr + S3C2410_IICSTAT); // 设置主发送并启动传送 while(get_ack == 0); // 等待传送完成 get_ack = 0; iowrite32(read_addr, base_addr + S3C2410_IICDS); // 写要读的地址(伪写操作) clear_pending(); // 清除中断pending位 while(get_ack == 0); // 等待传送完成 get_ack = 0; iowrite32(SLV_ADDR | 0x01, base_addr + S3C2410_IICDS); // 写从器件地址 iowrite32(0xB0, base_addr + S3C2410_IICSTAT); // 设置主接收模式并产生起始条件 clear_pending(); // 清除中断pending位 while(get_ack == 0); // 等待传送完成 get_ack = 0; iowrite32(0x2F, base_addr + S3C2410_IICCON); // 不产生应答 while(get_ack == 0); // 等待接收数据完成 get_ack = 0; read_data = ioread32(base_addr + S3C2410_IICDS); // 读取数据内容 iowrite32(0x90, base_addr + S3C2410_IICSTAT); // 主接收停止并产生停止条件 clear_pending(); // 清除中断pending位 msleep(1); // 等待停止条件起作用 DPRINTK( "read_data = %d\n", read_data); copy_to_user(buffer, &read_data, 1); // 复制到用户空间 return count;
} /*文件打开函数*/
static int at24c02_open(struct inode *inode, struct file *filp)
{
// DPRINTK( "i2c opened\n");
return 0;
} static int at24c02_release(struct inode *inode, struct file *filp)
{
// DPRINTK( "i2c closed\n");
return 0;
} static struct file_operations at24c02_fops = {
owner: THIS_MODULE,
open: at24c02_open,
read: at24c02_read,
write: at24c02_write,
release: at24c02_release,
}; /* i2c中断服务函数 */static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id){ get_ack = 1; return IRQ_HANDLED;}/*初始化并注册cdev*/
static void s3c2440i2c_setup_cdev(struct s3c2440i2c_dev *dev, int index)
{
int err, devno = MKDEV(s3c2440i2c_major, index);
cdev_init(&dev->cdev, &at24c02_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &at24c02_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
DPRINTK("Error %d adding devno%d", err, index);
}int __init at24c02_init(void){ int result; dev_t devno = MKDEV(s3c2440i2c_major, 0); /* 申请中断 */
result = request_irq(IRQ_IIC, s3c24xx_i2c_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
/* 中断号被占用 */
if (result) {
DPRINTK("IRQ%d already in use\n", IRQ_IIC);
return result;
} /* 申请设备号*/
if (s3c2440i2c_major)
result = register_chrdev_region(devno, 1, "s3c2440i2c");
else /* 动态申请设备号 */
{
result = alloc_chrdev_region(&devno, 0, 1, "s3c2440i2c");
s3c2440i2c_major = MAJOR(devno);
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存*/
s3c2440i2c_devp = kmalloc(sizeof(struct s3c2440i2c_dev), GFP_KERNEL);
if (!s3c2440i2c_devp) /*申请失败*/
{
result = -ENOMEM;
goto fail_malloc;
}
memset(s3c2440i2c_devp, 0, sizeof(struct s3c2440i2c_dev));
s3c2440i2c_setup_cdev(s3c2440i2c_devp, 0); /* 初始化 gpio */ s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA); s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL); i2c_clock = clk_get(NULL, "i2c"); if (!i2c_clock) { DPRINTK("failed to get i2c clock source\n"); return -ENOENT; } clk_enable(i2c_clock); base_addr = ioremap(I2C_REGS_BASE, 0x14); /* s3c2440 器件地址 */ iowrite32(S3C2440_SLAVE_ADDR, base_addr + S3C2410_IICADD); /* 使能ACK和中断, 50.625Mhz/16/(15+1) = 197.7Khz */ iowrite32(CMD, base_addr + S3C2410_IICCON); /* 串行输出使能 */ iowrite32(0x10, base_addr + S3C2410_IICSTAT); return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return result;
}void __exit at24c02_exit(void)
{
if (i2c_clock) { clk_disable(i2c_clock); clk_put(i2c_clock); i2c_clock = NULL; } cdev_del(&s3c2440i2c_devp->cdev); /* 注销cdev */
kfree(s3c2440i2c_devp); /* 释放设备结构体内存 */
unregister_chrdev_region(MKDEV(s3c2440i2c_major, 0), 1); /* 释放设备号 */ iounmap(base_addr); /* 释放申请的虚拟地址 */ free_irq(IRQ_IIC, NULL); /* 释放中断 */
}
MODULE_AUTHOR("lxm<lxm650@163.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION(VERSION_STRING);
module_init(at24c02_init);
module_exit(at24c02_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -