linux之I2C裸机驱动解析

发布者:诗意世界最新更新时间:2024-07-19 来源: cnblogs关键字:linux  I2C  裸机驱动 手机看文章 扫描二维码
随时随地手机看文章

1 硬件特性

1.1 概述


I2C总线是由Philips公司开发的两线式串行总线,这两根线为时钟线(SCL)和双向数据线(SDA)。由于I2C总线仅需要两根线,因此在电路板上占用的空间更少,带来的问题是带宽较窄。I2C在标准模式下传输速率最高100Kb/s,在快速模式下最高可达400kb/s。属于半双工。


嵌入式系统中,I2C应用非常广泛,大多数微控制器中集成了I2C总线,一般用于和RTC,EEPROM,智能电池电路,传感器,LCD以及其他类似设备之间的通信。


1.2 I2C总线传输时序


1.3 I2C总线的信号状态


1、空闲状态:SDA和SCL都是高电平;


2、开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据;


3、结束条件(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据;


4、数据有效:在SCL的高电平期间,SDA保持稳定,数据有效。SDA的改变只能发生在SCL的低电平期间;


5、CK信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。


1.4 从设备地址


I2C总线从设备使用7位地址,最后一个为读写控制位。下图是eeprom的原理图,我们可以计算出它的地址为0x50。


1.5 I2C读写方式


多字节写的时序


多字节读的时序



具体可参考datasheet


附:ok6410裸机I2C代码。


  1 #define INTPND (*(volatile unsigned long*)0x4a000010)

  2 #define SRCPND (*(volatile unsigned long*)0x4a000000)

  3 #define INTMSK (*(volatile unsigned long*)0x4a000008)

  4 #define GPECON (*(volatile unsigned long*)0x56000040)

  5 #define GPEUP  (*(volatile unsigned long*)0x56000048)

  6 

  7 #define IICCON    (*(volatile unsigned char*)0x54000000)

  8 #define IICSTAT   (*(volatile unsigned char*)0x54000004)

  9 #define IICDS     (*(volatile unsigned char*)0x5400000C)

 10 

 11 #define SLAVE_WRITE_ADD 0xa0  /* 写入数据时;方向位(第0位)为0 */

 12 #define SLAVE_READ_ADD 0xa1   /* 读取数据时;方向位(第0位)为1 */

 13 

 14 

 15 void delay(int i)

 16 {

 17    int j = 0;

 18    while (i--)    

 19    {

 20        for (j=0;j<100;j++)

 21        {    

 22            ;

 23        }  

 24    }    

 25 }

 26 

 27 

 28 void i2c_init()

 29 {

 30     //1.a 初始化中断

 31     INTPND |= (1<<27);

 32     SRCPND |= (1<<27);  

 33     INTMSK &= ~(1<<27);

 34      

 35     IICCON |= (1<<5); 

 36     

 37     //1.b 设置scl时钟

 38     IICCON &= ~(1<<6);

 39     IICCON &= ~(0xf<<0);

 40     IICCON |= (0x5<<0);

 41     

 42     //2. 设置IICSTAT    

 43     IICCON |= (1<<4);

 44     

 45     //3.设置引脚功能

 46     GPECON |= (0x2<<28)|(0x2<<30);

 47     GPEUP |= (0x3<<14);

 48     

 49     //4.允许产生ACK

 50     IICCON |= (1<<7);

 51 }

 52 

 53 

 54 void write_byte(unsigned char xchar, unsigned char daddr)  

 55 {

 56     /* 写入数据时,每发送一个数据收到一个ACK就产生一次中断

 57      * 写入下次发送的数据之后要清除中断                      */

 58 

 59     //1. 设置处理器为主设备+发送模式

 60     IICSTAT |= (3<<6);

 61     

 62     //2. 将从设备的地址写入到IICDS寄存器

 63     IICDS = SLAVE_WRITE_ADD;

 64 

 65     //清除中断

 66     IICCON &= ~(1<<4);

 67     

 68     //3. 写入0xF0写入IICSTAT M/T Start

 69     IICSTAT = 0xF0;

 70     

 71     //4. 等待ACK的产生

 72     while ((IICCON & (1<<4)) == 0 )

 73         delay(100);

 74     

 75     //5.1写入字节的地址到IICDS寄存器

 76     IICDS = daddr;

 77 

 78 

 79     //5.2清除中断

 80      IICCON &= ~(1<<4);   

 81 

 82     //5.3等待ACK的产生

 83     while ((IICCON & (1<<4)) == 0 )

 84         delay(100);

 85     

 86     //6. 将要传输的字节数据写入IICDS寄存器

 87     IICDS = xchar;

 88 

 89     //7. 清除中断

 90     IICCON &= ~(1<<4);  

 91     

 92     //8. 等待ACk的产生

 93     while ((IICCON & (1<<4)) == 0 )

 94         delay(100);

 95     

 96     //9. 写入0xD0到IICSTAT

 97     IICSTAT = 0xD0;

 98     

 99     //10. 清除中断    

100     IICCON &= ~(1<<4);    

101     

102     delay(100);

103 }

104 

105 void read_data(unsigned char *buf, unsigned char daddr, int length) /* 结合eeprom手册 */

106 {

107     /* 每接收一个数据产生一个中断 */

108 

109     int j =0;

110     unsigned char unusedata;

111     

112     //1. 设置处理器为主设备+发送模式

113     IICSTAT |= (3<<6);

114     

115     //2. 将从设备的地址写入到IICDS寄存器

116     IICDS = SLAVE_WRITE_ADD;

117 

118     //清除中断

119     IICCON &= ~(1<<4);

120     

121     //3. 写入0xF0写入IICSTAT M/T-Start

122     IICSTAT = 0xF0;

123     

124     //4. 等待ACK的产生

125     while ((IICCON & (1<<4)) == 0 )

126         delay(100);

127     

128     //5.1写入eeprom内部地址

129     IICDS = daddr;

130 

131 

132     //5.2清除中断

133      IICCON &= ~(1<<4);   

134 

135     //5.3等待ACK的产生

136     while ((IICCON & (1<<4)) == 0 )

137         delay(100);

138 

139     /**************eeprom代码**************/

140     /**************************************/

141     /***************i2c代码****************/

142 

143     //设置为主设备接收模式

144     IICSTAT &= ~(3<<6);

145     IICSTAT |= (2<<6);

146     

147     

148     //2.写入从设备地址到IICDS  /* 从设备地址成功发送之后产生中断,故要清除中断 */

149     IICDS = SLAVE_READ_ADD;

150     //清除中断

151     IICCON &= ~(1<<4);

152     

153     

154     //3.写入0xB0到IICSTAT开始接收,每接收道一个数据就产生一个中断

155     IICSTAT = 0xb0;

156 

157     //等待中断

158     while ((IICCON & (1<<4)) == 0 )

159         delay(100);

160         

161 #if 0   

162     /***写入设备内部地址***/

163     IICDS = daddr;

164     IICCON &= ~(1 << 4);

165     while((IICCON & (1 << 4)) == 0)

166     {

167         delay(100);

168     }    

169 #endif 

170     

171     //***丢掉收到的第1个字节  第一个数据无效 丢弃!

172     unusedata = IICDS;

173     IICCON &= ~(1<<4);

174     while ((IICCON & (1<<4)) == 0 )

175             delay(100);

176     

177 

178 

179     for(j=0;j

180     {

181         if(j == (length - 1))

182         {

183            IICCON &= ~(1<<7);         

184         }

185    

186     //5.1 从IICDS里取出数据

187         buf[j]=IICDS;

188     

189     //5.2 清除中断

190         IICCON &= ~(1<<4);

191     

192     //4.等待中断

193         while ((IICCON & (1<<4)) == 0 )

194             delay(100);

195     }

196         

197         

198     //写入0x90到IICSTAT

199     IICSTAT = 0x90;

200     

201  

202     // 清除中断

203     IICCON &= ~(1<<4);

204 }

205 

206 void i2c_test()

207 {

208     int i=0;

209     unsigned char sbuf[256]={0};

210     unsigned char dbuf[256]={0};    

211     

212     i2c_init();

213     

214     for(i=0;i<256;i++)

215     {

216         sbuf[i] = i+1;

217         dbuf[i] = 0;

218     }

219     

220     printf('dbuf befor I2C read:rn');   

221     for(i =0; i<256;i++)

222     {

223        if(i%8==0)

224            printf('rn');  /*  */

225            

226        printf('%dt',dbuf[i]);    /*t-空格 */

227     }    

228      

229     for(i=0;i<256;i++)

230         write_byte(sbuf[i],i);

231         

232     printf('i2c reading, plese wait!nr');

233     

234     read_data(dbuf,0,256);

[1] [2]
关键字:linux  I2C  裸机驱动 引用地址:linux之I2C裸机驱动解析

上一篇:S3C2440 LCD驱动(FrameBuffer)实例开发<二>
下一篇:linux之i2c子系统架构---总线驱动

推荐阅读最新更新时间:2026-03-20 11:30

Linux之ARM(IMX6U)裸机汇编LED驱动实验--驱动编写
1. I.MX6ULL的初始化 ①、使能时钟 使能时钟。CCGR0–CCGR6这七个寄存器控制着I.MX6ULL所有外设时钟的使能,为了简单,设置CCGR0–CCGR6这七个寄存器全部为0xFFFFFFFF,相当于使能所有的外设时钟 CCGR0: CCGR1: CCGR2: CCGR3: CCGR4: CCGR5: CCGR6: 汇编使能所有的外设时钟: ②、配置 GPIO_I003 PIN的复用为GPIO 将IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的bit3-0,设置为0101,这样GPIO_IO03就复用为GPIO 汇编实现: ③、配置 IOMUXC_SW_P
[单片机]
<font color='red'>Linux</font>之ARM(IMX6U)<font color='red'>裸机</font>汇编LED<font color='red'>驱动</font>实验--<font color='red'>驱动</font>编写
Linux之ARM(IMX6U)裸机汇编LED驱动实验--编译驱动
前言 我们是要编译出在 ARM 开发板上运行的可执行文件,所以要使用交叉编译器 arm-linux-gnueabihf-gcc 来编译。 交叉编译链的安装参考另外一篇博文:交叉编译链的安装 编译代码 本试验就一个 leds.s 源文件,所以编译比较简单。 源文件代码(leds.s): .global _start @全局标号 _start: /*使能所有外设时钟 */ LDR R0 , =0x020c4068 @CCGR0 LDR R1 , =0xffffffff @要想CCGR0写入的数据 STR R1 , @将R1的值写入到R0中 LDR R0 , =0x020c406
[单片机]
<font color='red'>Linux</font>之ARM(IMX6U)<font color='red'>裸机</font>汇编LED<font color='red'>驱动</font>实验--编译<font color='red'>驱动</font>
Linux之ARM(IMX6U)裸机C语言LED驱动实验--驱动编写,编译
简介 在开始部分用汇编来初始化一下 C 语言环境,比如初始化 DDR、设置堆栈指针 SP 等等,当这些工作都做完以后就可以进入 C 语言环境,也就是运行 C 语言代码,一般都是进入 main 函数。所以我们有两部分文件要做: ①、汇编文件 汇编文件只是用来完成 C 语言环境搭建。 ②、C 语言文件 C 语言文件就是完成我们的业务层代码的,其实就是我们实际例程要完成的功能 1.汇编文件初始化C语言运行环境 设置处理器进入 SVC 模式 以前的 ARM 处理器有 7 种运行模型:User、FIQ、IRQ、Supervisor(SVC)、Abort、Undef和 System,其中 User 是非特权模式,其余 6 中都是特
[单片机]
<font color='red'>Linux</font>之ARM(IMX6U)<font color='red'>裸机</font>C语言LED<font color='red'>驱动</font>实验--<font color='red'>驱动</font>编写,编译
Linux之ARM(IMX6U)裸机C语言蜂鸣器驱动实验--驱动编写,编译
前几篇博文试验中的驱动 LED 灯亮灭属于 GPIO 的输出控制,本章再巩固一下 I.MX6U 的 GPIO输出控制,在 I.MX6U-ALPHA 开发板上有一个有源蜂鸣器,通过 IO 输出高低电平即可控制蜂鸣器的开关,本质上也属于 GPIO 的输出控制 1、有源蜂鸣器简介 蜂鸣器常用于计算机、打印机、报警器、电子玩具等电子产品中,常用的蜂鸣器有两种:有源蜂鸣器和无源蜂鸣器,这里的有“源”不是电源,而是震荡源,有源蜂鸣器内部带有震荡源,所以有源蜂鸣器只要通电就会叫。无源蜂鸣器内部不带震荡源,直接用直流电是驱动不起来的,需要 2K-5K 的方波去驱动。 I.MX6U-ALPHA 开发板使用的是有源蜂鸣器,因此只要给其供电就会工作
[单片机]
<font color='red'>Linux</font>之ARM(IMX6U)<font color='red'>裸机</font>C语言蜂鸣器<font color='red'>驱动</font>实验--<font color='red'>驱动</font>编写,编译
arm裸机驱动错误总结
错误001: 从上图画红线部分可知错误是:arm-linux-gcc -o &@ start.S -c          正确写法:arm-linux-gcc -o $@ start.S -c 在Makefile文件中:buzzer.bin: start.o main.o buzzer.o buzzer.bin: start.o main.o buzzer.o arm-linux-ld -Ttext 0x20000000 -o buzzer.elf $^ arm-linux-objcopy -O binary buzzer.elf buzzer.bin arm-linux-objdump -D buzzer.e
[单片机]
S3C2440之IIC裸机驱动
花了两天的时间终于把这个搞定了,其实I2C的原理还是比较简单的,只是几个细节性的东西还是需要特别的注意,主要是需要注意一下几点: 1.rIICCON &= ~0x10; 清中断必须要在rIICDS = slvAddr; 和rIICSTAT = 0xf0; // 主设备,启动 之后 2.延时对于写外部的低速设备来说非常重要,比如while(flag)之后一定要加延时,还有在写数据时发现只能写入基数地址的数据,这也是由于延时导致的 3.开始调试的时候系统总是死在read的函数中,后来发现在数据手册的note中说当读取最后一个数据的时候一定不能返回ACK信号,而我却在程序中使用while(flag)来等待ACK引发中断,这不死才怪呢。。
[单片机]
s3c2440裸机-spi编程-3-gpio模拟spi驱动OLED
操作OLED,通过三条线(SCK、DO、CS)与OLED相连,这里没有DI是因为2440只会向OLED传数据而不用接收数据。 gpio_spi.c来实现gpio模拟spi,负责spi通讯。对于OLED,有专门的指令和数据格式,要传输的数据内容,在oled.c这一层来实现,负责组织数据。 因此,我们需要实现以上两个文件。 1.SPI初始化 新建一个gpio_spi.c文件,实现SPI初始化SPIInt() 1.1 GPIO init(pinmux管脚等配置) 上图J3为板子pin2pin到OLED的底座。 GPF1作为OLED片选引脚,设置为输出; GPG4作为OLED的数据(Data)/命令(Command)选择引脚,
[单片机]
S3c2440裸机-spi编程-3.gpio模拟spi驱动OLED
操作OLED,通过三条线(SCK、DO、CS)与OLED相连,这里没有DI是因为2440只会向OLED传数据而不用接收数据。 gpio_spi.c来实现gpio模拟spi,负责spi通讯。对于OLED,有专门的指令和数据格式,要传输的数据内容,在oled.c这一层来实现,负责组织数据。 因此,我们需要实现以上两个文件。 1.SPI初始化 新建一个gpio_spi.c文件,实现SPI初始化SPIInt() 1.1 GPIO init(pinmux管脚等配置) 上图J3为板子pin2pin到OLED的底座。 GPF1作为OLED片选引脚,设置为输出; GPG4作为OLED的数据(Data)/命令(Command)选择引脚,设置
[单片机]
S3c2440<font color='red'>裸机</font>-spi编程-3.gpio模拟spi<font color='red'>驱动</font>OLED
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved