51单片机 - 红外遥控时钟

使用资源


1、普中单核-A2开发板
2、红外遥控器、红外接收头
3、LCD1602
4、PCF856T时钟模块

程序结构


main.c - -主要调度放在这
delay.c - -延时函数
lcd1602.c - - LCD1602显示屏驱动函数
time.c - - 定时器相关
pcf8593.c - - 时钟芯片驱动
my_iic.c - - 模拟iic驱动
Infrared.c - - 红外遥控
EXIT.c - - 外部中断,配合进行红外接收

main.c

#include 
#include 
#include 
#include 
#include "time.h"
#include "delay.h"
#include "lcd1602.h"
#include "pcf8563T.h"
#include "infrared.h"
#include "EXIT.h"

//主函数
int main(void)
{
    LcdInit();
    Timer0Init();//定时器0初始化
    Timer1Init();//定时器0初始化
    infrared_Init();//红外遥控初始化
    pcf8563_init();//pcd8563初始化
    EA = 1;
    while(1)
    {
        display_time(pcf8563_time_buf);
        if(infraredValue != 0)
        {
            switch(infraredValue)
            {
            case 0x15:
                if(set_type == 0)//不在设置模式下
                {
                    set_type = 1;//进入设置模式
                }
                else
                {
                    set_type = 0;//退出设置模式
					pcf8563_time_buf[6]--;
					pcf8563_set_time(pcf8563_time_buf);
                }
                break;
            case 0x07: //从右往左选择修改项
                if(set_type > 1)
                {
                    set_type--;
                }
                else
                {
                    set_type = 7;
                }
				break;
            case 0x09: //从左往右选择修改项
                if(set_type < 7)
                {
                    set_type++;
                }
                else
                {
                    set_type = 1;
                }
                break;
            case 0x40://选择的修改项加1
                add_time(set_type - 1); 
                break; 
            case 0x19://选择的修改项减1
                dec_time(set_type - 1);
                break;
            }
            infraredValue = 0;
        }
    }
}

delay.c

#include "delay.h"
#include 
#include 

#define MACHINE_CYCLE          MAIN_FOSC_DELAY/12    //定义机器周期HZ  

void delay5us(void)
{
#if MAIN_FOSC_DELAY == 11059200UL
    _nop_();
#elif MAIN_FOSC_DELAY == 12000000UL
    _nop_();
#elif MAIN_FOSC_DELAY == 22118400UL
    _nop_();
    _nop_();
    _nop_();
#endif
}

void delay_us(uint16_t us)
{
    uint16_t i = us;
    i = (i > 4) ? (i - 4) : i;
    while(i--)
    {
        #if MAIN_FOSC_DELAY == 11059200UL
            _nop_();
        #elif MAIN_FOSC_DELAY == 12000000UL
            _nop_();
        #elif MAIN_FOSC_DELAY == 22118400UL
            _nop_();
            _nop_();
            _nop_();
        #endif
    }
}


void delay_ms(uint16_t ms)
{
    uint16_t i, j;
#if MAIN_FOSC_DELAY == 11059200UL
    for(i=ms; i>0; i--)
	{
        for(j=114; j>0; j--)
		{
			;
		}
	}
#elif MAIN_FOSC_DELAY == 12000000UL
	for(i=0; i

delay.h

#ifndef __DELAY_H_
#define __DELAY_H_

#include "typedef.h"

#define MAIN_FOSC_DELAY        11059200UL    //定义主时钟HZ  

//延时n*1ms
void delay_ms(uint16_t ms);
void delay5us(void);
void delay_us(uint16_t us);
void delay_30us(void);
void delay_40us(void);
void delay_init(void);
#endif

typedef.h

#ifndef __TYPEDEF_H_
#define __TYPEDEF_H_

#include  //可变参数必须引用这个头文件

//#define uint16_t unsigned int
#define __WEAK                                 __attribute__((weak))

typedef unsigned long uint32_t;
typedef unsigned int  uint16_t;
typedef unsigned char uint8_t;

#endif

lcd1602.c

#include "lcd1602.h"
#include "delay.h"
#include 
#include  //可变参数必须引用这个头文件
#ifdef UART //定义才调用下方的文件头
#include "uart.h"
#endif


/* 字符获取方式:
   点阵格式:阴码
   取模走向:顺向
   取模方式:逐行式
   输出数制:十六进制
   用8*8点阵的后5列
 */
unsigned char code table[]= {
    0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,//一
	
    0x00,0x00,0x0E,0x00,0x1F,0x00,0x00,0x00,//二

    0x00,0x1F,0x00,0x0E,0x00,0x1F,0x00,0x00,//三

    0x08,0x0f,0x12,0x0f,0x0a,0x1f,0x02,0x02,//年

    0x0f,0x09,0x0f,0x09,0x0f,0x09,0x0b,0x11,//月

    0x0F,0x09,0x09,0x0F,0x09,0x09,0x0F,0x00,//日

    0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,//冒号

    0x18,0x18,0x07,0x08,0x08,0x08,0x07,0x00//℃
};

code unsigned char userCodeBuf[] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}; //自定义字符数据地址

//重写putchar函数
char putchar(char c)
{
	if(c >= 0x20 && c <= 0x7E)
	{
		LcdWriteData(c); //显示一个字符
	}
	#ifdef UART
		sendByte(c);
	#endif

	return c;
}

void lcd1602_printf(unsigned char x, unsigned char y, const char *format,...) reentrant
{
	va_list my_ap;  //定义参数指针,获取可选参数
	va_start(my_ap,format);     //初始化参数指针,将ap指向第一个实际参数的地址
	
    LcdWriteCom(0x80 + x + 0x40 * y);    //设置数据指针起点,从LCD头部开始显示
	
	vprintf(format, my_ap);
	
	va_end(my_ap);      //不再使用参数指针,或者需要重新初始化参数指针时,必须先调用va_end宏
}

void setDisplayAddr(unsigned char x, unsigned char y)
{
    LcdWriteCom(0x80 + x + 0x40 * y);    //设置数据指针起点,从LCD头部开始显示
}

/*******************************************************************************
* 函 数 名         : displayString
* 函数功能		   : LCD1602显示一串字符串 ,buf 为要显示的数组,N为要显示的长度,Lin为要显示在哪行
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void displayString(uint8_t* buf, uint16_t N, uint8_t Lin)
{
    int i;
	N = (N > 16) ? 16 : N;//防止出现大于16的情况
    if(Lin == 0)
    {
        LcdWriteCom(0x80);    //设置数据指针起点,从LCD头部开始显示
    }
    else 
    {
        LcdWriteCom(0x80 + 0x40); //第二行显示
    }
    for(i = 0; i < N; i++)
    {
        LcdWriteData(buf[i]); //显示一个字符
    }
}

//判断忙指令
void Busy(void)
{
	LCD1602_DATAPINS = 0xff;
	LCD1602_RS = 0;
	LCD1602_RW = 1;
   	while(LCD1602_DATAPINS & 0x80)
   	{
		LCD1602_E = 0;
   		LCD1602_E = 1;
   	}
	LCD1602_E = 0;
}

/*******************************************************************************
* 函 数 名         : LcdWriteCom
* 函数功能		   : 向LCD写入一个字节的命令
* 输    入         : com
* 输    出         : 无
*******************************************************************************/
void LcdWriteCom(uint8_t com)	  //写入命令
{
	Busy();
	LCD1602_RS = 0;	   //选择发送命令
	LCD1602_RW = 0;	   //选择写入
	
	LCD1602_DATAPINS = com;     //放入命令
	delay_ms(1);		//等待数据稳定

	LCD1602_E = 1;	          //写入时序
	#ifdef QUICK
	delay_ms(1);	  //保持时间
	#else 
	delay_ms(5);	  //保持时间
	#endif
	LCD1602_E = 0;
}

/*******************************************************************************
* 函 数 名         : LcdWriteData
* 函数功能		   : 向LCD写入一个字节的数据
* 输    入         : dat
* 输    出         : 无
*******************************************************************************/		      
void LcdWriteData(uint8_t dat)			//写入数据
{
	Busy();
	LCD1602_RS = 1;	//选择输入数据
	LCD1602_RW = 0;	//选择写入

	LCD1602_DATAPINS = dat; //写入数据
	delay_ms(1);

	LCD1602_E = 1;   //写入时序
	#ifdef QUICK
	delay_ms(1);	  //保持时间
	#else 
	delay_ms(5);	  //保持时间
	#endif
	LCD1602_E = 0;
}

/*******************************************************************************
* 函 数 名       : LcdInit()
* 函数功能		 : 初始化LCD屏
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/		   
void LcdInit()						  //LCD初始化子程序
{
	unsigned char i = 0;
	
	//等待LCD1602初始化
    delay5us();
    delay_us(5);
	delay_30us();
	delay_40us();
 	LcdWriteCom(0x38);  //开显示
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
	
	setDisplayAddr(0, 0);
	displayString("lcd1602 Init", 12, 0);
	lcd1602_printf(0, 0, "LCD1602_Init");
	/* 开始写入你要显示的自定义字符、汉字代码 */
	LcdWriteCom(0x40); 
    for(i=0; i<64; i++)
    {
        LcdWriteData(table[i]);
    }
    displayString("            ", 12, 0);
}

