非常实用的模拟I2C程序
i2c读写程序的详细讲解

i2c读写程序的详细讲解i2c是Inter-IntegratedCircuit的缩写,是一种主从机多总线的通信协议,也就是说,它可以允许多个电子设备在同一个信号线上通信。
i2c是分布式共享总线,它支持主机和多个从机之间的双向传输数据,因此本范文将针对i2c读写程序进行详细的讲解。
首先,我们来看一下i2c协议的特性:i2c协议使用两根信号线,分别是SCL(时钟线)和SDA(数据线),以及一个地线。
其中,SCL线用于传输时钟信号,SDA线用于传输数据,而地线用于给一个共同的参考电位。
i2c的数据传输是先信号再数据,即SCL脉冲先于SDA脉冲,且SDA数据根据SCL时钟的上升沿来储存和传输。
i2c协议的常见的特性包括节点重用,三线模式,通用性,简单性等。
接下来,我们来看一下i2c读写程序。
i2c读写程序是基于i2c协议来操作i2c总线上分布式设备的软件。
它包括一系列的控制参数,如速率、时钟频率、地址空间等,以及一系列读写操作。
i2c读操作通常有三种形式,分别为单字节读取、多字节读取和8位快速读取。
其中,单字节读取是i2c读操作中最常见的模式,其工作机制如下:首先,从主机发出一个读控制信号,只有当从机完成该信号指令后,才会将数据发送给主机;随后,主机收到数据后,发出一个确认信号来确认接收到了数据,从机收到信号后,就会发送下一个字节的数据。
多字节读取和单字节读取有很多相似之处,主要的不同在于它支持一次读取多字节的数据,首先,主机发出一个读控制信号,只有当从机完成该信号指令后,才会将数据发送给主机;随后,主机持续接收数据,直到接收到所有的数据为止。
8位快速读取模式是i2c总线上读取操作中最快的模式,它和其他读取模式有很多相同之处,主要是有一个专门的8位快速读指令,使用这个指令可以实现一次读取多字节数据的功能,而不需要反复发出读控制信号。
另外,i2c读写程序还支持写操作,其工作机制大致相同,只是在发出控制信号后,主机会将数据发送给从机,而不是从机将数据发送给主机。
用IO模拟IIC方式读写EEPROM容易出现的问题

用IO模拟IIC方式读写EEPROM容易出现的问题 在嵌入式软件开发过程中,经常会用模拟IO的方式读写EEPROM 的操作。
有时会因程序编码的问题,出现读写数据不正确的情况。
根据笔者多年的开发经验与大家分享希望对大家有所帮助。
对EEPROM的操作常见的有两种方式:I2C,SPI;在应用中经常会用到模拟IO方式读写EEPROM。
在编写此类代码时必须要掌握I2C 与SPI协议。
I2C总线上的数据稳定规则,SCL为高电平时SDA上的数据保持稳定,SCL为低电平时允许SDA变化。
如果SCL处于高电平时,SDA上产生下降沿,则认为是起始位,SDA上的上升沿认为是停止位。
通信速率分为常规模式(时钟频率100kHz)和快速模式(时钟频率400kHz)。
同一总线上可以连接多个带有I2C接口的器件,每个器件都有一个唯一的地址,既可以是单接收的器件,也可以是能够接收发送的器件。
SPI,串行外围设备接口。
是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便。
在操作中出现读取数据不正确,或者不能读取数据可能有以下原因:1.没有遵照通信协议:这两种通信方式都有各自独立的协议,如果在编写程序时不按通信协议,自然就不能正常通信了;2.时序不对:两处通讯方式都有严格的时序,编写程序时要严格按对应的时序;3.IO口方式方向设置不正确:在通信中数据有输入与输出的时候,特别是在I2C的方式下,有数据输入时相应的IO口要设置为输入,反之亦然,初学都容易犯这样的错误;4.看页的大小,超过一个页连续写就回滚,前面的就被覆盖。
其实里面有一个页地址计数器,如果页是8个byte,那么这个计数器就3bit,增加的地址(连续写的地址范围)就相当于溢出或者相当于按页取模。
在应用中的建议:1. SCL与SDA引脚用2.2K--4.7K电阻上拉;2. IO口与EEPROM的相应引脚不能直接相连,串一个几百欧电阻;3.为保证数据的正确性,在写入数据时最好对数据进行检验;4.采用循环地址的方式进行存储,避免只在一个固定地址上读写数据;。
I2C通信原理及程序详细讲解

