51单片机小记_Kexie

This is 51单片机

文章目录

1基础知识

十六进制与二进制的转换

1111 1110 -> 0xFE

1111 1101 -> 0xFD

1111 1011 -> 0xFB

1111 0111 -> 0XF7

C51数据类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NawnF5wB-1682944199075)(F:\TyporaMarks\图床\51单片机\屏幕截图 2022-10-26 153140.png)]

引脚的定义:
sbit 自定义名称 = 基地址//2_7 err 2^7 √

可位寻址寄存器:可以单独赋值各单元
不可位寻址寄存器:只能整体赋值

2 LED的入门

2-1 点亮一个LED

芯片控制LED于芯片相接端口的低高电压

当LED的低电压处靠近芯片时,芯片的低电压使电流流过LED,发光

当LED的低电压处靠近芯片时,芯片的高电压阻止电流流过LED,不亮

我的单片机是P1.0~P1.7八个端口分别对应LED1~8

所以若要点亮第一个灯泡,让P1整个端口块为1111 1110即可(从右向左对应P1.0~P1.7)

#include <REGX52.H>//头文件包含了52芯片的接口变量
void main(){
    P1 = 0XFE;//由于c语言不能直接读取二进制,故只能用十六进制或十进制来表示,又因两个十六进制数表示8个二进制数相当优越,故选择十六进制
}

2 - 2 LDE闪烁

闪烁:即亮——灭——亮——灭……//自然会想到循环

只有循环还不够,这不就成了高频闪烁LED?

所以,要加入延时指令//即亮一次灭一次

延时指令

在STC-ISP中已经备好了延时子函数的生成工具——软件延时计算器

生成后复制粘贴到main函数前

但还缺少了_nop_()函数,故还需引入<INTRINS.h>头文件

然后完事。

做好了的xms延时子函数

void Delay1ms(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms--){
	i = 2;
	j = 239;
	do
	{
		while (--j);
	} 
	while (--i);
	}
}

2-3 LED流水灯

流水灯 = 循环 + 延时

延时指令的加强自定义版:

  1. 使用STC-ISP生成一个1ms的函数

  2. 给上函数添加个形参(xms)表示指定毫秒

  3. 将上函数内容包含在一个while循环内

  4. while循环条件设为(xms),循环一次xms–,直到xms = 0时,自动结束循环,达到x * 1ms的目的

  5. 主函数调用时传入xms实参即可

3独立按键

独立按键位置:单片机左下角独立按键区域

轻触按键:相当于电子开关,按下接通,松开断路

3-1独立按键控制LED亮灭

独立按键一头接在VCC一头接在io口

当按下按键时,io口变为高电频电频 1

然后寄存器就会读取io口的高电频2

好了,解释完毕,开始写代码:

void main(){
//	P1 = 0XFE;//先点亮一个灯泡
    P1_0 = 0;//单独操作
    while(1){
    	if(P3_0 == 0){//当P3_0口变为低电频时
            p1_0 = 1;//灭灯
		}
        esle  P1_0 = 0;//否则松手亮灯
	}
}

在这里插入图片描述

3-2独立按键控制LED的状态

由于按键的抖动3,即右图,并不能做到让IO口即时得到稳定的电压,故需要使用到按键的消抖

按键的消抖:使用delay延时函数,延时20ms再让程序进行

void main(){
		P1 = 0xFe;//给LED初始状态
		while(1){
			if(!P3_0){//按下按键
					Delay1ms(20);//延迟20ms避免后抖动造成的影响
					while(!P3_0);//当按键还是按着没松时,用循环使程序暂停
					Delay1ms(20);//延迟20ms避免前抖动造成的影响
					P1 = ~P1;//完成灯的状态切换
			}
		}
}

3-3独立按键控制LED显示二进制

不能直接对P口进行操作,比如P1++;P1 = ~P1后P1没有发生变化

应该定义一个unsighed char 类型变量4,在循环中修改该变量再赋值给P1;

void main(){
 		unsigned char LEDNum = 0;
		while(1){
			if(!P3_0){	
				Delay1ms(20);
				while(!P3_0);
				Delay1ms(20);
				LEDNum++;
				P1 = ~LEDNum;
			}
			if(!P3_1){	
				Delay1ms(20);
				while(!P3_1);
				Delay1ms(20);
				LEDNum--;
				P1 = ~LEDNum;
			}
		}
}

解释一下其中~LED存在的必要性:因为我的51单片机LED负极接芯片(具体参考2-1),故当LEDNum = 0000 0001时,~LEDNum就会等于1111 1110,第一位灯泡就会发光

3-4独立按键控制LED移位

知识点:

  • 用LEDNum表示对0X01移位量

  • 移位操作

  • 越界判断if(LEDNum > 8)LEDNum = 0;

4数码管

在这里插入图片描述

4-1静态数码管显示

由数码管引脚结构5决定了,单个数码管上无论有几个数字单元,静态显示都只能显示同一数字

选中数码管:(共阴极情况)让被选中数码管公共端为0,其他为1

所以使其显示步骤:

  1. 控制译码器6的左上角三个口,选中指定数码管7
  2. 给缓冲器8上的P0口段码,实现指定数码管的数字点亮

数码管段选代码(段选高电频选中数码管)

	0x3F,  //"0"
    0x06,  //"1"
    0x5B,  //"2"
    0x4F,  //"3"
    0x66,  //"4"
    0x6D,  //"5"
    0x7D,  //"6"
    0x07,  //"7"
    0x7F,  //"8"
    0x6F,  //"9"
    0x77,  //"A"
    0x7C,  //"B"
    0x39,  //"C"
    0x5E,  //"D"
    0x79,  //"E"
    0x71,  //"F"
    0x76,  //"H"
    0x38,  //"L"
    0x37,  //"n"
    0x3E,  //"u"
    0x73,  //"P"
    0x5C,  //"o"
    0x40,  //"-"
    0x00  //熄灭	

代码实现:

5模块化

5-1模块化编程

把各个模块的代码放在不同的.c文件中,在.h文件中提供外部可调用函数的声明,其他.c文件想使用其中的代码时,只需要引入头文件

在这里插入图片描述

预编译

以#开头

  • #include <文件名>就是把括号内文件所有内容复制粘贴到本文件

  • 常用头文件预编译套式

    #ifndef
    #define
    ......
    #endif
    

    作用:防止重复定义/声明函数

LCD1602调试工具

在这里插入图片描述

6矩阵键盘

6-1矩阵键盘

扫描的概念:读取第一行(列)——读取第二行(列)——读取第三行(列)……然后快速循环这个过程,最终实现所有按键同时检测的效果

在这里插入图片描述

扫描的实现:

逐列扫描:

  • 先给P10~P13赋低电频9

  • 然后在P14~P17口依次检测电频,为低,则行列交叉处按键被按下

6-2矩阵键盘密码锁

知识点:

  • 矩阵键盘的扫描
  • 十进制的左移(num*=10)
  • 用一个Count计数,限制密码长度
  • 取一个按键作为确认键
  • 判断密码正误
  • 保证程序可循环进行,正误判断完清零Password和Count

7中断

在这里插入图片描述

定时器

定时器作用:

  • 用于计时系统,完成软件计时,或者使程序每隔一段时间完成一项操作
  • 替代长时间的Delay,提高CPU的运行效率10

STC89C52的T1和T2有四种工作模式:

模式0:13位定时器/计数器(M1=0 ,M0=0)

模式1:16位定时器/计数器(常用)(M1=0 ,M0=1)
在这里插入图片描述

模式2:8位自动重装模式(M1=1 ,M0=0)

模式3:两个8位计数器(M1=1 ,M0=1)

中断系统解释

存在的需求:中断系统是为使 CPU 具有对外界紧急事件的实时处理能力而设置的。

定义:当中央处理机 CPU 正在处理某件事的时候外界发生了紧急事件请求,要求 CPU 暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。

中断优先级:STC89C52系列单片机提供了8个中断请求源,它们分别是:

​ 外部中断0(INT0)、
​ 定时器0中断、
​ 外部中断1(INT1)、
​ 定时器1中斯、
​ 串口( UART )中断、
​ 定时器2中断、
​ 外部中断2(INT2)、
​ 外部中断3(INT3)。

所有的中断都具有4个中断优先级

中断号

在这里插入图片描述

图上是声明加“;”,定义的时候是加“{}”。(可以放在main函数后面)

定时器和中断执行图

自动重载

当计数器溢出后,自动恢复到指定的重载值

TMOD:定时器/计数器工作模式寄存器(不可位寻址)

在这里插入图片描述

  • 由M1和M0共同决定定时器工作模式

TCON :定时器/计数器中断控制寄存器(可位寻址)

在这里插入图片描述

  • TF0:中断溢出标志位11(建议开局先清零,防止刚配置好就产生中断)
  • TR0:T0运行控制位12

定时器定时方法:
计数器只能从0记到65535,每隔1微妙(um)计数+1,每一次溢出需要经历时间65536us。
那么可以每隔1ms让计时器产生中断(即附初值,给64536)所以计时时间为1ms

启动T0定时器

  1. 选择定时器工作模式(设定TMOD)//TMOD = 0X01
  2. 设定TCON:清零TF0、设置TR0 //TF0 = 0;TR01
  3. 给定时器赋初值 //TH0 = 64536 / 256; TL0 = 64536 % 256 //这样赋值的话,1us后计数器+1
  4. 配置中断部分断路连通 //ET0 = 1; EA = 1; PT0 = 0;
  5. 定义void Timer0_Routine() interrupt 1中断程序
  6. 在中断程序中加入计数器的重新赋值语句保证每1us溢出一次 //TH0 = 64535 / 256; TL0 = 64535 % 256;
  7. 之后想定时xs运行某程序,创建个全局int变量,并if判断(int == x000)

