共3条
1/1 1 跳转至页
问
S1CON DATA 0D8H
S1STA DATA 0D9H
S1DAT DATA 0DAH
S1ADR DATA 0DBH
;/* S1CON */
CR0 BIT 0D8H
CR1 BIT 0D9H
AA BIT 0DAH
SI BIT 0DBH
STO BIT 0DCH
STA BIT 0DDH
ENS1 BIT 0DEH
CR2 BIT 0DFH
;/* P1 */
SDA BIT 97H;
SCL BIT 96H;
ES1 BIT 0ADH
;为平台定义变量
ACK BIT 10H ;应答标志位
SLA DATA 60H ;器件从地址
SUBA DATA 61H ;器件子地址
NUMBYTE DATA 62H ;读/写的字节数
MTD EQU 30H ;发送数据缓冲区首址 (缓冲区30H-4FH)
MRD EQU 50H ;接收数据缓冲区首址 (缓冲区50-5FH)
; 内存分配:
; 堆栈区 STACK (70H--7FH)
; 显示缓冲区 DISP_RAM (发送缓冲区MTD)
;===================常量定义=======================================>
SAA7111 EQU 48H ; SAA7111的地址
;================程序代码段================================================={
ORG 0000H
AJMP MAIN
;==========================================================>
;申请总线
;功能:进行I2C总线的初始化--包括时钟速率,I2C使能,发送起始信号等等。
GETBUS:
MOV S1CON,#0C5H ;设置时钟为100K(MCU主频为12M),ENS1和AA置位
SETB STA ;申请成为主机,起动总线
JNB SI,$ ;等待起始信号的发送
RET
;==========================================================>
;发送数据函数
;功能:用于向总线发送数据
;入口参数:ACC--待发送的数据
SENDBYTE:
MOV S1DAT,A ;发送数据
MOV S1CON,#0C5H ;清除SI位等等
JNB SI,$ ;等待数据的发送
RET
;==========================================================>
;进行随机地址多字节写多字节
;占用R0,R1,ACC
;入口参数:器件从地址SLA 子地址SUBA 发送数据区MTD 读入字节数NUMBYTE
;出口参数:ACK,ACK=1时操作正确。
IWRNBYTE:
CLR ACK
MOV R0,#MTD
MOV R1,NUMBYTE
ACALL GETBUS ;启动总线
MOV A,SLA
ACALL SENDBYTE
MOV A,S1STA
CJNE A,#18H,IWRNEND
MOV A,SUBA
ACALL SENDBYTE
MOV A,S1STA
CJNE A,#28H,IWRNEND ;无应答则退出
IWRN_L1: MOV A,@R0
ACALL SENDBYTE ;发送数据
MOV A,S1STA
CJNE A,#28H,IWRNEND ;无应答则退出
INC R0
DJNZ R1,IWRN_L1
SETB ACK ;置标志位
CLR P1.5
IWRNEND: MOV S1CON,#0D5H
RET
;==========================================================>
;进行随机地址多字节读
;占用R0,R1,ACC
;入口参数:器件从地址SLA 子地址SUBA 接收数据区MRD 读入字节数NUMBYTE
;出口参数:ACK,ACK=1时操作正确。
IRDNBYTE:
CLR ACK
SETB P1.5
MOV R0,#MRD
MOV R1,NUMBYTE
ACALL GETBUS
MOV A,SLA
ACALL SENDBYTE ;器件寻址
MOV A,S1STA
CJNE A,#18H,IRDNEND ;无器件应答即退出
MOV A,SUBA ;写入器件子地址
ACALL SENDBYTE
MOV A,S1STA
CJNE A,#28H,IRDNEND ;总线出错即退出
MOV S1CON,#0E5H ;重新启动总线
JNB SI,$
MOV A,SLA
INC A ;读操作的器件寻址
ACALL SENDBYTE
MOV A,S1STA
CJNE A,#40H,IRDNEND
IRDN_L1: DJNZ R1,IRDN_L2
MOV S1CON,#0C1H ;取最后一个数据后发送非应答位
JNB SI,$
MOV A,S1STA ;将数据取出
CJNE A,#58H,IRDNEND
MOV A,S1DAT
MOV @R0,A ;数据存入MRD区中(R0指针指向MRD区)
SETB ACK
CLR P1.5
IRDNEND: MOV S1CON,#0D5H
RET
IRDN_L2: MOV S1CON,#0C5H ;接收数据并发送应答位
JNB SI,$
MOV A,S1STA
CJNE A,#50H,IRDNEND;总线出错即退出
MOV A,S1DAT ;取数据
MOV @R0,A ;数据存入MRD区
INC R0
SJMP IRDN_L1
;===============================================================================
; 主程序MAIN
; 主要负责整个系统的监控,如发指令给各芯片,读取各芯片的状态.
ORG 0080H
MAIN:
MOV R5,#0FH
DJNZ R5,$ ;延时,等待其它芯片复位
CLR SCL
SETB SDA
JNB SDA,I2CWRO ; SCL SDA 短路或SDA 被锁为低
SETB SCL
JNB SCL,I2CWRO
ACALL INI_SAA7111 ;初始化SAA7111
ACALL READ_SAA7111 ;读SAA7111状态
SJMP $
; 子程序 INI_SAA7111
; 作用: 对SAA7111进行初始化
; 出入参数: 无
INI_SAA7111:
MOV SLA,#SAA7111
MOV SUBA,#00H
MOV NUMBYTE,#1CH ;写入28个命令参数
MOV R0,#MTD
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#0C0H
INC R0
MOV @R0,#33H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#0EBH
INC R0
MOV @R0,#0E0H
INC R0
MOV @R0,#88H
INC R0
MOV @R0,#01H
INC R0
MOV @R0,#80H
INC R0
MOV @R0,#47H
INC R0
MOV @R0,#40H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#01H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#40H
INC R0
MOV @R0,#1CH
INC R0
MOV @R0,#03H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
LCALL IWRNBYTE
RET
; 子程序 READ_SAA7111
; 作用: 读SAA7111状态
; 出入参数: 无
READ_SAA7111: MOV SLA,#SAA7111
MOV SUBA,#1AH
MOV NUMBYTE,#03H
LCALL IRDNBYTE
RET
I2CWRO:CLR P0.0
SJMP $
;==========================================================<
END
我用EASYPACK的仿真器仿87c652控制saa7111,仿真器可以写入,示波器看写过程(即从“进行随机地址多字节写多字节”的整个过程到“进行随机地址多字节读”的写入49H)都没问题,连返回的ACK都正确,但怎么读都读不回来,将读的子地址改成00或者其他的也不行,每次到读数据时钟线都被拉低了,然后就跳到I2CWRO。
刚刚用单片机,各位老大帮忙看看哪儿的问题。
数据线和时钟线都接1K上拉电阻到+5v了。
答 1: 51我不熟悉,给你一个AVR的把。硬件上过了的。/*本程序验证基于I2C通讯协议的EEPROM芯片
24LC256的读出通讯时序
程序开发:西南科技大学 江某*/
# include <iom8v.h>
# include <macros.h>
unsigned char addr_h,addr_l,data_send,data_rec=0;
unsigned char i2c_flag=1;
unsigned char bit[8];
/***************************************/
// 子程序段 //
#pragma ctask delay
/*微秒级延迟函数,函数参数"dt"
延迟时间为(dt+2)微秒*/
void delay (unsigned char dt)
{
while (dt)
dt--;
asm("nop");
}
void i2c_start (void)
/*本程序启动I2C总线
硬件环境:AVR单片机对基于I2C协议的EEPROM芯片24LC256通讯
在调用本函数前,必须确定I2C总线所使用的端口口线
这里假设PD.0为SDA线,PD.1为SCK线。
*/
{
i2c_flag=1; //清除总线传输标志位,以便等待应答信号
DDRD=0b00000011; //为避免逻辑混乱,再次定义端口方向
PORTD|=0b00000011; //准备总线,将两根线全部拉高
delay(2); //延时2微秒
/*本过程对应为Tsu(sta),I2C协议规定为160ns(min),本器件规定为600ns(常规条件下)*/
PORTD&=0b11111110; //拉低SDA线,启动总线
delay(2); //延时2微秒
/*本过程对应为Thd(sta),I2C协议规定为160ns(min),本器件规定为600ns(常规条件下)*/
PORTD&=0b11111101; //拉低SCK线
}
void i2c_rec (void)
/*本程序收取I2C总线上的一个字节到主机,并以全局变量data_rec
的形式返回给调用主函数,主函数必须在下次调用本函数前将该值取
走,否则将会造成该值被新的数据覆盖掉
硬件环境:AVR单片机对基于I2C协议的EEPROM芯片24LC256通讯
在调用本函数前,必须确定I2C总线所使用的端口口线,并且要求在调用
本函数前,已经用其他方式请求到总线通讯权,已经书写好控制字和
要读取的地址值
这里假设PD.0为SDA线,PD.1为SCK线。
注意:本程序对从器件所发送来的数据不给出应答信号,在调用本程序后,
应该立即终止总线传送过程
*/
{
unsigned char i,n,d_temp=0;
DDRD=0b00000010; //为避免逻辑混乱,再次定义端口方向
PORTD|=0b00000001; //打开内部弱上拉,以便接受数据
/*由于不给出应答信号,SDA线被始终设置为输入状态*/
data_rec=0;
/*由于是采用位运算的方式获得输入信号的,必须在新的数据输入前清空原来的数据*/
for (i=0;i<8;i++)
{
n=(7-i); //获取移位计数器的值
delay(2); //延时2微秒,在本低电平其间,器件传送一个BIT
/*本过程对应为Tlow,协议规定为160ns(min),本器件规定为1300ns(常规条件下)*/
PORTD|=0b00000010; //拉高SCK,以便开始采样过程
delay(1); //延时1微秒
/*本过程对应为Thd(dat),协议规定为0,但是考虑到器件的响应速度问题,做了1微秒的延时*/
d_temp=(PIND&0b00000001); //端口值采样
data_rec|=(d_temp<<n); //用右移运算组合数据
delay(1); //分两次延时,产生高电平,在其间采样了SDA线
PORTD&=0b11111101; //拉低SCK,使得从器件开始传送下一个BIT
}
/*上面的循环完成一个字节的接收过程,完成后总线的状态为:
SDA随从器件传输的数据而不同,SCK可以理解为一个低电平的前端*/
delay(2); //延时2微秒
/*上面的过程对应为Tlow,协议规定为160ns(min),本器件规定为1300ns(常规条件下)*/
PORTD|=0b00000010; //拉高SCK线
delay(2);
/*本过程对应为Thigh,I2C协议规定为60ns(min),本器件规定为600ns(常规条件下)*/
/*由于单字节接收时,主器件不需要发送应答信号,则应答等待时钟被主器件产生,但是
在其间没有进行应答操作*/
PORTD&=0b11111101; //拉低SCK结束应答等待周期
/*上面的过程完成了字节的收取,完成后总线的状态为:
SDA线随是否得到应答信号而不同,SCK线可以理解为一个时钟低电平的前端*/
}
void i2c_send (unsigned char data)
/*本程序发送一个字节到i2c总线
硬件环境:AVR单片机对基于I2C协议的EEPROM芯片24LC256通讯
在调用本函数前,必须确定I2C总线所使用的端口口线并且要求在调用
本函数前,已经用其他方式请求到总线通讯权,已经书写好控制字和
要写入的地址值
这里假设PD.0为SDA线,PD.1为SCK线。
根据I2C总线协议,每次发送的数只能是一个字节,
要发送的数据由形参"data"传输给本函数。
在字节传输完成后,本函数将等待从器件的应答信号,并调用函数
返回标志值,如果响应成功,将全局变量i2c_flag写为0,
如果响应失败,将全局变量i2c_flag写为1
*/
{
unsigned char i=0;
bit[7]=(data&0b00000001); //数据准备,将输入的数据拆成8个变量
bit[6]=(data&0b00000010);
bit[5]=(data&0b00000100);
bit[4]=(data&0b00001000);
bit[3]=(data&0b00010000);
bit[2]=(data&0b00100000);
bit[1]=(data&0b01000000);
bit[0]=(data&0b10000000);
DDRD=0b00000011; //为避免逻辑混乱,再次定义端口方向
for (i=0;i<8;i++)
{
PORTD&=0b11111101; //拉低SCK
PORTD&=0b11111110; //将数据预置为0
/*由于协议规定Thd(dat)的最小值为0,所以在送出数据前没有做延迟*/
if (bit[i]>0) //如果分离得到的BIT位为1,就将SDA置1
PORTD|=0b00000001;
delay(1); //延迟1微秒
/*本过程对应为Tsu(dat),I2C协议规定为10ns(min),本器件规定为100ns(常规条件下)*/
PORTD|=0b00000010; //将SCK拉高,使从器件采样SDA线
delay(2); //延迟2微秒
/*本过程对应为Thigh,I2C协议规定为60ns(min),本器件规定为600ns(常规条件下)*/
}
/*上面的循环完成一个字节的发送过程,发送完成后总线的状态为:
SDA线随最后一位BIT位而不同,SCK线可以理解为一个时钟高电平的末端*/
PORTD&=0b11111101; //拉低SCK,开始应答等待时钟周期
DDRD=0b00000010; //将SDA线设置为输入端口,以便采样总线
PORTD|=0b00000001; //用内部弱上拉将总线拉高,等待应答信号
PORTD|=0b00000010; //拉高SCK,开始采样SDA线
delay(1); //延迟1微秒等待SDA稳定
i2c_flag=(PIND&0b00000001); //采样SDA线,检测应答信号
delay(1); //延迟1微秒
/*本过程对应为Thigh,由两个delay函数组成,在其间采样了SCK线应答的状态
I2C协议规定为60ns(min),本器件规定为600ns(常规条件下)*/
PORTD&=0b11111101; //拉低SCK结束应答等待周期
/*上面的过程完成了字节的传送和应答信号的采样,完成后总线的状态为:
SDA线随是否得到应答信号而不同,SCK线可以理解为一个时钟低电平的前端*/
}
void i2c_stop (void)
/*本程序停止I2C总线
硬件环境:AVR单片机对基于I2C协议的EEPROM芯片24LC256通讯
在调用本函数前,必须确定I2C总线所使用的端口口线
这里假设PD.0为SDA线,PD.1为SCK线。
*/
{
DDRD=0b00000011; //为避免逻辑混乱,再次定义端口方向
delay(1); //延时1微秒
/*本过程对应为Tlow,I2C协议规定为160ns(min),本器件规定为1300ns(常规条件下)*/
PORTD&=11111100; //拉低SDA线
delay(1);
/*分两次完成延迟过程,在其间将低SDA线拉低*/
PORTD|=0b00000010; //拉高SCK
delay(2); //延时2微秒
/*本过程对应为Tsu(sto),I2C协议规定为160ns(min),本器件规定为600ns(常规条件下)*/
PORTD|=0b00000001; //拉高SDA,停止总线
}
// 子函数段结束 //
/**************************************/
void main (void)
{
addr_h=0x00;
addr_l=0x00;
i2c_start();
data_send=0b10100000;
i2c_send(data_send);
i2c_send(addr_h);
i2c_send(addr_l);
i2c_start();
data_send=0b10100001;
i2c_send(data_send);
i2c_rec();
i2c_stop();
DDRC=0xFF;
PORTC=i2c_flag;
DDRB=0xFF;
PORTB=data_rec;
}
S1STA DATA 0D9H
S1DAT DATA 0DAH
S1ADR DATA 0DBH
;/* S1CON */
CR0 BIT 0D8H
CR1 BIT 0D9H
AA BIT 0DAH
SI BIT 0DBH
STO BIT 0DCH
STA BIT 0DDH
ENS1 BIT 0DEH
CR2 BIT 0DFH
;/* P1 */
SDA BIT 97H;
SCL BIT 96H;
ES1 BIT 0ADH
;为平台定义变量
ACK BIT 10H ;应答标志位
SLA DATA 60H ;器件从地址
SUBA DATA 61H ;器件子地址
NUMBYTE DATA 62H ;读/写的字节数
MTD EQU 30H ;发送数据缓冲区首址 (缓冲区30H-4FH)
MRD EQU 50H ;接收数据缓冲区首址 (缓冲区50-5FH)
; 内存分配:
; 堆栈区 STACK (70H--7FH)
; 显示缓冲区 DISP_RAM (发送缓冲区MTD)
;===================常量定义=======================================>
SAA7111 EQU 48H ; SAA7111的地址
;================程序代码段================================================={
ORG 0000H
AJMP MAIN
;==========================================================>
;申请总线
;功能:进行I2C总线的初始化--包括时钟速率,I2C使能,发送起始信号等等。
GETBUS:
MOV S1CON,#0C5H ;设置时钟为100K(MCU主频为12M),ENS1和AA置位
SETB STA ;申请成为主机,起动总线
JNB SI,$ ;等待起始信号的发送
RET
;==========================================================>
;发送数据函数
;功能:用于向总线发送数据
;入口参数:ACC--待发送的数据
SENDBYTE:
MOV S1DAT,A ;发送数据
MOV S1CON,#0C5H ;清除SI位等等
JNB SI,$ ;等待数据的发送
RET
;==========================================================>
;进行随机地址多字节写多字节
;占用R0,R1,ACC
;入口参数:器件从地址SLA 子地址SUBA 发送数据区MTD 读入字节数NUMBYTE
;出口参数:ACK,ACK=1时操作正确。
IWRNBYTE:
CLR ACK
MOV R0,#MTD
MOV R1,NUMBYTE
ACALL GETBUS ;启动总线
MOV A,SLA
ACALL SENDBYTE
MOV A,S1STA
CJNE A,#18H,IWRNEND
MOV A,SUBA
ACALL SENDBYTE
MOV A,S1STA
CJNE A,#28H,IWRNEND ;无应答则退出
IWRN_L1: MOV A,@R0
ACALL SENDBYTE ;发送数据
MOV A,S1STA
CJNE A,#28H,IWRNEND ;无应答则退出
INC R0
DJNZ R1,IWRN_L1
SETB ACK ;置标志位
CLR P1.5
IWRNEND: MOV S1CON,#0D5H
RET
;==========================================================>
;进行随机地址多字节读
;占用R0,R1,ACC
;入口参数:器件从地址SLA 子地址SUBA 接收数据区MRD 读入字节数NUMBYTE
;出口参数:ACK,ACK=1时操作正确。
IRDNBYTE:
CLR ACK
SETB P1.5
MOV R0,#MRD
MOV R1,NUMBYTE
ACALL GETBUS
MOV A,SLA
ACALL SENDBYTE ;器件寻址
MOV A,S1STA
CJNE A,#18H,IRDNEND ;无器件应答即退出
MOV A,SUBA ;写入器件子地址
ACALL SENDBYTE
MOV A,S1STA
CJNE A,#28H,IRDNEND ;总线出错即退出
MOV S1CON,#0E5H ;重新启动总线
JNB SI,$
MOV A,SLA
INC A ;读操作的器件寻址
ACALL SENDBYTE
MOV A,S1STA
CJNE A,#40H,IRDNEND
IRDN_L1: DJNZ R1,IRDN_L2
MOV S1CON,#0C1H ;取最后一个数据后发送非应答位
JNB SI,$
MOV A,S1STA ;将数据取出
CJNE A,#58H,IRDNEND
MOV A,S1DAT
MOV @R0,A ;数据存入MRD区中(R0指针指向MRD区)
SETB ACK
CLR P1.5
IRDNEND: MOV S1CON,#0D5H
RET
IRDN_L2: MOV S1CON,#0C5H ;接收数据并发送应答位
JNB SI,$
MOV A,S1STA
CJNE A,#50H,IRDNEND;总线出错即退出
MOV A,S1DAT ;取数据
MOV @R0,A ;数据存入MRD区
INC R0
SJMP IRDN_L1
;===============================================================================
; 主程序MAIN
; 主要负责整个系统的监控,如发指令给各芯片,读取各芯片的状态.
ORG 0080H
MAIN:
MOV R5,#0FH
DJNZ R5,$ ;延时,等待其它芯片复位
CLR SCL
SETB SDA
JNB SDA,I2CWRO ; SCL SDA 短路或SDA 被锁为低
SETB SCL
JNB SCL,I2CWRO
ACALL INI_SAA7111 ;初始化SAA7111
ACALL READ_SAA7111 ;读SAA7111状态
SJMP $
; 子程序 INI_SAA7111
; 作用: 对SAA7111进行初始化
; 出入参数: 无
INI_SAA7111:
MOV SLA,#SAA7111
MOV SUBA,#00H
MOV NUMBYTE,#1CH ;写入28个命令参数
MOV R0,#MTD
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#0C0H
INC R0
MOV @R0,#33H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#0EBH
INC R0
MOV @R0,#0E0H
INC R0
MOV @R0,#88H
INC R0
MOV @R0,#01H
INC R0
MOV @R0,#80H
INC R0
MOV @R0,#47H
INC R0
MOV @R0,#40H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#01H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#40H
INC R0
MOV @R0,#1CH
INC R0
MOV @R0,#03H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
INC R0
MOV @R0,#00H
LCALL IWRNBYTE
RET
; 子程序 READ_SAA7111
; 作用: 读SAA7111状态
; 出入参数: 无
READ_SAA7111: MOV SLA,#SAA7111
MOV SUBA,#1AH
MOV NUMBYTE,#03H
LCALL IRDNBYTE
RET
I2CWRO:CLR P0.0
SJMP $
;==========================================================<
END
我用EASYPACK的仿真器仿87c652控制saa7111,仿真器可以写入,示波器看写过程(即从“进行随机地址多字节写多字节”的整个过程到“进行随机地址多字节读”的写入49H)都没问题,连返回的ACK都正确,但怎么读都读不回来,将读的子地址改成00或者其他的也不行,每次到读数据时钟线都被拉低了,然后就跳到I2CWRO。
刚刚用单片机,各位老大帮忙看看哪儿的问题。
数据线和时钟线都接1K上拉电阻到+5v了。
答 1: 51我不熟悉,给你一个AVR的把。硬件上过了的。/*本程序验证基于I2C通讯协议的EEPROM芯片
24LC256的读出通讯时序
程序开发:西南科技大学 江某*/
# include <iom8v.h>
# include <macros.h>
unsigned char addr_h,addr_l,data_send,data_rec=0;
unsigned char i2c_flag=1;
unsigned char bit[8];
/***************************************/
// 子程序段 //
#pragma ctask delay
/*微秒级延迟函数,函数参数"dt"
延迟时间为(dt+2)微秒*/
void delay (unsigned char dt)
{
while (dt)
dt--;
asm("nop");
}
void i2c_start (void)
/*本程序启动I2C总线
硬件环境:AVR单片机对基于I2C协议的EEPROM芯片24LC256通讯
在调用本函数前,必须确定I2C总线所使用的端口口线
这里假设PD.0为SDA线,PD.1为SCK线。
*/
{
i2c_flag=1; //清除总线传输标志位,以便等待应答信号
DDRD=0b00000011; //为避免逻辑混乱,再次定义端口方向
PORTD|=0b00000011; //准备总线,将两根线全部拉高
delay(2); //延时2微秒
/*本过程对应为Tsu(sta),I2C协议规定为160ns(min),本器件规定为600ns(常规条件下)*/
PORTD&=0b11111110; //拉低SDA线,启动总线
delay(2); //延时2微秒
/*本过程对应为Thd(sta),I2C协议规定为160ns(min),本器件规定为600ns(常规条件下)*/
PORTD&=0b11111101; //拉低SCK线
}
void i2c_rec (void)
/*本程序收取I2C总线上的一个字节到主机,并以全局变量data_rec
的形式返回给调用主函数,主函数必须在下次调用本函数前将该值取
走,否则将会造成该值被新的数据覆盖掉
硬件环境:AVR单片机对基于I2C协议的EEPROM芯片24LC256通讯
在调用本函数前,必须确定I2C总线所使用的端口口线,并且要求在调用
本函数前,已经用其他方式请求到总线通讯权,已经书写好控制字和
要读取的地址值
这里假设PD.0为SDA线,PD.1为SCK线。
注意:本程序对从器件所发送来的数据不给出应答信号,在调用本程序后,
应该立即终止总线传送过程
*/
{
unsigned char i,n,d_temp=0;
DDRD=0b00000010; //为避免逻辑混乱,再次定义端口方向
PORTD|=0b00000001; //打开内部弱上拉,以便接受数据
/*由于不给出应答信号,SDA线被始终设置为输入状态*/
data_rec=0;
/*由于是采用位运算的方式获得输入信号的,必须在新的数据输入前清空原来的数据*/
for (i=0;i<8;i++)
{
n=(7-i); //获取移位计数器的值
delay(2); //延时2微秒,在本低电平其间,器件传送一个BIT
/*本过程对应为Tlow,协议规定为160ns(min),本器件规定为1300ns(常规条件下)*/
PORTD|=0b00000010; //拉高SCK,以便开始采样过程
delay(1); //延时1微秒
/*本过程对应为Thd(dat),协议规定为0,但是考虑到器件的响应速度问题,做了1微秒的延时*/
d_temp=(PIND&0b00000001); //端口值采样
data_rec|=(d_temp<<n); //用右移运算组合数据
delay(1); //分两次延时,产生高电平,在其间采样了SDA线
PORTD&=0b11111101; //拉低SCK,使得从器件开始传送下一个BIT
}
/*上面的循环完成一个字节的接收过程,完成后总线的状态为:
SDA随从器件传输的数据而不同,SCK可以理解为一个低电平的前端*/
delay(2); //延时2微秒
/*上面的过程对应为Tlow,协议规定为160ns(min),本器件规定为1300ns(常规条件下)*/
PORTD|=0b00000010; //拉高SCK线
delay(2);
/*本过程对应为Thigh,I2C协议规定为60ns(min),本器件规定为600ns(常规条件下)*/
/*由于单字节接收时,主器件不需要发送应答信号,则应答等待时钟被主器件产生,但是
在其间没有进行应答操作*/
PORTD&=0b11111101; //拉低SCK结束应答等待周期
/*上面的过程完成了字节的收取,完成后总线的状态为:
SDA线随是否得到应答信号而不同,SCK线可以理解为一个时钟低电平的前端*/
}
void i2c_send (unsigned char data)
/*本程序发送一个字节到i2c总线
硬件环境:AVR单片机对基于I2C协议的EEPROM芯片24LC256通讯
在调用本函数前,必须确定I2C总线所使用的端口口线并且要求在调用
本函数前,已经用其他方式请求到总线通讯权,已经书写好控制字和
要写入的地址值
这里假设PD.0为SDA线,PD.1为SCK线。
根据I2C总线协议,每次发送的数只能是一个字节,
要发送的数据由形参"data"传输给本函数。
在字节传输完成后,本函数将等待从器件的应答信号,并调用函数
返回标志值,如果响应成功,将全局变量i2c_flag写为0,
如果响应失败,将全局变量i2c_flag写为1
*/
{
unsigned char i=0;
bit[7]=(data&0b00000001); //数据准备,将输入的数据拆成8个变量
bit[6]=(data&0b00000010);
bit[5]=(data&0b00000100);
bit[4]=(data&0b00001000);
bit[3]=(data&0b00010000);
bit[2]=(data&0b00100000);
bit[1]=(data&0b01000000);
bit[0]=(data&0b10000000);
DDRD=0b00000011; //为避免逻辑混乱,再次定义端口方向
for (i=0;i<8;i++)
{
PORTD&=0b11111101; //拉低SCK
PORTD&=0b11111110; //将数据预置为0
/*由于协议规定Thd(dat)的最小值为0,所以在送出数据前没有做延迟*/
if (bit[i]>0) //如果分离得到的BIT位为1,就将SDA置1
PORTD|=0b00000001;
delay(1); //延迟1微秒
/*本过程对应为Tsu(dat),I2C协议规定为10ns(min),本器件规定为100ns(常规条件下)*/
PORTD|=0b00000010; //将SCK拉高,使从器件采样SDA线
delay(2); //延迟2微秒
/*本过程对应为Thigh,I2C协议规定为60ns(min),本器件规定为600ns(常规条件下)*/
}
/*上面的循环完成一个字节的发送过程,发送完成后总线的状态为:
SDA线随最后一位BIT位而不同,SCK线可以理解为一个时钟高电平的末端*/
PORTD&=0b11111101; //拉低SCK,开始应答等待时钟周期
DDRD=0b00000010; //将SDA线设置为输入端口,以便采样总线
PORTD|=0b00000001; //用内部弱上拉将总线拉高,等待应答信号
PORTD|=0b00000010; //拉高SCK,开始采样SDA线
delay(1); //延迟1微秒等待SDA稳定
i2c_flag=(PIND&0b00000001); //采样SDA线,检测应答信号
delay(1); //延迟1微秒
/*本过程对应为Thigh,由两个delay函数组成,在其间采样了SCK线应答的状态
I2C协议规定为60ns(min),本器件规定为600ns(常规条件下)*/
PORTD&=0b11111101; //拉低SCK结束应答等待周期
/*上面的过程完成了字节的传送和应答信号的采样,完成后总线的状态为:
SDA线随是否得到应答信号而不同,SCK线可以理解为一个时钟低电平的前端*/
}
void i2c_stop (void)
/*本程序停止I2C总线
硬件环境:AVR单片机对基于I2C协议的EEPROM芯片24LC256通讯
在调用本函数前,必须确定I2C总线所使用的端口口线
这里假设PD.0为SDA线,PD.1为SCK线。
*/
{
DDRD=0b00000011; //为避免逻辑混乱,再次定义端口方向
delay(1); //延时1微秒
/*本过程对应为Tlow,I2C协议规定为160ns(min),本器件规定为1300ns(常规条件下)*/
PORTD&=11111100; //拉低SDA线
delay(1);
/*分两次完成延迟过程,在其间将低SDA线拉低*/
PORTD|=0b00000010; //拉高SCK
delay(2); //延时2微秒
/*本过程对应为Tsu(sto),I2C协议规定为160ns(min),本器件规定为600ns(常规条件下)*/
PORTD|=0b00000001; //拉高SDA,停止总线
}
// 子函数段结束 //
/**************************************/
void main (void)
{
addr_h=0x00;
addr_l=0x00;
i2c_start();
data_send=0b10100000;
i2c_send(data_send);
i2c_send(addr_h);
i2c_send(addr_l);
i2c_start();
data_send=0b10100001;
i2c_send(data_send);
i2c_rec();
i2c_stop();
DDRC=0xFF;
PORTC=i2c_flag;
DDRB=0xFF;
PORTB=data_rec;
}
共3条
1/1 1 跳转至页
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |