其他分享
首页 > 其他分享> > STC 51单片机自学笔记

STC 51单片机自学笔记

作者:互联网

一、准备

硬件问题

config.h

/******************************************************************************/
// 包含头文件
/******************************************************************************/
#include <reg52.h>
#include <intrins.h>


/******************************************************************************/
// 类型定义
/******************************************************************************/
typedef unsigned char uint8;
typedef unsigned int  uint16;
typedef unsigned long uint32;
typedef signed char   int8;
typedef signed int    int16;
typedef signed long   int32;


/******************************************************************************/
// P4端口配置
/******************************************************************************/
sfr P4 = 0xc0;
sfr P4M0 = 0xb4;
sfr P4M1 = 0xb3;


/******************************************************************************/
// LED引脚配置
/******************************************************************************/
sbit led1 = P0^0;
sbit led2 = P0^1;
sbit led3 = P0^2;
sbit led4 = P0^3;
sbit led5 = P0^4;
sbit led6 = P0^5;
sbit led7 = P0^6;
sbit led8 = P0^7;


/******************************************************************************/
// 独立按键引脚配置
/******************************************************************************/
sbit key1 = P3^6;
sbit key2 = P3^7;


/******************************************************************************/
// 数码管引脚配置
/******************************************************************************/
sbit dig1 = P3^5;//dig1
sbit dig2 = P3^4;//dig2
sbit dig4 = P3^3;
sbit dig3 = P4^0;//dig4

sbit sck = P4^6;
sbit dio  = P4^7;


/******************************************************************************/
// LCD1602/LCD12864引脚配置
/******************************************************************************/
sbit EN  = P4^3;
sbit RW  = P4^2;
sbit RS  = P4^1;
sbit PSB = P4^4;
#define lcdPort P2


/******************************************************************************/
// 蜂鸣器引脚配置
/******************************************************************************/
sbit buzz  = P4^5;


/******************************************************************************/
// DS18B20引脚配置
/******************************************************************************/
sbit DQ = P0^4;


/******************************************************************************/
// 红外接收引脚配置
/******************************************************************************/
sbit ird = P3^2;


/******************************************************************************/
// DS1302引脚配置
/******************************************************************************/
sbit sclk = P0^7;
sbit Data = P0^6;
sbit ce   = P0^5;


/******************************************************************************/
// OLED引脚配置
/******************************************************************************/
sbit oledSDA = P4^2;
sbit oledSCL = P4^1;


/******************************************************************************/
// 串口引脚及相关寄存器配置
/******************************************************************************/
sfr AUXR  = 0x8e;
sfr AUXR1 = 0xa2; //串口切换控制寄存器
sfr S2CON = 0x9a;
sfr T2H   = 0xd6;
sfr T2L   = 0xd7;
sfr P_SW1 = 0xa2;
sfr P_SW2 = 0xba;
sfr IE2   = 0xaf;
sfr S2BUF = 0x9b;

delay.h

/******************************************************************************/
// 延时函数
/******************************************************************************/
void Delay15ms();

delay.c

/******************************************************************************/
/*                           叁议电子                                         */
/*                        www.ppptalk.com                                     */
// 版本:      STC15F2K60S2
// 文件名:    delay.c
// 说明:      延时函数
// 编写日期:  2018年4月3日
// 免责声明:  该程序仅用于学习与交流
// (c) PPPTalk  All Rights Reserved
/******************************************************************************/


/******************************************************************************/
// 包含头文件
/******************************************************************************/
#include "config.h"
#include "delay.h"


/******************************************************************************/
// 函数名称:Delay15ms 
// 输入参数:无
// 输出参数:无
// 函数功能:延时15ms
/******************************************************************************/
void Delay15ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	_nop_();
	i = 1;
	j = 162;
	k = 89;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

二、名词解析

管脚(引脚,sbit)

引脚,又叫管脚,英文叫Pin。就是从集成电路(芯片)内部电路引出与外围电路的接线,所有的引脚就构成了这块芯片的接口。引线末端的一段,通过软钎焊使这一段与印制板上的焊盘共同形成焊点。引脚可划分为脚跟(bottom)、脚趾(toe)、脚侧(side)等部分。

sbit 是定义一个引脚。sbit 在c语言中不可视为int之类的变量,声明时就必须指向明确地址 bit Key_Press(sbit key)这种写法是错误的。

P0,P2 等

PX是一个并行口,有8个引脚,是不能sbit 来定义,可以用 sfr 定义。 P0^0 意义上等价于 P0.0

P0=0xFE

(十六进制)0xFE=(二进制)11111110; p0口有8位,就是把高7为置为1,最后一位置为0.

P0=0; 是将所有的PO 8位设置为低电平

按键抖动

原因

按键开关通常为机械式开关,因为机械触点有弹性,所以当我们按下某一个按键时往往不会马上稳定导通,断开时也不会马上断开,在闭合和断开的瞬间都会伴随弹性抖动。按键按下时的抖动称为前沿抖动,按键松开时的抖动称为后沿抖动,抖动波形如下图所示

img

软件去抖

软件去抖可利用延时程序进行去抖。由于抖动时间很短,当有按键按下并检测到按下时,对程序进行一小段延时,跳过前沿抖动,再进行一次按键按下检测,若检测到按下则说明确实有按键按下,这也避免了前沿抖动。对于后沿抖动,在检测到确实有按键按下时延时一小段延时,再去检测按键是否被抬起,检测到抬起则一次按键的检测完毕。

bit Key1_Press(void){
	bit flag = 1;	       //定义按键标志位
	key1 = 1;			   //设置成输入状态
	if(!key1){			   //查询按键是否按下
		Delay15ms();	   //跳过前沿抖动
		if(!key1){		   //确定是否按下
			while(!key1);  //等待释放
			Delay15ms();   //跳过后沿抖动
			while(!key1);  //等待释放
			flag = 0;	   //按键标志位清零
		}
	}	
	return flag;		   //返回0表示按键按下
}

断码

全称叫七段数码,如下图

img

七段指a,b,c,d,e,f,g段位.一段位实际是一个发光二极管,用这7个二极管组合能组成0-9,a-f等等数字或16进制数,所以叫七段数码,数码连接的共公端不同,就有共阳与共阴之分.有的为了显示小数点,又在后面再加了一个发光二极管,就是dp段位,这样就能很好的显示数字。

三、参考代码练习

1、LED灯闪亮

LED 实验过程中需要采用杜邦线连接 LED1-LED8 到对应单片机引脚,参考程序中使用的是 P0 端口

#include "config.h"
#include "delay.h"

int main()
{
  while(1){
		led1 = 0;
		Delay15ms();
		led1 = 1;	
		led2 = 0;
		Delay15ms();
		led2 = 1;
		//P0 = 0xff;			
	}
  return 0;
}

2、独立按键实验

当按下 S2 时,LED1 状态改变,当按下 S3 时,LED2 状态改变。

bit Key1_Press(void){
	bit flag = 1;	       //定义按键标志位
	key1 = 1;			   //设置成输入状态
	if(!key1){			   //查询按键是否按下
		Delay15ms();	   //跳过前沿抖动
		if(!key1){		   //确定是否按下
			while(!key1);  //等待释放
			Delay15ms();   //跳过后沿抖动
			while(!key1);  //等待释放
			flag = 0;	   //按键标志位清零
		}
	}	
	return flag;		   //返回0表示按键按下
}
#include "config.h"
#include "delay.h"
#include "key.h"

int main(void){
	led1 = 0;
	led2 = 0;	
	while(1){
		if(!Key1_Press())led1=~led1;	//二级制取反符
		if(!Key2_Press())led2=~led2;
	}	
}

3、矩阵键盘

在config中加入

/******************************************************************************/
// 矩阵键盘引脚配置
/******************************************************************************/
sbit row_1 = P0^0;
sbit row_2 = P0^1;
sbit col_1 = P0^2;
sbit col_2 = P0^3;

LED用的是5~6

#include "config.h"
#include "delay.h"
#include "keyboard.h"

int main(void){
	led5 = 1;
	led6 = 1;
	led7 = 1;
	led8 = 1;	
	while(1){
		switch (Keyboard_Press()){
			case 0:break;			 
			case 1:led5=~led5;break; //s4 按下
			case 2:led6=~led6;break; //s5 按下
			case 3:led7=~led7;break; //s6 按下
			case 4:led8=~led8;break; //s7 按下
		}
	}	
}

#include "config.h"
#include "keyboard.h"
#include "delay.h"
uint8 Keyboard_Press(void){
	col_1 = 1;
	col_2 = 1; 

	row_1 = 0;//检测第一行
	row_2 = 1;
	if(!col_1){			   //检测第一列第一行 是否按下
		Delay15ms();	   //跳过前沿抖动
		if(!col_1){		   // 检测是否是真按下
			while(!col_1);  //等待松开 col_1=1 说明开始松开了
			Delay15ms();   //跳过后沿抖动
			while(!col_1);  //检测是否真的松开
			return 1;	  // 返回1 表示s4按下了
		}
	}
	if(!col_2){			   
		Delay15ms();	   
		if(!col_2){		   
			while(!col_2);
			Delay15ms();   
			while(!col_2);  
			return 2;
		}
	}
	
	row_1 = 1;
	row_2 = 0;
	if(!col_1){			   
		Delay15ms();	   
		if(!col_1){		  
			while(!col_1); 
			Delay15ms();   
			while(!col_1);  
			return 3;
		}
	}

	if(!col_2){			  
		Delay15ms();	 
		if(!col_2){		 
			while(!col_2);  
			Delay15ms(); 
			while(!col_2); 
			return 4;
		}
	}
		
	return 0;		   //表示什么都没按下
}

4、数码管(74HC164 驱动)实验

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wCW4zWbV-1626342622134)(E:\文档\坚果云\Typora\51学习.assets\image-20210714102016608.png)]

一个数码管如上图所示 假如 数码管共阳极 即1不亮0亮 那么输入断码0xFF(1111 1111)表示为 什么都不显示

数码管共阳极 数字转换如下,segNum为

uint8 code segNum[11] = {0x03,0x9f,0x25,0x0d,0x99,0x49,0x41,0x1f,0x01,0x09};
十进制数字16进制数据
1. (注意有点)0x9e(1001 1110)
10x9f(1001 1111)
20x25(0010 0101)
30x0d(0000 1101)
40x99(1001 1001)
50x49(0100 1001)
60x41(0100 0001)
70x1f(0001 1111)
80x01(0000 0001)
90x09(0000 1001)
00x03(0000 0011)
//config 数码管引脚配置
sbit dig1 = P3^5;//dig1
sbit dig2 = P3^4;//dig2
sbit dig4 = P3^3;
sbit dig3 = P4^0;

sbit sck = P4^6;
sbit dio  = P4^7;

//seg 
//函数名称: SEG_ Write_ _Data
//输入参数: dataWrite-写入字节
//输出参数:无
//函数功能:写一个字节,串入并出
void SEG_Write_Data(uint8 dataWrite){
	uint8 i;
	for(i=0;i<8;i++){
		if(dataWrite&0x01)dio=1;
		else dio=0;
		sck = 0;
		sck = 1;
		dataWrite >>= 1;	
	}
}
//函数名称:Dig_ Reset
//输入参数:无
//输出参数:无
//函数功能:位控制引脚复位(关闭数码管显示)
void Dig_Reset(void){
	dig1 = 1;
	dig2 = 1;
	dig3 = 1;
	dig4 = 1;
}

