IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STC 51单片机自学笔记 -> 正文阅读

[嵌入式]STC 51单片机自学笔记

一、准备

硬件问题

  • 杜邦线连接 LED1-LED8 到对应单片机引脚,只要连接程序才会使灯光变化

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;

  • 而且发现led灯与矩阵键盘一起使用会冲突

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

  • 数码管 引脚配置全都是img sck dio 一脸懵逼
  • main 里面 dig=0; 不应该是在前面赋值选中吗 为什么要赋值为1是reset 0为选中
  • 写入数据的时候是如何确定写入位置的 **

3、实时时钟

  • 如何数据如何确定位置 时钟写入数据 stc 通过上升沿传入地址命令 通过下降沿数据写入
  • 为什么写入 0x50 表示50 里面用的是BCD码:用4位二级制表示一个十进制
  • 为什么处理读出的数据要+0x30
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-07-16 11:27:52  更:2021-07-16 11:28:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:39:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码