lcd1602.h

#ifndef __LCD1602_H_
#define __LCD1602_H_

#define QUICK 1 //有此宏定义时,为快速模式,否则为慢速模式

/**********************************
包含头文件
**********************************/
#include 
#include "typedef.h"

/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_RS=P2^6;
sbit LCD1602_RW=P2^5;
sbit LCD1602_E=P2^7;

extern unsigned char code userCodeBuf[];//用户自定义的7个字的地址数组

/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint16_t c);   //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uint8_t com);
/*LCD1602写入8位数据子函数*/	
void LcdWriteData(uint8_t dat)	;
/*LCD1602初始化子程序*/		
void LcdInit();				
void displayString(uint8_t* buf, uint16_t N, uint8_t Lin);
void setDisplayAddr(unsigned char x, unsigned char y);
void lcd1602_printf(unsigned char x, unsigned char y, const char *format,...)reentrant;
#endif

time.c

#include "time.h"
#include "delay.h"
#ifdef ADC0809 //定义才调用下方的文件头
#include "ADC0809.h"
#endif
#ifdef UART //定义才调用下方的文件头
#include "uart.h"
#endif

bit half_one_second_flag = 0;//0.5秒标志位
unsigned int time0Cnt = 0;

//定时器0中断
void Timer0Init(void) //定时器初始化为1ms一次
{
    TMOD |= 0X01; //选择为定时器0模式,工作方式1,仅用TR0打开启动。

    TL0 = T1MS_0;//给定时器赋初值,定时1ms ,计算方式(65536 -(12/12*1000))%256
    TH0 = T1MS_0 >> 8;//给定时器赋初值,定时1ms ,计算方式(65536 -(12/12*1000))/256
    ET0 = 1; //打开定时器0中断允许
    TR0 = 1; //打开定时器
}

//定时器1中断
void Timer1Init(void) //定时器初始化为1ms一次
{
    TMOD |= 0x10;                    //set timer1 as mode1 (16-bit)
    TL1 = T1MS_1;                     //initial timer1 low byte
    TH1 = T1MS_1 >> 8;                //initial timer1 high byte
    TR1 = 0;                        //timer1 start running
    ET1 = 1;                        //enable timer1 interrupt
}

void time0_isr() interrupt 1 using 0
{
	static unsigned int count = 0;
    TL0 = T1MS_0;//手动重装载
    TH0 = T1MS_0 >> 8;//手动重装载
	
	#ifdef ADC0809
	CLK = !CLK;//ADC0809的时钟信号
	#endif
	
	#ifdef UART
	if(timeout < RECEIVE_TIMEOUT)//当小于接收超时时间时
	{
		if(++timeout == RECEIVE_TIMEOUT)
		{
			receiveFinshFlag = 1;//串口1帧数据接收完毕
		}
	}
	#endif
	if(++count >= 500)
	{
		count = 0;
		half_one_second_flag = 1;
		time0Cnt++;
	}
}