事后:
TMOD = 0X01过于生草,目前控制一个定时器还好,要是此时还有另一个定时器在工作的话,一定会关闭另一个定时器,所以有了以下改进:
TMOD = TMOD & 0XF0;//把TMOD的低四位清零,高四位保持不变
TMOD = TMOD | 0X01;//把TMOD的最低位赋1,高四位保持不变

事实:定时器并不容易模块化,因为其中常需引用main函数中的一些数据

应对措施:在主函数外写个.c文件

STC-ISP小工具:
在这里插入图片描述

IE – 中断允许控制寄存器

IE 中断允许控制寄存器
序号 D7 D6 D5 D4 D3 D2 D1 D0
符号 EA – ET2 ES ET1 EX1 ET0 EX0
说明:
EA 全局中断允许位,当此位是1时中断可用。(重要)
ET2 定时器/计数器2中断允许位
ES 串口中断允许位
ET1 定时器/计数器1中断允许位
EX1 外部中断1允许位
ET0 定时器/计数器0中断允许位
EX0 外部中断0允许位

TCON – 控制寄存器

TCON 控制寄存器
序号 D7 D6 D5 D4 D3 D2 D1 D0
符号 TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
说明:
TF1 定时器1溢出标志位
TR1 定时器1运行控制位
TF0 定时器0溢出标志位
TR0 定时器0运行控制位
IE1 外部中断1请求标志 IE1=1则外部中断1在向CPU请求中断,当CPU响应中断时硬件清0。一般不用手动设置。
IT1 外部中断1触发方式选择位 该位为0时INT1引脚上的低电平信号可触发外部中断1。该位为1时INT1引脚上的负跳变信号可触发外部中断1。
IE0 外部中断0请求标志 IE0=1则外部中断0在向CPU请求中断,当CPU响应中断时硬件清0。一般不用手动设置。
IT0 外部中断0触发方式选择位 该位为0时INT0引脚上的低电平信号可触发外部中断1。该位为1时INT1引脚上的负跳变信号可触发外部中断1。(重要)

低电平信号 :即该引脚电平信号为0时有效。
负跳变信号 :意思是电平从高跳至低时有效,即1→0的信号,而一直维持低电平则不会触发中断。

外部中断

除了定时器中断外,51单片机还有两个外部中断源——外部中断0、外部中断1。分别由单片机的12号引脚(INT0/P3.2)、13号(INT1/P3.3)引脚的低电平/负跳变触发。

和定时器中断一样,要使用这两个外部中断,首先要进行初始化操作,即写入相关的寄存器。初始化外部中断需要写入下面两个寄存器。

和定时器中断类似,使用外部中断需要开启全局中断允许位EA,以及开启外部中断0允许位EX0

外部中断Init代码演示

void Int1_Init(){
  //中断允许寄存器IE
    EA = 1;     //开启总中断
    EX1 = 1;    //开启1号外部中断
 //控制寄存器TCON
    IT1 = 0;   // 设置外部中断触发方式.  // 0-低电平触发  // 1-负跳变触发
 //中断请求标志IE清零(没必要,但是保险起见)
    IE1 = 0;
}

按键控制LED流水灯模式

程序设计思路:

  1. 创建定时器并初始化 //使用Timer0_Init()函数(自定义);

  2. 初始化LED流水灯

    以下内容用死循环框住

  3. 记录按下的按键

  4. 对按键进行条件判断(每次按下按键都要对自定义计时变量进行清零(Count))

    • 按键1:MODE增加
    • 按键2:
  5. 在interrupt语块中完成各MODE的定义

定时器中断的特殊用处——MilliSecond(CD思维)

假设定时器1ms中断一次,需求是3s的CD(比如按键按了一次,3s内再按就无效)
那就定义一个MilliSecond全局变量(unsigned long),每进一次中断就自增一次。

然后在主函数中定义一个CD局部变量(unsigned long),每次按下按键CD = MilliSecond + 3000;
在按键检测函数外部套一个if语句判断CD是否小于MilliSecond,小于才能通过判断

题外话:

  1. 可以利用定时器内的counter变量,达到每1s让MilliSecond自增,达到CD和MilliSecond只需要unsigned char就能使用的目的
  2. 建议使用0号计时器,中断优先度较高,不容易被其他中断影响导致没记到时
  3. 学会使用这个可以大大减少Delay的数量并且可以一个MilliSecond给多个CD(buttonCD、distanceCD……)使用

代码实现

unsigned long MilliSecond = 0;
void main(){
    Init0_Init();
    unsigned long CD = 0;
    if(CD < MilliSecond){
        if(P3_2 == 0){
            //…………
            CD = MilliSecond + 3000;
        }
    }
}
void Timer0_Routine() interrupt 1{
	MilliSecond++;
}

8串口通信

8-1串口概念

51单片机内部自带 UART ( Universal Asynchronous ReceIver Transmitter ,通用异步收发器),可实现单片机的串口通信。

STC89C52的UART有四种工作模式:

  • 模式0:同步移位寄存器
  • 模式1:8位UART,波特率可变(常用)
  • 模式2:9位UART,波特率固定
  • 模式3:8位UART,波特率可变

硬件电路

  • 简单双向串口通信有两根通信线(发送端 TXD 和接收端 RXD )
  • XD 与 RXD 要交叉连接
  • 当只需单向的数据传输时,可以直接一根通信线
  • 当电平标准不一致时,需要加电平转换芯片
    在这里插入图片描述

电平标准

在这里插入图片描述

差分信号

稳定性非常好且安全传输距离1KM开外

CAN总线就是采用差分信号

常用通信接口比较

在这里插入图片描述

相关术语

在这里插入图片描述

串口参数及时序图

在这里插入图片描述

了解一下奇校验和偶校验

串口模式图

在这里插入图片描述

串口和中断系统

在这里插入图片描述

串口相关寄存器

在这里插入图片描述

SCON:串行控制寄存器(可位寻址)

在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 只需要用到工作方式1,所以SM0 = 0;SM1 = 1;SM2 = 0;TB8 = 0;RB8 = 0;
  • 根据实时需求软件控制REN的赋值
  • TI发送中断请求标志位,一开始赋予0,文件传完(用while(TI == 0);判断)后再给到1
  • RI接收中断请求标志位,一开始赋予0,文件接收完(用while(RI == 0);判断)后再给到1

PCON电源控制及波特率选择寄存器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sp2ujq7u-1682944199080)(F:\TyporaMarks\图床\51单片机\image-20221103090447357.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4bn7yh3w-1682944199080)(F:\TyporaMarks\图床\51单片机\image-20221103090553263.png)]

有关波特率

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CGTMCn6H-1682944199081)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20230216111229273.png)]

STC-ISP上的波特率计算器

相关设置要与图一致

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Out2VZy8-1682944199081)(F:\TyporaMarks\图床\51单片机\image-20221105144922457.png)]

ps:AUXR的两行删了,89C52单片机没有这个高级的模块

8-2串口向电脑发送数据

代码思路:

  • 先创建一个UART_Init()函数来初始化串口传输所需寄存器

    void UART_Init (void)		//4800bps@11.0592MHz
    {
    	PCON &= 0x7F;		//保持控制电源的几个接口数据不变,只控制SMOD和SOMD0的状态,因为需要波特率加倍,故SMOD给1
    	SCON = 0x40;		//串行控制寄存器,选择工作模式1,关闭接收使能,只给SM1赋1,其他保持复位状态
    	TMOD &= 0x0F;		//清空定时器1
    	TMOD |= 0x20;		//选择定时器工作模式3,故M1置1,M0置0
    	TL1 = 0xFA;		//设定定时器初值
    	TH1 = 0xFA;		设定定时器重装值
    	ET1 = 0;		//禁止定时器1中断
    	TR1 = 1;		//启动定时器1
    }
    
  • 再创建一个UART_SendByte(unsigned char Byte)()函数来传输数据

    void UART_SendByte(unsigned char Byte)(){
    	SBUF = Byte;//装载完,如果相关寄存器配置好了,自动会发送
    	while(TI == 0);//检测是否发送完成
    	TI = 0;//一旦发送完成软件复位TI
    }
    
  • 在main函数中:

  • 使用UART_Init()完成初始化

  • 使用UART_SendByte()装载数据

  • 发送……

如何发送字符串?

实用小工具里面调

8-3 串口接收电脑端数据

代码实现:

  • 先创建一个UART_Init()函数来初始化串口接收所需寄存器

    void UART_Init (void)		//4800bps@11.0592MHz
    {
    	PCON &= 0x7F;		//保持控制电源的几个接口数据不变,只控制SMOD和SOMD0的状态,因为需要波特率加倍,故SMOD给1
    	SCON = 0x40;		//串行控制寄存器,选择工作模式1,关闭接收使能,只给SM1赋1,其他保持复位状态
    	TMOD &= 0x0F;		//清空定时器1
    	TMOD |= 0x20;		//选择定时器工作模式3,故M1置1,M0置0
    	TL1 = 0xFA;		//设定定时器初值
    	TH1 = 0xFA;		设定定时器重装值
    	ET1 = 0;		//禁止定时器1中断
    	TR1 = 1;		//启动定时器1
        //在8-2的基础上增加两行,打开串口中断
    	EA = 1;			//总中断允许
    	ES = 1;			//串口中断允许
    }
    
    
  • 当串口接收到电脑端的数据时,会进入中断函数,故需要在中断函数中完成数据的提取

  • 完成中断函数的配置

    unsigned char byte;//创建一个全局变量来保存接收到的数据
    void UART_Rountine(void) interrupt 4{
    	if(RI == 1){//如果少了这个判断,发送的TI也会变成1然后进入中断
    		while(RI == 0);//检测是否接收完成
    		RI = 0;//一旦接收完成软件复位RI
    		byte = SBUF;//将寄存器收到的数据转存到byte内
    		//UART_SendByte(byte);若要将数据发回,就加上这一句话
        }	
    }
    
  • 在main函数中:

  • 使用UART_Init()完成初始化

  • while(1);暂停程序等待接收

