当前位置: 首页 > article >正文

51单片机学习记录

一、STC89C51RC/RD+系列单片机结构

        STC89C51RC/RD+系列单片机的内部结构框图如下图所示。STC89C51RC/RD+单片机中包含中央处理器(CPU)、程序存储器(Flash)、数据存储器(SRAM)、定时/计数器、UART串口、I/O接口、EEPROM、看门狗等模块。STC89C51RC/RD+系列单片机几乎包含了数据采集和控制中所需的所有单元模块,可称得上一个片上系统。  

单片机最小应用系统:

二、单片机程序实践

1.点亮LED灯

包含51单片机的头文件:#include ”reg52.h“

查询51单片机硬件原理图,可知LED灯由P2的8个引脚位控制。引脚输出电平为0,对应LED灯点亮,引脚输出电平为1,对应LED灯熄灭。从而控制P2引脚的输出电平就可以控制各个LED的亮灭。

#include "reg52.h"			
#include <intrins.h>	
 
void Delay1ms(unsigned int time)		//@12.000MHz
{
	unsigned char i, j;

	while(time--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

void led_on(unsigned char led)
{
		P2 = ~led;
}

void main()
{
	unsigned int i;

	while(1)
	{	    
		unsigned char led = 0x07;
		for(i=0;i<6;i++)	 //将led左移
		{
			led_on(led << i);
			Delay1ms(1000);	
		}
		led = led << 5;
		for(i=0;i<6;i++)	//将led右移
		{
		   led_on(led >> i);
		   Delay1ms(1000); 
		}
	}	
}	
2、获取按键的状态

根据原理图所示,K1~K4对应P1引脚的高4位,K5对应P3引脚的第6位即P35

检测引脚电平,如果是低电平,则说明该引脚对应的KEY被按下

按键初始化:将5个按键的引脚电平设置为高电平,表示没有被按下

读取按键状态:读取相应引脚的状态,判断按键是否被按下

按键状态读取中,消抖很重要,以下是按键消抖的原因:

         当用户按下或释放按键时,由于机械弹性的作用,会产生短暂的不稳定接触,称为抖动。这种抖动可能导致单片机或处理器错误地多次读取按键动作,从而产生错误的信号。因此,消除这种抖动是确保按键准确读取的关键。

#include <reg51.h>
#include "key.h"
#include "delay.h"

void key_init(void)
{
	P1 |= (0xF << 4);
	P3 |= (1 << 5);
}

unsigned char key_status(void)
{
	unsigned char num = 0;

	if(0 == (P1 & (1 << 4)))
	{
		delayms(10);
		if(0 == (P1 & (1 << 4)))
		{
			while(!(P1 & (1 << 4)));
				delayms(10);
				if(P1 & (1 << 4))
				 	num = 1;
		}		
	}	
	else if(0 == (P1 & (1 << 5)))
    {
        delayms(10);
		if(0 == (P1 & (1 << 5)))
		{
			while(!(P1 & (1 << 5)));
				delayms(10);
				if(P1 & (1 << 5))
		             num = 2;
        }
    }
	else if(0 == (P1 & (1 << 6)))
	 {
        delayms(10);
		if(0 == (P1 & (1 << 6)))
		{
			while(!(P1 & (1 << 6)));
				delayms(10);
				if(P1 & (1 << 6))
		             num = 3;
        }
    }						
	else if(0 == (P1 & (1 << 7)))
	 {
        delayms(10);
		if(0 == (P1 & (1 << 7)))
		{
			while(!(P1 & (1 << 7)));
				delayms(10);
				if(P1 & (1 << 7))
		             num = 4;
        }
    }						
	else if(0 == (P3 & (1 << 5)))
	 {
        delayms(10);
		if(0 == (P3 & (1 << 5)))
		{
			while(!(P3 & (1 << 5)));
				delayms(10);
				if(P3 & (1 << 5))
		             num = 5;
        }
    }

	return num;
}
3、数码管

每个数码管由abcdefg以及dp  8个段控制显示的内容,只需要控制8个段的电平输出,就可以改变显示内容。所使用的单片机数码管是共阴极数码管,因此高电平使对应段亮。

根据原理图可知,P1引脚控制数码管的段选,P1的低4位控制4个数码管的位选(即控制哪个数码管亮,哪个数码管灭)

#include <reg51.h>
#include "nixietube.h"
#include "delay.h"

void seg_select(unsigned char seg)
{
	unsigned char array[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f,0x40,0};
	if(seg <= 11)
	{
		P0 = array[seg];
	}
}

void pos_select(unsigned char pos)
{
	if(pos <= 4)
	{
		P1 = (1 << (pos -1));
	}
}

void display_num(int num)
{
    int flag = 0;
	if(num >= 0)
	{
		if(0 == num / 1000)
		{
			display(11,4);
		}
		else
		{
			display(num / 1000, 4);
			flag = 1;
		}

		num = num % 1000;
		if(0 == num / 100 && flag == 0)
		{
			display(11,3);
		}
		else
		{
			display(num / 100, 3);
			flag = 1;
		}

		num = num % 100;
		if(0 == num / 10 && flag == 0)
		{
			display(11,2);
		}
		else
		{
			display(num / 10, 2);
			flag = 1;
		}

		num = num % 10;
		display(num,1);
	}
	else
	{
		num = -num;

		num = num % 1000;
		if(0 == num / 100)
		{
			display(11,3);
		}
		else
		{
			display(num / 100, 3);
			if(0 == flag)
			{
				display(10,4);
				
			}
			flag = 1;
		}

		num = num % 100;
		if(0 == num / 10 && flag == 0)
		{
			display(10,2);
		}
		else
		{
			display(num / 10, 2);
			if(0 == flag)
			{
				display(10,3);		
			}		
			 flag = 1;
		}

		num = num % 10;
		display(num,1);
		if(num != 0 && flag == 0)
		{
			display(10,2);
		}
	}

}


void display(unsigned char seg,unsigned char pos)
{
	pos_select(pos);
	seg_select(seg);
	delayms(5);
	seg_select(10);
}
4、UART串口通信

SCON:设置SM0 = 0、SM1 = 1,使用8位UART模式,波特率可变;设置REN = 1,允许串行接收

PCON(波特率选择):设置SMOD = 1,使串行口1、2、3的波特率加倍

设置串口中断:将ES串口中断置1打开,EA总中断置1打开

设置定时器:

将TMOD.7  TMOD.6  定时器1的M0,M1 置0,表示打开定时器1,将定时器1/计数器1用作定时器

设置M1、M0 为 1 0,表示8位自动装载定时器,当溢出时将TH1存放的值自动装入TL1

设置定时器/计数器中断控制:设置TR1为1,TMOD.7为0,TR1要为1才能开始计数

void uart_init(void)
{
	//SM0 = 0, SM1 = 1 时为方式1:8位UART,波特率可变
	SCON &= ~(1 << 7);  // 将SM0 置0
	SCON |= (1 << 6);  // 将SM1 置1
	SCON |= (1 << 4); //  将REN 置1,表示允许串行接收

	//波特率选择
	PCON |= (1 << 7); //将SMOD 设置为1,方式1、2、3的波特率加倍

	//设置串口中断
	IE |= (1 << 7) | (1 << 4); 	 //将ES串口中断置1打开,EA总中断置1打开

	//设置定时器
	TMOD &= ~(0xf << 4); //将TMOD.7  TMOD.6  定时器1的M0,M1 置0,表示打开定时器1,将定时器1/计数器1用作定时器,
	TMOD |= (0x02 << 4); //设置M1、M0 为 1 0,表示8位自动装载定时器,当溢出时将TH1存放的值自动装入TL1
	
	TL1 = 243;		   //设置波特率4800
	TH1 = 243;

	TCON |= (1 << 6); //设置TR1为1,TMOD.7为0,TR1要为1才能开始计数
}

void uart_send(const unsigned char * dat, int len)
{
	int i = 0;
	for(i = 0; i < len; i++)	 
	{
		SBUF = dat[i];			 //将要发送的数据存入串口的缓存区中
		while(!(SCON & (1 << 1)));			//TI位,发送中断请求标志位,为1时表示数据发送完毕
		TI = 0;							   //将TI位手动置0
	}
}

unsigned char dat[24] = {0};			   //定义存放数据的数组
unsigned char pos = 0;

void uart_handler(void)  interrupt 4 
{
    if(RI)								   //判断接收中断请求标志位,为1时表示有数据发送过来,接收数据
	{									   //将串口缓存区中收到的数据放入dat中
		dat[pos++] = SBUF;
		RI = 0;							   //手动将RI置0,等待下一个数据的发送
		if(pos == 24)					   //防止对数组dat的越界访问
		{
			pos = 23;
		}
	}
}

unsigned char uart_recv(unsigned char * buf)
{
	unsigned char ret = 0;
	unsigned char i = 0;
	for(i = 0; i < pos; i++)    
	{
		buf[i] = dat[i];		//将接收的数据放入buf中
	}

	ret = i;
	for(i = 0; i < pos; i++)
	{
		dat[i] = 0;			   //清空dat
	}
	pos = 0;				   

	return ret;
}

5、DS18B20温度传感器的配套使用

模块简介:

        DS1820数字温度计提供9位温度读数,指示器件的温度。信息经过单线接口送入DS1820或从DS1820送出,因此从中央处理器到DS1820仅需连接一条线(和地)。读、写和完成温度变换所需的电源可以由数据线本身提供,而不需要外部电源。因为每一个DS1820有唯一的系列号(silicon1serialnumber),因此多个DS1820可以存在于同一条单线总线上。这允许在许多不同的地方放置温度灵敏器件。此特性的应用范围包括HVAC环境控制,建筑物、设备或机械内的温度检测,以及过程监视和控制中的温度检测。

特性简介:

        

引脚排列及说明:

 

 读写时序图说明:

主机读‘1’的时序以及读‘0’的时序:

DS18B20初始化:根据模块时序图,配合对应时间的延时,向模块发出初始化信号

向DS18B20写数据:按照时序图,进行延时和电平的转换,向模块写1或者写0

从DS18B20读取数据:按照时序图,进行延时和电平转换,判断模块发送的数据是1还是0

#include <reg51.h>
#include <intrins.h>
#include "delay.h"
#include "DS18B20.h"

sbit DQ = P3^7;

unsigned char DS18B20_init()
{
	char ack;
	DQ = 1;				   //拉高P3.7引脚
	delay10us(100);
	DQ = 0;				   //按照协议,拉低电平,给出初始化的信号
	delay10us(72);
	DQ = 1;				   //拉高电平
	delay10us(18);
	ack = DQ;			   //将DS18B20返回的信号接收
	delay10us(32);

	return ack;			   //返回收回的信号,可由此判断初始化成功与否
}

void DS18B20_write(unsigned char dat)
{
	unsigned char i = 0;
	for(i = 0; i < 8; i++)			   //1bit 1bit的向传感器发送数据
	{
		if(dat & (1 << i))			   //判断发送位的数据为1还是为0
		{
			//write 1
			DQ = 0;					   //为1,按照协议拉高电平
			_nop_();
			_nop_();				   //延时2us
			DQ = 1;					   //拉高电平
			delay10us(5);
		}
		else
		{
			// write 0
			DQ = 0;						//为0,先拉低电平
			delay10us(5);				//延时
			DQ = 1;						//拉高电平
			_nop_();
			_nop_();
		}
	}
}

unsigned char DS18B20_read(void)
{
	unsigned char dat = 0;
	unsigned char i = 0;
	for(i = 0; i < 8; i++)			//按位接收传感器返回的数据
	{
		DQ = 0;						//按照协议,拉低电平
		_nop_();
		_nop_();					//延时两秒
		DQ = 1;						//重新拉高电平
		if(DQ == 1)				
		{
			dat = dat | (1 << i);	  //收到的为1,将接受的数据放置到相应的位上
		}

		delay10us(4);
	}

	return dat;					   //返回收到的数据
}


short get_temp()
{
        unsigned char th = 0;
	    unsigned char tl = 0;
	    unsigned char ack = 0;
	    short temp = 0;
		ack = DS18B20_init();				   //初始化传感器
		if(1 == ack)
		{
			return 0xfff;
		}
		DS18B20_write(0xcc);				  //初始化成功,向传感器发送信号
		DS18B20_write(0x44);
		delayms(1000);

		ack = DS18B20_init();				  //初始化传感器
		if(1 == ack)
		{
			return 0xfff;
		}
		DS18B20_write(0xcc);				  //成功则发送信号
		DS18B20_write(0xbe);

		tl = DS18B20_read();				  //读取传感器返回的数据,先放入低8位
		th = DS18B20_read();				  //读取传感器返回的数据,放入高8位

		temp = (th << 8) | tl;				  //将高9位和低8位合成一个short数据

		return temp;						  //返回接收到的温度数据
}

5、自定义协议

协议格式:帧头、数据长度、数据、校验和、帧尾

#include <reg51.h>
#include "pack.h"

unsigned char check_sum(unsigned char * dat,unsigned char len)
{
	unsigned char sum = 0;
	int i = 0;

	for(i = 0; i < len; i++)
	{
		sum += dat[i];		  //计算数据位的总和,作为校验位的数据
	}

	return sum;
}

unsigned char package(unsigned char * dat, unsigned char len, unsigned char * outdat)
{
	int i = 0;
	int j =   0;
	
	outdat[i++] = FRAME_HEAD;		  //放入帧头
	outdat[i++] = len;				  //放入数据长度
	for(j = 0; j < len; j++)
	{
		outdat[i++] = dat[j];			   //放入数据
	}
	outdat[i++] = check_sum(dat,len);		   //计算校验和并放入帧数据中
	outdat[i++] = FRAME_TAIL;				   //放入帧尾

	return i;								  //返回一帧数据的长度
}

unsigned char data_transfor(data_type * datatype,unsigned char * dat)
{
	int i = 0;
	if(datatype->t_flag == 1)
	{
		dat[i++] = 0x01;
		dat[i++] = (datatype->temp) >> 8;
		dat[i++] = datatype->temp;
	}
	
	if(datatype->h_flag == 1)
	{
		dat[i++] = 0x02;
		dat[i++] = (datatype->humi) >> 8;
		dat[i++] = datatype->humi;
	}	

	if(datatype->l_flag == 1)
	{
		dat[i++] = 0x03;
		dat[i++] = (datatype->light) >> 8;
		dat[i++] = datatype->light;
	}
	
	return i;	
}

unsigned char package1(unsigned char * dat,unsigned char len,unsigned char * outdat)
{
	 int i = 0;
	 int j = 0;

	 outdat[i++] = FRAME_HEAD;
	 outdat[i++] = len;

	 for(j = 0; j < len; j++)
	 {
	 	outdat[i++] = dat[j];
	 }

	 outdat[i++] = check_sum(dat,len);		   //计算校验和并放入帧数据中
   	 outdat[i++] = FRAME_TAIL;

	 return i;
}

http://www.kler.cn/a/589736.html

相关文章:

  • 时间序列分析的军火库:AutoTS、Darts、Kats、PaddleTS、tfts 和 FancyTS解析
  • 【小项目】四连杆机构的Python运动学求解和MATLAB图形仿真
  • 机器学习--卷积神经网络原理及MATLAB回归实现
  • Docker 使用指南
  • C# WPF编程-画刷(Brush)填充图形对象的颜色或图案
  • SpringBoot3和企业版Splunk集成(附加docker配置)
  • CoreData 调试警告:多个 NSEntityDescriptions 声明冲突的解决
  • 数模AI使用教程(新) 2025.3.17
  • Windows安全日志Defender 的配置被修改5007
  • Python数据可视化——生成数据(一)
  • Python基于Django和协同过滤算法实现电影推荐系统功能丰富版
  • 跟着AI复习一下pytorch原理和操作
  • OpenCV计算摄影学(22)将输入的彩色图像转换为两种风格的铅笔素描效果函数pencilSketch()
  • 嵌入式硬件篇---龙芯GPIO控制
  • CTF WEB题
  • 每日一题--进程与协程的区别
  • 【在校课堂笔记】Python 第5节课 总结
  • axios 和 fetch异同点
  • Java继承与反思,单例模式与静态的思考
  • 【redis】Jedis 操作 Redis 基础指令(下)