/* Timer0 interrupt routine */
void time1_isr() interrupt 3 using 1
{
    TL1 = T1MS_1;                     //reload timer1 low byte
    TH1 = T1MS_1 >> 8;                //reload timer1 high byte

}

time.h

#ifndef __TIME_H_
#define __TIME_H_
#include 
#include "typedef.h"


extern uint16_t time0Cnt;
extern bit half_one_second_flag;//0.5秒标志位

#define T1MS_0 (65536-MAIN_FOSC_DELAY/12/1000)   //1ms timer calculation method in 12T mode
#define T1MS_1 (65536-MAIN_FOSC_DELAY/12/1000)   //1ms timer calculation method in 12T mode
#define TIMER_MODE0     0x00
#define TIMER_MODE1     0x01
#define TIMER_MODE2     0x02
#define TIMER_MODE3     0x03

//定时器0
void Timer0Init(void); //定时器初始化为1ms一次
//定时器1
void Timer1Init(void); //定时器初始化为1ms一次
#endif

pcf8563.c

#include "pcf8563T.h"
#include "my_iic.h"
#include "lcd1602.h"
#include "time.h"
#include "delay.h"

unsigned char set_type = 0; //设置类别 1-7分别代表 年月日时分秒周
unsigned char pcf8563_time_buf[7] = {0x21, 0x08, 0x14, 0x18, 0x29, 0x55, 0x06};

//日期转星期 周一到周日分别为 0-6
unsigned char day_to_weed(unsigned char* time_buf)
{
	unsigned int year;
	unsigned int month;
	unsigned int day;
	
	year = 2000 + BCD2HEX(time_buf[0]);
	month = BCD2HEX(time_buf[1]);
	day = BCD2HEX(time_buf[2]);
	
	day = (unsigned char)((day+2*month+3*(month+1)/5+year+year/4-year/100+year/400) % 7);
	lcd1602_printf(0, 0, "\r\n现在是周%d\r\n", day);
	
	return (unsigned char)day;
}

void pcf8563_send_one_byte(unsigned char addr, unsigned char dat)
{
	IIC_Start();
	IIC_Send_Byte(WRITE_COMMAND);
	IIC_Wait_Ack();
	IIC_Send_Byte(addr);
	IIC_Wait_Ack();
	IIC_Send_Byte(dat);
	IIC_Wait_Ack();
	IIC_Stop();
}

unsigned char pcf8563_read_one_byte(unsigned char addr)
{
	unsigned char temp;
	
	IIC_Start();
	IIC_Send_Byte(WRITE_COMMAND);
	IIC_Wait_Ack();
	IIC_Send_Byte(addr);
	IIC_Wait_Ack();
	IIC_Start();
	IIC_Send_Byte(READ_COMMAND);
	IIC_Wait_Ack();
	temp = IIC_Read_Byte(0);//发送非应答信号
	IIC_Stop();
	
	return temp;
}

void pcf8563_set_time(unsigned char* buf)
{
	pcf8563_send_one_byte(PCF8563_REG_STATUS1, 0x20);  // 关闭时钟
	
	pcf8563_send_one_byte(PCF8563_REG_YEAR, buf[0]);	//年
	pcf8563_send_one_byte(PCF8563_REG_MONTH, buf[1]);	//月
	pcf8563_send_one_byte(PCF8563_REG_DAY, buf[2]);		//日
	pcf8563_send_one_byte(PCF8563_REG_HOURS, buf[3]);	//时
	pcf8563_send_one_byte(PCF8563_REG_MINUTE, buf[4]);	//分
	pcf8563_send_one_byte(PCF8563_REG_SECOND, buf[5]);	//秒
	
	buf[6] = day_to_weed(buf);//日期转换为周
	pcf8563_send_one_byte(PCF8563_REG_WEEK, buf[6]);	//周
	
	pcf8563_send_one_byte(PCF8563_REG_STATUS1, 0x00);//启动时钟
}

void pcf8563_init(void)
{
	pcf8563_send_one_byte(PCF8563_REG_STATUS1, 0x00);//启动时钟
}

