51单片机系列实验(蜂鸣器、数码管、LCD显示时钟、串口通信)
数码管(LED 数码管)是一种常用于显示数字和部分字符的数字显示器件。它通常由多个发光二极管(LED)组成,每个LED代表数字中的一个段或一个点。LM016L:指令集(11条指令):常用指令:0x80+0xdd:dd为地址,这条命令用于设置显示起点坐标0x0c :开显示,无光标,光标不闪烁。一般做带键盘输入的才加入光标,如计算器。常用的计量显示不显示光标。0x06 :写一个数据,地址指针加1,由1
一、利用中断发出1Khz的方波信号,驱动蜂鸣器鸣叫。
1.1前期准备:
1.设置硬件:
连接一个蜂鸣器到你的微控制器的数字输出引脚。确保蜂鸣器的一个端口连接到数字引脚,另一个端口连接到地(GND)。
2.编写程序:
3.配置中断:
在 setup() 函数中设置中断。
4.编写中断服务函数:
创建一个中断服务函数,用于处理定时器中断。
在这个中断服务函数中,你可以在每次触发时改变方波的状态。例如,使用一个变量来控制方波的高低电平。
5.生成1kHz方波信号:
在中断服务函数中,根据你的需求,将方波信号输出到连接蜂鸣器的引脚。
为了产生1kHz的方波信号,你需要在中断服务函数中以适当的频率改变方波的状态。1kHz的周期是1ms,因此你需要在中断服务函数中以500Hz的频率改变方波状态(即每个周期内产生两个状态翻转)。
1.2控制蜂鸣器:
根据方波信号的状态,控制蜂鸣器的开启和关闭。方波的高电平时蜂鸣器应该响起,低电平时则停止响声。
1.3代码实现
在Keil环境下编写代码:
#include <REGX52.H>
sbit sound=P3^5;// 定义一个位变量 sound,对应于 P3 端口的第 5 位,用于控制蜂鸣器
void main()
{
EA=1;
ET1=1; // 开启定时器1中断
TMOD=0x10;
TH1=0xfe;//中断500微秒
TL1=0x33;
TR1=1;
while(1)
{ }
}
void Timer1_Routine() interrupt 3 // 中断函数
{
TH1=0xfe;
TL1=0x33;
sound=~sound;// 每次中断触发时,取反 sound 变量的值,用于控制蜂鸣器的开启和关闭
}
1.4作图仿真
在Protues中;示波器打开方法:
将Keil中可执行文件导入单片机中:
仿真效果为:
蜂鸣器
1.5普中开发板实现视频:
步骤:在PZ-ISP或者其他烧录器点击烧录即可
蜂鸣器普中视频
二、LED数码管秒表
2.1数码管LED模板介绍
数码管(LED 数码管)是一种常用于显示数字和部分字符的数字显示器件。它通常由多个发光二极管(LED)组成,每个LED代表数字中的一个段或一个点。
1.结构:
数码管 LED 模块通常由多个 LED 组成,排列成一定的形状,例如常见的7段数码管就由7个LED组成。每个 LED 代表数码管的一个段,通常分别表示数字中的a、b、c、d、e、f、g段,加上一个小数点。
2.工作原理:
数码管 LED 模块通过控制每个 LED 的亮灭来显示数字或字符。每个 LED 通过控制对应的引脚的高低电平来控制其亮灭状态。
3.控制方式:
通常,数码管 LED 模块的控制方式有两种:共阴极和共阳极。
共阴极:所有的LED的阴极(负极)连接在一起,而每个LED的阳极(正极)通过一个引脚连接到控制芯片。
共阳极:所有的LED的阳极(正极)连接在一起,而每个LED的阴极(负极)通过一个引脚连接到控制芯片。
控制芯片通常接收外部输入信号,并根据输入信号控制每个 LED 的亮灭状态。
4.应用:
数码管 LED 模块广泛应用于计时器、计数器、温度显示、电压显示、仪表盘等数字显示领域。
例题:
用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。 本秒表应用定时器模式,计时范围0.1~9.9s。此外还涉及如何编写控制LED数码管显示的程序。
2.2代码实现:
#include<reg51.h>
typedef unsigned int uint; //定义无符号整形和字符型
typedef unsigned char uchar;
uchar led[] ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
uchar led1[] = {0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
uchar second; //秒数
uchar key; //按键次数
uint t; //用来计数,每500,代表0.1s
sbit keyif = P3^7; //按键接口
void delay(){ //延时函数,用于消除抖动
uchar i,j;
for(i=0;i<255;i++){
for(j=0;j<100;j++);
}
}
void init(void) //初始化
{
TMOD = 0x01; //0000 0010 使用方式二
second = 0; //秒数初始化为0
EA = 1; //总中断,定时器0中断允许
ET0 = 1; //允许定时器0中断
key = 0; //按键次数初始化为0
t = 0; //计数初始化为0
}
void main(){
init();
P0 = led1[second/10];
P2 = led[second%10];
while(1){
if(keyif == 0){
delay();//消除抖动
if(keyif == 0){
key++;
switch(key){
case 1: //按一次,计时器开始
TH0 = 0xee;
TL0 = 0x00;
TR0 = 1;
break;
case 2: //按两次,暂停定时器
t = 0;
TR0 = 0;
break;
case 3: //按三次,停止计时,数据清零
key = 0;
second = 0;
P0 = led1[0];
P2 = led[0];
break;
}
while(keyif == 0); //若一直按下,使其停留
}
}
}
}
void timer() interrupt 1
{
TR0 = 0; //停止计时
t++;
if(t ==20){
second++;
P0 = led1[second/10];
P2 = led[second%10];
t = 0;
}
if(second == 99){ //当计数到9.9秒,重新开始计时
second = 0;
key = 2; //相当于重新开始计时
}
TR0 = 1; //继续启动计时器
}
2.3作图仿真
仿真视频为:
2数码管
2.4普中开发板实现
由于小组中成员均未携带数码管,效果表现不佳。
三、3显示时钟
3.1使用定时器实现一个LCD显示时钟。
基本的实现步骤:
1.初始化 LCD:连接 LCD 到微控制器,并初始化 LCD。
2.初始化定时器:选择合适的定时器和预分频器,并设置适当的计数值,以使定时器中断每秒触发一次。
3.编写中断服务程序:在定时器中断服务程序中,编写代码来更新时钟的时间,并将时间显示在 LCD 上。在中断服务程序中,需要考虑适当的时间格式,例如时:分:秒。
4.主程序:在主程序中,初始化所有组件,并启用定时器中断。然后进入一个无限循环,等待定时器中断触发。
5.更新时钟:在定时器中断服务程序中,更新时钟的时间,并将其显示在 LCD 上。
3.2LED1602简介:
LM016L:
指令集(11条指令):
常用指令:
0x80+0xdd:dd为地址,这条命令用于设置显示起点坐标
0x0c :开显示,无光标,光标不闪烁 。一般做带键盘输入的才加入光标,如计算器。常用的计量显示不显示光标。
0x06 :写一个数据,地址指针加1,由1602地址表可以看出,实际上就是设置成从左往右写数据而已。
0x38 : 设置显示模式,16x2显示 5x7点阵,8位数据接口。端口不够用时,这个命令也可以换用4位数据接口的。
0x01 :清屏。
3.3代码实现:
#include <REGX52.H>
//LCD引脚配置
sbit LCD_RS=P3^5;
sbit LCD_RW=P3^6;
sbit LCD_EN=P3^7;
#define LCD_DataPort P2
unsigned char Hour=23,Min=59,Sec=55;
//LCD延时函数
void Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
//LCD写命令
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
Delay();
LCD_EN=0;
Delay();
}
//LCD写数据
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
Delay();
LCD_EN=0;
Delay();
}
//LCD设置光标位置
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));
}
}
//LCD显示字符串
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
//LCD显示数字
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
//LCD初始化
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
//定时器初始化
void Timer0_Init()
{
TMOD=0x01;
TH0=0xFc;
TL0=0x66;
TF0=0;
TR0=1;
ET0=1;
EA=1;
}
void main()
{
LCD_Init();
Timer0_Init();
LCD_ShowString(1,1," : : ");
while(1)
{
LCD_ShowNum(1,1,Hour,2);
LCD_ShowNum(1,4,Min,2);
LCD_ShowNum(1,7,Sec,2);
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TH0=0xFc;
TL0=0x66;
T0Count++;
if(T0Count==1000)
{
Sec++;
T0Count=0;
}
if(Sec>=60)
{
Sec=0;
Min++;
}
if(Min>=60)
{
Min=0;
Hour++;
}
if(Hour>=24)
{
Hour=0;
}
}
3.4作图仿真
导入可执行文件,仿真效果为:
3显示时钟仿真
3.5普中开发板实现
3显示时钟
四、串口通信
4.1串口通信简介:
串口通信是计算机与外部设备或计算机与计算机之间通过串行接口传输数据的一种方式。串口通信的数据是以串行方式,即一位一位地顺序传输。这种通信方式常见于各种设备和计算机之间的低速数据交换。以下是串口通信的一些基础概念:
1. 串行与并行通信
- 串行通信:数据一位位顺序发送,只需一条数据线。速度较慢,但成本低,适合远距离通信。
- 并行通信:多位数据同时通过多条数据线传输,速度快,但成本高,适合短距离通信。
2. 标准串口协议
串口通信常见的标准有RS-232、RS-422、RS-485等。
- RS-232:最常用的串行通信协议,适合短距离、低速通信,通常用于PC机与设备间的连接。
- RS-422 和 RS-485:支持更长距离和更高速率的通信。
3. 串口通信参数
进行串口通信时需要设置以下参数以保证数据正确传输:
- 波特率:每秒传输的符号数量,常见的有9600、19200、38400等。
- 数据位:每个数据包的位数,通常是7或8位。
- 停止位:每个数据包后的停止位数,可以是1位或2位,用于标识数据包的结束。
- 校验位:用于错误检测,常见的有奇校验、偶校验和无校验。
4. 数据传输方式
- 异步传输:发送方和接收方各自独立工作,数据包之间通过起始位和停止位进行分隔。
- 同步传输:发送方和接收方通过一个共同的时钟信号来同步数据。
5. 实现方式
在微控制器或计算机中,串口通信通常通过以下方式实现:
- 内置串口控制器:如8051微控制器内置的UART(通用异步收发传输器)。
- 外部串口模块:如使用MAX232芯片将TTL电平转换为RS-232电平。
- USB转串口适配器:如使用FT232芯片实现USB到串口的转换。
4.2串口通信要求:
题目一:
.【例8-6】(甲乙两个单片机串口通信)在实物实验时,如果不能找到两个普中单片机,用笔记本电脑的串口助手程序代替其中一个单片机,实现课件上描述的主要功能。
代码实现:
单片机1:
#include <REGX51.H> // 包含51系列单片机的寄存器定义头文件
sbit T_P=PSW^0; // 定义位变量T_P,表示PSW寄存器的第0位
unsigned char code Tab[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; // 定义一个8位只读数组Tab,存储流水灯的数据模式
void Send(unsigned char dat)
{
TB8=T_P; // 将TB8位设置为T_P,用于串口发送时发送第9位(当TB8位为1时)
SBUF=dat; // 将要发送的数据存入串口数据寄存器SBUF
while(TI==0); // 等待发送完毕,TI为发送中断标志,为0时表示未发送完成
TI=0; // 发送完毕后,清除发送中断标志
}
void Delay1ms(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms)
{
i = 2;
j = 239;
do
{
while (--j); // 延时约1ms
} while (--i);
xms--; // 减少延时时间
}
}
void main()
{
unsigned char i;
TMOD=0x20; // 设置定时器1工作在模式2(8位自动重装载)
SCON=0xc0; // 设置串口工作在模式3,允许接收
PCON&=0x7f; // 关闭串口波特率倍增
TH1=0xfd; // 设置波特率9600
TL1=0xfd;
TR1=1; // 启动定时器1
while(1)
{
for(i=0;i<8;i++)
{
Send(Tab[i]); // 发送流水灯数据模式中的每个字节
Delay1ms(200); // 延时200毫秒,控制流水灯的速度
}
}
}
单片机2:
#include <REGX51.H>
sbit R_P=PSW^0;
unsigned char Receive()//接收一字节数据
{
unsigned char dat;
while(RI==0);//检测RI,RI=0,未接收完
RI=0; //接收数据完成RI手动清0
ACC=SBUF; //将接收缓冲器的数据存于ACC
if(RB8=R_P) //只有偶检验成功才能往下执行,接收数据
{
dat=ACC; //将ACC数据存于dat
return dat; //将接收的数据返回
}
}
void main()
{
TMOD=0x20;
SCON=0xd0;
PCON&=0x7f;
TH1=0xfd;
TL1=0xfd;
TR1=1;
while(1)
{
P2=Receive();
}
}
proteus仿真:
仿真视频:
双单片机
题目二:单片机串口通信:
将单片机串口与笔记本电脑串口模块相连,单片机每隔2秒发送“Hello C51”,笔记本电脑用串口助手软件接收。 如果串口助手发送字符“0" 给单片机,则单片机停止发送; 如果单片机收到“1”,则继续每隔2秒发送“Hello C51”。
烧录程序代码:
#include <REGX52.H>
#include "stdio.h"
unsigned char ch;
unsigned char Flag=1;
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA=1;
ES=1;
}
void UartSend()
{
TI=1;
puts("Hello C51");
while(!TI);
TI=0;
Delay(2000);
}
void main()
{
UartInit();
while(1)
{
if(Flag==1)UartSend();
}
}
//串口中断函数模板
void UART_Routine() interrupt 4 //串口中断
{
if(RI==1)
{
RI=0;
ch=SBUF;
if(ch=='1')Flag=1;
if(ch=='0')Flag=0;
}
}
最终结果:
总结:
本次文章系统学习了,蜂鸣器、LED数码管秒表、LCD显示时钟和窗口通信等内容,进一步加深对51单片机的理解,对protues仿真愈加熟练,对汇编语言理解更上一个台阶,也学习到了不同的仿真元件。尤其是对串口通信的学习,使得我更加理解了通信协议和相关的单片机之间的连接,对汇编代码理解得更加透彻。本人才疏学浅,若文章中出现不对得地方,欢迎大家批评指正。
参考文献:
更多推荐
所有评论(0)