回顾:
1. linux内核的分离思想
分离硬件和软件
struct platform_device:装载硬件信息
.name 用于匹配
.id 如果有多个,通过id编号
.resource 装载硬件信息
.start
.end
.flags
IORESOURCE_MEM
IORSOURCE_IRQ
.num_resources 资源个数
.dev = {//自己定义的硬件信息
.platform_data 装载硬件信息
}
struct platform_driver:装载软件信息
.driver ={
.name 用于匹配
}
.probe 匹配成功,内核调用
.remove 卸载,内核调用
注意:probe函数是否被内核调用很重要!!如果未调用,说明软件、硬件没有完全结合!!
注意:以上驱动的编程方法适合于所有设备!!
2. I2C 总线
概念:两线式串行总线
SCL:时钟控制信号线,控制双方的数据传输过程
SDA:数据线,传输数据
串行:一个时钟周期传输一个bit行!
总线:在I2C总线可以挂接很多设备!
上拉电阻:两个信号线的电平默认是高电平!
slave:
master:
MSB:
LSB:
I2C总线一次传输一个字节,如果是四个字节的数据要分四次传。
从高位开始传输!!
问:CPU是如何定位在I2C 总线上某个外设备?
问:CPU如何通过两根信号线和外设进行数据交互?
问:SDA和SCL如何配合使用?
答案都在I2C总线协议中!
START信号:SCL为高,SDA由高向低跳变
STOP信号:SCL为高,SDA由低向高跳变
设备地址:举例AT24C02和ADP8860为例
ACK信号:
读写信号:
读:1
写:0
ACK信号:第九个时钟周期,低电平有效
以将数据0x55写入到AT24C02的0x10地址中为例:
SDA和SCL如何配合使用呢?
例如:
CPU将某个bit位写入设备中:
1,CPU将SCL拉低(只要在低电平的过程中,都可以向数据线上放数据)
2,CPU将某个bit位的数据放在数据线上
3,CPU将SCL拉高,数据线上的数据保持稳定
4,设备在同周期的SCL为高电平,从数据线上获取数据
CPU读取设备发送来的某个bit位数据:
1,CPU将SCL拉低(让设备将数据写入到数据线上)
2,设备将数据放在数据线上
3,CPU将SCL拉高(为了让CPU自己读取数据),数据线上的数据保持稳定
4,CPU在同周期的SCL为高电平,从数据线上获取数据
总结:SCL为低电平时,SDA上的数据可以进行改变;SCL为低电平时,SDA上的数据保持稳定!
I2C 总线数据传输速度:
100K
400K
3.4M
总结:对于I2C设备的访问具体操作关键看芯片的操作时序图!!
案例:CW210开发板上有一个EEPROM(AT24C02),I2C接口,需要就是存储软件版本到AT24C02中,并且能够读取版本号从AT24C02!
软件版本号的格式:SYYMMDXY 例如 S14121700
char *p = “S14121700”;
驱动采用GPIO模拟I2C时序来实现!
了解AT24C02属于EEPROM(电可擦除存储器)
容量为256字节
工作频率:100K或者400K
可以按页进行访问:一页为8字节,共32个页。
涉及的GPIO管脚:
GPD1_0 -> SDA
GPD1_1 ->SDA
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/miscdevice.h>
#include <linux/workqueue.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <asm/gpio.h>
#include <linux/gpio-cfg.h>
#define DELAY(us) time_delay_us(us)
#define SCL (1<<0)
#define SDA (1<<1)
#define GPIO_I2C_READ 0x100001
#define GPIO_I2C_WRITE 0x100002
struct eeprom_data {
unsigned char addr;//片内地址
unsigned char data;//片内数据
}
static void i2c_set(unsigned whichline){
if(whichline == SCL){
gpio_direction_output(S5PV210_GPD(1),1);
return;
}
else if(whichline == SDA){
gpio_direction_output(S5PV210_GPD(0),1);
return;
}
else if(whichline == (SDA|SCL)){
gpio_direction_output(S5PV210_GPD(1),1);
gpio_direction_output(S5PV210_GPD(0),1);
return;
}
else{
printk("error input.\n");
}
}
static init gpio_i2c_init(void){
int ret;
ret = misc_register(&gpioi2c_dev);
if(0!=ret)
return -1;
gpio_request(S5PV21_GPD1(0),"SDA0");
gpio_request(S5PV21_GPD1(1),"SCL0");
i2c_set(SCL|SDA);
return 0;
}
static void gpio_i2c_exit(void){
misc_deregister(&gpioi2c_dev);
gpio_free(S5PV21_GPD1(0));
gpio_free(S5PV210_GPD1(1));
}
module_init(gpio_i2c_init);
module_exit(gpio_i2c_exit);
MODULE_LICENSE("GPL");
60:17