void pcf8563_read_time(unsigned char* buf)
{
//	buf[0] = BCD2HEX(pcf8563_read_one_byte(PCF8563_REG_YEAR));
//	buf[1] = BCD2HEX(pcf8563_read_one_byte(PCF8563_REG_MONTH)&0x1f);
//	buf[2] = BCD2HEX(pcf8563_read_one_byte(PCF8563_REG_DAY)&0x3f);
//	buf[3] = BCD2HEX(pcf8563_read_one_byte(PCF8563_REG_HOURS)&0x3f);
//	buf[4] = BCD2HEX(pcf8563_read_one_byte(PCF8563_REG_MINUTE)&0x7f);
//	buf[5] = BCD2HEX(pcf8563_read_one_byte(PCF8563_REG_SECOND)&0x7f);
//	buf[6] = BCD2HEX(pcf8563_read_one_byte(PCF8563_REG_WEEK)&0x07) + 1;	
	
	buf[0] = pcf8563_read_one_byte(PCF8563_REG_YEAR);
	buf[1] = pcf8563_read_one_byte(PCF8563_REG_MONTH)&0x1f;
	buf[2] = pcf8563_read_one_byte(PCF8563_REG_DAY)&0x3f;
	buf[3] = pcf8563_read_one_byte(PCF8563_REG_HOURS)&0x3f;
	buf[4] = pcf8563_read_one_byte(PCF8563_REG_MINUTE)&0x7f;
	buf[5] = pcf8563_read_one_byte(PCF8563_REG_SECOND)&0x7f;
	buf[6] = (pcf8563_read_one_byte(PCF8563_REG_WEEK)&0x07) + 1;	
}

void add_time(unsigned char index)
{
	
	pcf8563_time_buf[index]++;
	if((pcf8563_time_buf[index]&0x0f) > 9)//换成BCD码
	{
		pcf8563_time_buf[index] += 6;
	}
			
	switch(index)
	{
		case 0://修改年
			if(pcf8563_time_buf[index] > 0x99)
			{
				pcf8563_time_buf[index] = 0;
			}
		break;
		case 1://修改月
			if(pcf8563_time_buf[index] > 0x12)
			{
				pcf8563_time_buf[index] = 0;
			}
		break;
		case 2://修改日
			if(pcf8563_time_buf[index] > 0x31)
			{
				pcf8563_time_buf[index] = 0;
			}
		break;
		case 3://修改时
			if(pcf8563_time_buf[index] > 0x23)
			{
				pcf8563_time_buf[index] = 0;
			}
		break;
		case 4://修改分
			if(pcf8563_time_buf[index] > 0x59)
			{
				pcf8563_time_buf[index] = 0;
			}
		break;
		case 5://修改秒
			if(pcf8563_time_buf[index] > 0x59)
			{
				pcf8563_time_buf[index] = 0;
			}
		break;
		case 6://修改周
			if(pcf8563_time_buf[index] > 0x7)
			{
				pcf8563_time_buf[index] = 1;
			}
			break;
	}
}

void dec_time(unsigned char index)
{
	switch(index)
	{
		case 0://修改年
			if(pcf8563_time_buf[index] == 0)
			{
				pcf8563_time_buf[index] = 99;
			}
		break;
		case 1://修改月
			if(pcf8563_time_buf[index] == 0)
			{
				pcf8563_time_buf[index] = 0x13;
			}
		break;
		case 2://修改日
			if(pcf8563_time_buf[index] == 0)
			{
				pcf8563_time_buf[index] = 0x32;
			}
		break;
		case 3://修改时
			if(pcf8563_time_buf[index] == 0)
			{
				pcf8563_time_buf[index] = 0x24;
			}
		break;
		case 4://修改分
			if(pcf8563_time_buf[index] == 0)
			{
				pcf8563_time_buf[index] = 0x60;
			}
		break;
		case 5://修改秒
			if(pcf8563_time_buf[index] == 0)
			{
				pcf8563_time_buf[index] = 0x60;
			}
		break;
		case 6://修改周
			if(pcf8563_time_buf[index] == 0)
			{
				pcf8563_time_buf[index] = 0x08;
			}
			break;
	}
	pcf8563_time_buf[index]--;
	if((pcf8563_time_buf[index]&0x0f) > 9)//换成BCD码
	{
		pcf8563_time_buf[index] -= 6;
	}
}

