求I2C总线程序

求I2C总线程序,第1张

//实例85:将数据"0x0f"写入AT24C02再读出送P1口显示

#include <reg51h> // 包含51单片机寄存器定义的头文件

#include <intrinsh> //包含_nop_()函数定义的头文件

#define OP_READ 0xa1 // 器件地址以及读取 *** 作,0xa1即为1010 0001B

#define OP_WRITE 0xa0 // 器件地址以及写入 *** 作,0xa1即为1010 0000B

sbit SDA=P3^4; //将串行数据总线SDA位定义在为P34引脚

sbit SCL=P3^3; //将串行时钟总线SDA位定义在为P33引脚

/

函数功能:延时1ms

(3j+2)i=(3×33+2)×10=1010(微秒),可以认为是1毫秒

/

void delay1ms()

{

unsigned char i,j;

for(i=0;i<10;i++)

for(j=0;j<33;j++)

;

}

/

函数功能:延时若干毫秒

入口参数:n

/

void delaynms(unsigned char n)

{

unsigned char i;

for(i=0;i<n;i++)

delay1ms();

}

/

函数功能:开始数据传送

/

void start()

// 开始位

{

SDA = 1; //SDA初始化为高电平“1”

SCL = 1; //开始数据传送时,要求SCL为高电平“1”

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

SDA = 0; //SDA的下降沿被认为是开始信号

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

SCL = 0; //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)

}

/

函数功能:结束数据传送

/

void stop()

// 停止位

{

SDA = 0; //SDA初始化为低电平“0” _n

SCL = 1; //结束数据传送时,要求SCL为高电平“1”

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

SDA = 1; //SDA的上升沿被认为是结束信号

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

SDA=0;

SCL=0;

}

/

函数功能:从AT24Cxx读取数据

出口参数:x

/

unsigned char ReadData()

// 从AT24Cxx移入数据到MCU

{

unsigned char i;

unsigned char x; //储存从AT24Cxx中读出的数据

for(i = 0; i < 8; i++)

{

SCL = 1; //SCL置为高电平

x<<=1; //将x中的各二进位向左移一位

x|=(unsigned char)SDA; //将SDA上的数据通过按位“或“运算存入x中

SCL = 0; //在SCL的下降沿读出数据

}

return(x); //将读取的数据返回

}

/

函数功能:向AT24Cxx的当前地址写入数据

入口参数:y (储存待写入的数据)

/

//在调用此数据写入函数前需首先调用开始函数start(),所以SCL=0

bit WriteCurrent(unsigned char y)

{

unsigned char i;

bit ack_bit; //储存应答位

for(i = 0; i < 8; i++) // 循环移入8个位

{

SDA = (bit)(y&0x80); //通过按位“与”运算将最高位数据送到S

//因为传送时高位在前,低位在后

_nop_(); //等待一个机器周期

SCL = 1; //在SCL的上升沿将数据写入AT24Cxx

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

SCL = 0; //将SCL重新置为低电平,以在SCL线形成传送数据所需的8个脉冲

y <<= 1; //将y中的各二进位向左移一位

}

SDA = 1; // 发送设备(主机)应在时钟脉冲的高电平期间(SCL=1)释放SDA线,

//以让SDA线转由接收设备(AT24Cxx)控制

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

SCL = 1; //根据上述规定,SCL应为高电平

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

_nop_(); //等待一个机器周期

ack_bit = SDA; //接受设备(AT24Cxx)向SDA送低电平,表示已经接收到一个字节

//若送高电平,表示没有接收到,传送异常

SCL = 0; //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)

return ack_bit; // 返回AT24Cxx应答位

}

/

函数功能:向AT24Cxx中的指定地址写入数据

入口参数:add (储存指定的地址);dat(储存待写入的数据)

/

void WriteSet(unsigned char add, unsigned char dat)

// 在指定地址addr处写入数据WriteCurrent

{

start(); //开始数据传递

WriteCurrent(OP_WRITE); //选择要 *** 作的AT24Cxx芯片,并告知要对其写入数据

WriteCurrent(add); //写入指定地址

WriteCurrent(dat); //向当前地址(上面指定的地址)写入数据

stop(); //停止数据传递

delaynms(4); //1个字节的写入周期为1ms, 最好延时1ms以上

}

/

函数功能:从AT24Cxx中的当前地址读取数据

出口参数:x (储存读出的数据)

/

unsigned char ReadCurrent()

{

unsigned char x;

start(); //开始数据传递

WriteCurrent(OP_READ); //选择要 *** 作的AT24Cxx芯片,并告知要读其数据

x=ReadData(); //将读取的数据存入x

stop(); //停止数据传递

return x; //返回读取的数据

}

/

函数功能:从AT24Cxx中的指定地址读取数据

入口参数:set_addr

出口参数:x

/