8-4

11蜂鸣器

11-1蜂鸣器介绍

蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器

有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发生,频率固定

无源蜂鸣器:内部不自带振荡源,需要控制器提供振荡脉冲才可发生,调整提供振荡脉冲的频率,可发出不同的声音

驱动电路

三极管驱动电路

三极管驱动分为:

如果工作在放大状态 ,那么它就根据机极(B极)的信号控制导通或闭合(数字电路比模拟电路更加简单,要么导通要么闭合)

  • NPN:(把NPN比作开关连接上下方,左侧方是一个手)当左侧方来高电频,就会闭合开关,使上下方电路通路
  • PNP:(把PNP比作开关连接上下方,左侧方是一个手)当左侧方来低电频,就会闭合开关,使上下方电路通路
集成电路驱动(ULN2003D)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W9CmzxdL-1682944199081)(F:\TyporaMarks\图床\51单片机\image-20221106162744182.png)]

ULN2003D是由7对NPN达林顿管组成

达林顿管晶体管排列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAUc44Ak-1682944199081)(F:\TyporaMarks\图床\51单片机\image-20221106164904066.png)]

11-2蜂鸣器播放提示音

简单的,给NPN的左侧段上低电频

12 I2C总线

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JrmVSFfX-1682944199082)(F:\TyporaMarks\图床\51单片机\image-20221107085150541.png)]

I2C时序结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hl7pox61-1682944199082)(F:\TyporaMarks\图床\51单片机\image-20221107093553494.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UrGUydln-1682944199082)(F:\TyporaMarks\图床\51单片机\image-20221107093847798.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ytl03sUJ-1682944199082)(F:\TyporaMarks\图床\51单片机\image-20221107094108872.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ITrTKHKB-1682944199083)(F:\TyporaMarks\图床\51单片机\image-20221107094327873.png)]

I2C数据帧

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Wvx91qx-1682944199083)(F:\TyporaMarks\图床\51单片机\image-20221107094650520.png)]

语言描述:先发送起始指令,第一个字节一定要发送从机地址,然后等待从机的接收应答,若应答,则继续发送……

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aofOW8IP-1682944199083)(F:\TyporaMarks\图床\51单片机\image-20221107095103419.png)]

语言描述:先发送起始指令,第一个字节一定要发送从机地址,然后给从机接收应答,若应答,则开始接收……

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zcUfrVlY-1682944199084)(F:\TyporaMarks\图床\51单片机\image-20221107095421168.png)]

学完时序结构,就可以开始敲代码了

I2C代码实现

#include <REGX52.H>

sbit I2C_SCL = p2^1;
sbit I2C_SDA = p2^0;

void I2C_Strat(void){
	//开局I2C总线是空闲状态,没有人动他,所以SDA/SCL是两个高电频
    //还有个情况:根据往上第二张图,SDA还有可能是RA调用的,所以调用之后SDA有可能是0有可能是1
	//所以为了“S”这个模块能够拼上,先把SDA置1(拉高)。为什么呢?因为SDA有可能是0有可能是1,
    I2C_SDA = 1;
    //而SCL在其他拼图结束后都是0,所以先确保SDA是1,再把SCL拉高
    I2C_SCL = 1;
    //如果这就是刚开始,那上两句没用,如果这是程序进行到一半了,才使用到这个函数,那就需要按上面的流程来一遍拉高
    //然后根据往上第8个图,先把SDA拉低,再把SCL拉低
    I2C_SDA = 0;
    I2C_SCL = 0;
}//以上就是Start函数,“简单”写完

//接下来是 Stop函数
void I2C_Stop(void){
	//根据往上第8个图,先保证SDA是0,所以先把SDA拉低
    I2C_SDA = 0;
    //再把SCL拉高
    I2C_SCL = 1;
    //然后再把SDA拉高
    I2C_SDA = 1;
}//简单写完

//接下来写 Sendbyte函数
void I2C_SendByte(unsignde char Byte){
	//根据上7图,先把要传输字节的最高位赋给SDA,再把SCL置高,再把SCL置低
    //I2C_SDA = Byte & 0x80;//把Byte最高位取出来赋给SDA
    //I2C_SCL = 1;
 	//I2C_SCL = 0;
    //把上述三行 循环8次(这里我直接注释掉上面的,在下面重写)
    unsign char i;
    for(i = 1; i <= 8; i++){
        I2C_SDA = Byte & (0x80 >> (i-1);//这里需要对0x80动点手脚,实现从高到低位依次遍历
        I2C_SCL = 1;
        I2C_SCL = 0;
    }
}

//接下来写ReciveByte函数                           
unsigned char I2C_ReciveByte(){
	unsigned char Byte = 0X00;
    //根据往上第6图在接收之前需要先把SDA拉高
    I2C_SDA = 1;//不需要主动拉低所以不进入循环
    //再把SCL置高,接收最高位
//    I2C_SCL = 1;
//    if(I2C_SDA){Byte |= 0x80}//如果读取的是1,那么Byte最高位置一,否则不处理(还是0)
    //读取完之后把SCL拉低
//    I2C_SCL = 0;
    
    //然后还是循环8次,(这里我直接注释掉上面的,在下面重写)
    unsigned char i;
    for(i = 1; i <= 8; i++){
        I2C_SCL = 1;
        if(I2C_SDA){ Byte |= (0x80 >> (i-1)) }//这里也需要对0x80动点手脚,实现从高到低位依次遍历
        I2C_SCL = 0;
    }
    
}          
                         
//接下来写应答SendAck函数
void I2C_SendAck(unsigned char AckBit){//这里只需要传入0或1所以可以用51中特有的数据类型bit(只能保存0或1)
    //根据往上第5图需要变换一下SDA(如果给应答就给1,如果不给应答就是0)
    I2C_SDA = AckBit;//直接等于应答位
    //之后把SCL拉高,接收数据
    I2C_SCL = 1;
    //再拉低,恢复状态
    I2C_SCL = 0;
}
                          
//最后在写一个接收应答      
unsigned char I2C_ReciveAck(){
    unsigned char AckBit;
    //根据往上第4图,先把SDA拉高,再把SCL拉高
    I2C_SDA = 1;
    I2C_SCL = 1;
    //SDA接收到应答后,赋给Bit
    AckBit = I2C_SDA;
    //应答之后SCL继续清零
    I2C_SCL = 0;
    return AckBit;
}

13SPI总线

简单介绍一下

SPI(Serial Peripheral Interface)串行外接设备接口,是高速的(对比IIC或串口)、全双工(收发同时进行)、同步的(通信双方有公共的时钟参考)串行通信总线;采用主从方式工作,一般有一个或多个从设备。

SPI 设备间的数据传输之所以又被称为数据交换, 是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 “发送者(Transmitter)” 或者 “接收者(Receiver)”. 在每个 Clock 周期内, SPI 设备都会发送并接收一个 bit 大小的数据, 相当于该设备有一个 bit 大小的数据被交换了。所以在数据传输的过程中, 每次接收到的数据必须在下一次数据传输之前被采样. 如果之前接收到的数据没有被读取,
那么这些已经接收完成的数据将有可能会被丢弃, 导致 SPI 物理模块最终失效. 因此, 在程序中一般都会在 SPI传输完数据后, 去读取 SPI设备里的数据, 即使这些数据(Dummy Data)在我们的程序里是无用的

SPI主从设备关系图

SPI重要四根线

SCLK:时钟线,为收发数据双方提供时钟参考

MOSI / MISO:M -> Master(主机) S-> Slave(从机) I/O懂得都懂,不懂建议重开

CS(Chip Select):片选,用于寻址

寻址方式

当主设备要和某个从设备进行通信时,主设备需要先向对应从设备的片选线上发送使能信号(高电平或者低电平,根据从机而定)表示选中该从设备

判断从机使能信号(一般判断标准)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzEVgYtL-1682944199084)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221118190013008.png)]

上图表示低电频使能

下图表示高电频使能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FKOSpMdj-1682944199084)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221118190052965.png)]

通信过程(时序)

SPI总线在进行数据传送时,先传送高位,后传送低位;

数据线为高电平表示逻辑’1’,低电平表示逻辑’0’;

一个字节传送完成后无需应答即可开始下一个字节的传送;

SPI总线采用同步方式工作,时钟线在上升沿或下降沿时发送器向数据线上发送数据,在紧接着的下降沿或上升沿时接收器从数据线上读取数据,完成一位数据传送,八个时钟周期即可完成一个字节数据的传送;(第一个沿的时候发数据,第二个沿的时候收数据(这两个沿种类不同))

SPI交换完一个字节之后,没有应答这个概念,可以马上交换下一个字节(就会出现主机一直在发,但从机压根没收到的情况)

SPI没有起始信号,没有结束信号

时序图

MSB:最高位

LSB:最低位

如果是上图时钟信号,那么下降沿发数据,上升沿收数据

极性和相位

SPI工作模式介绍

SP1总钱有四种不同的工作模式,取决于极性( CPOL )和相位( CPHL )这两个因素

CPOL 表示 SCLK 空闲时的状态
CPOL =0,空闲时 SCLK 为低电平
CPOL =1,空闲时 SCLK 为高电平

CPHA 表示采样时刻
CPHA =0,每个周期的第一个时钟沿采样
CPHA =1,每个周期的第二个时钟沿采样

由于极性和相位各有两种情况,故SPI总线共有4中工作模式

举个例子,当CPOL=0;CPHA=0时,工作时序图如下:
在这里插入图片描述

这时候初始状态SCK为低电频,在SCK产生第一个沿(这里是上升沿)之前,第一个数据(字节的最高位)就已经输出了(无论主从机),当第一个上升沿产生时,设备开始采样并存入寄存器(无论主从机),第一个下降沿产生时,设备(无论主从机)继续输出下一个数据(第二位),之后以此类推……

SPI工作模式决定因素