void display_time(unsigned char* buf)
{
	static unsigned int count = 0;
	
	if(++count <= 5000)
	{
		delay_us(1);
		return;
	}
	else
	{
		count = 0;
	}
	if(set_type == 0)//不在设置模式的情况下才读取时间
	{
		pcf8563_read_time(pcf8563_time_buf);//读取时间
	}
    lcd1602_printf(0, 0, "20%02x %02x %02x", (int)buf[0], (int)buf[1], (int)buf[2]); //年月日

    setDisplayAddr(4, 0);
    LcdWriteData(userCodeBuf[3]); //显示年

    setDisplayAddr(7, 0);
    LcdWriteData(userCodeBuf[4]); //显示月

    setDisplayAddr(10, 0);
    LcdWriteData(userCodeBuf[5]); //显示日

	
    lcd1602_printf(0, 1, "%02x %02x %02x  %x\r\n", (int)buf[3], (int)buf[4], (int)buf[5], (int)buf[6]); //时分秒周
	
	setDisplayAddr(2, 1);
    LcdWriteData(userCodeBuf[6]); //显示冒号
	
	setDisplayAddr(5, 1);
    LcdWriteData(userCodeBuf[6]); //显示冒号
	
	if(set_type != 0)//在设置时间模式下
	{
		if(half_one_second_flag == 1)
		{
			half_one_second_flag = 0;
			delay_ms(100);
			switch(set_type)
			{
				case 1:
					lcd1602_printf(2, 0, "  ");//将年显示关闭,达到闪烁的效果
				break;
				case 2:
					lcd1602_printf(5, 0, "  ");//将年显示关闭,达到闪烁的效果
				break;
				case 3:
					lcd1602_printf(8, 0, "  ");//将年显示关闭,达到闪烁的效果
				break;
				case 4:
					lcd1602_printf(0, 1, "  ");//将年显示关闭,达到闪烁的效果
				break;
				case 5:
					lcd1602_printf(3, 1, "  ");//将年显示关闭,达到闪烁的效果
				break;
				case 6:
					lcd1602_printf(6, 1, "  ");//将年显示关闭,达到闪烁的效果
				break;
				case 7:
					lcd1602_printf(10, 1, "  ");//将年显示关闭,达到闪烁的效果
				break;
			}
		}
	}
	
}

pcf8563.h

#ifndef __PCF8563T_H_
#define __PCF8563T_H_

#define BCD2HEX(x) ((((x) >> 4) * 10) + ((x) & 0x0f))
#define HEX2BCD(x) ((((x) / 10) << 4) + ((x) % 10))

extern unsigned char pcf8563_time_buf[7];
extern unsigned char set_type; //设置类别 1-7分别代表 年月日时分秒周

#define WRITE_COMMAND 0xa2
#define READ_COMMAND  0xa3

//---寄存器定义
#define PCF8563_REG_STATUS1						0x00
#define PCF8563_REG_STATUS2						0x01
#define PCF8563_REG_SECOND						0x02
#define PCF8563_REG_MINUTE						0x03
#define PCF8563_REG_HOURS						0x04
#define PCF8563_REG_DAY							0x05
#define PCF8563_REG_WEEK						0x06
#define PCF8563_REG_MONTH						0x07
#define PCF8563_REG_YEAR						0x08
#define PCF8563_REG_MINUTE_ALARM				0x09
#define PCF8563_REG_HOURS_ALARM					0x0A
#define PCF8563_REG_DAY_ALARM					0x0B
#define PCF8563_REG_WEEK_ALARM					0x0C
#define PCF8563_REG_CLKOUT						0x0D
#define PCF8563_REG_TIMER_CTR					0x0E
#define PCF8563_REG_TIMER 						0x0F

//---CLKOUT的输出
#define PCF8563_CLKOUT_32768HZ					0
#define PCF8563_CLKOUT_1024HZ					1
#define PCF8563_CLKOUT_32HZ						2
#define PCF8563_CLKOUT_1HZ						3

