本片摘录一些通用特殊代码
LED灯
- 点亮一盏LED灯
延时函数:这里使用程序延时,后面有更精确的采用计时器中断来延时,但是在代码的某些时候还是使用程序延时
void delay(unsigned long int x)
{
while(x--);
}
- 流水灯设计
这里可以使用数组来储存每盏灯的状态,然后使用for循环显示。可自定义——因为数组里的每一个元素是对P1口及八个口对应的八盏灯的总状态的描述。
#include<reg51.h>
#define u8 unsigned char
#define u32 unsigned long int
u8 buf[]={0x81,0x42,0x24,0x18,0x99,0x5A,0x3C,0xBD,0x7E,0xFF,0x01,0x02,0x04,0x08,
0x10,0x20,0x40,0x80};
void delay(u32 x)
{
while(x--);
}
void main()
{
u8 i;
while(1)
{
for(i=0;i<sizeof(buf);i++)
{
P1=~buf[i];
delay(1000);
}
}
}
数码管
- 一位静态数码管
将数码管显示的数据编码出数码表
显示部分和流水灯类似
// 共阳极数码管的数码表
u8 code codebuf[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
- 两位静态数码管
主要是出现了将多位数分位数显示
while( 1 )
{
for( i=0;i<100;i++ )
{
P2 = codebuf[i/10]; // 数码管显示0~99的数字
P3 = codebuf[i%10]; // 数码管显示0~99的数字
Delay_us( 50000 ); // 50000 us的时间
}
}
- 两位动态数码管
主要是利用人眼的延时显示实现动态
u8 DisBuf[2] = { 1,7 }; // 数码管显示数字17
void main( void )
{
// 共阳极数码管的数码表
u8 code codebuf[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf,0xff};
u8 i = 0;
while( 1 )
{
for( i=0;i<2;i++ ) // 有2个数码管
{
P3 = 0xff; // 关闭显示,消除鬼影
P3 = (1<<i); // 输出片选
P2 = codebuf[DisBuf[i]]; // 根据缓冲区显示数据且数码管显示数字17
Delay_us( 200 ); // 200 us的时间
}
}
}
- 八位动态数码管
和两位的类似,就是改动了DisBuf[8]
要使数字变动起来,首先就是利用程序for循环变动DisBuf[8] 里面的值
按键
- 独立按键
按键扫描
void ScanKey(void)
{
if( 0 == Button1 ) return '+'; // 返回对数据执行的加数+
if( 0 == Button2 ) return '-'; // 返回对数据执行的减数-
return 0;
}
按键读取:其中包含按键判断,消除抖动,松手检测
u8 ReadKey(void)
{
u8 tmp;
tmp = ScanKey(); // 读取按键值
if( 0 == tmp ) return 0; // 判断是否有按键按下
if( tmp!=ScanKey() ) return 0; // 读回来的值是否与原来相同
while( 0 != ScanKey() ); // 读回来的按键为0,也就是说是松手检测
return tmp;
}
按键判断
使用switch语句
switch(ReadKey())
{
// 0~99范围步进为1 相加1
case '+': Num = (Num+1)%99; break;
// 0~99范围步进为1 相减1
case '-': Num == 0 ? Num = 99: Num --; break;
}
数据拆分
void SegDisp( u8 Num )
{
DisBuf[6] = Num/10; //
DisBuf[7] = Num%10; //
}
- 矩阵按键
按键扫描
sbit K4 = P1^4;
sbit K5 = P1^5;
sbit K6 = P1^6;
sbit K7 = P1^7;
u8 ScanKey( void )
{
P1 = ~(1<<0); // 扫描第一行按键
if( !K4 ) return '1'; // 返回值是字符1
if( !K5 ) return '2'; // 返回值是字符2
if( !K6 ) return '3'; // 返回值是字符3
if( !K7 ) return 'A'; // 返回值是字符A
P1 = ~(1<<1); // 扫描第二行按键
if( !K4 ) return '4'; // 返回值是字符4
if( !K5 ) return '5'; // 返回值是字符5
if( !K6 ) return '6'; // 返回值是字符6
if( !K7 ) return 'B'; // 返回值是字符B
P1 = ~(1<<2); // 扫描第三行按键
if( !K4 ) return '7'; // 返回值是字符7
if( !K5 ) return '8'; // 返回值是字符8
if( !K6 ) return '9'; // 返回值是字符9
if( !K7 ) return 'C'; // 返回值是字符C
P1 = ~(1<<3); // 扫描第四行按键
if( !K4 ) return '0'; // 返回值是字符0
if( !K5 ) return 'D'; // 返回值是字符D
if( !K6 ) return 'E'; // 返回值是字符E
if( !K7 ) return 'F'; // 返回值是字符F
return 0;
}
void SegDisp( u8 Num )
{
DisBuf[7] = Num;
}
while( 1 )
{
tmp = ReadKey(); // 读取按键值
if( tmp>='0'&&tmp<='9') // 按下按键 字符 1~9
{
SegDisp(tmp - '0' ); // 数码管显示 1~9
}
if( tmp>='A'&&tmp<='F') // 按下按键 字符 A~F
{
SegDisp(tmp - '7'); // 根据 ASSC 数码管显示 A~F
}
中断
- 外部中断
中断函数(写在主函数之外,中断发生就跳转到中断函数内执行中断函数内的程序,执行完又返回主函数)
反正就是配置寄存器
void Exit_Init(void)
{
IT0 = 1; // TCON.0 外部中断0:下降沿方法触发
EX0 = 1; // IE.0 外部中断0:中断允许
EA = 1; // IE.7 全局中断 :允许中断
}
定时器
- 定时器初始化
配置TMOD,TH0和TL0,定时器0装载初值
EA-中断总开关,ET0-中断溢出标志0,TR0-定时器0开关
// 定时器初始化
void time_init( void )
{
TMOD = 0x01; // 设置定时T0器工作方式1
TH0 = (65536-2000)/256; // 定时器T0高8位赋值
TL0 = (65536-2000)%256; // 定时器T0低8位赋值
EA = 1; // 开启T0总中断
ET0 = 1; // 开启计数/定时器T0中断
TR0 = 1; // 启动T0定时器
}
- 定时器中断函数
对TH0和TL0再次配置初值,然后在该函数内添加特定功能的程序
// 定时器中断函数
void time_0_handle( void ) interrupt 1
{
TH0 = (65536-2000)/256; // 定时器T0高8位赋值
TL0 = (65536-2000)%256; // 定时器T0低8位赋值
if( ++seconds <500 ) return; // 判断是否达到1秒
seconds = 0; // 达到1秒,重新累计
if( ++Sec<60 ) return;
Sec = 0;
if( ++Min<60 ) return;
Min=0;
if( ++Hour<24 ) return;
Hour = 0;
}
可调时钟
主要在于一个模式的调节
补充:这边有个三目运算符的使用
老师的代码很多地方会使用三目运算符
while( 1 )
{
switch( ReadKey() )
{
// 三种模式:时、分、秒
case 'M': Mode = (Mode+1)%4; break;
// 时钟调整 增操作
case '+':
if( 1 == Mode ) // 小时调整
{
Hour = (Hour+1)%24; // 一天 24 小时
}
else if ( 2== Mode )
{
Min = (Min+1)%60; // 一小时60 分钟
}
else if ( 3== Mode )
{
Sec = (Sec+1)%60; // 一分钟 60 秒
}
break;
// 时钟调整 减操作
case '-':
if( 1 == Mode ) // 小时调整
{
Hour == 0 ? Hour =23 : Hour--; // 一天 24 小时
}
else if ( 2== Mode )
{
Min == 0 ? Min =59 : Min--; // 一小时60 分钟
}
else if ( 3== Mode )
{
Sec == 0 ? Sec =59 : Sec--; // 一分钟 60 秒
}
break;
}
这边还有一段很特别的代码
// 定时器中断函数
void time_0_handle( void ) interrupt 1
{
TH0 = (65536-2000)/256; // 定时器T0高四位赋值
TL0 = (65536-2000)/256>>8; // 定时器T0低四位赋值
//有个说法说这边的return可以提高代码稳定性
if( ++seconds <500 ) return; // 判断是否达到1秒
seconds = 0; // 达到1秒,重新累计
if( ++Sec<60 ) return;
Sec = 0;
if( ++Min<60 ) return;
Min=0;
if( ++Hour<24 ) return;
Hour = 0;
}
1602驱动(比较复杂)
#include "reg52.h" // 52寄存器库
#include<stdio.h>
sbit RS= P2^0 ;
sbit RW= P2^1 ;
sbit EN= P2^2 ;
/****************************************************************************
* 名称:Delay()
* 功能:延时函数
****************************************************************************/
void Delay( unsigned int i )
{
while( i-- ) ;
}
/****************************************************************************
* 名称:WrOp()
* 功能:写命令函数
****************************************************************************/
void WrOp(unsigned char dat)
{
RS= 0; //全部清零
RW= 0;
P0= dat ;
EN= 1;
Delay(20);
EN= 0;
}
/****************************************************************************
* 名称:WrDat()
* 功能:写数据函数
****************************************************************************/
void WrDat(unsigned char dat)
{
RS= 1; //全部清零
RW= 0;
P0= dat ;
EN= 1;
Delay(20);
EN= 0;
}
/****************************************************************************
* 名称:lcd_init()
* 功能:lcd初始化函数
****************************************************************************/
void lcd_init(void)
{
//WrOp(0x01);
WrOp(0x38);
WrOp(0x06); //光标加1
WrOp(0x0F); //开显示
Delay(100);
}
/****************************************************************************
* 名称:DisText()
* 功能:显示文本函数
****************************************************************************/
void DisText(unsigned char addr,char *p)
{
WrOp(addr);
while( *p )WrDat(*(p++));
}
/****************************************************************************
* 名称:main()
* 功能:主程序
****************************************************************************/
void main( )
{
lcd_init( );
DisText(0x80,"welcome to fjnu");
DisText(0xc0+2,"14:15:20");
while( 1 );
}
第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。
- 1602可调时钟
#include "reg52.h" // 52寄存器库
#include<stdio.h>
bit disflash=0;
sbit RS= P2^0 ;
sbit RW= P2^1 ;
sbit EN= P2^2 ;
sbit key1= P1^0 ;
sbit key2= P1^1 ;
sbit key3= P1^2 ;
unsigned char mode=0;
unsigned int mSecond=0 ; // 系统计时的毫秒和秒变量
unsigned char Sec= 58,Min= 9,Hour= 16; // 时分秒
/****************************************************************************
* 名称:Delay()
* 功能:延时函数
****************************************************************************/
void Delay( unsigned int i )
{
while( i-- ) ;
}
/****************************************************************************
* 名称:WrOp()
* 功能:写命令函数
****************************************************************************/
void WrOp(unsigned char dat)
{
RS= 0; //全部清零
RW= 0;
P0= dat ;
EN= 1;
Delay(20);
EN= 0;
}
/****************************************************************************
* 名称:WrDat()
* 功能:写数据函数
****************************************************************************/
void WrDat(unsigned char dat)
{
RS= 1; //全部清零
RW= 0;
P0= dat ;
EN= 1;
Delay(20);
EN= 0;
}
/****************************************************************************
* 名称:lcd_init()
* 功能:lcd初始化函数
****************************************************************************/
void lcd_init(void)
{
WrOp(0x38);
WrOp(0x06); //光标加1
WrOp(0x0c); //开显示
Delay(100);
}
/****************************************************************************
* 名称:DisText()
* 功能:显示文本函数
****************************************************************************/
void DisText(unsigned char addr,char *p)
{
WrOp(addr);
while( *p )WrDat(*(p++));
}
/****************************************************
函数:定时器0初始配置
功能:1000uS中断一回,对毫秒变量和秒变量进行进位操作
*****************************************************/
void SysInit( void )
{
TMOD= 0x01 ; // 设置定时器0为方式1
TR0= 1 ; // 启动定时器0进行记数
ET0= 1 ; // 开启定时器0中断功能
EA= 1 ; // 开启总中断
}
/****************************************************
函数:定时器0中断程序
功能:10000uS中断一回,对毫秒变量和秒变量进行进位操作
*****************************************************/
void Time0Int( void ) interrupt 1
{
TL0= (65536-10000) ; // 10毫秒定时预置值的低8位给TL0
TH0= (65536-10000) >>8 ; // 10毫秒定时预置值的高8位给TH0
mSecond++ ; // 对毫秒进行加1操作
if( mSecond< 100 ) // 判断是否计满1秒
return ;
mSecond= 0;
if(mode==0)
{
Sec++ ;
}
if( Sec< 60 ) // 判断是否计满60秒
return ;
Sec= 0 ;
Min++ ;
if( Min< 60 ) // 判断是否计满60分
return ;
Min= 0 ;
Hour++ ;
if( Hour>= 24 )
Hour= 0;
}
/****************************************************
函数:扫描当前按键, 返回用户按键值
*****************************************************/
unsigned char ScanKey( void )
{
if( key1== 0 ) return '1'; // 按键1
if( key2== 0 ) return '2'; // 按键2
if( key3== 0 ) return '3'; // 按键3
return 0 ; // 说明没有按键
}
/****************************************************
函数:读一个按键值,如果没有按键返回为NULL
*****************************************************/
unsigned char ReadKey( void )
{
unsigned char temp ;
temp= ScanKey( ) ; // 读取一个键值
if( temp== 0 )
return 0 ; // 说明没有按键
Delay( 1000 ) ; // 等待10mS
if( temp!= ScanKey( ) ) // 如果10mS后的值和前面的值不相等退出
return 0 ;
while( ScanKey( )!= 0 ) ; // 等待用户放开按键
return temp; // 返回键值
}
/****************************************************************************
* 名称:Set_time()
* 功能:时间调整函数
****************************************************************************/
void Set_time(void)
{
switch(mode)
{
case 0:
WrOp(0x0c);
break;
case 1:
WrOp(0x87);
WrOp(0x0f);
break;
case 2:
WrOp(0x8a);
WrOp(0x0f);
break;
case 3:
WrOp(0x8d);
WrOp(0x0f);
break;
}
switch(ReadKey( ))
{
case '1':
mode=(mode+1)%4 ;
break;
case '2':
if(mode==1)Hour=(Hour+1)%24;
if(mode==2)Min=(Min+1)%60;
if(mode==3)Sec=(Sec+1)%60;
disflash=1;
break;
case '3':
if( 1 == mode ) // ????
{
Hour == 0 ? Hour =23 : Hour--; // ?? 24 ??
}
else if ( 2== mode)
{
Min == 0 ? Min =59 : Min--; // ???60 ??
}
else if ( 3== mode )
{
Sec == 0 ? Sec =59 : Sec--; // ??? 60 ?
}
disflash=1;
break;
}
}
/****************************************************************************
* 名称:main()
* 功能:主程序
****************************************************************************/
void main( )
{
unsigned char SecBak;
char idata buf[20] ;
SysInit( ) ; // 系统初始化
lcd_init( );
while( 1 )
{
Set_time( );
if(disflash||SecBak!=Sec)
{
SecBak=Sec;
disflash=0;
sprintf( buf, "Time: %02d:%02d:%02d", (unsigned int)Hour, (unsigned int)Min, (unsigned int)Sec );
DisText(0x80, buf ) ; // 0x80是第一行的开始位置
DisText(0x80+0x40, "Welcome to FJNU!" ) ; // 0xc0是第二行的开始位置
}
}
}
备注:这边使用了sprintf函数,将时分秒组合成一个字符串
这里还使用了disflash刷新标志,只有在数据变动的时候才刷新,使得这里的光标可以停留变动的地方
- 传感器
18B20 温度传感器