需要说明的是,对于一个特定的从设备来说,一般在出厂时就会将其设计为某种特定的工作模式;
我们在使用该设备时就必须保证主设备的工作模式和该从设备保持一致,否则是无法进行通信的;
所以一般我们需要对主设备的 CPOL 和 CPHA 进行配置;

从设备的极性和相位看指导手册

14AT24C02存储器

存储器简化模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qMnddbog-1682944199085)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221106172735576.png)]

AT24C02

引脚及应用电路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OID8OHnF-1682944199085)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221107083756073.png)]

内部结构框图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-efArn9FH-1682944199086)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221107084504328.png)]

AT24C02数据帧

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-peXTrW9J-1682944199086)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221107095616645.png)]

较为先进的表达方式:
在这里插入图片描述

12-1AT24C02数据存储

atc.c下有两个函数,I2C.c下有6个函数

代码编写思路(清翔51单片机)

//先编写好AT24C02.c/.h 和 I2C.c/.h (前者继承后者)
//1. I2C.c:
#include <REGX52.H>

sbit I2C_SCL = p2^1;
sbit I2C_SDA = p2^0;

void I2C_Strat(void){
	//开局I2C总线是空闲状态,没有人动他,所以SDA/SCL是两个高电频
    //还有个情况:根据往上第二张图,SDA还有可能是RA调用的,所以调用之后SDA有可能是0有可能是1
	//所以为了“S”这个模块能够拼上,先把SDA置1(拉高)。为什么呢?因为SDA有可能是0有可能是1,
    I2C_SDA = 1;
    //而SCL在其他拼图结束后都是0,所以先确保SDA是1,再把SCL拉高
    I2C_SCL = 1;
    //如果这就是刚开始,那上两句没用,如果这是程序进行到一半了,才使用到这个函数,那就需要按上面的流程来一遍拉高
    //然后根据往上第8个图,先把SDA拉低,再把SCL拉低
    I2C_SDA = 0;
    I2C_SCL = 0;
}//以上就是Start函数,“简单”写完

//接下来是 Stop函数
void I2C_Stop(void){
	//根据往上第8个图,先保证SDA是0,所以先把SDA拉低
    I2C_SDA = 0;
    //再把SCL拉高
    I2C_SCL = 1;
    //然后再把SDA拉高
    I2C_SDA = 1;
}//简单写完

//接下来写 Sendbyte函数
void I2C_SendByte(unsignde char Byte){
	//根据上7图,先把要传输字节的最高位赋给SDA,再把SCL置高,再把SCL置低
    //I2C_SDA = Byte & 0x80;//把Byte最高位取出来赋给SDA
    //I2C_SCL = 1;
 	//I2C_SCL = 0;
    //把上述三行 循环8次(这里我直接注释掉上面的,在下面重写)
    unsign char i;
    for(i = 1; i <= 8; i++){
        I2C_SDA = Byte & (0x80 >> (i-1);//这里需要对0x80动点手脚,实现从高到低位依次遍历
        I2C_SCL = 1;
        I2C_SCL = 0;
    }
}

//接下来写ReciveByte函数                           
unsigned char I2C_ReciveByte(){
	unsigned char Byte = 0X00;
    //根据往上第6图在接收之前需要先把SDA拉高
    I2C_SDA = 1;//不需要主动拉低所以不进入循环
    //再把SCL置高,接收最高位
//    I2C_SCL = 1;
//    if(I2C_SDA){Byte |= 0x80}//如果读取的是1,那么Byte最高位置一,否则不处理(还是0)
    //读取完之后把SCL拉低
//    I2C_SCL = 0;
    
    //然后还是循环8次,(这里我直接注释掉上面的,在下面重写)
    unsigned char i;
    for(i = 1; i <= 8; i++){
        I2C_SCL = 1;
        if(I2C_SDA){ Byte |= (0x80 >> (i-1)) }//这里也需要对0x80动点手脚,实现从高到低位依次遍历
        I2C_SCL = 0;
    }
    
}          
                         
//接下来写应答SendAck函数
void I2C_SendAck(unsigned char AckBit){//这里只需要传入0或1所以可以用51中特有的数据类型bit(只能保存0或1)
    //根据往上第5图需要变换一下SDA(如果给应答就给1,如果不给应答就是0)
    I2C_SDA = AckBit;//直接等于应答位
    //之后把SCL拉高,接收数据
    I2C_SCL = 1;
    //再拉低,恢复状态
    I2C_SCL = 0;
}
                          
//最后在写一个接收应答      
unsigned char I2C_ReciveAck(){
    unsigned char AckBit;
    //根据往上第4图,先把SDA拉高,再把SCL拉高
    I2C_SDA = 1;
    I2C_SCL = 1;
    //SDA接收到应答后,赋给Bit
    AckBit = I2C_SDA;
    //应答之后SCL继续清零
    I2C_SCL = 0;
    return AckBit;
}
//2.AT24C02.c
#include <REGX52.H>
#inlcude "I2C.h"

//需要宏定义AT24C02的地址
#define  AT24C02_ADDRESS  0XA0//这是写地址,把最后一位置1(0xA1)就是读地址了
void AT24C02_WriteByte(unsigned char WordAddress,Data){//根据往上第11图可知写数据函数需要传入字地址、数据两个变量
    I2C_Strat();//启动
    I2C_SendByte(AT24C02_ADDRESS);//写地址
    I2C_ReciveAck();//接收AT24C02的应答
    I2C_SendByte(WordAddress);//写字地址
    I2C_ReciveAck();//接收AT24C02的应答
    I2C_SendByte(Data);//写数据
    I2C_ReciveAck();//接收AT24C02的应答
    I2C_Stop();//结束
    //可以在这里给个测试,判断电路是否连接正常,如果正常,就应该有应答
    //if(I2C_ReciveByte() == 0)P1 = 0x00;//把灯全点亮
}

unsigned char AT24C02_ReadByte(unsigned char WordAddress){
    unsigned char Data;
    I2C_Strat();//启动
    I2C_SendByte(AT24C02_ADDRESS);//写地址
    I2C_ReciveAck();//接收AT24C02的应答
    I2C_SendByte(WordAddress);//写字地址
    I2C_ReciveAck();//接收AT24C02的应答
    
    I2C_Strat();//重新启动
    I2C_SendByte(AT24C02_ADDRESS | 0x01);//置1最后一位,变成读地址
    I2C_ReciveAck();//接收AT24C02的应答
    Data = I2C_ReciveByte();//接收数据
    I2C_SendAck(1);//发送不应答
    I2C_Stop();
    return Data;	
}
		


根据交流电气特性,写周期比较长,所以每次写入ROM数据后需要Delay个5ms,避免没写成功

知道上文之后,开始写main函数

#include "Key.h"
#include "Delay.h"
#include "LCD1602.h"
#include <REGX52.H>
#include "AT24C02.h"


void main(){
	unsigned char Data;
	LCD_Init();
	LCD_ShowString(1,1,"Welcome");
	AT24C02_WriteByte(1,121);//往第一位写入66
	Delay(5);
	Data = AT24C02_ReadByte(1);
	LCD_ShowNum(2,1,Data,3);	
	while(1){
		
	}
	
}//至此以上

15直流电机驱动(PWM)

直流电机介绍

当电机正接时,电机正转;当电机反接时,电机反转。

除直流电机外,常见的电机还有步进电机、舵机、无刷电机、空心杯电机等。

电机驱动电路

大功率器件直接驱动:不可以变换方向
图中D1续流二极管用来保护电路,因为电机电感很强(高中知识)//由此可衍生出如果设计电路时,有强电感元件,要留意该元件发生电感时是否会击穿别的原件

H桥驱动:可以变换方向
但是由于M桥驱动可以正反通电,无法加装续流二极管,所以要求四个三极管具有很强的耐压性

PWM(Pulse Width Modulation)

PWN介绍

PWM:即脉冲宽度调制,在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速、开关电源等领域。

PWM重要参数: 频率=1/ Ts 占空比 = Ton / Ts 精度=占空比变化步距
图解:

产生PWM方法

PWM频率不宜过快,会加快开关的损耗;也不宜过慢,会产生抖动

15-1 LED呼吸灯

//直接上代码
#include <REGX52.H>
sbit LED = P1^0;

void Delay(unsigned int times){//
	while(times --);
}

void main(){
	unsigned int time = 50;//time%
	unsigned int i;
	while(1){//将time作为占空比
		for(time = 0; time < 100; time++){//由暗变亮
			for(i = 0; i < 20; i++){//每个状态执行个20次,降低频闪
				LED = 0;
				Delay(time);
				LED = 1;
				Delay(100 - time);
			}
		}
		for(time = 100; time > 0; time--){//由亮变暗
			for(i = 0; i < 20; i++){
				LED = 0;
				Delay(time);
				LED = 1;
				Delay(100 - time);
			}
		}
	}
}//以上即为PWM思路(但不完整,应当在定时器中断中完成)

15-2在中断中实现PWM

定时器设置图

代码思路

  1. 先完成定时器的初始化(其中定时器计时范围要适当改变,因为PWM频率不宜过快,会加快开关的损耗;也不宜过慢,会产生抖动)

    //代码设置参考上图
    #include <REGX52.H>
    
    void Timer0_Init(){//100微秒@11.0592MHz
    	TMOD &= 0XF0;
    	TMOD |= 0X01;
    	TL0 = 0XA4;
    	TH0 = 0XFF;
    	TF0 = 0;
    	TR0 = 1;
    	ET0 = 1;
    	EA = 1;
    	PT0 = 0;
    }
    
    
  2. 配置中断函数

    void Timer0_Routine() interrupt 1{		
    	TL0 = 0XA4;
    	TH0 = 0XFF;
    	Counter++;
    	Counter %= 100;
    	if(Counter < Compare){
    		LED = 0;
    	}
    	else {
    		LED = 1;
    	}
    }
    
  3. 完成main函数

    #include <REGX52.H>
    #include "Timer0.h"
    #include "Key.h"
    #include "Nixie.h"
    
    sbit LED = P1;
    unsigned char Counter,Compare;//Counter影响精度,也影响频率
    unsigned char KeyNum,Speed;
    void main(){
    	Timer0_Init();
    	//Compare = 90;//值越大,“真实电流”越大
    	while(1){
    		KeyNum = Key();
    		if(KeyNum == 1){
    			Speed++;
    			Speed %= 4;
    			if(Speed == 0){Compare = 0;}
    			if(Speed == 1){Compare = 5;}
    			if(Speed == 2){Compare = 50;}
    			if(Speed == 3){Compare = 100;}
    		}
    		Nixie(1,Speed);
    	}
    }
    

16 AD/DA

介绍

AD ( Analog to Digital ):模拟﹣数字转换,将模拟信号转换为计算机可操作的数字信号

DA ( Digital to Analog ):数字﹣模拟转换,将计算机输出的数字信号转换为模拟信号(可被PWM替代)

AD / DA 转换打开了计算机与模拟信号的大门,极大的提高了计算机系统的应用范围,也为模拟信号数字化处理提供了可能

数字信号来源:光敏、热敏、麦克风等阻值会变化的元器件与一个电阻串联,当阻值变化时元器件两端电压也发生变化,变化值即为数字信号

ps:其实DA原理就是一种根据模拟信号将电压改变成指定大小的装置,自己设置的电路也可以

运算放大器

​

放大器放大倍数理想情况下是无穷大,所以在实际使用中会加上负反馈,这时候放大倍数不由内部放大决定,而是由负反馈系数决定,负反馈图如下:
在这里插入图片描述

放大计算

运放电路

电压跟随器:Vin和Vout相同但是Vout的频率可以被增大,提高电流的驱动能力

DA原理(数字﹣模拟转换)

DA原理图

通过D7~D0输入的信号来控制上图中各个开关,以达到电压的不同值

PWM型DA转换器

<img src="F:\TyporaMarks\51单片机小记_Kexie.assets\ima

坏处:浪费cpu性能来输出PWM波形

好处:精度比较高,PWM占空比越精细,输出电压就越精细

AD原理(模拟﹣数字转换)

AD原理图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8IrwKlHY-1682944199087)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221109204947358.png)]