//---函数定义
void pcf8563_send_one_byte(unsigned char addr, unsigned char dat);
unsigned char pcf8563_read_one_byte(unsigned char addr);
void pcf8563_set_time(unsigned char* buf);
void pcf8563_read_time(unsigned char* buf);
void pcf8563_init(void);

void add_time(unsigned char index);
void dec_time(unsigned char index);
void display_time(unsigned char* buf);

#endif

my_iic.c

#include "my_iic.h"
#include "delay.h"
#include 

void IIC_Start(void)//启动信号,将时钟线与数据线都拉高,然后先将数据线拉低,再将时钟线拉低,通讯就这样开始了
{
    I2C_SDA = 1;//拉高数据线
    I2C_CLK = 1;//拉高时钟线
    delay_us(5);//适当延时,不能太低,高了也不好,低了芯片识别不了,久了通讯速率太慢。
    I2C_SDA = 0;
    delay_us(5);
    I2C_CLK = 0;
}


void IIC_Stop(void)
{
    I2C_SDA = 0;
    I2C_CLK = 0;
    delay_us(5);//适当延时,不能太低,高了也不好,低了芯片识别不了,久了通讯速率太慢。
    I2C_CLK = 1;
    I2C_SDA = 1;
    delay_us(5);//适当延时,不能太低,高了也不好,低了芯片识别不了,久了通讯速率太慢。
}

//返回1说明错误
uint8_t IIC_Wait_Ack(void)//写数据时等待对方应答
{
    uint8_t i= 0;
    
    I2C_SDA = 1;
    _nop_();
    I2C_CLK = 1;
    _nop_();
    while(I2C_SDA)
    {
        if(++i > 250)
        {
            return 1;
        }
        _nop_();
    }
    I2C_CLK = 0;

    return 0;      
}

void IIC_Ack(void)//读数据的时候给对方应答
{
    I2C_CLK = 0;
    delay_us(2);
    I2C_SDA = 0;
    delay_us(2);
    I2C_CLK = 1;
    delay_us(2);
    I2C_CLK = 0;
}


void IIC_NAck(void)//读数据即将结束的时候不给对方应答
{
    I2C_CLK = 0;
    delay_us(5);
    I2C_SDA = 1;
    delay_us(5);
    I2C_CLK = 1;
    delay_us(5);
}

void IIC_Send_Byte(uint8_t txd)//发送一个字节
{
    uint8_t i;
    I2C_CLK = 0;
    for(i=0; i<8; i++)
    {
        I2C_SDA = (txd&0x80)>>7;
        txd <<= 1;
        delay_us(2);
        I2C_CLK = 1;
        delay_us(2);
        I2C_CLK = 0;
        delay_us(2);
    }
}


uint8_t IIC_Read_Byte(uint8_t ack)//读取一个字节
{
    uint8_t  i;
    uint8_t receive = 0;
    for(i=0; i<8; i++)
    {
        I2C_CLK = 0;
        delay_us(1);
        I2C_CLK = 1;
        receive <<= 1; 
        if(I2C_SDA)
        {
            receive++;   
        }
		delay_us(1); 
    }
    if (!ack)
    {
        IIC_NAck();//发送nACK
    }
    else
    {
        IIC_Ack(); //发送ACK   
    }
    return receive;
}

my_iic.h

#ifndef __MY_IIC_H_
#define __MY_IIC_H_
#include 
#include "typedef.h"

sbit I2C_CLK = P2 ^ 3;
sbit I2C_SDA = P2 ^ 4;

void IIC_Start(void);//启动信号,将时钟线与数据线都拉高,然后先将数据线拉低,再将时钟线拉低,通讯就这样开始了
void IIC_Stop(void);
uint8_t IIC_Wait_Ack(void);//写数据时等待对方应答
void IIC_Ack(void);//读数据的时候给对方应答
void IIC_NAck(void);//读数据即将结束的时候不给对方应答
void IIC_Send_Byte(uint8_t dat);//发送一个字节
uint8_t IIC_Read_Byte(uint8_t ack);//读取一个字节

