零基础——最实用的STC8051单片机学习资料

2023.10.7

首先是C51的keil软件安装,注意keil5有很多版本,虽然图标都一样,但是C51有专门的C51版本,32位的有32位的版本,还有在安装C51版本的时候,一定要新建一个文件夹,安装路径就是它,要不然就会散乱地安装在你地D盘里,到时候很麻烦。然后就是安装一个USB转UART通信地驱动,这个是因为使用到了CH340芯片,所以需要安装一下这个驱动,让电脑识别到这个串口。
我现在才知道单片机和CPU地区别,单片机里面已经有CPU了,是集成了作为一个电脑的基础硬件,插电就可以工作,但是CPU不一样,性能强劲,插电也工作不了,需要配合其他硬件一起使用
在这里插入图片描述
ROM,RAM,FLASH的区别:
RAM是一种临时存储器,用于存储计算机在运行时需要的数据和程序,这意味着在断电或重启计算机时,RAM中存储的数据会被清除,计算机的操作系统、应用程序和正在运行的程序都需要RAM来存储和访问数据,速度很快,不需要顺序查表
ROM是一种存储器,其中存储的数据在计算机断电或重启时不会被清除,因此它是非易失性的存储器。数据通常是预先编程好的,用户无法直接写入或修改其中的内容。这些数据包括计算机的基本启动程序(固件)和永久存储的固定数据,如系统的 BIOS(基本输入/输出系统)或设备的固件。固件就是ROM的一种应用
FLASH用户烧写的程序存储在这里面。其实FLASH就是ROM的一种
固件和用户烧录代码的区别:
固件其实就是维持了产品的基本运行和功能,如果用户需要新增加一些功能,就需要烧录特定的代码。固件可以看作是产品的核心操作系统和控制程序,它维护了产品的基本运行和功能,通常由制造商在生产过程中预先加载。如果用户需要新增加一些特定功能或自定义设备的行为,通常可以编写用户烧入的代码,然后将这些代码烧录到设备中。这些用户烧入的代码可以在固件的基础上运行,以扩展设备的功能。这为用户提供了灵活性,使他们能够根据自己的需求自定义设备,添加新功能,或适应不同的应用场景。总之,固件维护了产品的基本操作和功能,而用户烧入的代码可以用于个性化定制和扩展产品的功能。这种组合可以使产品更加灵活和适应各种不同的用途。

2023.10.8

在这里插入图片描述

为什么需要电容,为什么需要电阻,查阅资料了解到rst是需要几个机械周期的正电平才能复位,加入电容之后,上电瞬间电容有几微秒的充电时间,这个时候电路中有电流流过,具体可以看电容的工作原理,因为需要高电平,光靠导线的固有电阻是不够产生高电平,根据v=ir可以知道需要设置一个比较大的电阻,才能让rst那端的电势处于高电平,因此需要放置一个10k的电阻,总的来说就是用电容充电的那很短的一瞬间,产生几个机械周期的高电平让rst复位,电容充满之后,电流消失,电容呈现断路状态,rst直接连接电阻进入地,因此是低电平状态,单片机正常工作
电阻阻值的简略命名:
例如排阻的封装上面写着102,意思就是取前两个数字为基数,后面那个2指的是10的几次方,那么102的意思就是10*10的2次方,那就是1K。
电容的缩写
例如104,其实就是1 * 10四次方,那就是10 000pF
pF nF uF mF F
51单片机延时代码自动生成
系统频率就是晶振的频率,注意设定延时的时间,然后就是8051指令集的系列,找对应的就行,注意其中_nop_()函数表示啥也不做,需要程序中include对应的库是#include <INTRINS.H>
在这里插入图片描述
STC-ISP使用流程
四步走。

在这里插入图片描述

2023.10.9

这两天51单片机开发板到了,所以开始看江科大的视频来学习51,目前的视频进度是如下:
在这里插入图片描述
大概就是熟悉了一下C51的代码下载流程以及工程的创建流程,这比stm32要简单很多,直接选好芯片之后,就编写代码就Ok了,目前还在只是熟悉一下IO的输出高低电平这里,只知道P2口一共有8位,P2.0-P2.7,可以设置16进制数来控制输出电平来实现点灯,0代表TTL低电平,1代表TTL高电平。其中这两个十六进制的低位表示0-3,高位表示4-7.
轻触开关那些事
下面是轻触开关暴力拆解和原理图,可以看出A和B其实是一根线,C和D是一个线路,原理图上可以直接1和3或者只接2和4!
在这里插入图片描述
在这里插入图片描述
有些人喜欢把这样的开关画成这样子的,只引出来两个角,但是要在PCB封装中显出这俩引脚到底是在哪:

在这里插入图片描述
新get到一个点,在51单片机中,全局变量刚开始上电的时候,默认值为0,可以不用去给他初始化。
按键在原理图中要这么画:

在这里插入图片描述

2023.10.10

按键抖动
通常按键有10ms的抖动,注意这个抖动是在按键按下以及按键抬起来都会有抖动,因此检测完按键的电平后,无论是按下还是没按下都需要加一个延时!!!,有软件消抖和硬件消抖,一般软件消抖更加的容易一点,硬件的话就得考虑加一个施密特触发器之类的东西了
在这里插入图片描述
在这里插入图片描述

数码管这个器件
下图中,上面这个是共阴极,下面这个是共阳极连接,这种4个一组的连接方式只要是为了节省引脚资源,这么连接只需15个引脚就行,如果单独的4个连一起,需要48个引脚,其中的12,9,8,6号引脚我们叫他位选端,通俗易懂,为什么叫位选呢?这是因为可以根据这个位来设定到底是哪个数码管亮,假如说我们选择共阴极连接,想让第三个数码管亮,那么就设定1101给12,9,8,6引脚即可,就可以选中第三个数码管,但是这样的情况只能单次控制一个数码管的亮灭,因此就有了静态显示动态扫描显示的概念。动态扫描显示就是利用了人眼的视觉残留的特点来设计的。
在这里插入图片描述
在说静态显示和动态扫描之前,先说说加入这个板子要控制8个数码管,要怎么节省位选端的线路资源呢?那就用到了38译码器,这里以74HC138为例:
在这里插入图片描述
其中简单介绍一下这个译码器,C是最高位,B次之,A是最低位,他的真值表如下:
在这里插入图片描述
单片机的驱动能小tip
单片机的IO的驱动能力其实很弱,电流小,以一个LED灯为例,拿高电平驱动比拿低电平驱动的亮度要低很多,因此在驱动数码管的时候要加上一个74HC245模块来提高IO的驱动能力。下面是原理图和真值表
在这里插入图片描述
在这里插入图片描述

数码管的消影
在动态显示数码管数字的时候,如果中间不加入延时函数消除阴影,则会出现阴影,下面解释为什么:
段选和位选之间如果不清出段选,那么下一次的位选完成之后,会保留上一次的段选结果,然后段选完成之后才成功设置,因此需要在位选和段选之间加入清除段选的操作。
在这里插入图片描述
注意在段选之后要延时一点时间再消影,要不然会影响数码管的亮度,因为段选完数码管还没有达到最大亮度的时候就消影了,所以会变暗:

在这里插入图片描述
如果用单片机直接驱动数码管,主循环中如果出现了一些运行时间不一样的代码,那么就会引起数码的不显示或者闪烁等问题,因此可以直接使用专门的驱动芯片去驱动:
在这里插入图片描述
我对数码管驱动的理解
首先switch要显示哪一个数码管并设置位选,然后给选中的数码管设定要显示的数字段选值。

工程上的#ifdef和#endif作用
例如一个系统的读取方式可以设置为SD卡读取以及U盘读取,有不同的模式,可以通过预编译的这个命令来实现代码选择的作用,用到哪部分,去#define哪部分开启对应代码的权限。
在这里插入图片描述
具体的代码应用,根据是否定义了标志位,来开启对应代码权限!:
在这里插入图片描述

keil5打开别人的程序乱码怎么办?
可能是编码格式不一样,只需要找到一个写代码的人用的格式就行,建议使用UTF-8,支持全球语言
在这里插入图片描述

在这里插入图片描述
头文件的位置小tip
尖括号的表示从编译器的安装目录下找这个头文件,冒号表示从当前工程目录下找这个头文件,所以如果用户自己写的头文件,一般是用冒号表示。
在这里插入图片描述
矩阵键盘
下面介绍矩阵键盘按列扫描的逻辑,首先设置P13,P12,P11,P10为0111,如果在P17,16,15,14,检测到了0,那么表示第一列的某行被按下了,以检测到0为基准,判断按键是否被按下。一般这种矩阵式的扫描方法主要是为了节省IO口,如果是16个独立按键,就需要16个IO口,但是搞成矩阵的形式,就只需要用到4+4=8个IO即可,规模庞大液晶屏显示使用这种方法的话,原先是1080*1920,变成了1080+1920,大大减小了运算量
在这里插入图片描述
关于STC89C52RC这个芯片IO的解释
89C52RC,IO口一共有三个工作模式,其中他的若上拉输出模式,输出的电压大概是3.3V,很弱的驱动能力,但是具有强下拉的驱动能力,其中P0口作为IO口的时候,需要加上上拉电阻,这也是为什么我上次看嘉立创EDA视频的时候,他们说P0口需要加上拉电阻,要不然就不能正常工作P0口

在这里插入图片描述
不建议直接使用VCC接入单片机引脚,这样会导致额外的功率消耗,这也是为什么一般在做按键检测的时候,喜欢使用0来判断按键是否被按下!
在这里插入图片描述
这里他们是直接接入了VCC,因此做了限流电阻的隔离
在这里插入图片描述

2023.10.12

下面是常用的C51具体数据类型,今天要做的是一个矩阵按键密码锁项目,unsigned int类型最大就是65535,因此输入超过5位的时候,就会数据溢出。
在这里插入图片描述
关于单片机寄存器的一些小想法
先给出结论!!!!,单片机通过配置各种寄存器来实现电路的连接,不同的电路完成不同的模式,实现不同的功能,有些是起到开关的作用,有些是起到影响的作用
STC89C52是一款8位的单片机,设计的是片上外设,它们以一个字节(8位bit)为一个单元,以此对比可以知道,一般32位的单片机,他们的片上外设是以4个字节(4*8=32位)为一个单元,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
下面给以张更能看懂栈中的数据类型存储的图片:
在这里插入图片描述
可以看出左侧的例如地址为0x0031FC40的区域,存储了一个字节,也就是两位十六进制的数。
例如下图中,这个0x80其实就是P0口这个寄存器的地址,里面存储了一个字节的数据,也就是8位的数据,可以通过指针的形式访问这个地址,来实现相应的功能,一般sfr都已经定义好了相应的指针功能,但是我目前无法得知这个定义好的东西是啥。这也可以解释,为什么我们在看8位的单片机的时候,寄存器总是只有8位,而32位的单片机,每个寄存器有32位了。

在这里插入图片描述
还有一个小tip,那就是一些可位寻址的寄存器可以单独拉出来某一位进行赋值
在这里插入图片描述
下面就是51单片机头文件中的地址位置,可以直接对它赋值,0或1。
在这里插入图片描述
一些不能位寻址的寄存器,只能通过整体赋值才能配置,例如:
在这里插入图片描述
类比stm32的IO口,stm32是32位处理器,按理说每个寄存器有32个位,但是为什么只有16个IO口呢?
这是因为配置GPIO口有两个寄存器,一个是CRL,一个是CRH,可以看出除了16位是关于16个IO口的定义,其余的都是IO的模式配置,相当的复杂啊,比起51单片机一个IO只需要一个8位的寄存器,就能控制8个IO的电平来说,算是很复杂的了
在这里插入图片描述
在这里插入图片描述

STC89C52RC的定时器

这款芯片有3个定时器,分别是T0,T1,T2,他们有4个工作模式,其中模式1是最常用的,关于为什么叫他计数/定时器呢?下面以T0的模式一电路图为例:
在这里插入图片描述
可以看到脉冲的输入可以是SYSclk(系统时钟,在这款单片机中比较简单,本质上就是外接晶振的频率,但是类比stm32的时钟系统简直是太简单了),还有T0口输入的脉冲,很显然,当C/T置0的时候,起到timer(定时器)的作用,由系统时钟给脉冲,当他为1的时候,起到count(计数器)的作用,他会计T0口来了多少脉冲,然后进行下一步动作
在这里插入图片描述
在这里插入图片描述
还有一部分主要是起到了计数的功能,可以看出一共16位,可以计65536个数,当超过这个数的时候,会置标志位TF0,引起中断程序
在这里插入图片描述
还有一部分起到了开关的作用,他就是控制计数器启动和暂停,它可以由两种方式控制定时器的暂停,一种是根据TR0,一种是根据外部中断请求。
在这里插入图片描述
当GATE是0的时候,允许TR0控制定时器的计数。
在这里插入图片描述
当GATE为1的时候,允许外部中断配合TR0控制计数,这个INT1其实对应的就是引脚,如下图:
在这里插入图片描述
在这里插入图片描述

下图是定时器0以模式1的方式出发终端的结构图,可以通过配置寄存器来实现这之间的线路连接,具体寄存器可以参考数据手册
在这里插入图片描述

2023.10.13

在配置定时器的下面这俩寄存器的时候
在这里插入图片描述
需要初始化一下计数器的初始值,然后就可以设定定时的时间啦,例如晶振频率是12MHZ,选择12T模式,那么来的脉冲周期就是1us,我们只需要让他计1000个数,就能制造出一个1ms的定时器了,16位代表他的取值范围是0-65535,那么可以将初值设定在64535来达到这样的效果,可是这个初值需要变成16进制,分配给TL0和TH0,那么我们可以类比10进制数的拆解,例如我们想将123的前两位和后1位拆分,那么可以让他和10进行运算
123/10 = 12
123%10 = 3
如果想拆分1234的前两位和后两位,那么可以跟10的平方进行运算,类比到2进制,8进制,16进制都是这么做就OK,64535对应16进制是FC17,如果想要分离前后两位,那么只需要跟16的平方做运算就OK
64535/256 = FC
64535%256 = 17
我终于知道怎么看寄存器上电复位后的初值了
其实就是在每个寄存器的介绍部分,介绍完寄存器的每一位的功能之后,就会说这个寄存器上电复位后的初值:

在这里插入图片描述
而且在寄存器清单也会列出来这个寄存器的地址和复位值:
在这里插入图片描述
对于不可位寻址寄存器只想改变某一位的操作
对于不可位寻址的寄存器,如下图:在配置的时候就很头疼,
在这里插入图片描述
如果我们想让第一位置1,那么最简单的方式就是下面:
TMOD = 0x01,进行整体复制,那么会覆盖其他标志位,因此很有必要对他进行位操作:
想让某位置1就用|操作,例如:
TMOD = TMOD | 0x01; 这是让寄存器的最后以为变1
简写: TMOD &= 0x01
想让某位置0就有&操作,例如:
TMOD = TMOD & 0x00; 这是让寄存器的所有位变0
简写: TMOD |= 0x00
关于static前缀的全局变量,局部变量作用范围
加入static前缀的变量通常可以不赋初值,默认是0,而且地址也是固定的
如果下面这样写,T0Count就是一个全局变量,生命周期是整个程序(一般不会这么写,都是放在main开头)
在这里插入图片描述
如果在函数里面的话,他就是局部变量,但是这个数值不会因为函数执行而消亡,第二次进入这个函数的时候,这个变量的值还保留第一次执行完成后的结果