基本思路:

  1. 一个信号,不确定多少电压(比如2.5V),我就用DAC输出一个电压,和这个未知的电压进行比较,如果未知的更大,那就把DAC输出高一点,反之;最终让这两个电压最终接近相等(在比较器进行)

    ps:这里涉及到一个二等分判断,在二进制中的二分法是:先找到被比电压的含1最高位,DAC中对应位置0(更高位当然也是全0),然后比较后续数字,如果被比是0,那就置1,反之。

  2. 得到最终的近似电压后,在加一些定时和控制,将DAC的电压输入缓冲器,然后输出

  3. 将输出的电压计算成VIN/VREF * 256(结果取整)

对ADC0809的理解

源自CSDN

ADC0809简介

  • ADC0809是采用COMS工艺制造的双列直插式单片8位A/D转换器。分辨率8位,精度7位,带8个模拟量输入通道,有通道地址译码锁存器,输出带三态数据锁存器。启动信号为脉冲启动方式,最大可调节误差为±1LSB。
  • ADC0809内部没有时钟电路,故CLK时钟需由外部输入,fclk允许范围为500kHz-1MHz,典型值为640kHz。每通道的转换需要66~73个时钟脉冲,大约100-110us。(转换时间)工作温度范围为-40℃—+85℃。功耗为15mW,输入电压范围为0–5V,单一+5V电源供电。

ADC0809的引脚介绍

  • IN0~IN7 : 8路模拟量输入端
  • D0~D7:8位数字量输出端
  • ADDA、ADDC、ADDC:3位地址输入线,用于选择8路模拟通道中的一路,选择情况见表。
  • ALE:地址锁存允许信号,输入,高电平有效
  • START:A/D转换启动信号,输入,高电平有效
  • EOC:A/D转换结束信号,输出。当启动转换时,高引脚为低电平,当A/D结束转换时,高引脚输出高电平。
  • OE:数据输出允许信号,输入,高电平有效。当转换结束后,如果从该引脚输入高电平,则打开输出三态门,输出锁存器的数据
  • D0~D7送出。
  • CLK:时钟脉冲输入端。要求时钟频率不高于640KHZ.
  • REF+、REF-:基准电压输入端。
  • VCC:电源,接+5V;
  • GND:地。

AD/DA性能指标

分辨率:指 AD / DA 数字量的精细程度,通常用位数表示。例如,对于5V电源系统来说,8位的 AD 可将5V等分为256份,即数字量变化最小一个单位时,模拟量变化5V/256=0.01953125V,所以,8位 AD 的电压分辨率为0.01953125V, AD / DA 的位数越高,分辨率就越

转换速度:表示 AD / DA 的最大采样/建立频率,通常用转换频率或者转换时间来表示,对于采样/输出高速信号,应注意 AD / DA 的转
换速度

XPT2046

### 功能说明:

XPT2046时序图

特点:可复用(一组总线挂多个负载)

DCLK、BUSY、DOUT三条线是公用的

DIN有时也叫MISO(主设备输入,从设备输出),DOUT有时也叫MOSI(主设备输出,从设备输入)

DCLK上升沿输入,下降沿输出

XPT2046运行顺序

  1. CS拉低(位选)
  2. DIN发送一个字节(控制字)(高位在前)(DCLK上升沿)
  3. 然后连续读两个字节(第二个字节的后四位没用,用0填充)(DCLK下降沿),
  4. CS拉高结束时序
  5. 就得出了12位的AD值,读入单片机

XPT2046代码实现

#include <REGX52.H>

sbit XPT2046_CS = P3^7;
sbit XPT2046_DCLK = P2^1;
sbit XPT2046_DIN = P2^0;
sbit XPT2046_DOUT = P2^5;

unsigned int XPT2046_ReadAD(unsigned char Command){//Command是控制字
	unsigned int ADValue;//返回值
	unsigned char i;
	XPT2046_DCLK = 0;//在程序开始时DCLK默认是0,这里给个0防错误
	XPT2046_CS = 0;//CS置低,选中XPT2046

	for(i = 0; i < 8; i++){
		XPT2046_DIN = Command & (0x80 >> i);//将控制字的从左往右第(i+1)位给DIN
		XPT2046_DCLK = 1;//上升沿让数据输入主机
		XPT2046_DCLK = 0;//下降沿恢复
	}
	//再给DCLK个上升沿接下降沿,开始接收数据
	XPT2046_DCLK = 1;
	XPT2046_DCLK = 0;
	
	
	for(i = 0; i < 16; i++){
		XPT2046_DCLK = 1;//上升一下起高电平
		XPT2046_DCLK = 0;//下降沿让DOUT输出数据
		if(XPT2046_DOUT){ADValue |= (0x8000 >> i);}//如果DOUT为1时,就把Value的第(i+1)位赋值1
	}

	XPT2046_CS = 1;//CS置高,取消片选
	return ADValue;
}

关于控制字(Command)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QCtdQzw9-1682944199087)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221111190937289.png)]

起始位第一位,即S位必须是1

地址 表中显示+IN的就是表示;XP就是X+;YN就是Y-

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yFnfkFDs-1682944199087)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221111191558025.png)]

MODE使用的是8位模式,置一(位数越高显示越精细)

SER/DFR根据单片机上线路连接情况,选择单端模式,置一

PD1-PD0如果给11的话,使用的是内部的参考电压(2.5V),又因为单片机上的电压为0~5V所以会有2.5到5V的实际电压检测不到,故选择00

17时钟

晶振

​ 首先,单片机能正常工作的必要条件之一就是时钟电路,时钟是单片机的脉搏,是单片机的驱动源,单片机工作是在统一的时钟脉冲控制下一拍一拍进行工作的。这个脉冲由单片机控制器中的时序电路发出的。所以单片机就很需要晶振。

​ 晶振,全称是石英晶体振荡器,是一种高精度和高稳定度的振荡器。通过一定的外接电路来,可以生成频率和峰值稳定的正弦波。而单片机在运行的时候,需要一个脉冲信号,做为自己执行指令的触发信号,可以简单的想象为:单片机收到一个脉冲,就执行一次或多次指令。

   单片机工作时,是一条一条地从ROM中取指令,然后一步一步地执行。单片机访问一次存储器的时间,称之为一个机器周期,这是一个时间基准。—个机器周期包括12个时钟周期。如果一个单片机选择了12兆赫兹晶振,那么它的时钟周期是1/12us,它的一个机器周期是12×(1/12)us,也就是1us。

概述

​ 任何外设都需要时钟,51单片机,stm32等等,我们知道寄存器是由D触发器组成的,往触发器里面写东西,前提条件是有时钟输入。

​ 51单片机不需要配置时钟,是因为一个时钟开了之后所有的功能都可以用了,而这个时钟是默认开启的,比如有一个水库,水库有很多个闸,这些闸默认是开启的,因此每个闸都会出水,任意一个闸可以直接用,但存在一个问题,没用到的闸也在出水,即也在耗能。那么水库是能源,闸可认为每个外设的使用状态,时钟可认为是闸的开关。

 stm32之所以是低功耗,它将所有的门都默认设置为disable,需要用哪个开哪个闸,即你用什么外设,打开相应的外设的时钟就可以,其它还是disable,即耗能就会减少。
  51单片机中一个时钟把所有的都包了,而stm32的时钟是有分工的,并且每类时钟的频率不一样,因为没必要所有的时钟都是最高频率,够用即可,好比一个闸水流大小,我就洗个水,出来洗澡的水,没必要,消耗能源也多,所以不同的时钟也会有频率差别,或者在配置的时候可以配置时钟分频。

内部时钟与外部时钟

​ 单片机的时钟信号由外部振荡和内部振荡两种方式得到

