目录
十三、AT24C02(I2C总线)
十四、DS18B20温度传感器(单总线)
十五、LCD1602液晶显示屏
十六、直流电机(PWM)
十七、AD/DA
十八、红外遥控(外部中断)
#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;I2C_SCL=0;
}
/*** @brief I2C停止* @param 无* @retval 无*/
void I2C_Stop(void){I2C_SDA=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; // 读取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 AckBit 应答位,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;
}
#ifndef __I2C_H__
#define __I2C_H__
void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char Byte);
unsigned char I2C_ReceiveByte(void);
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveAck(void);
#endif
#include <REGX52.H>
#include "I2C.h"
#define AT24C02_ADDRESS 0xA0
/*** @brief AT24C02写入一个字节* @param WordAddress 要写入字节的地址* @param Data 要写入的数据 * @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 WordAddress 要读出字节的地址* @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;
}
#ifndef __AT24C02_H__
#define __AT24C02_H__
void AT24C02_WriteByte(unsigned char WordAddress,Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
#endif
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "LCD1602.h"
#include "AT24C02.h"
unsigned char KeyNum;
unsigned int Num; // 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); // 低八位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," ");}}
}
#include <REGX52.H>
/*** @brief 使用定时器扫描按键,获取独立按键键码* @param 无* @retval 按下按键的键码,范围:0~4,无按键按下时,返回0*/
unsigned char Key_KeyNumber;
unsigned char Key(void){unsigned char Temp=0;Temp=Key_KeyNumber;Key_KeyNumber=0;return Temp;
}
unsigned char Key_GetState(){ // 定时器每隔20秒扫描一次,所以不用消抖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;}
}
#ifndef __KEY_H__
#define __KEY_H__
unsigned char Key(void);
void Key_Loop(void);
#endif
#include <REGX52.H>
/*** @brief 使用定时器扫描数码管,数码管显示函数* @param Location 显示位置 范围:1~6* @param Number 显示数值 范围:0~16* @retval 无*/
//段选
unsigned char NixieTable[]={ // 只显示0~9和黑屏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;
}
void Nixie_Scan(unsigned char Location, int 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){ // 供定时器调用,不能有Delaystatic unsigned char i=1;Nixie_Scan(i,Nixie_Buf[i]);i++;if(i>=9) i=1;
}
#ifndef __NIXIE_H__
#define __NIXIE_H__
void Nixie_SetBuf(unsigned char Location,Number);
void Nixie_Scan(unsigned char Location, int Number);
void Nixie_Loop(void);
#endif
#include <REGX52.H>
#include "AT24C02.h"
#include "Key_Timer.h"
#include "Nixie_Timer.h"
#include "Timer0.h"
#include "Delay.h"
unsigned char KeyNum;
unsigned char Min,Sec,MinSec;
unsigned char RunFlag;
void main(){Timer0Init();while(1){KeyNum=Key();if(KeyNum==1){RunFlag=!RunFlag;}if(KeyNum==2){Min=0,Sec=0,MinSec=0;}if(KeyNum==3){ // 写入存储器AT24C02_WriteByte(0,Min);Delay(5); // 写周期AT24C02_WriteByte(1,Sec);Delay(5);AT24C02_WriteByte(2,MinSec);Delay(5);}if(KeyNum==4){ // 从存储器读出Min=AT24C02_ReadByte(0);Sec=AT24C02_ReadByte(1);MinSec=AT24C02_ReadByte(2);}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,MinSec/10); // 100为1秒Nixie_SetBuf(8,MinSec%10);}
}
void Sec_Loop(void){if(RunFlag){MinSec++; // 到100加1秒if(MinSec>=100){MinSec=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 = 0x18; //设置定时初值TH0 = 0xFC; //设置定时初值T0Count1++;if(T0Count1>=20){ // 20msT0Count1=0;Key_Loop(); // 独立键盘}T0Count2++;if(T0Count2>=2){ // 2msT0Count2=0;Nixie_Loop(); // 数码管}T0Count3++;if(T0Count3>=10){ // 10msT0Count3=0;Sec_Loop(); // 秒表计时}
}
#include <REGX52.H>
sbit OneWire_DQ=P3^7;
/*** @brief 总线初始化* @param 无* @retval AckBit 从机应答标志位*/
unsigned char OneWire_Init(void){unsigned char i;unsigned char AckBit;OneWire_DQ=1;OneWire_DQ=0;i=247; while(--i); // 延时500us,根据STC-ISP生成的OneWire_DQ=1; // 释放总线i=32; while(--i); // 延时70us,根据STC-ISP生成的AckBit=OneWire_DQ; // 从机响应主机i=247; while(--i); // 延时500us,根据STC-ISP生成的return AckBit;
}
/*** @brief 发送一位* @param Bit 要发送的一位数据* @retval 无*/
void OneWire_SendBit(unsigned char Bit){ // 根据发送1来设定,这样就不用区分0和1unsigned char i;OneWire_DQ=0;i=4; while(--i); // 延时10us,根据STC-ISP生成的OneWire_DQ=Bit;i=24; while(--i); // 延时50us,根据STC-ISP生成的OneWire_DQ=1;
}
/*** @brief 发送一位* @param 无* @retval Bit 接收到的一位数据*/
unsigned char OneWire_ReceiveBit(void){ // 根据接收1来设置unsigned char i;unsigned char Bit;OneWire_DQ=0;i=2; while(--i); // 延时5us,根据STC-ISP生成的OneWire_DQ=1;i=2; while(--i); // 延时5us,根据STC-ISP生成的Bit=OneWire_DQ;i=24; while(--i); // 延时50us,根据STC-ISP生成的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 Byte=0x00;unsigned char i;for(i=0;i<8;i++){if(OneWire_ReceiveBit()) Byte|=(0x01<<i); // 从低位开始}return Byte;
}
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
unsigned char OneWire_Init(void);
void OneWire_SendBit(unsigned char Bit);
unsigned char OneWire_ReceiveBit(void);
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte(void);
#endif
#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); // 跳过ROMOneWire_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); // 跳过ROMOneWire_SendByte(DS18B20_READ_SCRATCHPAD); // 读暂存器TLSB=OneWire_ReceiveByte();TMSB=OneWire_ReceiveByte();Temp=(TMSB<<8)|TLSB;T=Temp/16.0; // 左移四位,无符号转有符号return T;
}
#ifndef __DS18B20_H__
#define __DS18B20_H__
void DS18B20_ConvertT(void);
float DS18B20_ReadT(void);
#endif
#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
float T;
void main(){LCD_Init();LCD_ShowString(1,1,"Temperature:");while(1){DS18B20_ConvertT(); // 温度转换Delay(1000); // 读出的不是默认值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); // 取小数部分}
}
#include <REGX52.H>
sbit OneWire_DQ=P3^7;
/*** @brief 总线初始化* @param 无* @retval AckBit 从机应答标志位*/
unsigned char OneWire_Init(void){unsigned char i;unsigned char AckBit;EA=0; // 屏蔽中断OneWire_DQ=1;OneWire_DQ=0;i=247; while(--i); // 延时500us,根据STC-ISP生成的OneWire_DQ=1; // 释放总线i=32; while(--i); // 延时70us,根据STC-ISP生成的AckBit=OneWire_DQ; // 从机响应主机i=247; while(--i); // 延时500us,根据STC-ISP生成的EA=1; // 打开中断return AckBit;
}
/*** @brief 发送一位* @param Bit 要发送的一位数据* @retval 无*/
void OneWire_SendBit(unsigned char Bit){ // 根据发送1来设定,这样就不用区分0和1unsigned char i;EA=0; // 屏蔽中断OneWire_DQ=0;i=4; while(--i); // 延时10us,根据STC-ISP生成的OneWire_DQ=Bit;i=24; while(--i); // 延时50us,根据STC-ISP生成的OneWire_DQ=1;EA=1; // 打开中断
}
/*** @brief 发送一位* @param 无* @retval Bit 接收到的一位数据*/
unsigned char OneWire_ReceiveBit(void){ // 根据接收1来设置unsigned char i;unsigned char Bit;EA=0; // 屏蔽中断OneWire_DQ=0;i=2; while(--i); // 延时5us,根据STC-ISP生成的OneWire_DQ=1;i=2; while(--i); // 延时5us,根据STC-ISP生成的Bit=OneWire_DQ;i=24; while(--i); // 延时50us,根据STC-ISP生成的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 Byte=0x00;unsigned char i;for(i=0;i<8;i++){if(OneWire_ReceiveBit()) Byte|=(0x01<<i); // 从低位开始}return Byte;
}
#include <REGX52.H>
#include "DS18B20.h"
#include "Delay.h"
#include "LCD1602.h"
#include "AT24C02.h"
#include "Key_Timer.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);Timer0Init();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)(TShow*100)%100,2);// 阈值判断及显示if(KeyNum){if(KeyNum==1){ //K1按键,THigh自增THigh++;if(THigh>125){THigh=125;}}if(KeyNum==2){ //K2按键,THigh自减THigh--;if(THigh<=TLow){THigh++;}}if(KeyNum==3){ //K3按键,TLow自增TLow++;if(TLow>=THigh){TLow--;}}if(KeyNum==4){ //K4按键,TLow自减TLow--;if(TLow<-55){TLow=-55;}}LCD_ShowSignedNum(2,4,THigh,3);LCD_ShowSignedNum(2,12,TLow,3);// 存储阈值AT24C02_WriteByte(0,THigh); //写入到At24C02中保存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," ");}}
}
void Timer0_Routine() interrupt 1{static unsigned int T0Count;TL0 = 0x18; //设置定时初值TH0 = 0xFC; //设置定时初值T0Count++;if(T0Count>=20){T0Count=0;Key_Loop(); //每20ms调用一次按键驱动函数}
}
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/*** @brief LCD1602延时函数,12MHz调用可延时1ms* @param 无* @retval 无*/
void LCD_Delay()
{unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i);
}
/*** @brief LCD1602写命令* @param Command 要写入的命令* @retval 无*/
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}
/*** @brief LCD1602写数据* @param Data 要写入的数据* @retval 无*/
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}
/*** @brief LCD1602设置光标位置* @param Line 行位置,范围:1~2* @param Column 列位置,范围:1~16* @retval 无*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}
}
/*** @brief LCD1602初始化函数* @param 无* @retval 无*/
void LCD_Init()
{LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动LCD_WriteCommand(0x01);//光标复位,清屏
}/*** @brief 在LCD1602指定位置上显示一个字符* @param Line 行位置,范围:1~2* @param Column 列位置,范围:1~16* @param Char 要显示的字符* @retval 无*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}
/*** @brief 在LCD1602指定位置开始显示所给字符串* @param Line 起始行位置,范围:1~2* @param Column 起始列位置,范围:1~16* @param String 要显示的字符串* @retval 无*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='