zfgege 发表于 2007-1-12 11:26:14

用MEGA88的I/O口模拟I2C读写PCF8563的程序,已经调试成功.送给需要的朋友!

//该程序LCD用的PORTB0,1,2,3口;

//模拟I2C口是SDA为PORTD6.SCL为PORTD5

#include<avr/io.h>

#include<avr/signal.h>

#include<avr/interrupt.h>

#define uint unsigned int

#define uchar unsigned char

#define PCF8536_ADDR 0XA2

#define TW_WRITE0

#define TW_READ1

uchar g_aTimeBuf={0,0x02,0x50,0x44,0x10,0x12,0x05,0x01,0x07,0x45,0x80,0x80,0x80,0x00,0x00,0x00};

//写入2007年01月12日,10:44:50,分钟报警功能有效(45)其他功能无效

uchar g_aTimeBin;//时钟/日历bcd格式缓冲区



/***********************端口初始化************************/

//*******PC4=SDA;*******PC5=SCL

//用PC4、5来模拟

void port_init(void)

{

DDRC=_BV(PC4)|_BV(PC5);

PORTC=0X00;

PORTC|=_BV(PC4)|_BV(PC5);

DDRD=0x00;//中断入口置为输入

PORTD=0xff;//使能内部上拉电阻

}

/***************************中断初始化***********************/

//如果要用到PCF8563的中断功能,只需要把PCF8563的INT/引脚接到单片机的外部中断0或1

//在这里我接的是中断1口

void INT_init(void)

{

EICRA=0x00;//外部中断1低电平触发

EIMSK=0x02;//使能外部中断1

}



/*************PCF8536 操作********************开始**************/

//*******PC4=SDA;*******PC5=SCL

//DDRC    _SFR_IO8 (0x07)

//PORTC   _SFR_IO8 (0x08)

void DELAY_us(void)//延时5US

{

asm volatile("nop"::);

asm volatile("nop"::);

asm volatile("nop"::);

asm volatile("nop"::);

asm volatile("nop"::);

}

void i2c_start(void)//起始信号

{

   asm volatile ("cbi 0x08,5"::);

   asm volatile("nop"::);//延时1US

   asm volatile ("sbi 0x08,5"::);

    asm volatile("nop"::);//延时1US

   asm volatile ("sbi 0x08,4"::);

   DELAY_us();

   asm volatile ("cbi 0x08,4"::);

    DELAY_us();

   asm volatile ("cbi 0x08,5"::);

    asm volatile("nop"::);//延时1US

}//起始信号后SCL,SDA都为低电平



void i2c_stop(void)//停止信号

{

   asm volatile ("cbi 0x08,5"::);

    asm volatile("nop"::);//延时1US

   asm volatile ("cbi 0x08,4"::);

   DELAY_us();

asm volatile ("sbi 0x08,5"::);

   DELAY_us();

asm volatile ("sbi 0x08,4"::);

}//停止信号后SCL,SDA都为高电平

void i2c_ack(uchar ack_data) //读PCF8563时用的

{

//asm volatile ("cbi 0x08,5"::);

    //asm volatile("nop"::);//延时1US

if(ack_data==0)

    asm volatile ("cbi 0x08,4"::);//应答拉低SDA

else

   asm volatile ("sbi 0x08,4"::);//不应答拉高SDA

    DELAY_us();

asm volatile ("sbi 0x08,5"::);

   DELAY_us();

asm volatile ("cbi 0x08,5"::);

    asm volatile("nop"::);//延时1US

}

/****************************************************************/

uchar i2c_writebyte(uchar c)

{//将字节发送出去,可以是地址,也可以是数据,发完后等待应答并返回

uchar ack_flag;

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

   {

      if((c<<i)&0x80)

       asm volatile ("sbi 0x08,4"::);

      else

       asm volatile ("cbi 0x08,4"::);

       DELAY_us();

      asm volatile ("sbi 0x08,5"::); //置时钟线为高,通知被控器开始接收数据位

       DELAY_us();//保证时钟高电平周期大于4μs

      asm volatile ("cbi 0x08,5"::);

   }

   DELAY_us();

asm volatile ("cbi 0x08,4"::);

asm volatile ("cbi 0x07,4"::);//SDA引脚位变为输入

   asm volatile("nop"::);

asm volatile ("sbi 0x08,5"::);

    DELAY_us();

    DELAY_us();

if(PINC&0x10)//判断是否接收到应答信号

   ack_flag=0;

else

   ack_flag=1;//有应答信号

asm volatile ("sbi 0x07,4"::);//SDA引脚位变为输出

   asm volatile("nop"::);

asm volatile ("cbi 0x08,5"::);

asm volatile("nop"::);

return(ack_flag);

}



