目录
蜂鸣器
蜂鸣器播放按键提示音
蜂鸣器播放音乐
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;//准备放入数据,拉高SCL
I2C_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();//从机RA
I2C_SendByte(WorkAddress);//写入字地址
I2C_ReceiveAck();//从机RA
I2C_SendByte(Data);//写入数据
I2C_ReceiveAck();//从机RA
I2C_Stop();//停止
}
/**
* @brief AT24C02读取一个字节
* @param WorkAddress 要读取字节的地址
* @retval Data 读出的数据
*/
unsigned char AT24C02_ReadByte(unsigned char WorkAddress)//随机读
{
unsigned char Data;
I2C_Start();//开始
I2C_SendByte(AT24C02_ADDRESS);//SlaveAdress+W
I2C_ReceiveAck();//RA
I2C_SendByte(WorkAddress);//输入字地址
I2C_ReceiveAck();//RA
I2C_Start();//开始
I2C_SendByte(AT24C02_ADDRESS | 0x01);//SlaveAdress+R
I2C_ReceiveAck();//RA
Data = 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);//延迟500us
OneWire_DQ = 1;//释放总线
i = 29;while (--i);//延迟70us
AckBit = 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);//延时10us
OneWire_DQ = Bit;//读取典型值
i = 22;while (--i);//延时50us
OneWire_DQ = 1;//完成后拉高
}
/**
* @brief 接收一位数据
* @param 无
* @retval Bit 接收到的一位数据
*/
unsigned char OneWire_ReceiveBit(void)
{
unsigned char Bit,i;
OneWire_DQ = 0;
i = 2;while (--i);//延时5us
OneWire_DQ = 1;
i = 2;while (--i);//延时5us
Bit = OneWire_DQ;
i = 22;while (--i);//延时50us,完成后总线应为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;
}
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);//延迟500us
OneWire_DQ = 1;//释放总线
i = 29;while (--i);//延迟70us
AckBit = 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);//延时10us
OneWire_DQ = Bit;//读取典型值
i = 22;while (--i);//延时50us
OneWire_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);//延时5us
OneWire_DQ = 1;
i = 2;while (--i);//延时5us
Bit = OneWire_DQ;
i = 22;while (--i);//延时50us,完成后总线应为1
EA = 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();
}
}