STC89C52通过串口控制流水灯亮灭

本次笔记包含两个方面:

1.只是控制LED的亮灭,不返回数值

2.控制LED的亮灭,并返回数值

看了几讲的视频,都是在讲串口的方式1。其他的还没接触,这里也只用串口的方式1实现这两个功能。串口里面需要计算的地方是根据所要使用的波特率求定时器的初始值。定时器使用的是方式2,可以自动装初始值,避免赋值语句装初始值时出现误差。

以9800bps,定时器使用方式2,串口使用方式1,晶振波特率为 11.0592MHZ,求TH1跟TL1的值。

方式1的波特率 = (2^SMOD/32)xT1溢出率。单片机复位后,电源管理寄存器PCON全部清零,SMOD作为其中一位自然也清零。

波特率已经知道了。这样就剩下T1溢出率了。

假设初值为X,则定时器每次计256-X个数溢出一次(定时器为8位,最大为255 。256时发生溢出)。每计一个数的时间为一个机器周期,机器周期 = T时钟周期 X 12 。于是溢出的时间为 = 个数X 每个时间 = ( 256 - X) * 12/Fosc . 那么基础率就是溢出时间的倒数。

于是结合公式“方式1的波特率 = (2^SMOD/32)xT1溢出率”,式子可以总结为:

9600 = 2^0 /32 * Fosc / (256 - X)*12     带入全部已知数据得到     9600 = 2^0 /32 * 11059200/ (256 - X)*12    =====》》》》  求得的X为: 253  .

在此基础上,如果把SMOD 设为1 ,则 求得波特率为  :

波特率 = (2^1/32) * 11059200 / (256 - 253 )  = 2 * [  1/32 * 11059200 / (256 - 253)] = 2 * 9600 =  19200  。即变为原来的两倍。

如果把晶振换成12MHZ再求初值,求得的X为: 252.744792…… 无穷小数。这样就会产生误差。以前一直感觉整数的晶振挺好,现在才知道为什么会有11.0592MHZ这种晶振的存在了,呵呵。

这样计算得到了初值,下面贴代码。

只是控制LED的亮灭,不返回数值

实现这个又分为查询和中断两种方法。

A。先用查询。感觉叫判断更好些,因为是用if判断来实现的

#include <reg52.h>

void main()
{  
		//设置参数
		TMOD = 0x20; //设定定时器1的工作方式为方式2
		TH1 = 0xfd;
		TL1 = 0xfd;	 //装载TH1、TL1
		TR1 = 1; //启动定时器1

		REN = 1; //允许串行接收位
		SM0 = 0;
		SM1 = 1; //设定串口工作方式为方式1
/*
*    	       EA = 1; //全局中断允许位
*		ES = 1; //串口中断允许位
*		此处使用的是查询法判断接收中断标志位,所以即便不开启中断允许位,也可以
*
*/
	while(1)
	{	 	
		//查询法检测RI
		if(RI == 1)//RI为接收中断标志位。硬件置为1,必须软件清0
		{
			P1 = SBUF;
			RI = 0;
		} 		
	}

}
B 中断法

#include <reg52.h> 
void main()
{  
		//设置参数
		TMOD = 0x20; //设定定时器1的工作方式为方式2
		TH1 = 0xfd;
		TL1 = 0xfd;	 //装载TH1、TL1
		TR1 = 1; //启动定时器1

		REN = 1; //允许串行接收位
		SM0 = 0;
		SM1 = 1; //设定串口工作方式为方式1

		EA = 1; //全局中断允许位
		ES = 1; //串口中断允许位

		while(1) ;	//等待中断的发生
		
}

//中断检测RI
void ser() interrupt 4
{
	   P1 = SBUF;
	   RI = 0;
}

这两个除了代码,感觉就是是否开启中断允许了。因为RI置为1是硬件自动执行的。即便是不开启中断允许位,照样可以用if进行判断。


上面这两个是单方向的,再来个双向的。

/*
 *通过串口给下位机发送数据,并使之显示在P1口的流水灯上。
 *同时单片机返回接收到的数据,显示在串口助手上
 */
#include <reg52.h>

unsigned char flag;