​ 内部时钟:一般采用使用11.0592MHz的晶体振荡器作为振荡源,由于单片机内部带有振荡电路,所以外部只要连接一个晶振和两个电容(C1、C2)即可,电容容量一般在15pF至50pF之间,对频率有微调作用。即构成了自激振荡器,发出的脉冲直接送入内部时钟电路。

​ 注意:晶振和电容尽可能安装的与单片机引脚XTAL1和XTAL2靠近。为了减少寄生电容,更好的保证振荡器稳定。

​ 外部时钟:将外部振荡脉冲接入XTAL1和XTAL2,即把已有的时钟信号引入单片机内,外部时钟方式适宜用来使单片机的时钟与外部信号一致。对于HMOS的单片机,外部时钟信号由XTAL2引入,对于CHMOS的单片机,外部时钟由XTAL1引入。

18OLED

介绍

0.96寸:显示画面128*64个led组成

要确定显示位置和内容其实也就是确定指定的led发光

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k3Mor5Tq-1682944199087)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221115151250925.png)]

  1. GND 电源地
  2. VCC 电源正(3~5.5V)
  3. SCL OLED 的 D0 脚,在 IIC 通信中为时钟管脚
  4. SDA OLED 的 D1 脚,在 IIC 通信中为数据管脚

写函数

OLED默认地址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GXTqXyfM-1682944199088)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221113205542540.png)]

0111100x[^x -> R/W]
一般都是写入地址,就是0X78

控制字节

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-50hrfp87-1682944199088)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221113210400459.png)]

Co:如果Co置0,那么后续发送的数据将会一个字节一个字节的处理

D/C:如果为0,表示后续发送为的是命令;如果为1,表示后续发送的是数据

如果是发送控制字节命令,就是0x00;如果是发送写数据命令,就是0X40。

OLED_Write_Cmd(unsigned char Cmd)

  1. I2C_Start();//启动
  2. I2C_SendByte(0X78);//写地址
  3. I2C_ReciveAck();//接收从机应答
  4. I2C_SendByte(0X00);//发送控制命令字节
  5. I2C_ReciveAck();//接收从机应答
  6. I2C_SendByte(Cmd);//发送命令
  7. I2C_ReciveAck();//接收从机应答
  8. I2C_Stop();//停止

OLED_Write_Byte(unsigned char byte)

  1. I2C_Start();//启动
  2. I2C_SendByte(0X78);//写地址
  3. I2C_ReciveAck();//接收从机应答
  4. I2C_SendByte(0X40);//发送写数据字节
  5. I2C_ReciveAck();//接收从机应答
  6. I2C_SendByte(Cmd);//发送命令
  7. I2C_ReciveAck();//接收从机应答
  8. I2C_Stop();//停止

初始化

需要通过“写函数”连续通过I2C写数据进OLED以达成初始化,其中写的内容从表上照抄就行,全国统一,但这里就需要学习“写函数”

当封装好OLED的写操作相关函数之后,就可封装OLED的初始化函数

清屏函数

void OLED_Clear(void){
    unsigned char i,j;
    OLED_Write_Cmd(0x20);//设置地址模式
    OLED_Write_Cmd(0x02);//页寻址模式
    for(i=0;i<8;i++){
        OLED_Write_Cmd(0xb0+i);
        OLED_Write_Cmd(0x00);
        OLED_Write_Cmd(0x10);
        for(j=0;j<128;j++){
            OLED_Write_Byte(0x00);
        }
    }
}

初始化函数

void OLED_initial(void)//OLED初始化函数
{
Delay(500);
OLED_Write_Cmd(0xae);//–turn off OLED panel 关闭显示
OLED_Write_Cmd(0x00);//—set low column address设置起始列的低四位 0x0x
OLED_Write_Cmd(0x10);//—set high column address设置起始列的高四位0x1x
OLED_Write_Cmd(0x40);//–set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_Write_Cmd(0x81);//–set contrast control register设置对比度
OLED_Write_Cmd(0xff); // Set SEG Output Current Brightness对比度为oxff
OLED_Write_Cmd(0xa1);//–Set SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_Write_Cmd(0xc8);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_Write_Cmd(0xa6);//–set normal display
OLED_Write_Cmd(0xa8);//–set multiplex ratio(1 to 64)
OLED_Write_Cmd(0x3f);//–1/64 duty
OLED_Write_Cmd(0xd3);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_Write_Cmd(0x00);//-not offset
OLED_Write_Cmd(0xd5);//–set display clock divide ratio/oscillator frequency
OLED_Write_Cmd(0x80);//–set divide ratio, Set Clock as 100 Frames/Sec
OLED_Write_Cmd(0xd9);//–set pre-charge period
OLED_Write_Cmd(0xf1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_Write_Cmd(0xda);//–set com pins hardware configuration
OLED_Write_Cmd(0x12);
OLED_Write_Cmd(0xdb);//–set vcomh
OLED_Write_Cmd(0x40);//Set VCOM Deselect Level
OLED_Write_Cmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)设置地址模式
//水平寻址,垂直寻址,页寻址
OLED_Write_Cmd(0x02);// 地址模式为页寻址
OLED_Write_Cmd(0x8d);//–set Charge Pump enable/disable
OLED_Write_Cmd(0x14);//–set(0x10) disable
OLED_Write_Cmd(0xa4);// Disable Entire Display On (0xa4/0xa5)
OLED_Write_Cmd(0xa6);// Disable Inverse Display On (0xa6/a7)
OLED_Write_Cmd(0xaf);//–turn on OLED panel开启显示
Delay(100);
OLED_clear();//清屏
}

初始化OLED完成之后,还需要封装一个在OLED显示字符的函数

封装OLED显示函数

//设置显示坐标函数,t为0时,字符为8x16t;为1时,字符为16x16
void OLED_Put_Char_8x16(unsigned char x,unsigned char y){//x(0~15),y(0~6)此时输出的循环应该j<16(从上往下输出)
    unsigned char t = 0;
    OLED_Write_Cmd(0x20);
    OLED_Write_Cmd(0x00);//设置地址模式为水平选址
    //set page
    OLED_Write_Cmd(0x22);
    OLED_Write_Cmd(y);
    OLED_Write_Cmd(0x01+y);
    //set colum
    OLED_Write_Cmd(0x21);
    OLED_Write_Cmd((0x08+0x08*t)*x);
    OLED_Write_Cmd((0x08+0x08*t)*x+(0x07+0x08*t));
}
void OLED_Put_Char_16x16(unsigned char x,unsigned char y){//x(0~7),y(0~3)此时输出的循环应该j<32(从左往右输出,当j=16时跳到下一个Page继续从左往右)
    unsigned char t = 0;
    OLED_Write_Cmd(0x20);
    OLED_Write_Cmd(0x00);//设置地址模式为水平选址
    //set page
    OLED_Write_Cmd(0x22);
    OLED_Write_Cmd(y*2);
    OLED_Write_Cmd(0x01+y*2);
    //set colum
    OLED_Write_Cmd(0x21);
    OLED_Write_Cmd((0x08+0x08*t)*x);
    OLED_Write_Cmd((0x08+0x08*t)*x+(0x07+0x08*t));
}

如何在OLED上显示字符呢

例:显示一个字符可以这样:

1.首先定义好显示位置x,y,以及字符大小t

OLED_put_char_16x16(0,0,0); //水平为0,垂直为0,字符大小为8*16,大约在左上角

2.向OLED中写入字模数据(定义一个字模数组,里面存放16进制数)

for(i=0;i<16;i++){
    OLED_Write_data(character[0][i]);
}

传送字模数组中第一个字模的16进制数,一共有16个

亮格子

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F2bsu5KY-1682944199088)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221113220453321.png)]

128行,分为8个page,一个page含有8行。

这样,一列中的一个page可以用8个字节表示,低位在上高位在下

当写入0X08时,上图中的1数据位是亮的,其他7个全部熄灭。

如果再写入同样的数据的话,他有两种可能:

  1. 填充到Page 0,Col 1
  2. 填充到Page1,Col 0

如何控制自动填充方向?下面来学习下寻址

寻址模式

如何选择寻址模式?

先发送0X20

在发送模式选择字节:
在这里插入图片描述

0X00 -> 水平寻址 0X01 -> 垂直寻址 0X02 -> 页寻址 0X03 -> 无效

  • 水平寻址模式

  • 垂直寻址模式

  • 页寻址

    1. 确定第几个Page

      这里只看第二行,需要发送一个寻页字节,前5位以固定(前4位为0XB),通过更改后三位来寻找指定Page,如果三个都取0,那么就是Page 0

      即Oled_Write(0XB0 + i)(表示第i页)

    2. 确定第几Col

      根据说明书解释

    3. 写入数据