uchar i2c_readbyte(void) //读一个字节

{

uchar get_data=0;

asm volatile ("cbi 0x07,4"::);//SDA引脚位变为输入

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

   {

    asm volatile("nop"::);

    asm volatile ("cbi 0x08,5"::);//置时钟线为低,准备接收数据位

   DELAY_us();//时钟低电平周期5μs;

   asm volatile ("sbi 0x08,5"::); //置时钟线为高使数据线上数据有效

   get_data<<=1;

   if(PINC&0x10)//如果SDA数据线为高电平

    get_data++;

   asm volatile("nop"::);

   asm volatile("nop"::);

   }

asm volatile ("cbi 0x08,5"::);

   asm volatile ("sbi 0x07,4"::);//SDA引脚位变为输出

   asm volatile("nop"::);

return(get_data);

}

//当对PCF8563连续读的时候最后一个字节不需要主接受器应答

/********************************************************************/

uchar write_8563(uchar addr,uchar *buf,uchar len)//对PCF8536 连续的写操作

{

uchar i;

i2c_start();//起始信号

i2c_writebyte(PCF8536_ADDR|TW_WRITE);

i2c_writebyte(addr);//write address

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

i2c_writebyte(buf);

i2c_stop();

}



uchar read_8563(uchar addr,uchar *buf,uchar len)//对PCF8536 连续的读操作

{

uchar i;

i2c_start();

i2c_writebyte(PCF8536_ADDR|TW_WRITE);

i2c_writebyte(addr);//write word address

i2c_start();

i2c_writebyte(PCF8536_ADDR|TW_READ);

for(i=0;i<len-1;i++)

{

buf=i2c_readbyte();

i2c_ack(0);

}

buf=i2c_readbyte();

i2c_ack(1);

i2c_stop();

}

void PCF8563_CAF(void)//在中断函数里清除8563AF标志位

{

uchar TimeBuf;

read_8563(0x01, TimeBuf,1);

TimeBuf&=0x17;

write_8563(0x01,TimeBuf,1);//清除AF

}

/*-------------------------------------------------------------------------   

功能:将读出的时间数据的无关位屏蔽掉

说明:PCF8563时钟寄存器的有些无关位的状态是不定的,所以应该将无效位屏蔽掉

---------------------------------------------------------------------------*/

void Data_ajust(void) //时间无效位屏蔽

{   

   g_aTimeBin=g_aTimeBin&0x7f;//秒时间无效位的屏蔽

   g_aTimeBin=g_aTimeBin&0x7f;//分钟时间无效位的屏蔽

   g_aTimeBin=g_aTimeBin&0x3f;//小时时间无效位的屏蔽

   g_aTimeBin=g_aTimeBin&0x3f;//日时间无效位的屏蔽

   g_aTimeBin=g_aTimeBin&0x07;//星期时间无效位的屏蔽   

   g_aTimeBin=g_aTimeBin&0x1f;//月/世纪时间无效位的屏蔽,如果最高位0,世纪为21,

   g_aTimeBin=g_aTimeBin&0xff;//年时间无效位的屏蔽   

}

AVR-MEGA128 发表于 2007-1-12 12:41:59

应该鼓励一下

yanrz 发表于 2008-7-16 22:46:23

呵呵很好,谢谢!

tidal 发表于 2008-7-17 08:27:30

有硬接口不用?

n0831 发表于 2008-7-17 09:04:31

顶!

zzyzheng12 发表于 2010-4-24 23:03:32

mark

hubinghuandi 发表于 2012-3-28 00:03:21

留个记号先!

crazydtone 发表于 2013-6-3 16:29:37

向LZ学习,觉的编译器inline assembly这个功能很好!谢谢。。。

airwolf09921 发表于 2015-1-6 19:23:50

请教楼主一下 关闭闹铃的操作使用您的这种AF的方法 还是把闹铃位都屏蔽掉?

waymcu 发表于 2015-9-12 22:56:11

留个记号先!
页: [1]
查看完整版本: 用MEGA88的I/O口模拟I2C读写PCF8563的程序,已经调试成功.送给需要的朋友!