在这里插入图片描述
下面这个就是普通的局部变量,当函数执行完,这个变量也会被销毁,需要赋初值,要不然会初始化成一个随机数。而且地址是随机开辟的。
在这里插入图片描述

2023.10.14

刚刚看完用定时器中断实现按键控制流水灯的程序,准备自己写一下,总体思想就是主循环while(1)一直在检测按键是否按下,然后定时器一直在计数,计到一定时间之后触发中断,完成灯光的操作,然后返回主循环接着判断按键的模式,类比我每天晚上煮面,在有定时器的情况下,我需要煮面的全程都在监视他是不是开锅,然后才能干别的事情,但是有了定时器,我可以找个人帮我看着锅,期间我可以干任何事情,等锅好了我再关火称面,然后回来接着干任何事情。

2023.10.15

今天来手写了一下main中while(1)中检测按键,定时器以500ms的频率完成LED灯闪烁,并且可以根据按键设定LED闪烁模式。其中有一个点需要注意:
当触发一次中断后,需要重新设定初值,以16位计数器模式为例,当从64536计到65535触发中断的时候,T0H和T0L会被清零,重新从0开始计到65535,因此从原先的1ms中断,变成了1us * 65535 = 65ms中断,原本设定count到500才改变灯光,现在变成了65ms * 500 = 32s才执行一次灯光的改变,所以我在没有重新赋初值的情况下,等了好久,灯光才交换闪烁。
在这里插入图片描述
常见的通讯协议

在这里插入图片描述
其中同步通讯方式的协议,都会定义一个时钟线,例如I2C中的SCL,以及SPI中的SCLK,由时钟的上升沿为标准对信号采样读取
在这里插入图片描述
下面我们主要介绍串口通信,想要很详细的了解请看它,串口通信主要分物理层以及协议层,
物理层
首先是物理层有很多的规定,例如TTL直接连接,USB,RS232,RS485等等,他们的区别一个是接口的样子不一样,还有一个就是规定的电平不一样:
在这里插入图片描述
讲一下RS232这么设计是为了提高通信的稳定性,RS485采用差分信号的模式,有两根线,测量两个线的电压差来判断0和1,抗干扰强,适合长距离、高速传输。下面是USB以及RS232接口的封装:
在这里插入图片描述
在这里插入图片描述
既然物理层这么多型号的规定,不同的标准电平是不一样的,不同一的话容易烧板子,下面我来一一介绍这几个协议的电路设计:
首先是TTL直接连接
在这里插入图片描述
然后是利用RS232接口通信的电路
注意的是电平转换芯片一般有CH340、PL2303、CP2102、FT232
在这里插入图片描述
可以看到CH340有专门的USB接口以及RS232接口,可以转化为TTL的电平输出TXD以及接受RXD
在这里插入图片描述

然后是电脑和单片机通信
注意的是电脑需要安装CH340的驱动,会模拟一个电脑串口出来,叫做COM

在这里插入图片描述
单片机的电路设计一般是这样的:
D+和D-是usb的信号线
在这里插入图片描述
协议层
在这里插入图片描述
51单片机中的串口通信
我们以模式1,8位UART可变波特率的模式讲解:
STC89C52这款单片机内部已经配置好了串口通信的电路图,只需要我们操作SMOD寄存器就可以搭建完成串口通信电路,主要思想就是通过定时器的溢出率来控制波特率
在这里插入图片描述
中断的话,就需要配置ES以及EA,还有PS和PSH了

在这里插入图片描述
51单片机串口通信编程
需要注意定时器1要设置成8位重装载模式,不允许定时器触发中断,要在发送完数据后软件将TI以及RI清0

在这里插入图片描述
发送的数据是十六进制也可以是字符类型,他们通过ASCLL码的形式转换:
在这里插入图片描述
下面是实测TXD在发送数据的时候产生波形,当没有数据给SUBF发送时,TXD始终是高电平,当有数据发送时,首先是检测一个低电平位开始,后面接8位数据位和一个停止位(1个比特的高电平),波特率设定为9600,也就是将近104us发送一个bit。
在这里插入图片描述
点阵LED显示

在这里插入图片描述
如果用16个IO口直接驱动单LED的点阵,一个是太耗费单片机IO的引脚资源,还有一个就是驱动能不足,因为单片机IO口是弱上拉驱动,输出电流很小,因此可以考虑接8个三极管在上面,但是比较麻烦,因此下面需要用到74HC595来扩展IO口

其中SERCLK每来一个上升沿,SER的数据就往下移位一次,当左侧区域有了8位的数据,给RCLK一个上升沿将着八位数据锁存在右侧缓冲区,一次全部发送,这样就可以实现拿3根线扩展8根线了,其中QH‘是级联口,如果两个74HC595级联,那么他们SERCLK和RCLK连在一起,当上一片存满8位后,进入下一片,一共存满16位后给RCLK上升沿直接输出16位数据。区别于38译码器,只能控制一个线输出。
在这里插入图片描述
读各种进制数字的误区
所有进制,都是最左侧是最高位,最右侧是最低位,不要反了!!!
LED点阵动态显示
LED点阵的动态扫描其实就是先取自模:
在这里插入图片描述
这里选的是逐列扫描每列对应一个16进制的数,然后拍出来一个数组,根据LED点阵的宽度设定滑动窗口的大小,然后每隔一段时间滑动一下窗口,这样就跟滑动字幕一样了

在这里插入图片描述
注意,这么大的数组,如果定义为全局变量,那么会消耗很大的一整块儿空间,51单片机的RAM是很小的,因此可以考虑将他分配到FLASH中,写法如下,缺点就是不能该其中的数值了,因为FLASH只允许读,不允许写(这就是ROM):
在这里插入图片描述

2023.10.18

RTC其实就是一个时钟芯片,51单片机并没有内部时钟芯片,因此需要外接一个,具体的功能去看DS1302的芯片手册,不过stm32有内部的RTC外设。
在这里插入图片描述
如果一个函数没有形参,那么写不写void有区别吗,在C++中明确规定了写和不写是一样的,

在这里插入图片描述

裸板开发的一般开发模式
1.轮询模式
就是将很多个任务写在一个while(1)循环中,单次循环要一个任务一个任务执行,非常堵塞!!!
在这里插入图片描述
2.前后台模式
这个模式是使用到了中断的思想,while(1)循环中一直运行的是后台程序,当触发中断时才会运行前台程序,这个模式也有相应的缺点,假如中断处理的时间很长,那么也会造成程序的阻塞
在这里插入图片描述
3.定时器模式
这个是将不同的任务设定不同的时间去执行,假如任务1指定每1ms执行,任务2指定每10ms执行,任务3指定每50ms执行,那么只需要设定一个定时器中断,产生一个1ms执行一次中断,这个中断中可以定义一个变量count进行自加,假如count%1 == 0,就执行任务一,假如count %10 ==0,就执行任务二,假如count%50 ==0就执行任务三,这样就很好的将每个任务分配不同的执行频率进行执行。一般会给每个任务执行以下的结构体,里面有三个参数,分别是enable_flag(是否运行这个任务,1表示运行,0表示不运行),interval_time(执行频率,100ms执行一次就设定为100,到时候直接判断count % interval_time ==0就行),然后就是ready_flag(一般是看他是否已经执行完成,是否准备好了)

