专注网站制作,科研实验室网站建设,ui设计页面布局和功能,seo推广团队首先编写I2C模块#xff0c;根据下面的原理图进行位声明#xff1a;
sbit I2C_SCL P2^1;
sbit I2C_SDA P2^0;再根据下面的时序结构图编写函数#xff1a;
/*** brief I2C开始* param 无* retval 无*/
void I2C_Start(void)
{I2C_SDA 1; I2C_SCL 1; I2C_SDA 0;I2C_S… 首先编写I2C模块根据下面的原理图进行位声明
sbit I2C_SCL P2^1;
sbit I2C_SDA P2^0;再根据下面的时序结构图编写函数
/*** brief I2C开始* param 无* retval 无*/
void I2C_Start(void)
{I2C_SDA 1; I2C_SCL 1; I2C_SDA 0;I2C_SCL 0;
}/*** brief I2C停止* param 无* retval 无*/
void I2C_Stop(void)
{I2C_SDA 0;I2C_SCL 1;I2C_SDA 1;
}/*** brief I2C发送一个字节* param 要发送的字节* retval 无*/
void I2C_SendByte(unsigned char Byte)
{unsigned char i;for(i 0; i 8; i ) {I2C_SDA Byte (0x80 i); //由高位到低位依次取出写入I2C_SCL 1;I2C_SCL 0;}
}/*** brief I2C接受一个字节* param 无* retval 接收到的字节*/
unsigned char I2C_ReceiveByte(void)
{unsigned char i, Byte 0x00;I2C_SDA 1;for(i 0; i 8; i ) {I2C_SCL 1;if(I2C_SDA) {Byte | (0x80 i);} //由高位到低位把读到的 取出I2C_SCL 0;}return Byte;
}/*** brief I2C发送应答* param 应答位0为应答1为非应答* retval 无*/
void I2C_SendAck(unsigned char AckBit)
{I2C_SDA AckBit;I2C_SCL 1;I2C_SCL 0;
}/*** brief I2C接收应答* param 无* retval 接收到的应答位0为应答1为非应答*/
unsigned char I2C_ReceiveAck(void)
{unsigned char AckBit;I2C_SDA 1;I2C_SCL 1;AckBit I2C_SDA;I2C_SCL 0;return AckBit;
}接下来利用写好的I2C模块创建AT24C02模块来凑成数据帧 同样根据图中的时序模拟就可以了
#include REGX52.H
#include I2C.h#define AT24C02_ADDRESS 0xA0 //写地址 /*** brief AT24C02写入一个字节* param 要写入字节的地址,0~255* param 要写入的数据* retval 无*/
void AT24C02_WriteByte(unsigned char WordAddress, Data)
{I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_SendByte(Data);I2C_ReceiveAck();I2C_Stop();
}/*** brief AT24C02读取一个字节* param 要读出字节的地址 0~255* retval 读出的数据*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{unsigned char Data;I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_Start();I2C_SendByte(AT24C02_ADDRESS | 0x01);I2C_ReceiveAck();Data I2C_ReceiveByte();I2C_SendAck(1); //非应答I2C_Stop();return Data;
}在main中调用看一下效果如何这种存储写入之后是掉电不丢失的
#include REGX52.H
#include Delay.h
#include Key.h
#include LCD1602.h
#include AT24C02.hunsigned char Data;void main()
{LCD_Init();LCD_ShowString(1, 1, Hello);AT24C02_WriteByte(1, 66);Delay(5); //写完不要马上去读写需要时间Data AT24C02_ReadByte(1);LCD_ShowNum(2, 1, Data, 3);while(1){}
}接下来配合上按键就可以做出一个按键存储器啦有了之前编写的模块只要适当地调用就可以了以下是按键存储器的main.c文件代码 按键1增大数据 按键2减小数据 按键3写入 按键4读出
#include REGX52.H
#include Delay.h
#include Key.h
#include LCD1602.h
#include AT24C02.hunsigned char KeyNum;
unsigned int Num;void main()
{LCD_Init();LCD_ShowNum(1, 1, Num, 5);while(1){KeyNum Key();if(KeyNum 1){Num ;LCD_ShowNum(1, 1, Num, 5);}if(KeyNum 2){Num --;LCD_ShowNum(1, 1, Num, 5);}if(KeyNum 3) //写入{AT24C02_WriteByte(0, Num % 256); //Num十六位,先取低八位Delay(5);AT24C02_WriteByte(1, Num % 256); //存高八位Delay(5);LCD_ShowString(2, 1, Write OK);Delay(1000);LCD_ShowString(2, 1, );}if(KeyNum 4) //读出{Num AT24C02_ReadByte(0); //拿到低八位Num | (AT24C02_ReadByte(1) 8); //高八位左移8位或上去LCD_ShowNum(1, 1, Num, 5);LCD_ShowString(2, 1, Read OK);Delay(1000);LCD_ShowString(2, 1, );}}
}接下来利用定时器扫描按键和数码管制作一个秒表 由于按键和数码管都需要中断函数所以就为Key和Nixie都定义一个中断的时候需要执行的操作然后在中断函数中调用它们即可。。
先来看重新编写的Key模块只要在中断函数中每隔20ms执行一次Key_Loop()就能保证读到每一次按键松开时的键码值这样也达到了定时器扫描按键的目的
#include REGX52.H
#include Delay.hunsigned char Key_KeyNumber;/*** brief 获取独立按键键码* param 无* retval 按下的按键的键码范围0~4无按键按下时返回0*/
unsigned char Key(void)
{unsigned char Temp;Temp Key_KeyNumber;Key_KeyNumber 0;return Temp;
}/*** brief 获取按键实时状态0为松开* param 无* retval 无*/
unsigned char Key_GetState()
{unsigned char KeyNumber 0;if(P3_1 0){KeyNumber 1;}if(P3_0 0){KeyNumber 2;}if(P3_2 0){KeyNumber 3;}if(P3_3 0){KeyNumber 4;}return KeyNumber;
}void Key_Loop(void) //捕捉按键松开瞬间并得到键码
{static unsigned char NowState, LastState;LastState NowState;NowState Key_GetState();if(LastState 1 NowState 0) //此时按键从1松开{Key_KeyNumber 1;}if(LastState 2 NowState 0) //此时按键从2松开{Key_KeyNumber 2;}if(LastState 3 NowState 0) //此时按键从3松开{Key_KeyNumber 3;}if(LastState 4 NowState 0) //此时按键从4松开{Key_KeyNumber 4;}
}接下来要改写Nixie使定时器扫描数码管每隔2ms调用一次Nixie_Loop();
#include REGX52.H
#include Delay.hunsigned char NixieTable[] {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00};unsigned char Nixie_Buf[9] {0,10,10,10,10,10,10,10,10};void Nixie_SetBuf(unsigned char Location, Number)
{Nixie_Buf[Location] Number;
}/*** brief 在对应位置显示对应数字* param 位置数字* retval 无*/
void Nixie_Scan(unsigned char Location, Number)
{P0 0x00; //清零消影switch(Location){case 1: P2_4 1; P2_3 1; P2_2 1; break;case 2: P2_4 1; P2_3 1; P2_2 0; break;case 3: P2_4 1; P2_3 0; P2_2 1; break;case 4: P2_4 1; P2_3 0; P2_2 0; break;case 5: P2_4 0; P2_3 1; P2_2 1; break;case 6: P2_4 0; P2_3 1; P2_2 0; break;case 7: P2_4 0; P2_3 0; P2_2 1; break;case 8: P2_4 0; P2_3 0; P2_2 0; break;}P0 NixieTable[Number];
}void Nixie_Loop(void) //每调用一次就向后显示一位
{static unsigned char i 1;Nixie_Scan(i, Nixie_Buf[i]);i ;if(i 9) i 1;
}因此要同时维护这两个中断中断函数需要这样写
void Timer0_Routine() interrupt 1
{static unsigned int T0Count1, T0Count2;TL0 0x66; //设置定时初值TH0 0xFC; //设置定时初值T0Count1 ;if(T0Count1 20) //20ms扫描一次{T0Count1 0;Key_Loop();}T0Count2 ; if(T0Count2 2) //2ms扫描一次{T0Count2 0;Nixie_Loop();}
}有了以上这些模块就可以开始编写秒表了
#include REGX52.H
#include Timer0.h
#include Key.h
#include Nixie.h
#include Delay.hunsigned char KeyNum;
unsigned char Min, Sec, MiniSec;
unsigned char RunFlag;void main()
{Timer0_Init();while(1){KeyNum Key();if(KeyNum 1) //启动/暂停{RunFlag !RunFlag;}if(KeyNum 2) //清零复位{Min 0;Sec 0;MiniSec 0;}Nixie_SetBuf(1, Min / 10);Nixie_SetBuf(2, Min % 10);Nixie_SetBuf(3, 11);Nixie_SetBuf(4, Sec / 10);Nixie_SetBuf(5, Sec % 10);Nixie_SetBuf(6, 11);Nixie_SetBuf(7, MiniSec / 10);Nixie_SetBuf(8, MiniSec % 10);}
}void Sec_Loop(void) //每过一个MiniSec执行一次
{if(RunFlag 0) return;//暂停中不执行MiniSec ;if(MiniSec 100){MiniSec 0;Sec ;if(Sec 60){Sec 0;Min ;if(Min 60){Min 0;}}}
}void Timer0_Routine() interrupt 1
{static unsigned int T0Count1, T0Count2, T0Count3;TL0 0x66; //设置定时初值TH0 0xFC; //设置定时初值T0Count1 ;if(T0Count1 20) //20ms扫描一次{T0Count1 0;Key_Loop();}T0Count2 ; if(T0Count2 2) //2ms扫描一次{T0Count2 0;Nixie_Loop();}T0Count3 ;if(T0Count3 10) //一个MiniSec扫描一次{T0Count3 0;Sec_Loop();}
}如果要和之前的AT24C02结合起来那就KeyNum为3的时候写入秒表的时间KeyNum为4的时候读出时间利用WriteByte和ReadByte即可