I2C 通信:起始条件:SCL为高电平,SDA电平从高变低,这一变化即完成了通信的起始条件。
起始条件和数据通信间,通常会有延时要求具体的指标见设备说明。
数据传输阶段:一字节需要9个时钟周期;且每一位需要一个时钟周期;如上图所示,ADDRESS为目标设备的地址,R/ w为通信的的方向位;"1"时表示读,即后续的数据由目标设备发出,主机进行接收;"0"时表示写,即后续的数据由主机发出目标设备进行接收。
当ACK信号为"0"时,说明接收成功;为"1"时,说明接收失败。
每个字节的传输都是由高位(MSB)到低位(LSB)依次进行传输。
在数据通信过程中,总是由数据接收方发出ACK信号。
终止阶段:当主机完成数据通信,并终止本次传输时会发出终止信号。
当SCL 是高电平时,SDA电平由低变高,这个变化意味着传输终止。
注:每个时钟周期的高电平期间,SDA的数据状态达到稳定。
下面给出了模拟I2C总线进行读写的伪代码,用以说明如何使用GPIO 实现I2C通信:int i2c_start() /* I2C起始条件*/{//初始化GPIO口set_gpio_direction(SDA, OUTP); //设置SDA方向为输出set_gpio_direction (SCL, OUTP); //设置SCL方向为输出set_gpio_value(SDA, 1); //设置SDA为高电平set_gpio_value(SCL, 1); //设置SCL为高电平delay(); //延时//起始条件set_gpio_value(SDA, 0); //SCL为高电平时,SDA由高变低delay(); //适当延时}void i2c_stop() /* I2C终止条件*/{set_gpio_value(SCL, 1);set_gpio_direction(SDA, OUTP);set_gpio_value(SDA, 0);delay();set_gpio_value(SDA, 1); //SCL高电平时,SDA由低变高}/* I2C读取ACK信号(写数据时使用)返回值:0表示ACK信号有效;非0表示ACK信号无效*/unsigned char i2c_read_ack(){unsigned char r;set_gpio_direction(SDA, INP); //设置SDA方向为输入set_gpio_value(SCL,0); // SCL变低r = get_gpio_value(SDA); //读取ACK信号delay();set_gpio_value(SCL,1); // SCL变高delay();return r;}/* I2C发出ACK信号(读数据时使用) */int i2c_send_ack(){set_gpio_direction(SDA, OUTP); //设置SDA方向为输出set_gpio_value(SCL,0); // SCL变低set_gpio_value(SDA, 0); //发出ACK信号delay();set_gpio_value(SCL,1); // SCL变高delay();}void i2c_write_byte(unsigned char b) /* I2C字节写*/{int i;set_gpio_direction(SDA, OUTP); //设置SDA方向为输出for (i=7; i>=0; i--){set_gpio_value(SCL, 0); // SCL变低delay();set_gpio_value(SDA, b & (1<<i)); //从高位到低位依次发送数据set_gpio_value(SCL, 1); // SCL变高delay();}i2c_read_ack(); //检查目标设备的ACK信号}/* I2C字节读*/unsigned char i2c_read_byte(){int i;unsigned char r = 0;set_gpio_direction(SDA, INP); //设置SDA方向为输入for (i=7; i>=0; i--){set_gpio_value(SCL, 0); // SCL变低delay();r = (r <<1) | get_gpio_value(SDA); //高位到低位依次数据读取set_gpio_value(SCL, 1); // SCL变高delay();}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(); //终止条件,结束数据通信}。
stm32的i2c读写程序的详细讲解

一、概述STMicroelectronics瑞士意法半导体公司的STM32系列微控制器被广泛应用于各种嵌入式系统中,其强大的性能和丰富的外设功能受到了众多开发者的青睐。
其中,STM32的I2C总线通信功能在实际应用中具有重要意义,本文将对STM32的I2C读写程序进行详细讲解。
二、I2C总线介绍I2C(Inter-Integrated Circuit)总线是一种串行通信接口协议,由Philips公司推出。
它具有双向传输数据线(SDA)、时钟线(SCL)、起始条件、停止条件、数据传输的应答信号等特点。
I2C总线在各种传感器、存储器、外设等设备之间进行通信时,具有简单高效的优势。
三、STM32的I2C外设1. STM32的I2C外设功能STM32系列微控制器内置了丰富的外设功能,其中包括了I2C总线通信。
STM32的I2C外设支持主机和从机模式,可以实现与各种I2C设备的通信和数据交换。
2. STM32的I2C外设配置在使用STM32的I2C外设之前,需要对其进行配置,包括设置时钟、GPIO、寄存器参数等。
通过正确的配置,可以使STM32的I2C外设正常工作,并与其他设备进行可靠的通信。
四、STM32的I2C读写程序详解1. 初始化I2C外设在使用I2C总线进行读写操作之前,首先需要对STM32的I2C外设进行初始化设置。
具体步骤包括设置GPIO管脚为I2C功能模式、配置时钟、设置I2C的工作模式、设定传输速率等。
2. 发送起始信号当I2C通信开始时,主机会发送起始信号(Start),表明要开始一次通信过程。
起始信号的发送方式是通过在SDA线上拉低电平,同时保持SCL线处于高电平状态。
3. 选择设备位置区域在发送起始信号后,主机需要选择要通信的设备位置区域。
针对每个I2C设备,都有唯一的位置区域标识,主机需要向目标设备发送其位置区域信息。
位置区域信息由设备的7位位置区域和读写方向位组成。
4. 数据传输经过起始信号和设备位置区域选择后,接下来进行数据的传输。
I2C的原理与应用

EEPROM的I2C与并口的读写通设计试验目的:认识计算机并口和I2C总线,用计算机并口模拟I2C总线,最后,以24CL02为例,完成对I2C EEPROM的读写操作。
试验器材:一台装有 Tubor C 2.0 的计算机、一条25针并口电缆(看图1插头可要选对了)、自制的用于插入EEPROM芯片的适配器(图2)、一片 EEPROM 如HT24LC02或AT24C02等。
试验前的准备知识:一、I2C总线:i2c总线是 Philips 公司首先推出的一种两线制串行传输总线。
它由一根数据线(SDA)和一根时钟线(SDL)组成。
i2c总线的数据传输过程如图3所示,基本过程为:1、主机发出开始信号。
2、主机接着送出1字节的从机地址信息,其中最低位为读写控制码(1为读、0为写),高7位为从机器件地址代码。
3、从机发出认可信号。
4、主机开始发送信息,每发完一字节后,从机发出认可信号给主机。
5、主机发出停止信号。
I2C总线上各信号的具体说明:开始信号:在时钟线(SCL)为高电平其间,数据线(SDA)由高变低,将产生一个开始信号。
停止信号:在时钟线(SCL)为高电平其间,数据线(SDA)由低变高,将产生一个停止信号。
应答信号:既认可信号,主机写从机时每写完一字节,如果正确从机将在下一个时钟周期将数据线(SDA)拉低,以告诉主机操作有效。
在主机读从机时正确读完一字节后,主机在下一个时钟周期同样也要将数据线(SDA)拉低,发出认可信号,告诉从机所发数据已经收妥。
(注:读从机时主机在最后1字节数据接收完以后不发应答,直接发停止信号)。
注意:在I2C通信过程中,所有的数据改变都必须在时钟线SCL为低电平时改变,在时钟线SCL为高电平时必须保持数据SDA信号的稳定,任何在时钟线为高电平时数据线上的电平改变都被认为是起始或停止信号。
下面以24LC02为例,对几个主要工作时序做详细说明。
24LC02的控制字(节)格式(图4):发送时紧跟开始信号后的4位是器件选择位,通常为‘1010’,它和后面的3位器件地址码(由24LC02的A0、A1、A2上的电平决定)共同构成了7位的从机地址。
I2C verilog (非常详细的i2c学习心得)

图 4. AT24C02/4/8/16 字节写入帧格式
启 动 SDA线 S 1 0 1 0 X X X 应 答 应 S 1 0 答 1 0 X X X 应 答 非 P 应 答 控制字节 写 读 EEPROM存储单元 指定地址(n) 启 动 控制字节 数据(n) 停 止
( 1)总线非忙状态( A 段) :数据线 SDA 和 时钟线 SCL 都保持高电平。 ( 2)启动数据传输( B 段) :当时钟线( SCL)为高电平状态时,数据线( SDA)由 高电平变为低电平的下降沿被认为是 “ 启动 ”信号。只有出现 “ 启动 ”信号后,其它的命令 才有效。 ( 3)停止数据传输( C 段) :当时钟线( SCL)为高电平状态时,数据线( SDA)由 低电平变为高电平的上升沿被认为是 “ 停止 ”信号。随着 “停在 ” 信号出现,所有的外部操 作都结束。 ( 4)数据有效( D 段) :在出现 “启动 ”信号以后,在时钟线( SCL)为高电平状态时 数据线是稳定的,这时数据线的状态就要传送的数据。数据线( SDA )上的数据的改变 必须在时钟线为低电平期间完成, 每位数据占用一个时钟脉冲。 每个数传输都是由 “启动 ” 信号开始,结束于 “停止 ”信号。 ( 5)应答信号:每个正在接收数据的从机 EEPROM 在接到一个字节的数据后,通 常需要发出一个应答信号。而每个正在发送数据的 EEPROM 在发出一个字节的数据后, 通常需要接收一个应答信号。 EEPROM 读写控制器必须产生一个与这个应答位相联系的 额外的时钟脉冲。在 EEPROM 的读操作中, EEPROM 读写控制器对 EEPROM 完成的最 后一个字节产生一个高的应答位,这叫做非应答信号,随后给 EEPROM 一个结束信号。 第四步,理解各个模块的代码。 首先介绍核心模块——EEPROM_WR 模块,这个模块就干一件事,严格控制 SDA 与 SCL 总线上的信号, 使其满足 I2C 总线时序要求。 这里要用到状态机来控制 SDA 与 SCL 上的信号, 所以难点就在于对状态机的编写。因为 EP2C8‐2010 开发板采用的 EEPROM 是 AT24C02, 所以该状态机控制的 SCL 与 SDA 时序就要满足 AT24C02 的写入和读取格式。图 4、5 分别 是 AT24C02/4/8/16 字节写入帧格式和读指定地址存储单元的数据帧格式,简单分析一下字 节写入格式,如图 4 所示:第 1 位启动信号,接下来的第 2‐9 位是控制字节写入,其中 2‐5 位是固定的机器码 1010,6‐8 位是页地址,第 10 位是 EEPROM 给出的应答信号 0,第 11‐18 位是存储单元地址,19 位是 EEPROM 给出的应答信号 0,第 20‐27 位是写入的数据,28 位 应答信号,29 位停止信号。而 AT24C02/4/8/16 的字节读取格式也是大同小异,先写入控制 字和存储地址, 然后是启动信号与控制字节信号, 这时, 控制字节的第 8 位变为了 1 (读取) , 最后读取数据,并且在读取完毕后,主机将 SDA 拉高作为非应答信号。最后是停止位。 可能有人还是对这种字节写入与读取格式不明白, 其实这种格式是别人定好的, 我们所 需要的就是要让 SDA 与 SCL 的信号满足这种格式要求即可,下面我们来看夏老师的 EEPROM_WR 程序。
I2C实例解析
实例解析linux内核I2C体系结构(1)一、概述谈到在linux系统下编写I2C驱动,目前主要有两种方式,一种是把I2C设备当作一个普通的字符设备来处理,另一种是利用linux I2C驱动体系结构来完成。
下面比较下这两种驱动。
第一种方法的好处(对应第二种方法的劣势)有:●思路比较直接,不需要花时间去了解linux内核中复杂的I2C子系统的操作方法。
第一种方法问题(对应第二种方法的好处)有:●要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器操作;●要求工程师对I2C的设备器及I2C的设备操作方法都比较熟悉,最重要的是写出的程序可移植性差;●对内核的资源无法直接使用。
因为内核提供的所有I2C设备器及设备驱动都是基于I2C子系统的格式。
I2C适配器的操作简单还好,如果遇到复杂的I2C适配器(如:基于PCI的I2C适配器),工作量就会大很多。
本文针对的对象是熟悉I2C协议,并且想使用linux内核子系统的开发人员。
网络和一些书籍上有介绍I2C子系统的源码结构。
但发现很多开发人员看了这些文章后,还是不清楚自己究竟该做些什么。
究其原因还是没弄清楚I2C子系统为我们做了些什么,以及我们怎样利用I2C子系统。
本文首先要解决是如何利用现有内核支持的I2C适配器,完成对I2C设备的操作,然后再过度到适配器代码的编写。
本文主要从解决问题的角度去写,不会涉及特别详细的代码跟踪。
二、I2C设备驱动程序编写首先要明确适配器驱动的作用是让我们能够通过它发出符合I2C标准协议的时序。
在Linux内核源代码中的drivers/i2c/busses目录下包含着一些适配器的驱动。
如S3C2410的驱动i2c-s3c2410.c。
当适配器加载到内核后,接下来的工作就要针对具体的设备编写设备驱动了。
编写I2C设备驱动也有两种方法。
一种是利用系统给我们提供的i2c-dev.c来实现一个i2c 适配器的设备文件。
然后通过在应用层操作i2c适配器来控制i2c设备。
PIC单片机I2C从模式程序
//I2C从模式(适用用于PIC16F1705/1708/1709)//函数初始化与主体函数,经过反复检验,程序OK,程序贴出来帮助和我同样曾经迷惑的人们. //如有疑问可以联系我,QQ:370886719 ………给更多的人照亮道路void i2c_slave_init(void){ANSA4=0;ANSA2=0;WPUAbits.WPUA2 = 0;WPUAbits.WPUA4 = 0;RA4PPS=0X10; //CLKRA2PPS=0X11; //SDASSP1CON1bits.SSPM0 = 0;SSP1CON1bits.SSPM1 = 1;SSP1CON1bits.SSPM2 = 1;SSP1CON1bits.SSPM3 = 0; // I2C slave mode ,7bit addressSSP1CON1bits.CKP = 1; // enable clocSSP1STAT =0;SSP1MSK=0XFE; //允许数据地址匹配SSPCON2bits.SEN=1;SSPCON3bits.SDAHT=1;SSPCON3bits.SBCDE = 1; // Enable slave bus collision detect interrupts//SSPCON3bits.AHEN=1; ////////////////////////////////SSPCON3bits.DHEN=1; /////////////////////////////SSPCON3bits.BOEN=1;PIR1bits.SSP1IF = 0; // Clear the serial port interrupt flagPIR2bits.BCL1IF = 0; // Clear the bus collision interrupt flagPIE2bits.BCL1IE = 1; // Enable bus collision interruptsPIE1bits.SSP1IE = 1;//Enabe interrupt MSSPINTCONbits.PEIE = 1;INTCONbits.GIE = 1;}#include"proc.h"//#include <stdio.h>//#include <stdlib.h>//#define RX_BUF_LEN 16#define while_delay 10000u8i2c_address,word_address,array_receive_x[32]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,1 9,20,21,22,23,24,25,26,27,28,29,30,31};u8array_receive_y[32]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, 28,29,30,31};u8 Register_flow[5]={10,1,2,3,4};u8 Register_temp[9]={36,1,2,3,4,5,6,7,8};u8 Register[17];u8 i2c_counter,RX_BUF_LEN,TX_BUF_LEN;bit read_temp_F,receive_data_end_F;/***********************************************************************/void i2c_slave_tx(void){u16 timercounter;PIR2bits.BCL1IF = 0; // Clear the bus collision interrupt flag//asm("nop");asm("nop");asm("nop");asm("nop");if(SSP1STATbits.R_nW ==0)//Read operation.{PIR1bits.SSP1IF = 0;i2c_address = SSP1BUF;//i2c_counter = word_address; //得到要发送的数据地址i2c_counter = 0;if(read_temp_F==0){TX_BUF_LEN=5; //发送5个温度相关数据给上位机while(i2c_counter < TX_BUF_LEN){SSP1BUF=Register_flow[i2c_counter]; //send datatimercounter=while_delay;while( PIR1bits.SSP1IF == 0){timercounter--;if(timercounter==0){return;}}//waiting for ~`ACKPIR1bits.SSP1IF = 0;asm("nop");asm("nop");asm("nop");asm("nop");if(SSP1CON2bits.ACKSTAT == 1) //主机没有应答{return ; //NOACK}else{i2c_counter++;//ACK}}SSP1IF = 0;}else //协议切换,发送流量传感器数据给上位机{TX_BUF_LEN=9; //发送5个温度相关数据给上位机while(i2c_counter < TX_BUF_LEN){while( PIR1bits.SSP1IF == 0){timercounter--;if(timercounter==0){return;}}//waiting for ~`ACKPIR1bits.SSP1IF = 0;asm("nop");asm("nop");asm("nop");asm("nop");if(SSP1CON2bits.ACKSTAT == 1) //主机没有应答{return ; //NOACK}else{i2c_counter++;//ACK}}SSP1IF = 0;read_temp_F=0;}}}/*****************************************************************/void i2c_salve_rx(void) //master writer{if(SSP1STATbits.R_nW ==0){PIR1bits.SSP1IF = 0;i2c_address = SSP1BUF;// timercounter=while_delay;/*while(PIR1bits.SSP1IF == 0){timercounter--;if(timercounter==0){timercounter=while_delay;return ;}} //waiting for send ~ACKPIR1bits.SSP1IF = 0;*///word_address = SSP1BUF;// i2c_address = SSP1BUF;asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");word_address=0;return ;}if(SSP1STATbits.R_nW ==0 && SSP1STATbits.D_nA==1){/****************************************************************/if(Register[0]==0xd0) //读温度模式{read_temp_F=1;RX_BUF_LEN=0;SSP1CON1bits.CKP=1;return;}/****************************************************************/if(Register[0]==0xc0) //{RX_BUF_LEN=3;read_temp_F=0;}/***************************************************************/switch (Register[0]){case 0xe0: read_temp_F=0;RX_BUF_LEN=17;if(word_address>0 && word_address<=16){*(array_receive_x+word_address-1)=*(Register+word_address);}break;case 0xe1: read_temp_F=0;RX_BUF_LEN=17;if(word_address>0 && word_address<=16){*(array_receive_x+word_address+16-1)=*(Register+word_address);}break;case 0xe2: read_temp_F=0;RX_BUF_LEN=17;if(word_address>0 && word_address<=16){*(array_receive_y+word_address-1)=*(Register+word_address);}break;case 0xe3: read_temp_F=0;RX_BUF_LEN=17;if(word_address>0 && word_address<=16){*(array_receive_y+word_address+16-1)=*(Register+word_address);}break;}word_address++;/**************************************************************/if(word_address>=RX_BUF_LEN){word_address=0;if(Register[0]==0xe3){receive_data_end_F=1; //所有完整的数据都已经收到}}asm("nop");asm("nop");asm("nop");asm("nop");return;}}。
iic标准程序
IIC(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器、传感器、存储器和其他外围设备。
以下是IIC标准程序的示例代码,用于在微控制器和外围设备之间进行通信:#include <stdio.h>#include <stdint.h>#include <unistd.h>#include <fcntl.h>#include <sys/ioctl.h>#include <linux/i2c-dev.h>int main(){int file;char *filename = "/dev/i2c-1"; // I2C bus 1 的设备文件路径uint8_t address = 0x50; // I2C 设备的地址char *buffer = malloc(2); // 用于存储从I2C 设备读取的数据的缓冲区// 打开I2C 总线设备文件file = open(filename, O_RDWR);if (file < 0) {perror("Failed to open i2c bus");return -1;}// 设置I2C 总线设备的地址和速率if (ioctl(file, I2C_SLAVE, address) < 0) {perror("Failed to acquire bus access and/or talk to slave");return -1;}// 向I2C 设备写入数据buffer[0] = 0x01; // 要写入的数据的第一个字节buffer[1] = 0x0A; // 要写入的数据的第二个字节int n = write(file, buffer, 2); // 将数据写入I2C 设备if (n < 1) {perror("Write failed");return -1;}// 从I2C 设备读取数据n = read(file, buffer, 1); // 从I2C 设备读取一个字节的数据到缓冲区中if (n < 1) {perror("Read failed");return -1;}printf("Read data: %x\n", buffer[0]); // 打印读取的数据的十六进制表示形式// 关闭I2C 总线设备文件close(file);free(buffer);return 0;}以上代码使用了Linux下的I2C-dev驱动,需要包含`<linux/i2c-dev.h>`头文件。
I2C程序(AT24C1024)测试通过的
_nop_();
}
//*********************************************
//从机接收一位数据应答0
//*********************************************
voidACK(void)
{
SDA=0;
_nop_();
void write_byte(uchar ch)
{
uchar i, n=8;//向SDA发送一个字节数据,8位
for(i=0;i<n;i++)
{
if((ch&0x80)==0x80)
{//若要发送的位为1,则SDA=1
SDA=1;
SCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
SCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
SCL=0;
_nop_();
_nop_();
_nop_();
SDA=1;
_nop_();
}
//*********************************************
{
receive_data[i]=read_byte();//读出来的数据存到receivedata[]数组中
if(i==15)//是否读完,未读完全部数据,则应答0
NoACK();
else
ACK();
}//停止总线
stop();
}
void main()
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
/******************************************************************************* * File Name : SI2C.c * Author : zhang * Version : V1.0.0 * Date : 2011.12.08 *******************************************************************************/
/* Inlcudes ------------------------------------------------------------------*/ #include "SI2C.h" #include "delay.h"
/* defines ------------------------------------------------------------------*/ #define Slave_Address_W 0xa0 #define Slave_Address_R 0xa1
#define Byte_Address 0x00 #define SDA_PORT GPIOE #define SDA_PIN GPIO_PIN_2 #define SDA_HIGH() GPIO_WriteHigh(SDA_PORT,SDA_PIN) #define SDA_LOW() GPIO_WriteLow(SDA_PORT,SDA_PIN)
#define SCL_PORT GPIOE #define SCL_PIN GPIO_PIN_1 #define SCL_HIGH() GPIO_WriteHigh(SCL_PORT,SCL_PIN) #define SCL_LOW() GPIO_WriteLow(SCL_PORT,SCL_PIN)
#define SDA_READ() GPIO_ReadInputPin(SDA_PORT,SDA_PIN)
/* function ------------------------------------------------------------------*/
/******************************************************************************* * Function Name : SI2C_GPIOConfigration * Description : 初始化模拟I2C通用端口 * Input : None * Output : None * Return : None *******************************************************************************/
void SI2C_GPIOConfigration(void) { GPIO_Init(SDA_PORT,SDA_PIN,GPIO_MODE_OUT_OD_LOW_FAST); //SDA开漏输出 GPIO_Init(SCL_PORT,SCL_PIN,GPIO_MODE_OUT_PP_LOW_FAST); //SCL推挽输出 /*I2C总线初始为高电平*/ SDA_HIGH(); SCL_HIGH(); }
/******************************************************************************* * Function Name : SI2C_Start * Description : I2C起始条件 * Input : None * Output : None * Return : None *******************************************************************************/
void SI2C_Start(void) { SDA_HIGH();
SCL_HIGH(); //delay(10); // nop(); //起始条件建立时间大于4.7us //nop();
SDA_LOW(); //起始条件锁定时间大于4us // nop(); // nop();
SCL_LOW(); //钳住总线准备发数据
} /******************************************************************************* * Function Name : SI2C_Stop * Description : I2C结束条件 * Input : None * Output : None * Return : None *******************************************************************************/
void SI2C_Stop(void) {
SDA_LOW();
SCL_HIGH(); //发送总线时钟信号 // nop(); // nop(); //结束总线时间大于4us
SDA_HIGH(); //结束总线 // nop(); // nop();
}
/******************************************************************************* * Function Name : SI2C_SendAck * Description : 主机发送ACK * Input : None * Output : None * Return : None *******************************************************************************/ void SI2C_SendAck(void) { SDA_LOW(); SCL_HIGH(); //nop(); // nop();
SCL_LOW(); SDA_HIGH(); }
/******************************************************************************* * Function Name : SI2C_SendNAck * Description : 主机发送ACK * Input : None * Output : None * Return : None *******************************************************************************/ void SI2C_SendNAck(void) { SDA_HIGH(); SCL_HIGH(); // nop(); // nop();
SCL_LOW(); SDA_LOW();
}
/******************************************************************************* * Function Name : SI2C_SendByte * Description : I2C发送一个字节 * Input : SendByte :要发送的字节 * Output : None * Return : None *******************************************************************************/ void SI2C_SendByte(u8 SendByte) { u8 i;
for( i = 0 ; i < 8 ; i ++) {
(((SendByte & 0x80) == 0x80) ? SDA_HIGH():SDA_LOW());
SCL_HIGH(); //nop(); //nop();
SCL_LOW(); SendByte = SendByte << 1; } }
/******************************************************************************* * Function Name : Test_Ack * Description : 应答位检测程序 * Input : None * Output : None * Return : FALSE or TRUE *******************************************************************************/ int Test_Ack(void) { SDA_HIGH(); /*为输入做准备,确保从机ACK真正为低*/ SCL_HIGH(); // nop(); // nop();
if (SDA_READ()) { SCL_LOW(); SDA_LOW(); // nop(); // nop();
return FALSE;
} else {
SDA_LOW(); SCL_LOW();