在这里插入图片描述
这个模式也有一个很明显的缺点,假如任务一执行需要耗费10ms,但是任务一要求每1ms执行一次,那么就会将后面的任务延迟执行,也会造成任务之间的冲突。
基于状态机
这样的意思就是在轮询模式的基础上进行的修改,他不是说一个大任务需要很长的时间执行吗,我们使用状态机将每个任务分成不同的状态,每轮循环都执行每个任务的一个状态,这样会大大加速运行的效率,但是将一个任务分成不同的状态是一件很麻烦的事情!!!

在这里插入图片描述

2023.10.21

仔细阅读了一下DS1302这个计时器芯片的数据手册,以下是一点总结:
经典电路如下:
在这里插入图片描述
其中这个芯片通过VCC2作为主电源供电,VCC1作为备用电池供电,一般是接一个纽扣电池作为备用电池,保证芯片掉电也不会停止计数,同时通过配置命令字可以实现涓流充电。
在这里插入图片描述
在这里插入图片描述
与CPU连接的重要线仅3根:
在这里插入图片描述
I/O口单片机可以对这个芯片发送命令字,标准如下:
在这里插入图片描述
通过调整命令字的数值,例如这8位命令字对应的16进制数值是81H,那么就是读取芯片的秒寄存器,如果是80H,就是对秒寄存器进行写的操作,下面介绍读和写的数据流:
在这里插入图片描述
当CE达到高电平的时候,芯片开始工作,在读操作中,SCLK每来一个上升沿,就读取一次单片机发送的命令字,当读满8位时,立刻下一个下降沿就开始发送数据,一共8位。写的操作类比如此
在这里插入图片描述
这里注意一个细节,那就是写保护位,在while(1)之前初始化的时候,向芯片发送命令字8EH,并且将最高位的WP置0,要不然会进入写保护。
在这里插入图片描述
同时,由于SCLK是单片机发送的脉冲,通过程序让SCLK输出高低电平,如果单片机执行代码的速度很快,那么电平会急速的反转,因此需要考虑一下芯片手册中tCH和tCL小持续时间的限制:
在这里插入图片描述
只需要最小250ns就好,51单片机的频率达不到这么快,因此不需要加入延时。
在这里插入图片描述

下面来讲一下DS1302的涓流充电模式,只需要在VCC1断接一个3V的纽扣电池然后接地就行,具体可以通过命令字对芯片的寄存器设置为充电模式,芯片充电部分的寄存器电路如下:只需要配置相应的开关来实现电路搭建就OK。
在这里插入图片描述
这一个8位寄存器的每一位搭配出来的功能如下,其中diode表示二极管:
在这里插入图片描述
下面给出充电电流的计算方法,当寄存器配置电路为一个二极管和一个2k欧姆的电阻,选用3V的纽扣电池供电的话,最大电流是(3V - 0.7V)/2kΩ = 1.15mA
在这里插入图片描述
电池充电时间和电流关系如下,因此充电电流越大,充电时间越短:
在这里插入图片描述
需要注意的是,纽扣电池,例如 CR2032 锂锰纽扣电池,使用的时间越长,电池的电压(蓝线)会下降,内阻(绿线)会上升,
在这里插入图片描述

2023.10.22

寄存器地址映射和指针:
以地址为89H的TMOD为例,下面演示一般指针的操作:

unsigned int *p;
int a=10;
p = &a;

这样就完成了指针p的初始化,p取了a的地址,但是还可以以下面这样子初始化指针:

*(unsigned int *) (0x89) = 0x00;

这个意思是直接直接对0x89这个内存空间进行操作,以TMOD为例,之所以在程序中可以直接使用TMOD = 0x00进行赋值,是因为做了如下的define:

#define TMOD *(unsigned int *) (0x89);

2023.10.24

exturn关键字的作用,就是在除了main.c文件以外定义了某个变量,想要在main.c中使用,可以在那个文件的头文件中加上exturn去声明用到的那个变量,如下:
例如我在DS1302.c这个文件中定义了一个数组,我想在main.c中使用。
在这里插入图片描述
那么我们就需要在.h文件去用extern声明。
在这里插入图片描述
越界清零的小技巧

假如说一个变量num,从0开始加到5之后,再加就要清零,可以直接写为

num++;
num %=6

2023.10.26

今天在github上找了一个按键驱动代码,单击双击这些操作,我细看了一下代码,发现有很多的细节需要注意,首先C++中的类的应用,是为了方便函数和变量的管理,某一类操作需要用到的函数和变量都可以存,C语言也可以使用结构体实现这样的操作:
C语言中结构体只能存变量,不能存函数,因此如果存函数的话就会用到函数指针!
在这里插入图片描述
在对结构体赋初值的时候,可以直接给函数传递结构体的地址(一般都这样,可以节省内存),例如下面这样,调用函数对结构体内部变量操作的时候,也是直接传递结构体的地址!
在这里插入图片描述
按键消抖的一个很牛逼的方式,不利用while(1)等待,而且可以放到定时器中,取代delay影响主进程的运行:
下面解析一下这段是什么意思,这个代码是每20ms运行一次,作用是按键消抖,无论是按下还是抬起的抖动,一般20ms消抖是比较常规的做法,首先是使用了一个&&运算符,这个运算符有一个短路的作用,就是当前一个表达式的数值为0的时候,后面的表达式不进行计算,因此后面的自加操作在按键的电平没有反转的时候不执行,当按键的电平反转,此时正处于按下的瞬间,后面表达式中用到了==前置的++==操作,意思是先自加然后再做运算,Debounce_Time的初值是0,第一次自己为1,BUTTON_DEBOUNCE_TIME是消抖时间,设置为2,因此他的计算规则是:(n-1)*调用周期,按下瞬间这个IF是不执行的,当过了20ms一个周期之后,这个IF中值是1,进入IF内部执行相应按键的状态,配合下面的switch来实现按键的消抖,非常的机智!!
在这里插入图片描述
看完代码之后,我对我的写代码规范有了一些新的认识,C语言可以模仿C++的类的使用方法,C++使用类的时候是有构造函数和析构函数,还有内部函数以及私有变量,C语言利用结构体实现上述功能的操作如下:

  1. 结构体定义阶段:首先定义功能的结构体,内部包括变量以及内部函数指针
  2. 结构体实例化:可以写一个专门的Create函数(如下图),内部是初始化结构体的内部变量以及关于这个硬件的自身操作
  3. 绑定事件与回调函数:将这些函数指针绑定到一个函数指针数组中,用到的时候直接调用这个数组然后写()就行

在这里插入图片描述
下面来具体说一下客制化事件的回调函数吧,首先定义了很多事件
在这里插入图片描述
然后定义了回调函数的类型
在这里插入图片描述
然后定义了一个数组,里面下标索引就是不用的事件,存储的内容就是相应事件的回调函数
在这里插入图片描述
然后使用Attach函数将回调函数与相应事件进行绑定,一个函数绑定一个事件和回调函数。
在这里插入图片描述
需要用的时候直接调用即可,因为本次程序中多次需要调用这个执行回调函数的函数,因此用#define 将其变成内联形式,可以大大加速程序运行效率。
在这里插入图片描述
这种事件加回调函数的变成模式很适合于一个功能他有多种模式,直接查看不同模式对应的回调函数就可以很方便理解什么模式对应什么操作,后期进行维护也很方便
关于void* 类型是啥
如果一个函数的形参是void *,表示可以接受任何类型的指针,下面是gpt给出的示例程序