unsigned char ReadSet(unsigned char set_addr)

// 在指定地址读取

{

start(); //开始数据传递

WriteCurrent(OP_WRITE); //选择要 *** 作的AT24Cxx芯片,并告知要对其写入数据

WriteCurrent(set_addr); //写入指定地址

return(ReadCurrent()); //从指定地址读出数据并返回

}

/

函数功能:主函数

/

main(void)

{

SDA = 1; // SDA=1,SCL=1,使主从设备处于空闲状态

SCL = 1;

WriteSet(0x36,0x0f); //在指定地址“0x36”中写入数据“0x0f”

P1=ReadSet(0x36); //从指定地址“0x36中读取数据并送P1口显示

}

假设手上有一块从淘宝上买来的开发板,我要在开发板的I2C总线上增加一个从设备(如at24c08),那么我要怎样写这个“I2C设备驱动”,让

应用程序可以访问at24c08呢

先来看一个最简单的i2c设备驱动:

static struct i2c_board_info at24cxx_info = { //所支持的i2c设备的列表

I2C_BOARD_INFO("at24c08", 0x50), //一项代表一个支持的设备,它的名字叫做“at24c08”,器件地址是0x50

};

static struct i2c_client at24cxx_client;

static int at24cxx_dev_init(void)

{

struct i2c_adapter i2c_adap; //分配一个适配器的指针

i2c_adap = i2c_get_adapter(0); //调用core层的函数,获得一个i2c总线。这里我们已经知道新增的器件挂接在编号为0的i2c总线上

at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info); // 把i2c适配器和新增的I2C器件关联起来,这个用了i2c总线0,地址是0x50。这就组成了一个客户端

at24cxx_client i2c_put_adapter(i2c_adap);

return 0;

}

static void at24cxx_dev_exit(void)

{

i2c_unregister_device(at24cxx_client);

}

module_init(at24cxx_dev_init);

module_exit(at24cxx_dev_exit);

从上面的程序可以看到,写一个i2c设备驱动程序,与写普通的字符驱动基本一样。特别之处是它调用了i2c的core层的函数,以获得对i2c总线的控制。因为用的是开发板,板上的与soc芯片(一般来说就是arm的芯片)i2c总线驱动一般都做好了,直接调用core层的函数就可以控制soc的i2c模块了。也就是说,写i2c设备驱动不需要关注arm内部的i2c模块的寄存器,我们需要关注的是设备(at24c08)的寄存器以及它的datasheet对时序的要求。

其实,添加i2c设备的方法很灵活。根据Linux的官方文档《linux-342\Documentation\i2c\instantiating-devices》,添加i2c设备的方法总结有4种:

1 i2c_register_board_info:根据总线编号、设备名字(“at24c08”)、设备地址(0x50)注册一个字符驱动。这种方法最简单、最粗暴,最贴近平时在开片机上开发i2c器件的。

2 i2c_new_device:根据i2c总线的编号,声明一个i2c设备:这种方法就是上面例子用的方法。这种方法也简单,但是需要事先知道器件挂接在哪条总线上。对于设备,还实现知道了设备地址0x50,总线适配器也支持名字为“at24c08”的设备

3 i2c_new_probed_device:

4从用户空间实例化一个器件:这个方法相当智能快速,如下输入指令,即可增加一个i2c设备,同时增加了对应的设备文件。

# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device

根据英文文档的标题,添加i2c设备有称之为“i2c设备的实例化”。

从上述可以知道,在实例化一个i2c设备之前,除了有对应的驱动支持总线外(这里是总线0),还需要有一个驱动使用了总线0发送时序,支持名字为"at24c08"的器件。这个驱动用总线驱动的函数,配置了at24c08的寄存器。

如果你学单片机的哈,肯定是要学会自己写的,最起码你要能把别人的程序能调通,能通信,而且不同单片机的I2C协议是有部分的不同的,例如有的I2C在接收的时候会发两次,第一次不是数据而是要把它读空,第二次才是数据,如果你用正常的单片机协议肯定就读不到数据了

希望能给你解答:

1、时钟信号都是主机产生的,从机只有一种情况下才能控制时钟线,即在忙的时候,主机还在发送数据,从机会主动把时钟拉低,表示我正在忙,不能收数据。

2、两个主机的话,在发送的时候一定会有一个从属关系,这个需要自己设置。

3、响应:i2c上每传输一个字节,都必须要有响应,方向是从机到主机。

以上就是关于求I2C总线程序全部的内容,包括:求I2C总线程序、在linux上怎样增加一个i2c设备、请问I2C总线的驱动程序 要学会自己写嘛 还是平时写的时候只要复制过来,拿来用就可以 求指教啊!!!等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/9790101.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-02
下一篇 2023-05-02

发表评论

登录后才能评论

评论列表(0条)

保存