C51单片机实践部分
C51单片机实践部分
- LED灯
- 数码管
- 按键
- 中断
- 定时器
- 可调时钟
- 1602驱动(比较复杂)
本片摘录一些通用特殊代码
LED灯
- 点亮一盏LED灯
延时函数:这里使用程序延时,后面有更精确的采用计时器中断来延时,但是在代码的某些时候还是使用程序延时
void delay(unsigned long int x)
{while(x--);
}
- 流水灯设计
这里可以使用数组来储存每盏灯的状态,然后使用for循环显示。可自定义——因为数组里的每一个元素是对P1口及八个口对应的八盏灯的总状态的描述。
#include
#define u8 unsigned char
#define u32 unsigned long intu8 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 }; // 数码管显示数字17void 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]]; // 根据缓冲区显示数据且数码管显示数字17Delay_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 相加1case '+': Num = (Num+1)%99; break;// 0~99范围步进为1 相减1case '-': 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'; // 返回值是字符1if( !K5 ) return '2'; // 返回值是字符2if( !K6 ) return '3'; // 返回值是字符3if( !K7 ) return 'A'; // 返回值是字符AP1 = ~(1<<1); // 扫描第二行按键if( !K4 ) return '4'; // 返回值是字符4if( !K5 ) return '5'; // 返回值是字符5if( !K6 ) return '6'; // 返回值是字符6if( !K7 ) return 'B'; // 返回值是字符BP1 = ~(1<<2); // 扫描第三行按键if( !K4 ) return '7'; // 返回值是字符7if( !K5 ) return '8'; // 返回值是字符8if( !K6 ) return '9'; // 返回值是字符9if( !K7 ) return 'C'; // 返回值是字符CP1 = ~(1<<3); // 扫描第四行按键if( !K4 ) return '0'; // 返回值是字符0if( !K5 ) return 'D'; // 返回值是字符Dif( !K6 ) return 'E'; // 返回值是字符Eif( !K7 ) return 'F'; // 返回值是字符Freturn 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 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); //光标加1WrOp(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 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); //光标加1WrOp(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为方式1TR0= 1 ; // 启动定时器0进行记数ET0= 1 ; // 开启定时器0中断功能EA= 1 ; // 开启总中断
}/****************************************************函数:定时器0中断程序功能:10000uS中断一回,对毫秒变量和秒变量进行进位操作
*****************************************************/
void Time0Int( void ) interrupt 1
{TL0= (65536-10000) ; // 10毫秒定时预置值的低8位给TL0TH0= (65536-10000) >>8 ; // 10毫秒定时预置值的高8位给TH0mSecond++ ; // 对毫秒进行加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'; // 按键1if( key2== 0 ) return '2'; // 按键2if( key3== 0 ) return '3'; // 按键3return 0 ; // 说明没有按键
}/****************************************************函数:读一个按键值,如果没有按键返回为NULL
*****************************************************/
unsigned char ReadKey( void )
{unsigned char temp ;temp= ScanKey( ) ; // 读取一个键值if( temp== 0 )return 0 ; // 说明没有按键Delay( 1000 ) ; // 等待10mSif( 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 温度传感器
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