在这里插入图片描述
关于结构体
一般结构体定义的形式如下:typedef就是起小名的意思,将struct button起了一个小名叫Button_t,因此在定义结构体的时候,可以直接用Button_t button1来定义

在这里插入图片描述
其中还有一个点,就是下面这里用了冒号,后面是规定了这个变量最多有多少位,这样的做法是为了节省单片机的空间,尤其是一些空间小的单片机,这些变量本来数值范围就不大,因此可以合理的规定位数,要是2的整数倍哦

在这里插入图片描述
我准备借鉴上面这段按键代码重构一下当前已有的按键驱动
首先我在写定时器中断的时候,老是调不出来,最后发现是数据类型不对,原本我设定的是char类型,可是2的8次方才256,因此一直计不到1000,导致无法翻转LED的电平,所以观察不到现象,变成int类型就没问题了。
在这里插入图片描述

2023.10.27

之前是实现了一个按键,这次我准备实现多个按键同时扫描,因此需要定义一个数据结构存储需要扫描的按键,逐一扫描,我借鉴的源程序是使用链表的形式存储按键,由于按键的个数 以及引脚的配置都是事先知道的,因此链表删除方便的优势并没有凸显,并且链表多定义的那个指针会占用一定的空间,因此我采用数组的方式存储按键,下面是遇到的一些问题:
结构体不初始化无法存入数组中
原本我打算这样直接存入数组中,但是报错说不正确的类型转换,后面我对结构体初始化之后,才解决
在这里插入图片描述
sbit类型并不能当做形参传入函数
这类的参数只能进行写,读操作也只能使用if来判断,进行电平的读
在这里插入图片描述

2023.10.28

在使用单片实现脉冲发送数据的时候,当需要发时,要先准备好数据,然后再产生脉冲,当接受数据时,要先产生一个脉冲,然后再接受,注意当读完之后,IO口要清零
在这里插入图片描述

2023.10.29

看了一下之前我的C++笔记以及回想到了以前看到的C++设计习惯,避免使用#define,而是用const代替,于是我将代码中使用#define定义的东西换成了const,我的理解是使用#define定义的变量并不需要指明是什么类型的,这需要程序员自己记在心里,这样就会造成程序有隐藏的出错风险,下面举例const的使用案例:
DS1302_driver.h
注意是在头文件中声明这个变量
在这里插入图片描述
DS1302_driver.c
在.c文件中定义变量的具体数值
在这里插入图片描述

补码
学习了补码,我对于不同类型之间变量的赋值,以及某个整形变量的最大最小值,可以放心的去操作,并且脱口而出,计算机中的数据都是以补码的形式存储的(这里指的是整形),补码存储数据的规则如下,以二进制数举例:
在这里插入图片描述

例如一个

char i = 127

可以先求127的二进制数是0111 111,那么会被认为是整数,以10进制输出i可以发现是127,
如果:

char i = 128

求出128的二进制数是1000 0000,那么先取反再加1,就是1000 0000,也就是-128
因此一个char类型变量范围是-128到127。
为什么要学习补码呢?

  1. 可以快速判断一个数据类型的十进制数值范围
  2. 了解了在计算机中,当对一个变量进行赋值时,首先将其转化为二进制数,然后进行赋值。因此可以放心的对char类型的数据使用十进制的数赋值

int向char赋值
假如说一个int类型像一个char类型赋值,截取Int类型的低8位给char!
重新了解一下达林顿晶体管
由于需要用到ULN2003来驱动步进电机,因此来讲讲达林顿管吧:
达林顿管其实就是两级的三极管组成的,大大提高了IO口的驱动能力,注意一点这么链接的话,逻辑上等同于取非
在这里插入图片描述
困扰我很久的RAM以及ROM
例如89C52RC的寄存器地址,以及程序运行过程中定义的变量,都是存在在SRAM中,SRAM的有点就是速度快,但是价格比较贵,因此都不是很大。
最一开始是Mask ROM,这是一个只能在设计芯片的时候固定数据,并不能写和读,然后衍生出了PROM,但只支持一次写,不能读,然后有了EPROM,中间挖了个洞,用强紫外线照射一会儿就能擦除然后再写了,然后有了E2PROM,这个就是比较方便用程序去读写的掉电不丢失数据的存储器了,但是应用不多,大多数会用FLASH去做存储,单片机的程序就存储在FLASH中,FLASH的应用例如U盘之类的,热插拔,掉电不丢失数据。
在这里插入图片描述
I2C总线标准
之所以要将SCL和SDA设置成开漏模式,是因为开漏模式如果输出为0,那么会直接下拉接地,如果输出是1,那么会出现高阻态,相当于没有链接,因此当多个设备再I2C总线上时,单独访问一个设备可以只将这个设备输出0,其余输出1,相当于断开其他设备连接,同时当哪个设备都没选中时,保持SDA和SCL高电平即可,因此需要上拉电阻。
在这里插入图片描述
以至于为什么SDA以及SCL端口为什么要设置成开漏输出模式呢?可不可以设置成推挽输出模式呢?以stm32的电路为例:
设备1输出高电平1释放总线,设备2输出低电平接手总线,MOS管开关后,红线部分形成回路,之间并无电阻,因此会直接短路
在这里插入图片描述
当IO口设置成开漏模式后如下,当A为高电平,端口输出为高阻态,相当于这个设备和总线断开了,当A是低电平的时候,端口输出是0,拉低总线,因为有限流电阻的存在,并不会出现短路的状况
在这里插入图片描述

下面以24C02为例学习I2C
下面是页写的时序图以及每个型号的存储结构,以24C02为例,他的字地址有8位,范围就是0x00到0xFF,一个字地址中一般是存放一个8位的数据,这是由用户在SDA总线中发送的数据决定的,因此一个字地址中存放一个字节,一页可以存8个字节,当存一个字节的时候,芯片会自动将字地址加1,那么可以算一共存满,有多少页,就是2的8次方除以8就可以知道有32页,其余型号也一样,这么计算,注意的是,如果在写到255个字节的时候,再写一点进去,就会覆盖最一开始0x00位置的数据。尽量避免哦。
在这里插入图片描述
在这里插入图片描述
下面以24C02为例,画出了页数,字地址,每页字节数,字地址中存储的数据之间的关系,假设只是在0x00和0x01中存储了两个数据。
在这里插入图片描述

2023.11.1

今天准备来写一下I2C的时序,用89C52,根据数据手册可以知道高电平的持续时间最小是0.4us,单片机用的是12M的晶振,执行一个机器周期就是1/12M/12也就是1us,因此在用命令反转电平的时候不需要加一个延时,因为他的速度没那么快,如果还是不放心可以用示波器去看一下频率最快是多少。

在这里插入图片描述
关于I2C的一些小细节以及ROM的小细节
在数据传输的时候,框里的部分其实只有1位,上下两条线表示可以是0也可以是1。因此如果需要发送1个字节的数据,也就是8位的话,需要来回执行8次。
在这里插入图片描述
当SCL以及SDA是高电平的时候,表示总线是空闲状态!
在这里插入图片描述
在写序列有效停止后,需要等待一个写周期,24C02的写周期是5ms,然后可以进行下一个开始序列,这也是为什么ROM要比RAM慢的原因,需要花一段时间将数据写入到芯片中!
在这里插入图片描述
在单片机读的时候,需要释放SDA总线,也就是让SDA置1,然后置SCL位高电平开始读取SDA中数据。

在这里插入图片描述
江科大版本的按键检测
主体思想就是将每个按键的状态进行编码,以普中的开发板为例,我们拿左下角4个独立按键进行按键检测实验

按键状态数值
没有按下0
K1按下1
K2按下2
K3按下3
K4按下4