//坐标设置

	void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 	OLED_WR_Byte(0xb0+y,OLED_CMD);
	OLED_WR_Byte((((x+2)&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte(((x+2)&0x0f),OLED_CMD); 
}   	
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示				 
//size:选择字体 16/12 
void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char Char_Size)
{      	
	unsigned char c=0,i=0;	
		c=chr-' ';//得到偏移后的值			
		if(x>Max_Column-1){x=0;y=y+2;}
		if(Char_Size ==16)
			{
			OLED_Set_Pos(x,y);	
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
			OLED_Set_Pos(x,y+1);
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
			}
			else {	
				OLED_Set_Pos(x,y);
				for(i=0;i<6;i++)
				OLED_WR_Byte(F6x8[c][i],OLED_DATA);
				
			}
}
//m^n函数
unsigned int oled_pow(unsigned char m,unsigned char n)
{
	unsigned int result=1;	 
	while(n--)result*=m;    
	return result;
}				  
//显示2个数字
//x,y :起点坐标	 
//len :数字的位数
//size:字体大小
//mode:模式	0,填充模式;1,叠加模式
//num:数值(0~4294967295);	 		  
void OLED_ShowNum(unsigned char x,unsigned char y,unsigned int num,unsigned char len,unsigned char size2)
{         	
	unsigned char t,temp;
	unsigned char enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
				continue;
			}else enshow=1; 
		 	 
		}
	 	OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); 
	}
} 
//显示一个字符号串
void OLED_ShowString(unsigned char x,unsigned char y,unsigned char *chr,unsigned char Char_Size)
{
	unsigned char j=0;
	while (chr[j]!='\0')
	{		OLED_ShowChar(x,y,chr[j],Char_Size);
			x+=8;
		if(x>120){x=0;y+=2;}
			j++;
	}
}
//显示汉字
void OLED_ShowCHinese(unsigned char x,unsigned char y,unsigned char no)
{      			    
	unsigned char t,adder=0;
	OLED_Set_Pos(x,y);	
    for(t=0;t<16;t++)
		{
				OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
				adder+=1;
     }	
		OLED_Set_Pos(x,y+1);	
    for(t=0;t<16;t++)
			{	
				OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
				adder+=1;
      }					
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{ 	
 unsigned int j=0;
 unsigned char x,y;
  
  if(y1%8==0) y=y1/8;      
  else y=y1/8+1;
	for(y=y0;y<y1;y++)
	{
		OLED_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
	    {      
	    	OLED_WR_Byte(BMP[j++],OLED_DATA);	    	
	    }
	}
} 

代码实现

代码的思路分析:

  1. 初始化

    //1.OLED.c
    
    //在指定位置显示一个字符,包括部分字符
    //x:0~127
    //y:0~63
    //mode:0,反白显示;1,正常显示				 
    //size:选择字体 16/12 
    void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char Char_Size)
    {      	
    	unsigned char c=0,i=0;	
    		c=chr-' ';//得到偏移后的值			
    		if(x>Max_Column-1){x=0;y=y+2;}
    		if(Char_Size ==16)
    			{
    			OLED_Set_Pos(x,y);	
    			for(i=0;i<8;i++)
    			OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
    			OLED_Set_Pos(x,y+1);
    			for(i=0;i<8;i++)
    			OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
    			}
    			else {	
    				OLED_Set_Pos(x,y);
    				for(i=0;i<6;i++)
    				OLED_WR_Byte(F6x8[c][i],OLED_DATA);
    				
    			}
    }
    //m^n函数
    unsigned int oled_pow(unsigned char m,unsigned char n)
    {
    	unsigned int result=1;	 
    	while(n--)result*=m;    
    	return result;
    }				  
    //显示2个数字
    //x,y :起点坐标	 
    //len :数字的位数
    //size:字体大小
    //mode:模式	0,填充模式;1,叠加模式
    //num:数值(0~4294967295);	 		  
    void OLED_ShowNum(unsigned char x,unsigned char y,unsigned int num,unsigned char len,unsigned char size2)
    {         	
    	unsigned char t,temp;
    	unsigned char enshow=0;						   
    	for(t=0;t<len;t++)
    	{
    		temp=(num/oled_pow(10,len-t-1))%10;
    		if(enshow==0&&t<(len-1))
    		{
    			if(temp==0)
    			{
    				OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
    				continue;
    			}else enshow=1; 
    		 	 
    		}
    	 	OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); 
    	}
    } 
    //显示一个字符号串
    void OLED_ShowString(unsigned char x,unsigned char y,unsigned char *chr,unsigned char Char_Size)
    {
    	unsigned char j=0;
    	while (chr[j]!='\0')
    	{		OLED_ShowChar(x,y,chr[j],Char_Size);
    			x+=8;
    		if(x>120){x=0;y+=2;}
    			j++;
    	}
    }
    //显示汉字
    void OLED_ShowCHinese(unsigned char x,unsigned char y,unsigned char no)
    {      			    
    	unsigned char t,adder=0;
    	OLED_Set_Pos(x,y);	
        for(t=0;t<16;t++)
    		{
    				OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
    				adder+=1;
         }	
    		OLED_Set_Pos(x,y+1);	
        for(t=0;t<16;t++)
    			{	
    				OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
    				adder+=1;
          }					
    }
    /***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
    void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
    { 	
     unsigned int j=0;
     unsigned char x,y;
      
      if(y1%8==0) y=y1/8;      
      else y=y1/8+1;
    	for(y=y0;y<y1;y++)
    	{
    		OLED_Set_Pos(x0,y);
        for(x=x0;x<x1;x++)
    	    {      
    	    	OLED_WR_Byte(BMP[j++],OLED_DATA);	    	
    	    }
    	}
    } 
    
  2. 显示位置

  3. 显示内容

光标概念

对STC89C52芯片的解读

  • 51 单片机引脚高电平为 5V。
  • 不需要配置输入输出模式,直接对引脚进行输出控制或者输入读取。
  • 使用串口时,需要根据自身使用的晶振频率计算波特率。
  • 51开发板上的单片机所有IO口初始都是高电频
  • STC89C52系列的5V单片机的P0口的灌电流最大为12mA, 其他I/O口的灌电流最大为6mA。

单片机中ISP 下载的原理

STC 系列单片机在上电时会执行在 ISP-FLASH 的 ISP 程序,只要在串口上收到连续的 0x7F,便会进入 ISP 模式。

单片机读取外部设备给io引脚的电频

51单片机写1的时候的上拉是内部的上拉电阻提供的,从机在这条线上写0的时候相当于开漏,0就相当于把这条线接地,尽管单片机是写1但IO的实际电压还是0,所以读出的是0。但如果反过来说,如果单片机写0,不管从机怎么给电压这个地方实际电压还是0。

所以以后只要需要io口读外部设备给的电频都需要先把io口拉高(增加个别元件可以更改这个现状)

对STC15W4K32S4芯片的解读

基本介绍

命名规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N1l5w2ir-1682944199089)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221120202643381.png)]

引脚排布图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hJEEEKrs-1682944199089)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221120195100030.png)]

内置ADC模块

来自官方的建议:
在这里插入图片描述

与ADC相关引脚

引脚引脚编号复用功能说明
P1.04ADC08通道模/数转换器ADC输入口
CCP1捕获/脉冲输出/脉宽调制通道1
RXD2串口2数据接收端
P1.1ADC18通道模/数转换器ADC输入口
CCP0捕获/脉冲输出/脉宽调制通道0
TXD2串口2数据发送端
P1.27ADC28通道模/数转换器ADC输入口
SS单片机用作SPI从机时的从机片选输入控制端
ECI可编程计数阵列定时器的外部时钟输入
CMP0比较器的比较结果输出端
P1.38ADC38通道模/数转换器ADC输入口
MOSISPI主机输出,从机输入
P1.49ADC48通道模/数转换器ADC输入口
MISOSPI主机输入,从机输出
P1.510ADC58通道模/数转换器ADC输入口
SCLKSPI主机时钟输出或从机时钟输入
P1.611ADC68通道模/数转换器ADC输入口
RxD_3串口1接收端备用切换引脚
XTAL2外部晶振输入端口
SysClkO_2主时钟输出备用切换引脚
PWM6脉宽调制输出通道6
P1.712ADC78通道模/数转换器ADC输入口
TxD_3串口1发送端备用切换引脚
XTAL1外部晶振输入端口
PWM7脉宽调制输出通道7

A/D转换器结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8oGThfM-1682944199089)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221120203517916.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HRVBVjYd-1682944199090)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221120203543210.png)]

​ STCI5系列单片机 ADC 由多路选择开关、比较器、遥次比较寄存器、10位 DAC 、转换结果寄存器( ADC RES 和 ADC RESL )以及 ADC CONTR 构成。

​ STCI5系列单片机的 ADC 是逐次比较型 ADC 。逐次比较型 ADC 由一个比较器和 D / A 转换器构成,通过逐次比较逻辑,从最高位( MSB )开始,顺序地对每一输入电压与内置 D / A 转换器输出进行比较,经过多次比较,使转换所得的数字量逐次逼近输入模拟量对应值。逐次比较型 A / D 转换器具有速度高,功耗低等优点。

​ 从上图可以看出,通过模拟多路开关,将通过ADC0~7的模拟量输入送给比较器。用数/模转换器( DAC )转换的模拟量与输入的模拟量通过比较器进行比较,将比较结果保存到逐次比较寄存器,并通过逐次比较寄存器输出转换结果。 A / D 转换结束后,最终的转换结果保存到 ADC 转换结果寄存器 ADC RESADC RESL ,同时,置位 ADC 控制寄存器 ADC _ CONTR 中的 A / D 转换结束标志位 ADC FLAG ,以供程序查询或发出中断中请。模拟通道的选择控制由 ADC 控制寄存器 ADC _ CONTR 中的CHS2-CHS0确定。 ADC 的转换速度由 ADC 控制寄存器中的SPEED1和SPEED0确定。在使用 ADC 之前,应先给 ADC 上电,也就是置位 ADC 控制寄存器中的ADC POWER 位。

AD转换结果计算公式

A/D相关寄存器

P1模拟功能控制寄存器P1ASF

代码实现

8位数据,哪个口需要使用哪个口给1
比如我使用了P1.2 -> 0x04 //0000 0100

ADC控制寄存器ADC_CONTR

在这里插入图片描述

代码实现

0xE8 1 11 0 1 000(假设使用P1.0)

有关定时器/计数器

有关中断

外部中断接口

P32: INT0
P33: INT1

外部中断触发方式

INT0和INT1有两种触发方式:上升沿或下降沿均可触发方式、仅下降沿触发方式。
INT2、INT3、INT4都只能仅下降沿触发方式

中断号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XFLrKWoA-1682944199090)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123131400457.png)]

有关串口

官方介绍

​ STC15W4K32S4系列单片机具有4个采用 UART ( Universal Asychronous Receiver / Transmitter )

​ 每个串行口由2个数据缓冲器、一个移位寄存器、一个串行控制寄存器和一个波特率发生器等组成。每个串行口的数据缓冲器由2个互相独立的接收、发送缓冲器构成,可以同时发送和接收数据。发送缓冲器只能写入而不能读出,接收缓冲器只能读出而不能写入,因而两个缓冲器可以共用一个地址码。
​ 串行口1的两个缓冲器共用的地址码是99H;
​ 串行口2的两个缓冲器共用的地址码是9BH;
​ 串行口3的两个缓冲器共用的地址码是 ADH ;
​ 串行口4的两个缓冲器共用的地址码是85H。

​ 串行口1的两个缓冲器统称串行通信特殊功能寄存器 SBUF ;
​ 串行口2的两个缓冲器统称串行通信特殊功能寄存器S2BUF;
​ 串行口3的两个……

​ STC15W4K32S4系列单片机的串行口1有4种工作方式,其中两种方式的波特率是可变的,另两种是固定的,以供不同应用场合选用。
​ 串行口2/串行口3/串行口4都只有两种工作方式,这两种方式的波特率都是可变的。用户可用软件设置不同的波特率和选择不同的工作方式。主机可通过查询或中断方式对接收/发送进行程序处理,使用十分灵活。

​ STCI5W4K32S4系列单片机串行口1对应的硬件部分是 TxD 和 RxD 。串行口1可以在3组管脚之间进行切换。通过设置特殊功能寄存器AUXR1/P_SW1中的位S1_S1/AUXR1.7和S1_S0/PSW1.6,可以将串行口1从[ RxD /P3.0, TxD /P3.1]切换到[ RxD_2/P3.6, TxD_2/P3.7],还可以切换到[ RxD_3/P1.6/XTAL2, TxD _3/P1.7/XTAL1]。注意,当串行口1在[ RxD _2/P1.6, TxD _2/P1.7]时,系统要使用内部时钟。串口1建议放在[P3.6/RxD_2,P3.7/TxD_2]或[P1.6/RxD_3/XTAL2,P1.7/ TxD _3/XTAL1]上。

​ STC15W4K32S4系列单片机串行口2对应的硬件部分是TxD2和RxD2。串行口2可以在2组管脚之间进行切换。通过设置特殊功能寄存器PSW2中的位S2 S / P SW2.0,可以将串行口2从[RxD2/P1.0,TxD2/P1.1]切换到[RxD2_2/P4.6,TxD2_2/P4.7]。

​ STC15W4K32S4系列单片机串行口3对应的硬件部分是TxD3和RxD3。串行口3可以在2组管脚之间进行切换。通过设置特殊功能寄存器 P _SW2中的位S3_ S / P _SW2.1,可以将串行口3从[RxD3/P0.0,TxD3/P0.1]切换到[RxD3_2/P5.0,TxD3_2/P5.1]

​ STC15W4K32S4系列单片机串行口4对应的硬件部分是TxD4和RxD4。串行口4可以在2组管脚之间进行切换。通过设置特殊功能寄存器PSW2中的位S4 S / P SW2.2,可以将串行口4从[RxD4/P0.2,TxD4/P0.3]切换到[RxD4_2/P5.2,TxD4_2/P5.3]。

引脚个人总结

  1. 串行口1:RxD/P3.0, TxD /P3.1 RxD_2/P3.6, TxD_2/P3.7(不推荐使用) RxD _3/P1.6/XTAL2, TxD _3/P1.7/XTAL1
  2. 串行口2:RxD2/P1.0,TxD2/P1.1 RxD2_2/P4.6,TxD2_2/P4.7
  3. 串行口3:RxD3/P0.0,TxD3/P0.1 RxD3_2/P5.0,TxD3_2/P5.1
  4. 串行口4:RxD4/P0.2,TxD4/P0.3 RxD4_2/P5.2,TxD4_2/P5.3

相关寄存器(串行口1)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ddBrtgw-1682944199090)(F:\TyporaMarks\51单片机小记
_Kexie.assets\image-20221123210136072.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zcn6oQ2C-1682944199091)(F:\TyporaMarks\51单片机小记
_Kexie.assets\image-20221123210153564.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tV1gImkk-1682944199091)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210211022.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K4tmdYii-1682944199091)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210223107.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E5OjG8E7-1682944199091)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210233932.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZPjGXgFE-1682944199092)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210247638.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WWWu1vrU-1682944199092)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210303885.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qwpASIB5-1682944199092)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210314303.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YNesGlQZ-1682944199093)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210325910.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Of50fvG-1682944199093)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210336049.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7wRbsUQI-1682944199093)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210348708.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-peaD7xbZ-1682944199093)(F:\TyporaMarks\51单片机小记_Kexie.assets\image-20221123210425697.png)]

相关寄存器(串行口2)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

工作模式介绍(串行口2)

串行口2:RxD2/P1.0,TxD2/P1.1 RxD2_2/P4.6,TxD2_2/P4.7

串行口2的工作模式0----8位 UART ,波特率可变

​ 10位数据通过RxD2/P1.0(RxD2_2/P4.6)接收,通过TxD2/P1.1(TxD22/P4.7)发送。

​ 一帧数据包含一个起始位(0),8个数据位和一个停止位(1)。接收时,停止位进入特殊功能寄存器S2CON的S2RB8位。波特率由定时器T2的溢出率决定。

​ 串口2在模式0的波特率=定时器T2的溢出率/4
​ 当T2工作在1T模式( AUXR.2/T2x12=1)时,定时器2的溢出率= SYSclk /(65536-[RL_TH2, RL _TL2]);即此时,串行口2的波特率= SYSclk /(65536-[ RL_TH2,RL_TL2])/4

​ 当T2工作在12T模式( AUXR .2/T2x12=0)时,定时器2的溢出率= SYSclk /12/(65536-[ RL _TH2, RL _TL2]);即此时,串行口2的波特率= SYSclk /12/(65536-[ RL _TH2, RL _TL2])/4
上式中 RL_TH2是T2H的重装载寄存器, RL_TL2是T2L的重装载寄存器。

在程序中如何具体使用串口2

  1. 设置串口2的工作模式,S2CON寄存器中的S2SM0决定了串口2的2种工作模式
  2. 设置串口2的波特率相应的寄存器:定时器2寄存器T2H/T2L
  3. 启动定时器2,让T2R位为1,定时器2就立即开始计数。
  4. 设置 AUXR .2/T2x12,确定定时器2的速度
  5. 设置串口2的中断优先级,及打开中断相应的控制位是: PS ,PS2H,ES2, EA
  6. 如要串口2接收,将S2REN置1即可
    如要串口2发送,将数据送入S2BUF即可,
    接收完成标志S2RI,发送完成标志S2TI,要由软件清0。

​ 串口2在模式0的波特率=定时器T2的溢出率/4
​ 当T2工作在1T模式( AUXR.2/T2x12=1)时,定时器2的溢出率= SYSclk /(65536-[RL_TH2, RL _TL2]);即此时,串行口2的波特率= SYSclk /(65536-[ RL_TH2,RL_TL2])/4

​ 当T2工作在12T模式( AUXR .2/T2x12=0)时,定时器2的溢出率= SYSclk /12/(65536-[ RL _TH2, RL _TL2]);即此时,串行口2的波特率= SYSclk /12/(65536-[ RL _TH2, RL _TL2])/4
上式中 RL_TH2是T2H的重装载寄存器, RL_TL2是T2L的重装载寄存器。

在程序中如何具体使用串口2

  1. 设置串口2的工作模式,S2CON寄存器中的S2SM0决定了串口2的2种工作模式
  2. 设置串口2的波特率相应的寄存器:定时器2寄存器T2H/T2L
  3. 启动定时器2,让T2R位为1,定时器2就立即开始计数。
  4. 设置 AUXR .2/T2x12,确定定时器2的速度
  5. 设置串口2的中断优先级,及打开中断相应的控制位是: PS ,PS2H,ES2, EA
  6. 如要串口2接收,将S2REN置1即可
    如要串口2发送,将数据送入S2BUF即可,
    接收完成标志S2RI,发送完成标志S2TI,要由软件清0。

  1. 根据独立按键模块图:单片机上电前所有io口都是高电频(没有接着GND) ↩︎

  2. 寄存器可以把值送到IO口,同样也会实时读取IO变化的值 ↩︎

  3. 对于机械开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬舜间会伴随一连串的抖动。 ↩︎

  4. unsighed char范围为0~255,刚好对应十六进制0~0XFF ↩︎

  5. 屏幕截图 2022-10-27 224605 ↩︎
  6. 这里引用江科大教学视频的译码器型号 ↩︎

  7. P24 P23 P22分别对应三位二进制数 0 0 0,这样一来,就可以通过对这3个P的电频修改,达到选中8个不同数码管中的任意个的目的。比如P24 = 0 P23 = 1 P22 = 1,那么二进制数为011,化为十进制即3,所以选中第三个LED(Y2) ↩︎

  8. 缓冲器:高电频驱动能力弱,低电频驱动能力强,能力越强灯越亮。缓冲器起到将高电频降为自己电源的低电频,让数码管以比较亮的形式显示 ↩︎

  9. 因为当口线输出为1时驱动能力很弱,允许外外部装置将其拉低。当引脚输出为为低时,它的驱动能力很强,可吸收相当大的电流。 ↩︎

  10. 使用Delay时,CPU在等待Delay的结束,被占用了,无法做别的事 ↩︎

  11. 定时器/计数器T0溢出中断标志。 TO 被允许计数以后,从初值开始加1计数,当最高位产生溢出时,由硬件置“1" TFO ,向 CPU 请求中断,一直保持 CPU 响应该中断时,才由硬件清“0" TFO (TF0也可由程序查询清“0”) ↩︎

  12. 定时器 TO 的运行控制位。该位由软件置位和清零。当 GATE(TMOD.3)=0,TR0=1时就允许T0开始计数,TR0=0时禁止T0计数。当 GATE(TMOD.3)=1,TR0=1且INT0输入高电平时,才允许T0计数。 ↩︎

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值