主函数配置 (注意这个板子没有解码器一共四个 对应申明sbit即可控制是否选中 选中的意思表示开共阳极的那一段供电)在main中给digX赋值0;表示选中第X个数码管

#include "config.h"
#include "delay.h"
#include "seg.h"


/******************************************************************************/
// 段码数组
/******************************************************************************/
uint8 code segTable[4] = {0x9e,0x25,0x0d,0x99};

int main(void){	
	uint8 i;
	while(1){
		for(i=0;i<4;i++) {
			Dig_Reset();
			SEG_Write_Data(segTable[i]);
			if(i==0)dig1=0;
			if(i==1)dig2=0;
			if(i==2)dig3=0;
			if(i==3)dig4=0;	
	   }			
	}	
}

10、DS1302 实时时钟

delay新增

void Delay1us()		//@11.0592MHz
{
	_nop_(); //_nop_();   代表运行一个机器周期。如果这个单片机的晶振是12M的,那么这调代码会运行1US;
	_nop_();
	_nop_();
}

DS1302 寄存器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dmG6uOhb-1626342622143)(E:\文档\坚果云\Typora\51学习.assets\image-20210715153033970.png)]

//DS1302 config 引脚配置
sbit sclk = P0^7;
sbit Data = P0^6;		//io
sbit ce   = P0^5;
/*
cmdWrite 地址命令 ds1302芯片固定地址配置
函数功能:读一个字节时钟信息

ox81 读秒
0x83 读分
0x85 读小时
*/
uint8 Ds1302_Read_Data(uint8 cmdWrite){
	uint8 dataRead,i;
	ce = 0;	                       // 上电ce为低电平
	sclk = 0;					   //ce拉高之前保证sclk为低
	Delay1us();
	ce = 1;// ce为使能
	for(i=0;i<8;i++){			   //8个上升沿写一个字节 写入命令字
		sclk = 0;
		Delay1us();
		if(cmdWrite&0x01)Data=1;
		else Data = 0;
		Delay1us();
		sclk = 1;
		Delay1us();
		cmdWrite >>= 1;
	}
	Data = 1;					   //释放数据线控制权
    //dataRead 根本不需要赋值 左移会移没原先的数据
	for(i=0;i<8;i++){			   //8个下降沿读取一个字节
		dataRead >>= 1;
		sclk = 1;
		Delay1us();
		sclk = 0;
		Delay1us();
		if(Data)dataRead|=0x80;	//先读的数据是最后一位
	}								  
	ce = 0;						   //读写完成 ce拉低 关闭读写
	Delay1us();
	return dataRead;
}
/*
Ds1302_Write_Data(0x8e,0x00);           // 使能打开
Ds1302_Write_Data(0x80,0x50);           //设置为50秒
Ds1302_Write_Data(0x82,0x59);           // 59分
Ds1302_Write_Data(0x84,0x91);           // 11小时
cmdWrite 地址命令 dataWrite 写入数据
函数功能:设置时钟信息
*/
void Ds1302_Write_Data(uint8 cmdWrite,uint8 dataWrite){
	uint8 i;
	ce = 0;
	sclk = 0;
	Delay1us();
	ce = 1;	//开始操作
	for(i=0;i<8;i++){				  //写入命令 倒着写
		sclk = 0;
		Delay1us();
		if(cmdWrite&0x01)Data=1;
		else Data = 0;
		Delay1us();
		sclk = 1;
		Delay1us();
		cmdWrite >>= 1;
	}
	for(i=0;i<8;i++){		//写入数据
		sclk = 0;
		Delay1us();
		if(dataWrite&0x01)Data=1;
		else Data = 0;
		Delay1us();
		sclk = 1;
		Delay1us();
		dataWrite >>= 1;
	}
	sclk = 0;
	ce = 0;
	Delay1us();			
}
#include "config.h"
#include "delay.h"
#include "seg.h"
uint8 code segNum[11] = {0x03,0x9f,0x25,0x0d,0x99,0x49,0x41,0x1f,0x01,0x09};