当数值从1或2或3或4变为0时,判断为按键按下,设定这个变化周期是20ms,以此来进行按键的消抖,也正对应着检测按键从按下到抬起的瞬间。
缺点:
当两个按键同时按下时,只认为最后一个抬起的按键为被按了,下面举了俩例子:
在这里插入图片描述
在这里插入图片描述

2023.11.7

好久都没有来更新啦,这两天跟老师去开会了。
接着上回按键的说明,今天我发现普中开发板中的K3出问题了,导致按键被判断为一直按下弹起按下弹起,因此以后只能使用K1,K2以及K4了。
对于我找到的这个很好用的按键检测代码来说,因为这个回调函数是在中断时调用的,因此不应该进行很多的额外操作,只让他来置一下标志位就行,如果进行复杂的操作,会影响程序的时序!!
在这里插入图片描述

2023.11.8

我在main中定义了三个静态全局变量,我想在别的.c文件中使用,我在对应的.h文件使用exturn声明以后程序报错。

在这里插入图片描述
通过查阅资料发现,全局静态变量作用域是当前文件,在别的文件中使用extern声明也不能使用,但是全局变量可以通过extern在别的文件中使用!因此如果想要定义一个能在多个文件中使用的变量,那就不能用静态符号修饰
在这里插入图片描述
找到了按键失灵的原因了,K3按键和红外接收的OUT共引脚了,因此OUT端一直发送01,导致按键处于一个反复被按下的状态,如果要使用K3,那么就需要将红外接收模块拔下来。

在这里插入图片描述
下面是常用温度传感器DS18B20的内部结构,首先DQ端也就是数据端接一个上拉电阻,默认空闲状态是高电平,首先经过一个寄生电源部分,意思是当VDD接地时,DQ的上拉电阻可以为电路提供电源,顺便给电容充电,当DQ为低电平的时候,电容开始放电保持电路一直有点,但是这样的驱动能力不强,某些功能需要再强上拉一个电阻。访问ROM,比较地址来获取存储器控制权,操作RAM来使用其内部的右侧寄存器,第一个是正常的温度读数,第二个第三个是内部E2PROM中存储的温度上限以及下限,第四个是配置参数,第五个是CRC校验,防止出错
在这里插入图片描述
细看一下右下角:
在这里插入图片描述

通讯协议需要用到的线
UARTRXD,TXD,VCC,GND一共四根
I2CSCLK,SDTA,VCC,GND一共四根
单总线DQ,VCC,GND一共三根或者DQ与GND一共两根

单总线的出现就是为了省线,只需要两根线就可以进行通讯,但是目前还没有广泛应用,再这个温度传感器中最为常用。
在这里插入图片描述

2023.11.18

好久不见,现在我来总结一下DS18B20这个温度传感器的使用:
DS18B20
下面是DS18B20的芯片内部总览,一共分为3个部分,第一部分是供电,DS18B20支持独立VDD外接供电,以及使用DQ总线寄生供电第二部分是一块儿ROM区域,内部存储一个64位的识别码,以供多机通信的时候可以找到从机,这个码是唯一的,第三部分就是数据暂存器,这是一块儿RAM区域,内部存储转换后的温度值(2个字节),一个自己的温度上限,一个字节的温度下限,一个字节的配置寄存器(内部可以设置DS18B20的分辨率,分别是9, 10, 11, or 12 位,对应的系数为0.5°C, 0.25°C, 0.125°C 和 0.0625°C,上电默认是12位)

在这里插入图片描述
第一部分供电
模式一:VDD外部直流供电
上面那个二极管是为了保护信号线DQ不受外部电源电流的影响,因此在DQ由低电平变高电平的时候是一个弧度,这是由于上拉电阻的影响,弱上拉模式的话电平反转会慢一点
在这里插入图片描述
下面是DS18B20使用外部供电的典型电路图
在这里插入图片描述

模式二:使用DQ进行偷电模式
将VDD接GND,使用DQ既当信号线又当电源线,当DQ是低电平时,CPP会放电持续提供电量,DQ是高电平时,不仅会为芯片供电,还会为电容充电,下面那个二极管的作用是为了防止POWER-SUPPLY SENSE这个标志位被设置

在这里插入图片描述
下面是DS18B20使用寄生电源供电的典型电路,之所以加上一个VPU强上拉,是因为在DS18B20执行温度转换以及将RAM中3字节配置存储在EEPROM中这俩操作需要大电流(高达1.5mA),因此在发出Convert T [44h] 或 Copy Scratchpad[48h] 命令后的10微秒内(最大),1-Wire总线必须切换到强上拉电阻,并且在转换(tCONV)或数据传输(tWR = 10ms)的整个过程中,总线必须保持上拉电阻高电平。上拉电阻启用时,1-Wire总线上不能进行其他活动。
在这里插入图片描述
64位的ROM介绍
下面是64位码的意义,高8位是CRC校验,是通过后面的所有位进行计算得到的,中间的48位是厂家自己设定的唯一身份,最后8位是DS18B20家族的固定码28H.
在这里插入图片描述

暂存器SCRATCHPAD介绍
下面是暂存器的SRAM内存映射
在这里插入图片描述

第一个暂存器,TEMPRATURE SENSOR
这是一个温度转换暂存器(存储个),它有两个字节,其中数据是以2进制补码形式存储,15-11位都是符号位(正数的话都是0,负数的话都是1),温度传感器的分辨率可以由用户配置为9, 10, 11, 或 12位,对应于0.5°C,0.25°C, 0.125°C, 和0.0625°C的增量。上电默认分辨率为12位。DS18B20在一个低功耗的空闲状态启动。为初始化一个温度测量和模拟到数字转换,主机必须发出Convert T[44h] 命令。转换结束后,来源于转换的热量数据存储在暂存器内存的2字节温度寄存器中,DS18B20返回其空闲状态。如果DS18B20由外部电源供电,主机可以在Convert T命令后发出“读取时间插槽”(见1-Wire总线系统部分),DS18B20将通过在温度转换过程中传输0,转换完成是传输1来回应。 如果用寄生电源供电DS18B20,这种通知技术不能使用,因为在整个温度转换过程中总线必须由强上拉电阻拉高。如果想省事的话,直接一个延时解决
在这里插入图片描述

对于11位分辨率,位0未定义。对于10位分辨率,位1和0未定义,对于9位分辨率,位2, 1, 和0未定义(这里的未定义到底是什么意思呢?)。
下面是以12位分辨率为例,温度对应的2进制补码,只需要将二进制补码转化为十进制,然后乘以分辨率即可算出温度。其中以12位分辨率存储的,以12位分辨率为例有个小技巧,那就是bit 0-bit 3存储的是小数位,BIT4开始存储的整数位,可以实际计算去验证 注意默认上电是85度
在这里插入图片描述
下面展示了不同分辨的温度转换时间:
可以看到使用12位的精度的话,需要750ms,非常的长,如果精度不高,只需要93ms即可,但也比较慢了
在这里插入图片描述

