目录
蜂鸣器
蜂鸣器播放按键提示音
蜂鸣器播放音乐
AT24C02(IIC)总线
AT24C02数据存储
AT24C02秒表(定时器扫描按键)
DS18B20温度传感器(单总线)
温度显示
温度报警器
蜂鸣器
蜂鸣器播放按键提示音
Buzzer.c
#include <REGX52.H>
#include "INTRINS.h"
#include "Delay.h"
//蜂鸣器端口
sbit Buzzer = P2^5;/*** @brief 蜂鸣器私有延迟函数,延时500us* @param 无* @retval 无*/void Buzzer_Delay500us() //@11.0592MHz
{unsigned char i;_nop_();i = 227;while (--i);
}/*** @brief 蜂鸣器发生持续时间* @param ms 发声时长* @retval 无*/
void Buzzer_Time(unsigned int ms)
{unsigned int i;for(i = 0;i < ms *2;i++)//这里个人理解是持续时间(循环次数),如果要持续ms这么久//下面做一次就过去500us(0.5ms),要过去1000ms,则需要2000次,所以要*2{Buzzer = !Buzzer;Buzzer_Delay500us();//发出1000Hz频率声音//因为周期为1000微秒,即T = 0.001s,频率f = 1/T ,f = 1kHz}
}
main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Buzzer.h"unsigned char KeyNum;void main(){Nixie(1,0);while( 1 ){KeyNum = Key();if(KeyNum){Buzzer_Time(1000);//响的时间Nixie(1,KeyNum);}}}
蜂鸣器播放音乐
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"sbit Buzzer = P2^5;//播放速度,值为四分音符的时长(ms)
#define SPEED 500//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P 0
#define L1 1
#define L1_ 2
#define L2 3
#define L2_ 4
#define L3 5
#define L4 6
#define L4_ 7
#define L5 8
#define L5_ 9
#define L6 10
#define L6_ 11
#define L7 12
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36//索引与频率对照表
unsigned int FreqTable[]={0,63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};//曲谱以及持续时长
unsigned char code Music[]={P,4,P,4,P,4,M6,2,M7,2,H1,4+2,L7,2,H1,4,H3,4,M7,4+4+4,M3,2,M3,2,M6,4+2,};unsigned char FreqSelect,MusicSelect;void main(){Timer0Init();while( 1 ){if(Music[MusicSelect] != 0xFF){FreqSelect=Music[MusicSelect];MusicSelect++;Delay(SPEED/4*Music[MusicSelect]);MusicSelect++; TR0 = 0;//关闭定时器Delay(5);TR0 = 1;//重启定时器}else{TR0 = 0;while(1);}}}void Timer0_Routine() interrupt 1{if(FreqTable[FreqSelect] != 0)
{TL0 = FreqTable[FreqSelect]%256;TH0 = FreqTable[FreqSelect]/256;Buzzer = !Buzzer;
}}
AT24C02(IIC)总线
AT24C02数据存储
I2C.c(结合时序结构图进行理解)
#include <REGX52.H>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;//SCL处于高电平期间拉低进行操作I2C_SCL = 0;//准备发送字节
}/*** @brief I2C停止* @param 无* @retval 无*/void I2C_Stop(void)//终止条件{I2C_SDA = 0;//可能为1/0,保证为0进行操作I2C_SCL = 1;//高电平期间I2C_SDA = 1;//由低到高}/*** @brief I2C发送一个字节* @param Byte 要发送的字节* @retval 无*/void I2C_SendByte(unsigned char Byte)//发送一个字节{unsigned char i;for( i = 0; i<8; i++){I2C_SDA = Byte & (0x80 >> i);//取出各位数据I2C_SCL = 1;//准备放入数据,拉高SCLI2C_SCL = 0;//马上拉低也能读到数据,准备放入下一位数据}}/*** @brief I2C接收一个字节* @param 无* @retval Byte 接收到的一个字节数据*/unsigned char I2C_ReceiveByte(void){unsigned char i,Byte;I2C_SDA = 1;//释放总线for( i=0;i<8;i++){I2C_SCL = 1;//进行拉高读取if(I2C_SDA){Byte |= (0x80 >> i);//对八位进行相或运算Byte读出是几就是几}I2C_SCL = 0;//一位读取完毕,重置SCL,}return Byte;}/*** @brief I2C发送应答* @param AckBit 应答位,0为应答,1为非应答* @retval 无*/void I2C_SendAck(unsigned int AckBit){I2C_SDA = AckBit;I2C_SCL = 1;//拉高进行发送I2C_SCL = 0;//发送完成,恢复}/*** @brief I2C接收应答位* @param 无* @retval AckBit 0为应答,1为非应答*/unsigned char I2C_ReceiveAck(void){unsigned char AckBit = 0;I2C_SDA = 1;//释放总线I2C_SCL = 1;//拉高进行接收AckBit = I2C_SDA;//将发送过来的数据存储I2C_SCL = 0;//结束接收return AckBit;}
AT24C02.c(结合数据帧最后一张ppt进行理解)
#include <REGX52.H>
#include "I2C.H"#define AT24C02_ADDRESS 0xA0/*** @brief AT24C02写入一个字节* @param WorkAddress 要写入字节的地址* @param Data 要写入的数据* @retval 无*/
void AT24C02_WriteByte(unsigned char WorkAddress,Data)//字节写{unsigned char Ack;I2C_Start();//开始I2C_SendByte(AT24C02_ADDRESS);//SLAVEADRESS + w,从机地址I2C_ReceiveAck();//从机RAI2C_SendByte(WorkAddress);//写入字地址I2C_ReceiveAck();//从机RAI2C_SendByte(Data);//写入数据I2C_ReceiveAck();//从机RAI2C_Stop();//停止}/*** @brief AT24C02读取一个字节* @param WorkAddress 要读取字节的地址* @retval Data 读出的数据*/
unsigned char AT24C02_ReadByte(unsigned char WorkAddress)//随机读{unsigned char Data;I2C_Start();//开始I2C_SendByte(AT24C02_ADDRESS);//SlaveAdress+WI2C_ReceiveAck();//RAI2C_SendByte(WorkAddress);//输入字地址I2C_ReceiveAck();//RAI2C_Start();//开始I2C_SendByte(AT24C02_ADDRESS | 0x01);//SlaveAdress+RI2C_ReceiveAck();//RAData = I2C_ReceiveByte();//接收一个字节数据I2C_SendAck(1);//SA,结束接收I2C_Stop();//结束return Data;}
main.c
#include <REGX52.H>
#include "Delay.h"
#include "AT24C02.h"
#include "Key.h"
#include "LCD1602.h"unsigned char KeyNum;
unsigned int Num;void main(){LCD_Init();LCD_ShowNum(1,1,Num,5);while( 1 ){KeyNum = Key();if(KeyNum == 1)//按下显示+1{Num++;LCD_ShowNum(1,1,Num,5);}if(KeyNum == 2)//按下显示-1{Num--;LCD_ShowNum(1,1,Num,5);}if(KeyNum == 3)//存储当前显示数据{AT24C02_WriteByte(0,Num%256);//存储低位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);取出高位并将其合并LCD_ShowNum(1,1,Num,5);LCD_ShowString(2,1,"Read ok");Delay(1000);LCD_ShowString(2,1," ");}}}
AT24C02秒表(定时器扫描按键)
main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Timer0.h"
#include "I2C.h"
#include "AT24C02.h"unsigned 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;}if(KeyNum == 3){//写入AT24C02_WriteByte(0,Min);Delay(5);//写周期AT24C02_WriteByte(1,Sec);Delay(5);AT24C02_WriteByte(2,MiNiSec);Delay(5);}if(KeyNum == 4){//读出Min = AT24C02_ReadByte(0);Sec = AT24C02_ReadByte(1);MiNiSec = AT24C02_ReadByte(2);}NiXie_SetBuf(1,Min / 10);//分高位NiXie_SetBuf(2,Min % 10);//分低位NiXie_SetBuf(3,10);//显示 -NiXie_SetBuf(4,Sec / 10);//秒高位NiXie_SetBuf(5,Sec % 10);//秒低位NiXie_SetBuf(6,10);//显示 -NiXie_SetBuf(7,MiNiSec / 10);NiXie_SetBuf(8,MiNiSec % 10);}}void Sec_Loop(void)//越界判断及处理
{if(RunFlag)//判断运行状态{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;T0Count1 ++;TL0 = 0x18; //设置定时初值TH0 = 0xFC; //设置定时初值(1ms)if( T0Count1 >= 20 ){//每隔20ms,扫描按键T0Count1 = 0;Key_Loop();}T0Count2 ++;if( T0Count2 >= 2 ){//每隔2ms,调用显示T0Count2 = 0;NiXie_Loop();}T0Count3 ++;if( T0Count3 >= 10 ){//每隔10ms,秒表进行运行自增T0Count3 = 0;Sec_Loop();}
}
Key.c
#include <REGX52.H>
#include "Delay.h"unsigned char Key_KeyNum;/*** @brief 获取独立按键键码* @param 无* @retval 按下按键的键码,范围 0~4,无按键按下时,返回值为0*/
unsigned char Key(void)
{unsigned char Temp = 0;Temp = Key_KeyNum;Key_KeyNum = 0;return Temp;
}/*** @brief 判断按下哪个按键* @param 无* @retval 无*/
unsigned char Key_GetState(viod){unsigned char KeyNum = 0;if(P3_1 == 0){ KeyNum = 1;}if(P3_0 == 0){ KeyNum = 2;}if(P3_2 == 0){ KeyNum = 3;}if(P3_3 == 0){ KeyNum = 4;}return KeyNum;}void Key_Loop(void)
{static unsigned char NowState , LastState;LastState = NowState;NowState = Key_GetState();if(LastState == 1 && NowState == 0)//按下按键1后松手{Key_KeyNum = 1;}if(LastState == 2 && NowState == 0)//按下按键2后松手{Key_KeyNum = 2;}if(LastState == 3 && NowState == 0)//按下按键3后松手{Key_KeyNum = 3;}if(LastState == 4 && NowState == 0)//按下按键4后松手{Key_KeyNum = 4;}
}
NiXie.c
#include <REGX52.H>
#include "Delay.h"unsigned char NiXie_Buf[9] = {0,10,10,10,10,10,10,10,10};
//初始化显示
unsigned char NixieTable [] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x40};
//数字显示,以及 ‘-’ 显示void NiXie_SetBuf(unsigned char Location,Number)
{NiXie_Buf[Location] = Number;//设置显示数字
}
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)//这个函数中不能出现delay函数
{static unsigned char i = 1;NiXie_Scan(i,NiXie_Buf[i]);//不断扫描八个显示管i++;if(i>=9){i=1;}}
DS18B20温度传感器(单总线)
温度显示
OneWire.c
#include <REGX52.H>sbit OneWire_DQ = P3^7;/*** @brief 初始化单总线* @param 无* @retval 无*/
unsigned char OneWire_Init(void)
{unsigned char AckBit,i;//应答位OneWire_DQ = 1;//初始化为高再拉低OneWire_DQ = 0;//拉低进入初始化状态i = 227;while (--i);//延迟500usOneWire_DQ = 1;//释放总线i = 29;while (--i);//延迟70usAckBit = OneWire_DQ;i = 227;while (--i);//延迟500us(超过480us即可)return AckBit;
}/*** @brief 单总线传输一位数据* @param Bit 被传输的一位数据* @retval 无*/
void OneWire_SendBit( unsigned char Bit)
{unsigned char i;OneWire_DQ = 0;//直接拉低,初始化后是高i = 4;while (--i);//延时10usOneWire_DQ = Bit;//读取典型值i = 22;while (--i);//延时50usOneWire_DQ = 1;//完成后拉高
}/*** @brief 接收一位数据* @param 无* @retval Bit 接收到的一位数据*/
unsigned char OneWire_ReceiveBit(void)
{unsigned char Bit,i;OneWire_DQ = 0;i = 2;while (--i);//延时5usOneWire_DQ = 1;i = 2;while (--i);//延时5usBit = OneWire_DQ;i = 22;while (--i);//延时50us,完成后总线应为1return Bit;
}/*** @brief 传输一个字节* @param Byte 被传输的字节* @retval 无*/
void OneWire_SendByte(unsigned char Byte)
{unsigned char i;for( i=0;i<8;i++){OneWire_SendBit(Byte & (0x01 << i));//从最低位开始传输}
}/*** @brief 接收一个字节* @param 无* @retval Byte 接收到的字节*/
unsigned char OneWire_ReceiveByte(void)
{unsigned char i;unsigned char Byte = 0x00;for( i=0;i<8;i++){if(OneWire_ReceiveBit()){Byte |= (0x01<<i);} //从最低位开始接收,有1则入}return Byte;
}
DS18B20.c
#include <REGX52.H>
#include "OneWire.h"#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE/*** @brief 转变温度* @param 无* @retval 无*/
void DS18B20_ConvertT(void)//温度变换
{OneWire_Init();//初始化OneWire_SendByte(DS18B20_SKIP_ROM);//跳过ROM指令OneWire_SendByte(DS18B20_CONVERT_T);//开始温度变换
}/*** @brief 读取温度* @param 无* @retval T 浮点型温度数据*/
float DS18B20_ReadT(void)//温度读取,负数的温度是以补码形式存储的
{unsigned char TLSB,TMSB;int Temp;float T;OneWire_Init();//初始化OneWire_SendByte(DS18B20_SKIP_ROM);//跳过ROM指令OneWire_SendByte(DS18B20_READ_SCRATCHPAD);//读暂存器TLSB = OneWire_ReceiveByte();//低位TMSB = OneWire_ReceiveByte();//高位Temp = (TMSB << 8) | TLSB; //合并高低位并强转为有符号类型,有移位相当于扩大16倍T = Temp / 16.0;//还原并防止精度损失return T;
}
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "DS18B20.h"float T;void main(){LCD_Init();LCD_ShowString(1,1,"Temperature:");while( 1 ){DS18B20_ConvertT();//转变温度T = DS18B20_ReadT();//读取温度if(T < 0){LCD_ShowChar(2,1,'-');T = -T;//转为正值,进行显示}else{LCD_ShowChar(2,1,'+');}LCD_ShowNum(2,2,T,3);//整数部分LCD_ShowChar(2,5,'.');LCD_ShowNum(2,6,(unsigned long)(T*10000) % 10000,4);//小数部分}}
温度报警器
OneWire.c
#include <REGX52.H>sbit OneWire_DQ = P3^7;/*** @brief 初始化单总线* @param 无* @retval 无*/
unsigned char OneWire_Init(void)
{unsigned char AckBit,i;//应答位EA = 0;//防止延时被打断,屏蔽中断OneWire_DQ = 1;//初始化为高再拉低OneWire_DQ = 0;//拉低进入初始化状态i = 227;while (--i);//延迟500usOneWire_DQ = 1;//释放总线i = 29;while (--i);//延迟70usAckBit = OneWire_DQ;i = 227;while (--i);//延迟500us(超过480us即可)EA = 1;//执行完成后,将计时器还原return AckBit;
}/*** @brief 单总线传输一位数据* @param Bit 被传输的一位数据* @retval 无*/
void OneWire_SendBit( unsigned char Bit)
{unsigned char i;EA = 0;//防止延时被打断,屏蔽中断OneWire_DQ = 0;//直接拉低,初始化后是高i = 4;while (--i);//延时10usOneWire_DQ = Bit;//读取典型值i = 22;while (--i);//延时50usOneWire_DQ = 1;//完成后拉高EA = 1;//执行完成后,将计时器还原
}/*** @brief 接收一位数据* @param 无* @retval Bit 接收到的一位数据*/
unsigned char OneWire_ReceiveBit(void)
{unsigned char Bit,i;EA = 0;//防止延时被打断,屏蔽中断OneWire_DQ = 0;i = 2;while (--i);//延时5usOneWire_DQ = 1;i = 2;while (--i);//延时5usBit = OneWire_DQ;i = 22;while (--i);//延时50us,完成后总线应为1EA = 1;//执行完成后,将计时器还原return Bit;
}/*** @brief 传输一个字节* @param Byte 被传输的字节* @retval 无*/
void OneWire_SendByte(unsigned char Byte)
{unsigned char i;for( i=0;i<8;i++){OneWire_SendBit(Byte & (0x01 << i));//从最低位开始传输}
}/*** @brief 接收一个字节* @param 无* @retval Byte 接收到的字节*/
unsigned char OneWire_ReceiveByte(void)
{unsigned char i;unsigned char Byte = 0x00;for( i=0;i<8;i++){if(OneWire_ReceiveBit()){Byte |= (0x01<<i);} //从最低位开始接收,有1则入}return Byte;
}
main.c
#include <REGX52.H>
#include "DS18B20.h"
#include "LCD1602.h"
#include "AT24C02.h"
#include "Delay.h"
#include "Key.h"
#include "Timer0.h"float T,TShow;//温度与显示温度
char TLow,THigh;//低阈值,高阈值
unsigned char KeyNum;//获取到的键码void main(){DS18B20_ConvertT();Delay(1000);THigh = AT24C02_ReadByte(0);//读取内部存储的高阈值TLow = AT24C02_ReadByte(1);//读取内部存储的低阈值if(THigh > 125 || TLow < -55 || THigh <= TLow){THigh = 20;TLow = 15;}LCD_Init();LCD_ShowString(1,1,"T:");LCD_ShowString(2,1,"TH:");LCD_ShowString(2,9,"TL:");LCD_ShowSignedNum(2,4,THigh,3);LCD_ShowSignedNum(2,12,TLow,3);Timer0_Init();while( 1 ){KeyNum = Key();/*温度读取及显示*/DS18B20_ConvertT();T = DS18B20_ReadT();if(T < 0)//零下{LCD_ShowChar(1,3,'-');TShow = -T;//转为正值}else{LCD_ShowChar(1,3,'+');TShow = T;}LCD_ShowNum(1,4,TShow,3);//整数部分LCD_ShowChar(1,7,'.');LCD_ShowNum(1,8,(unsigned long)(T*100)%100,2);//小数部分/*阈值判断及显示*/if(KeyNum){if(KeyNum == 1)//增加高阈值{THigh++;if(THigh > 125){THigh = 125;}}if(KeyNum == 2)//降低高阈值{THigh--;if(THigh <= TLow){THigh ++;}}if(KeyNum == 3)//增加低阈值{TLow++;if(TLow >= THigh){TLow -- ;}}if(KeyNum == 4)//降低低阈值{TLow--;if(TLow < -55){TLow = -55;}}LCD_ShowSignedNum(2,4,THigh,3);LCD_ShowSignedNum(2,12,TLow,3);AT24C02_WriteByte(0,THigh);//写入高阈值Delay(5);//写周期AT24C02_WriteByte(1,TLow);//写入低阈值Delay(5);//写周期}if(T > THigh){LCD_ShowString(1,13,"OV:H");}else if(T < TLow){LCD_ShowString(1,13,"OV:L");}else{LCD_ShowString(1,13,"OV: ");}}
}//中断函数
void Timer0_Routine() interrupt 1{static unsigned int T0Count;T0Count ++;TL0 = 0x18; //设置定时初值TH0 = 0xFC; //设置定时初值if( T0Count >= 20 ){T0Count = 0;Key_Loop();}}