int main(void){	
	uint8 dataSecond,dataMin,dataHour, dataCell,showCell;             
	uint8 showData[10]={'0','0','0','0','0','0','0','0','0','0'},i;
	Ds1302_Write_Data(0x8e,0x00); 
	Ds1302_Write_Data(0x80,0x50);      
	Ds1302_Write_Data(0x82,0x59);  
	Ds1302_Write_Data(0x84,0x91); 

	while(1){
		dataSecond = Ds1302_Read_Data(0x81);                 
		dataMin    = Ds1302_Read_Data(0x83);                
		dataHour   = Ds1302_Read_Data(0x85);                  
		showData[0] = ((dataHour >> 4)&0x01) + 0x30;     //小时的十位
		showData[1] = (dataHour & 0x0f) + 0x30;          //小时的个位
		showData[2] = ((dataMin & 0x7f) >> 4) + 0x30;    //分的十位
		showData[3] = (dataMin & 0x0f) + 0x30;           //分的个位
		showData[4] = ((dataSecond & 0x7f) >> 4) + 0x30; //秒的十位
		showData[5] = (dataSecond & 0x0f) + 0x30;        //秒的个位
		for(i=0;i<4;i++){//显示时钟信息
			Dig_Reset();
			//转化为数字
			dataCell=showData[i+2]-'0';
			//判断数据是否有问题
			if(dataCell<0&&dataCell>9)
				dataCell=segNum[0];
			//数码管字符转换
			showCell=segNum[dataCell];
            //第二位加点
			if(i==1){
					showCell-=1;
			}
			SEG_Write_Data(showCell);
			if(i==0)dig1=0;
			if(i==1)dig2=0;
			if(i==2)dig3=0;
			if(i==3)dig4=0;
            
            //刷新过快会导致前几个很暗 加入延时提高各自数码管亮度
			delayXms(1);
		}
	}
}

11、蜂鸣器

//config 蜂鸣器引脚配置
sbit buzz  = P4^5;
#include "config.h"
#include "delay.h"

void buzzOn(void){
	buzz = 0;
}

void buzzOff(void){
	buzz = 1;
}


int main(void){	
	uint8 i;
	while(1){
		buzzon();
		delayXms(5);
		buzzoff();
		delayXms(5);
	}	
}

自定义delay

void DelayXms(int x)		//@12MHz
{
	unsigned char i, j;

	while(x--){
		// inside 1ms
	i = 12;
	j = 169;
		do
		{
				while (--j);
		} while (--i);
	}
}

四、报错分析与解决

*** WARNING L7: MODULE NAME NOT UNIQUE *** WARNING L2: REFERENCE MADE TO UNRESOLVED EXTERNAL

建工程的时候添加了STARTUP.A51文件。如果删除这个文件,依旧不能解决,只能重新创建项目(不要创建startup文件)才能解决,原因未知。

如果一直找不出原因 全部保存关闭软件然后重新打开!!!

五、疑惑问题

1. 矩阵引脚 LED引脚为什么会一样?

//config.
sbit led1 = P0^0;
sbit led2 = P0^1;
sbit led3 = P0^2;
sbit led4 = P0^3;
sbit led5 = P0^4;
sbit led6 = P0^5;
sbit led7 = P0^6;
sbit led8 = P0^7;

sbit row_1 = P0^0;
sbit row_2 = P0^1;
sbit col_1 = P0^2;
sbit col_2 = P0^3;

2.数码管(74HC164 驱动)实验 疑惑

3、实时时钟

标签:P0,引脚,STC,51,单片机,while,sbit,include,Data
来源: https://blog.csdn.net/TY_GYY/article/details/118765169