第二个暂存器BYTE2和BYTE3存储的温度上下限
这俩字节中的数据存储方式相同,BIT7是符号位,
在这里插入图片描述
在TH和TL的比较中,温度寄存器的第11位到第4位被使用,因为TH和TL是8位寄存器。如果测量的温度低于或等于TL或高于或等于TH,就存在一个警报条件,并在DS18B20内部设置一个警报标志。这个标志在每次温度测量后更新;因此,如果警报条件消失,下一次温度变换后标志将被关闭。小技巧,这里以12位分辨率为例,可以简单认为只是将温度的整数部分和上下限进行对比
BYTE4 配置寄存器
其中BIT7,BIT4 - BIT0都是不能动的,BIT6 的R1和BIT5的R0是可以设置的,用户可以使用此注册器中的R0和R1位设置DS18B20的转换分辨率,如表2所示。这些位的上电默认值是R0 = 1和R1 = 1(12位分辨率)
在这里插入图片描述
在这里插入图片描述
剩下就是DS18B20的指令集以及时序
初始化时序
首先主机拉低总线至少480us(这一步是主机发送reset指令),然后释放总线,由于是弱上拉模式,因此会弧形上升,当DS18B20检测到这个上升电平时,等待15-60us后将总线拉低60-240us(响应主机),然后释放总线
在这里插入图片描述
发送和接收时序:
发送
DS18B20是以60us为一个时间片,当主机发送0时,将总线拉低60到120us,DS28B20在总线拉低15us后开始进行检测(进入DS18B20的采样窗口期),当整个采样窗口期(15us - 60us)是0,就接收0,发送1同理,主机首先拉低总线,在15us内释放总线,在总线拉低15us后,DS18B20开始进入采样窗口期,当整个采样窗口期(15us - 60us)是1,就接收1
接收
主设备必须在发出读取暂存器[BEh]或读取电源供应[B4h]指令后立即生成读时间槽,以便 DS18B20 能够提供请求的数据。此外,主设备还可以在发出转换 T [44h] 或 Recall E2 [B8h] 命令后生成读取时间槽,以便了解操作状态.
主设备首先拉低总线至少1us,然后释放总线,在拉低后15us内主设备读取总线上的电平即可知道DS18B20发送的是啥了
在这里插入图片描述
具体的模式控制请看手册,这里就不再花时间了

2023.11.19

今天来学习一下PWM以及电机控制:
下面是两种典型的驱动电路,第一个是利用三极管开关来驱动电机,值得注意的是电机中有电感,因此需要并联一个续流二极管,下面将简单介绍一下:
在这里插入图片描述
电感类的感性元器件,在断电瞬间,会产生很大的感应电动势,然后会击穿电路中反向击穿电压值比较低的元器件,例如三极管这些,为了解决这个方向电动势,要在感性元器件上并联一个续流二极管,来消耗掉这个反向电动势,第一个图片只能控制电机单向旋转,一般会采用H桥驱动电机正向和逆向旋转,但是这里加不上续流二极管,因此需要三极管有很大的击穿电压。通常并不会直接设计电机驱动电路,而是通过电机驱动芯片去驱动电机

在这里插入图片描述
在具有惯性的系统中,可以用PWM来使用一些列的脉冲等效地获得所需要的模拟量,PWD地几个重要参数,一个是频率,一个是占空比,一个是占空比地增量大小(精度)。

在这里插入图片描述
PWM波生成地方式有硬件和软件两种方式,一般最新地芯片都会有硬件PWM,也就是占用了一个计时器的PWM,软件实现PWM方式模拟硬件PWM,使用定时器来实现:
首先是设定定时器需要的计的总个数,例如100,然后从0开始计,用户设定一个数值,假如说是60,设定计数器的计数值小于设定值时输出是0,大于时输出1,也就完成了PWM的占空比的设定。
在这里插入图片描述
如何通过上述模型来确定这个PWM的三要素呢?也就是频率,占空比,以及分辨率
下面COUNTsetmax是软件设定的计数最大值,这个数值决定了分辨率,假如这个数值是100,那么分辨率就是1%,Countusr是用户设定的阈值,计数值大于它输出1,小于它输出0,决定了占空比,然后Ttimer是定时器的中断周期。
因此确定PWM波的参数时分为一下步骤:
1.确定分辨率,确定Countsemax
2.确定PWM频率,也就是在1的基础上确定定时器的中断频率Ttimer。
在这里插入图片描述
注意,在日常驱动直流电机的时候,PWM的频率一般设定在10KHZ-20KHZ之间,设定高频的目的是为了减轻电机的噪音。

2023.11.20

今天了解了运算放大器的内部结构以及功能:
在这里插入图片描述
这个电路包含三个级,输入级,中间级,输出级。

输入级的功能是放大输入差分Vp-Vn,并将其转换为单端信号。中间级进一步放大信号并提供频率补偿。输出级提供输出驱动能力,相当于推挽输出放大电流。

这也解释了为啥运算放大器会有电压比较器的作用:
当VIN+电压大于VIN-时,OUT会输出一个无穷大的电压,由于芯片VCC供电的限制,因此输出VCC
当VIN+电压小于VIN-时,OUT会输出一个无穷小的电压,由于芯片GND的限制,因此输出GND
在这里插入图片描述
反向放大器
假设IN端输入是1.5V电压,根据电压比较器的特性,可以知道OUT端将输出一个无穷小的电压,由于OUT端连了一根导线回去,这会将负端的电动势无限拉低,当小于正端时,OUT将输出无穷大的电压,循环往复,最终使得正端和负端的电压都是0V,这就是虚短的概念,由于R2上是有电流流过的,因此1.5/R1 *R2是R2右侧的电压,由于电阻会产生压降,因此是反向的电压,假如这个放大器接在VCC和GND,它并不能产生小于GND的电压,因此我们需要设计一下电路,如下图:
在这里插入图片描述
使用俩电源供电,这是很麻烦的,因此一般不使用反向放大器
在这里插入图片描述
同向放大器
根据虚短的概念可以知道,负端的电压是VIN,那么电流的流向就是向左,计算R2的压降就是VIN/R1 * R2,用VIN加上这个压降就是VOUT
在这里插入图片描述
电压跟随器
这么设计就是输入电压等于输出电压,因为运算放大器内部有功率放大器,因此这么做是为了提高这个电压值的驱动能力,也就是放大了电流
在这里插入图片描述
很容易发现,同向电压放大器可以变成电压跟随器,那就是将R1的阻值变为无穷大,就相当于断路了,也就变成了电压跟随器

2023.11.21

古老的DA电路
下面是一个古老的8位DA电路,合并电阻知道,总阻值是R,所以干路电流就是Vref/R.
在这里插入图片描述
假如拿I0作为电流基本单位,I1就是2倍的I0,逐次往上走,就可以发现每一个电流都代表了2进制的位权,因此D7-D0哪个开关开,就相当于哪一位置1。
在这里插入图片描述
这个放大器就相当于反相放大器,我们用虚短虚断来分析,流过Rfb上的电流是前面所有支路的电流之和,那么所有支路的电流之和是(D7 - D0 ) * I0 ,因此I = VREF / R 并且I = 256 * I0,因此I0 = VREF / R * 256 ,因此电流之和就是(D7 - D0 ) * VREF / R * 256 ,然后乘以Rfb就是V0的电压,注意这里的V0是个负的,为了简化起见,另Rfb = R, 那么V0的式子就会更简单一点,但是要注意数值是负的,因此需要双电源,来实现负电压。
PWM输出的DA
由于 PWM波是周期的方波,通过傅里叶变换可以知道它是由基波和多次谐波分量构成的,因此使用低通滤波器滤除掉多次谐波,保留基波就可以得到稳定的电压,通过稳定的电压来输出不同的模拟信号。下图就是利用了二阶的RC电路来实现低通滤波器,并且后面加了一个电压跟随器,这个的作用是增大IO的驱动能力,并且起到减小纹波(贝塞尔)的作用,一般加上电压跟随器的RC低通滤波器,我们叫它有源低通滤波器,不加的就是无源。
在这里插入图片描述