void main()
{  
		//设置参数
		TMOD = 0x20; //设定定时器1的工作方式为方式2
		TH1 = 0xfd;
		TL1 = 0xfd;	 //装载TH1、TL1
		TR1 = 1; //启动定时器1

		
		SM0 = 0;
		SM1 = 1; //设定串口工作方式为方式1
		REN = 1; //允许串行接收位

    	EA = 1; //全局中断允许位
		ES = 1; //串口中断允许位

	while(1)
	{	
		/* 刚开始单片机缓冲寄存器为空,无数据可以显示
		 * 先从串口接收数据,再返回该数据
		 * 在中断中接收数据,同时将flag标志位置为1.说明接收到了数据
		 * 若接收到数据(flag == 1),说明接收到了;否则说明未接收到数据,不显示。继续判断flag数值
		 */
		
		if(flag == 1) 
		{						
			//发送数据
			ES = 0; //关闭串口中断,发送数据
			SBUF = P1; //数据写入SBUF寄存器
			while(!TI); //等待
			TI = 0;
			ES = 1; 
			flag = 0;
		
		}		
	}

}

void ser() interrupt 4
{
	//接收数据
	P1 = SBUF;
	flag = 1;
	RI = 0;
}

主函数里面那个flag = 0  。 一定不能少了。否则只要一小会儿的功能,串口助手就卡了。。。。

这个例子里还有两条语句比较关键:

P1 = SBUF;//把SBUF寄存器中的数值赋给P1

SBUF = P1;//把P1的数值写入到SBUF

SBUF是这么写的:SBUF 串行数据缓冲寄存器,一个发送缓冲寄存器,一个接收缓冲寄存器。两个公用一个地址99H,但在物理上是两个独立的寄存器。那么如何区分是发送还是接收呢?就用语句来区分了。

控制流水灯的话,需要发送十六进制格式的。

比如发送FB(1111,1011)。在我的开发板上是L2灯亮。如果发送字符,就不太好控制了。如果用2中的例程,以字符方式发送“fb”,单片机返回串口助手并用十六进制显示为“62” .这个,嗯,目前不会算 :P


没啥值得纪念的图片,还是几个流水灯。不过此时的流水灯,非彼时的流水灯。现在的流水灯,可是我从电脑上就能控制开发板上的了:D

只是不知道下次自己写个上位机是什么时候了,呵呵

  • 24
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,我可以帮你设计一个基于 STC89C52 单片机的 UART 串口控制流水灯。 首先,你需要连接单片机的串口通信引脚 (TXD 和 RXD) 到一块串口转 USB 模块上,以便与电脑进行通信。 然后,你需要定义一个变量来存储串口接收到的数据。在主函数中,你可以使用以下代码来接收串口数据并将其存储到变量中: ```c unsigned char data; // 存储串口接收到的数据 void main() { // 初始化串口通信 UART_Init(); while (1) { // 检查是否接收到数据 if (RI) { data = SBUF; // 读取串口接收缓冲区中的数据 RI = 0; // 清除接收中断标志位 // 处理接收到的数据 if (data == '1') { // 开始流水灯效果 LED_Flow(); } } } } ``` 接下来,你需要编写一个函数来控制流水灯的效果。以下是一个简单的流水灯控制函数示例: ```c // 控制流水灯效果 void LED_Flow() { unsigned char i, j; while (1) { for (i = 0; i < 8; i++) { P1 = ~(1 << i); // 点亮当前 LED 灯 for (j = 0; j < 100; j++) { // 延时一段时间 Delay(); } } } } // 延时函数 void Delay() { unsigned int i, j; for (i = 0; i < 500; i++) { for (j = 0; j < 100; j++); } } ``` 这个函数会不断循环点亮 LED 灯,从第一个到第八个,然后从第八个回到第一个,形成流水灯的效果。当接收到电脑发送的字符 '1' 时,就会调用这个函数。 最后,你需要编写一个串口初始化函数来设置串口通信的参数,例如波特率、数据位、停止位和校验位等。以下是一个简单的串口初始化函数示例: ```c // 初始化串口通信 void UART_Init() { TMOD = 0x20; // 设置定时器 1 为模式 2 (自动重载) TH1 = 0xFD; // 波特率为 9600,TH1 = 256 - (1 / (32 * 9600)) = 253 TL1 = TH1; // 定时器 1 初值等于波特率设定值 TR1 = 1; // 启动定时器 1 SM0 = 0; // 设置串口为模式 1 (8 位数据位,无校验位,1 位停止位) SM1 = 1; REN = 1; // 允许串口接收 EA = 1; // 开启全局中断 ES = 1; // 开启串口中断 } ``` 这个函数会初始化定时器 1 和串口通信模式,以便进行数据的发送和接收。 希望这个设计能够帮助到你!
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值