#endif

Infrared.c

#include "infrared.h"
#include "delay.h"
#include "EXIT.h"
#include 

unsigned char infraredValue;//红外接收键值

//红外通讯初始化
void infrared_Init(void)
{
	EXIT0_Init();//外部中断0初始化
	EA = 1;
	infrared_in = 1;
}

void readInfrared(unsigned char* dat)
{
    unsigned char i,j;//循环控制变量
    unsigned char Time;//高电平时间计算
	unsigned int errorCnt;//等待计时
    unsigned char readBuf[2];//临时存放数组
    
	Time = 0;					 
	delay_ms(7);	//7ms
	if(!infrared_in)		//确认是否真的接收到正确的信号
	{
		errorCnt = 1000;				//1000*10us=10ms,超过说明接收到错误的信号
		/*当两个条件都为真是循环,如果有一个条件为假的时候跳出循环,免得程序出错的时
		侯,程序死在这里*/	
		while((infrared_in == 0)&&( errorCnt > 0))	//等待前面9ms的低电平过去  		
		{			
			delay_us(1);
			errorCnt--;
		} 
		if(infrared_in == 1)			//如果正确等到9ms低电平
		{
			errorCnt = 500;
			while((infrared_in == 1) && (errorCnt > 0))		 //等待4.5ms的起始高电平过去
			{
				delay_us(1);
				errorCnt--;
			}
			for(i=0; i<4; i++)		//共有4组数据
			{				
				for(j=0; j<8; j++)	//接收一字节数据
				{
					errorCnt = 60;		
					while((infrared_in == 0)&&(errorCnt > 0))//等待信号前面的560us低电平过去
					{
						delay_us(10);
						errorCnt--;
					}
					errorCnt = 500;
					while((infrared_in == 1) && (errorCnt > 0))	 //计算高电平的时间长度。
					{
						delay_us(10);	 //0.1ms
						Time++;
						errorCnt--;
						if(Time > 30)
						{
							return;
						}
					}
					readBuf[i%2] >>= 1;	    //i表示第几组数据
					if(Time >= 8)			//如果高电平出现大于565us,那么是1
					{
						readBuf[i%2] |= 0x80;
					}
					Time = 0;		//用完时间要重新赋值							
				}
			}
		}
        //校验失败,则让值等于0
		if(readBuf[0] != ~readBuf[1])
		{
			*dat = 0;
		}
        else
        {
            *dat = readBuf[0];//校验成功,得到键值
        }
	}
}

Infrared.h

#ifndef __INFRARED_H_
#define __INFRARED_H_

#include 
#include "typedef.h"

sbit infrared_in = P3 ^ 2;//红外接收头引脚

extern unsigned char infraredValue;//红外接收键值

void infrared_Init(void);
void readInfrared(unsigned char* dat);
#endif

EXIT.c

#include "EXIT.h"
#include "infrared.h"
#include 

#ifdef EXIT_0
void EXIT0_Init(void)
{
	IT0=1;	//INT0负跳变触发	
    EX0 = 1; //开外部INTO中断
}

//外部中断0中断服务函数
void exint0() interrupt 0           //(location at 0003H)
{   
#ifdef INFRARED //若在keil编译指令中定义了这个宏,且为1,这调用下方读取红外信号的函数
    readInfrared(&infraredValue);
#endif
}
#endif

#ifdef EXIT_1
void EXIT1_Init(void)
{
	IT1=1;	//INT1负跳变触发	
    EX1 = 1; //开外部INTO中断
}

//外部中断1中断服务函数
void exint1() interrupt 2           //(location at 0013H)
{

}
#endif

EXIT.h

#ifndef __EXIT_H_
#define __EXIT_H_
#include "typedef.h"

void EXIT0_Init(void);
void EXIT1_Init(void);

#endif

51单片机 - 红外遥控时钟_第1张图片
请注意,编译的时候增加宏定义开关 “INFRARED,EXIT_0”
51单片机 - 红外遥控时钟_第2张图片

你可能感兴趣的:(红外遥控,时钟,mcu,单片机)