下面是加入放大器的作用,以及为啥叫有源
在这里插入图片描述
下面是周期是T,幅度是1,脉宽是涛的周期矩形信号进行的傅里叶变换分析
在这里插入图片描述
可以发现基波的峰值时占空比,每次谐波分量的间距是2 pif,第一个零点是2pi / 脉宽。假如矩形波的幅度是VH,那么基波的峰值就是VH乘以占空比,就跟之前那张图的计算是一样的。其中VH就是PWM输出的电压,由于我们上拉了一个上拉电阻,因此这个VH就差不多是5V,因此V0的输出范围就是0-5V。这个DA的分辨率就是PWM的精度乘以5V,因为采用的是计数到100,因此分辨率是0.01 * 5 = 0.05V,分辨率不高呀,得提高PWM的最大计数值来提高PWM精度,以提高最后建立的DA的精度

在这里插入图片描述
在整个滤波的过程,通常会将低通滤波器的截止频率设定在基波附近,因此对滤波后波形影响最大的就是一次谐波分量,因此我们来考察一次谐波分量在占空比从0到100的过程中是怎么变化的:

PWM占空比逐渐增大,频域是怎么变化的

可以看到随着占空比的增加,基波分量在增加,一次谐波分量是先增加再减小,在50%占空比的时候最大。

低通滤波器的设计
上面这个点还有一处没解释清楚,就是这个低通滤波器如何设计呢?
有两种方法,第一种就是使用RC电路来构造,第二种是利用代码实现的数字滤波器
RC电路构建低通滤波器
一阶和2阶RC滤波器的电路如下图:

在这里插入图片描述
在这里插入图片描述
其中RC低通滤波器的截止频率计算公式如下:
在这里插入图片描述
一阶低通滤波器的伯德图如下图所示,横坐标是频率,在过渡带中,一阶RC滤波器的滚降系数是每10倍频,-20db,也就是说频率每增大10倍,衰减10倍。注意截止频率附近是衰减-3db的位置,也就是衰减至最大值0.707倍的地方
在这里插入图片描述
2阶RC低通滤波器的过度带中滚将悉数是40DB每10倍频,就是频率每增长10倍,衰减100倍,3阶同理
在这里插入图片描述
需要注意一个问题,那就是一阶的截止频率是fc的地方衰减-3db,但是二阶在fc的地方衰减就是大约-6db了,这样的话会造成信号的损失。
在这里插入图片描述
有一个这种的解决办法,就是每增加一阶,电阻增大10倍,电容减小10倍,下面是电路图以及频率图,可以看出橙色线在一定程度上解决了这个问题。

在这里插入图片描述
在这里插入图片描述
下面给出一个PWM波实现DAC的RC低通滤波器阶数以及fc的确定方法:
fc一般是取pwm波频率的10分之一,滤波器阶数就要根据DAC的位数来确定,如果是12位的DAC,那么m就约等于4
在这里插入图片描述

在这里插入图片描述

还需要注意的一点就是电容不能选取的太大,这样会降低电压变化的灵敏度。

使用现有的成熟低通滤波器,来构建模拟低通滤波器
两种方法,第一种是直接使用matlab来运算,第二种是首先构建低通滤波器的H(s)利用z变换,转化成H(z),然后将H(Z)变成微分方程,然后再变成差分方程,然后就能得到跟软件计算出来的系数一样的公式了
直接使用matlab运算的请看这个帖子
下面我们来手推一下,如何构建数字的巴特沃斯滤波器:
首先来构建原始的模拟巴特沃斯滤波器,设计它需要4个重要指标,分别是Rp,Rs,Wp和Wst,分别是通带衰减最大db,阻带衰减最小db,通带截止频率Wp,以及阻带的开始频率Wst。其中Rp通常设定为3db,RS如果想效果好一点的话就设置成40db。
在这里插入图片描述
首先我们假定四个参数如下图的最上面所示,我们要去模拟一个巴特沃斯滤波器,使用双线性变换法将S频域映射到Z频率,这样的好处是可以实现S频率到Z频率的一对一的映射关系,然后套公式就可以求出通带和阻带的预畸变的频率,作用是将数字量转化为模拟量
在这里插入图片描述
下一步就是去计算滤波器的最小阶数,套公式可以得到是5.3,那么N就取6
在这里插入图片描述
通过查巴特沃斯的表,就可以知道表达式是下面这个,但是这个是归一化的表达式(认为Wc截止频率是1),需要去归一化
在这里插入图片描述
下面这个是按阻带的预畸变截止频率算的通带截止频率,然后进行第五步的变量替换就可以了
在这里插入图片描述
然后再做一个变量替换就可以求出H(Z),通过
在这里插入图片描述
然后利用下面这个方法,将系统函数转成差分方程最后算的系数和matlab会基本差不多
在这里插入图片描述
下面我们来看一下点符战车里的低通滤波器的代码:
上面是从matlab中导出来的系数,目测应该是个6阶的低通滤波器,下面就是常规的一些带系数的操作了
在这里插入图片描述
于此同时,我又复习了一下塔克创新小车的代码,发现了一种很牛逼的设计,之前江科大的任务调度,是在中断程序中完成的各项任务,然后利用一个计数值以及%符号来分配的运行周期,由于中断程序一般不能写的非常耗时,通常都是用来置标志位的,因此塔克就在main的while中来实现各个任务,在中断中只是置一下标志位:
在这里插入图片描述
在这里插入图片描述

ADC的设计
下面是一个逐次逼近型的ADC,一共可以搞8个通道出来,为啥叫逐次逼近嘞,他的内部是有一个DAC的,通过DAC产生一个电压和输入的进行比较,大了就减小,小了就增大,差不多就输出这个数字量
在这里插入图片描述
实际上这个比较是数字量一位一位的比,实际上原理就是二分法。
ADC和DAC的重要指标
分辨率就是数字量变化最小的一个单位,模拟量的变化量。
这个ADC的转换速度,就是对这个模拟信号的采样速率,这个采样速度是需要遵守奈奎斯特的采样速率要求的,也就是转化速率要大于2倍的信号最高频率
在这里插入图片描述

2023.11.22

今天测试了一下用PWM作为DA来输出一个100HZ正弦波,我们采用的pwm的频率是1KHZ,精度是1%

由于PWM波是周期性矩形脉冲,只要保留基波分量就可以得到稳定的电平,因此我们可以先对正弦信号进行采样,由于PWM的等效电平就是PWM的占空比,占空比是从0-100,因此我们限定这个正弦信号的幅度是从0-100,生成一个PWM就会产生一个采样点,因此正弦波的频率和PWM的频率是如下关系:Fsin = Fpwm / N,其中N是采样点的个数。首先我们用matlab生成一个这样的正弦波,获取每个采样点的数值,这个数值其实对应的就是占空比:
编写代码可以得到这样的一个正弦波,因为我们使用的单片机并不能产生很高分辨率的PWM,因此占空比的增量只能是1%,因此这个正弦波就比较的有锯齿,不过没关系。

在这里插入图片描述
下面是我们获取的占空比,编写代码即可
在这里插入图片描述
我在protues上进行了仿真,使用了2阶的低通滤波器,通过仿真发现,我们输出的PWM的一次谐波分量在80到100HZ附近,因此我们电阻和电容的值也就选定了,这么设计的低通滤波器截止频率在87HZ附近,因此可以滤掉各谐波分量

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

下面是示波器中的仿真图,可以看到直接输出的PWM信号还是方波,因此我们需要滤掉一次谐波分量以外的频率,黄色的波是一阶RC滤波器的结果,可以看到在占空比是50%的时候,一次谐波分量的影响达到峰值。蓝色是二阶低通滤波器后的效果,很不错,但是由于分辨率有低,主要是PWM的精度不高。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值