个人成就
- 发布了50篇内容
- 获得了4次赞同
- 获得了3次收藏
个人简介
擅长领域
暂时没有设置哦~
-
单片机延时的两种实现方法
大家好!我是张飞实战电子蔡琰老师,今天给大家分享单片机延时的两种实现方法。
单片机延时大家程序中都会用到,那么怎么实现呢?通常我们有软件实现,就是语句循环;或者硬件实现,通过定时器/计数器。
对于精准而言,从实现语言方向看,底层语言有汇编和C语言,通常我们单片机程序用C语言比较多,但是相对而言汇编更精准。从实现方式来看,有软件方式和硬件方式,那么对于C语言延时的方法哪种更精准呢?自然是定时器计数器的方式了,为什么呢?一起来看看吧。
1、软件延时-循环实现
在很多时候,定时器会被用作其他用途,不方便再用作计数了,所以这个时候就只能用软件方法延时。软件方法延时就是循环语句来实现。
通过使用带_NOP_();语句的函数实现,定义一系列不同的延时函数。我们都知道C语言最终通过编译生成汇编,所以一条C语言可能会反汇编成多条汇编语句,每条汇编指令都有指令周期,比如我们时钟是8Mhz的,那么一个指令周期就是125ns,那么NOP是一个空指令,占用一个指令周期。假如我们定义一个1ms的延时函数,那么函数里面全部用NOP指令也需要循环多次,那么循环语句也可能会有多条汇编实现,具体的根据编译器不同也不同,我们就很难精确的计算出实际的延时,那么计算大概也可以,然后最有效直接的方法就是通过示波器去测试然后再去调整循环数。不要太纠结计算数值,可以调试的。示波器测试方法更简单,在延时前后加一个引脚的输出反转信号就可以了。
到这里大家是不是觉得其实用汇编写延时函数就能做到精确了呢?相比较C语言,的确是汇编可以做到很精确的数值,因为我们可以确定每条指令的指令周期是多少,根据延时函数用到的所有指令都可以计算出来,最终计算得出比较精准的循环数值。
2、硬件延时-定时器/计数器实现
当然在大多情况下,我们还是会选择定时器来做延时处理,首先我们可以通过时钟配置定时器工作,获得精准计数,具体精准程度要看给定时器用的时钟了,内部时钟或者外部晶振的精度。可以实现极短时间的精确延时。
在实际应用中,定时常采用中断方式,通过对定时器的配置,获得中断方式和定时时间,然后通过判断计数值获得想要的延时效果,用这种方法从程序的执行效率和稳定性方面考虑都是最佳的方案。大部分项目主循环需要处理很多事情,如果在主循环中用软件延时方法难免需要等待过程,尤其是长延时的时候不能处理其他,如果好多外设在工作,会造成通信不上或者响应不及时。
总结:大部分程序中我们可以写个小的软件延时,必须等待的小延时可以用软件的实现,就可以省去对延时时间的判断了,等待就可以了。但是需要测试延时时间是否准确,或者可以直接用汇编写延时函数。硬件延时也有必要实现,根据实际需求去应用。
大部分延时应用过程中还是会有些误差的,这个是在多个小的误差基础上叠加的,是可以接受的。
-
你知道I2C为什么要接上拉吗?
实际工程项目中,有很多地方都会用到I2C总线通信,比如说24C02存储、传感器接口等,能使用I2C这项技能,就成为了我们工程师日常必备的武器,那么我们就来详细的说说I2C。
I2C是一种简单的双向二线制同步串行总线。只需要两根线即可在连接于总线上的器件之间传送信息,一根SCL时钟线,一根就是SDA数据。需要注意的是SDA它是一个双向传输的线,主机向从机发送信号通过SDA把数据送出去,从机向主机发送信号也是从SDA线把数据送出来。
I2C也可以一个一,也可以一对多,每个连接到总线的外围设备都有一个独立的地址,主机可以通过该地址来访问不同设备。主机可通过SDA线发送设备地址查找从机。
因为I2C 通信IO口输出结构都是配置为漏极开路或集电极开路输出。所以时钟线和数据线必须外部都接上拉电阻,当一对多输出的时候,很多GPIO口会连接在同一根线上,可能会存在某个GPIO输出高电平,另一个GPIO输出低电平的情况。如果使用推挽输出,你会发现这个GPIO的VCC和另一个GPIO的GND接在了一起,也就是短路了。如果换成开漏输出VCC和GND之间多了个电阻,这样电路就是安全的。上拉电阻一般取值在1K-10K之间,上拉电阻的取值和通信速率会有一定的关系,如果上拉电阻的取值太大,那么信号的上升沿、下降沿就会很缓,会影响通信速率,这个具体的看应用的通信速率去测试波形,如果取的太小了,功耗很大,取的太大了,信号的上升沿就会慢,影响通信速率(如下图的1 2表示)。
一般情况下I2C标准模式最高传输速率100Kbit/s,高速模式最高传输速率400Kbit,当然也有用软件模拟I2C的时序来进行通信的,这个速率就需要自己去掌控测试了,说白了就是我们对着时序图,抄一个同样的波形出来。只要遵守同样的约定,按照时序图对应管脚拉高拉低就可以读写数据了。
在标准协议中区分启动、停止、应答、等动作,下面我们来一起学习一下I2C的标准协议。
当检测到SDA线上为下降沿,SLC线上为高电平,就表示收到了启动信号,当检测到SDA线上为上升沿,SLC线上为高电平,就表示收到了停止信号。
收到启动信号之后就可以开始数据传输,收到停止信号表示帧数据传输完毕。在传输过程中,时钟线SCL为高电平的时候,数据线SDA必须保持稳定,不能改变电平状态,这样才能有效传输,在时钟线SCL为低电平的时候,数据线SDA可以改变状态,即:在SCL为高电平的时候,数据线SDA如果为高,那么传送的就是1,如果SDA为低,那么传送的就是0,在SCL为低电平的时候,你可以改变数据可以把数据变为0/1(图中红框交叉就表示数据的改变,从1变为0 从0变为1所以有了一个上升沿或者一个下降沿)。
在SCL的第9个时钟位,对应的SDA数据为应答数据,应答数据是有方向的,可以是主设备应答,可以是从设备应答,应答不是固定的,可以是低电平为应答,也可以是高电平应答,高应答或者低应答是由通信协议决定的,只要约定一个信号作为应答就可以了,主机给从机发送数据,从机接到了应答主机,从机给主机发送数据,主机接到了,主机就要应答,这样就形成了一个闭环。以上就是一个标准的IIC通信标准规范,你清楚了吗?
-
hex文件在单片机中是如何工作起来的?
大家好!我是张飞实战电子蔡琰老师,今天给大家分享单片机hex文件。
通常我们写的单片机程序放在哪里了?ROM和RAM有什么区别?有没有关系呢?程序又是怎么工作起来的?其实通常这些问题对于单片机初级工程师来说可能都是大大的问号,好多人可能会让单片机工作起来,比如有例程,然后根据烧录工具的流程走一遍就觉得会用单片机了,好多人会想单片机如此简单还有什么可学的。
太多人是不学底层的工作原理,也不想。觉得那么多库函数和例程,用起来就可以了。可是自己真的懂吗?碰到问题你会解决吗?所以还是会了别人不会的才是有更多选择的资本。
RAM:随机存储器,掉电数据会丢失,也就是会存储一些临时数据。
ROM:只读存储器,掉电数据不会丢失,主要存储程序数据以及常量数据或需要存储的变量数据
概念有了,到底是怎么工作起来的?
单片机运行时需要调用程序或者函数时就需要读取ROM,然后在RAM中执行这些程序或者函数的功能,所产生的临时数据也都存在RAM,掉电后临时数据就会丢失了。
程序经过编译、汇编、链接后生成HEX文件,然后再通过烧录器将HEX文件烧录到ROM中。
那么ROM是只读存储器,CPU只能从里面读数据,而不能往里面写数据,掉电后数据依然保存在存储器中。RAM是随机存储器,CPU可以从里面读取数据也可以写数据,掉电不保存。
那么这里就涉及到了单片机启动的过程了,就像我们电脑,手机这些设备开机都需要一个启动过程,就相当于初始化的过程,并不是直接跑到主程序的,单片机也是一样,这个就是启动流程,之后才会跑到主程序运行的。
启动流程我们拿ST的一款来说一下,其他厂家的也是大同小异的。通常,当处理器从复位启动时,它首先会访问 0 地址的向量表,复位时处理器首先读取向量表的前 2 个字,第一个字为堆栈指针 MSP 初始值;第二个字为复位向量,它表示程序执行的起始地址。当读取到复位地址之后,会自动跳到复位向量处开始执行程序。简单的说上电的时候就是要从地址0x0000 0000处开始的,那么从地址0处开始存储了一个向量表,就是我们说的中断向量表。换句话来讲,我们写的代码是在FLASH区域的,那么复位向量地址也是在FLASH区域,那么就相当于程序跳到了FLASH存储区开始执行代码。
那么RAM上电数据不是人为写入的,就是CPU写入的,就是上电启动后CPU开始从ROM中包含的所有程序内容第一行代码处开始执行指令的。然后为后面整个程序运行做准备,需要对RAM进行初始化。
首先是对变量分配地址空间,有已赋初值的全局变量,就需要将初值从ROM中拷贝到RAM中。
没有赋初值,就是随机值。
其次设置堆栈的地址,一般我们只用到栈空间,也就是中断处理时的“保护现场”和“恢复现场”的作用。
最后分配代码中的数据段,也是从ROM中拷贝到RAM中来分配起始地址。
启动之后完成这些准备工作就是根据不同的指令一条一条的执行了,发生中断的时候会根据中断向量表找到中断服务函数去执行中断,整个程序就这样的过程跑起来了。
-
STM32学习USART之基础篇
今天分享USART设计前的基础知识。
下面我们先来了解一下有关通信的基本概念:
通信:两个设备之间进行数据交换时,称这个过程为两个设备间的通信。
并行通信:设备之间的数据是以一位一位的二进制数进行传输的,并行通信的方式就是传输的二进制数同时从主机发送,然后同时到达从机,其优点就是传输速度快、效率高,但当需要传输很多位数据时,就需要很多根数据线,造成成本偏高,还有就是其传输距离不远。
早期的打印机就使用并口来传输数据。下图为并口数据线:
串行通信:两设备之间的数据是一位一位的从主机发送,然后一位一位的到达从机,数据传送可在一根线上进行传输,其传输速度相对并行通信较慢,但其优点是成本低,可以远距离传输。下图为9针串口数据线。
串行通信的分类:当在串行通信方式下,发送数据是在时钟的控制下进行的,当主机和从机都用同一时钟(时钟的相位和幅值均相同)来进行数据传输时,称这种方式为同步串行通信。当主机和从机不使用同一时钟来进行数据传输时,称这种方式为异步串行通信方式。
主机:两个设备进行通信时,对另一个设备发送数据的设备称为主机。
从机:两个设备进行通信时,接收数据的设备称为从机。
我们已经知道并行通信方式虽然传输速度快,但传输数据位数多时,其所需要的数据线就很多,成本偏高,传输距离远,然而串行通信因其成本低,需要数据线少,传输距离远,而且随着技术的发展串行通信的数据传输速度也变得很快了,有些甚至比并行通信传输速度更快了,所以基于这些优点,串行通信的方式被广泛的使用。下面我们重点介绍串行通信。
串行通信
串行通信因其优势突出,被广泛运用,因此产生了一些串行通信的接口标准,如RS-232标准、RS-485标准、RS-422标准等,各个标准在使用过程中又衍生出了一些子标准,如RS-232标准下又有RS-232C类型等,因其各个标准特点不同,所以使用场合也就不同了,但相对于其他标准,RS-232使用较广泛(如大家的台式电脑主机后都有一个RS-232接口),所以下面我们就以RS-232为例给大家进行介绍。
RS-232是串行通信的一种标准,在RS-232标准中主要规定了其通信接口、信号的电平标准、信号的用途。下图描述了基于RS-232标准的两设备之间的通信原理图:
从上图我们可以了解两个串口设备1和设备2之间使用RS-232标准进行通信的原理,因为控制器1/2使用TTL电平标准(正逻辑标准,逻辑“0”为0V,逻辑“1”为5V),但RS-232标准使用的是电平标准是负逻辑标准(逻辑“0”为+3至+15V,逻辑“1”为-3至-15V),所以当使用RS-232标准对这两个串行设备进行数据传输时,就需要在RS-232接口与控制器之间接一个电平转换芯片,两设备之间通过RS-232接口线连接(有25针或9针接口),这样才能确保两设备正常通信。下面我们分别介绍一下RS-232标准的接口标准和电平标准。
1. 接口标准
RS-232标准有两种接口定义,一种为DB25接口,一种为DB9接口,DB25接口现已很少使用,大多数都使用DB9接口,下面我们对这两种接口进行介绍:
DB25接口
接口的各个引脚含义如上图所示,其中第2脚和第3脚分别为数据发送和接收脚,第7脚为信号地,其他脚为一些状态标志引脚,在使用时,将接口线对应接在两个设备上,即可正常通信。
DB9接口
从上图可以看出,DB9接口对DB25接口进行了简化,同样也能传输数据,且DB9被广泛使用,下面我们对DB9的各个引脚功能进行简要说明。
第一脚(DCD):
该引脚为接收线路信号检测,该信号是从发送设备->接收设备,用于告知接收设备是否收到载波信号。
第二脚(RXD):
数据接收引脚,用于接收数据。
第三脚(TXD):
数据发送引脚,用于发送数据。
第四脚(DTR):
数据终端准备就绪引脚,用于告知接收设备,发送设备是否准备就绪。
第五脚(SG):
信号地,用于给两个设备共地,因为两个设备之间地电位可能不一致,不一致就会影响数据收发的电平信号,所以必须给两设备进行共地,保证数据的正确传输。
第六脚(DSR):
数据准备好引脚,该信号用于接收设备告知发送设备数据发送准备就绪,随时可以发送数据。
第七脚(RTS):
请求发送引脚,该信号用于发送设备请求向接收设备发送数据。
第八脚(CTS):
清除发送引脚,该信号用于接收设备告知发送设备是否可以发送数据。
第九脚(RT):
振铃提示,表示接收端与线路已经接通。
在两个设备通信时,发送设备的TXD应该接接收设备的RXD,发送设备的RXD应该接接收设备的TXD,SG接SG,在现在的串口通信中,一般就使用这三根线来传输数据信号,具体接线图如下所示:
按上图对应接线正确,两设备就可正常通信。
2. 电平标准
前已提及,RS-232采用的是负逻辑电平标准,所以两设备(使用TTL电平)之间进行串口通信时,必须外加一个电平转换芯片(如MAX232),才能保证通信成功,下面我们介绍RS-232的电平标准。
RS-232标准规定:
逻辑“1”电平为:-5—-15V或-3—-12V
逻辑“0”电平为:+5—+15V或+3—+12V
TTL电平规定(5V):
逻辑“1”电平为:+2.4—+5V
逻辑“0”电平为:0—0.5V
当用不同电平表示同一逻辑信号时,可用下图对比:
上面讲了两个设备进行串口通信的硬件支持部分,那么两个设备间传输数据应该遵守一些规定,如开始和停止条件、传输数据的位数、传输数据的快慢(波特率)、数据校验等等,只有双方都遵循这些规定,才能使传输数据不容易出错,更加能保证通信过程的顺利进行。因此我们需要对这些规定有一定的了解。
3. 串行通信的分类
1) 同步通信
这种通信方式要求发送设备和接收设备的工作时钟频率一致,发送数据时一般要有一至两个同步字符,保证两设备时钟频率一致后然后开始传输数据,最后为校验位。该种方式可用下图表示:
2) 异步通信
这种通信方式不要求发送设备和接收设备的工作时钟频率一致,发送数据要有一个开始位,然后为数据位,后面有一个校验位和停止位,相邻两个数据之间常会有空闲位,其方式可用下图描述:
4.串行通信的方式
1) 单工通信:
两个设备之间数据传输时,数据只能从一个设备发送到另一个设备,收发固定,不能互换。可用下图表示上述传输过程:
2) 半双工通信:
两个设备之间数据传输时,两个设备都可以作为发送方或接收方,但同一时刻数据只能从一个设备发送到另一个设备。其数据传输线可以是一条或者两条。可用下图表示上述传输过程:
3)全双工通信:
两个设备之间进行数据传输时,双方都可以作为发送方或接收方,双方可同时双向传输数据,有两条数据传输线。可用下图表示上述传输过程:
5.数据传输协议
因串行同步通信方式对时钟要求很高,若两个设备间时钟频率稍有不一致,则会造成数据错误,所以我们一般都使用串行异步通信,这样对时钟要求就没那么严格,可以为我们使用,前已提及,两个设备间进行数据传输时,需要遵守一些约定,这样能保证数据正确发送,那么下面我们就来介绍一下这个约定。
1) 波特率
波特率为每秒钟传送二进制数码的位数,也称为比特率,单位为b/s,位/秒,它反映了数据传输的速度,波特率越高,数据传送越快,两设备进行数据传输时,需要把发送设备波特率和接收设备波特率设为一致,这样才能保证发送数据的速度和接收数据的速度一致,从而保证通信的正确进行。
我们常用的波特率为300b/s、600b/s、1200b/s、2400b/s、4800b/s、9600b/s、115200b/s。
2) 起始位
两设备要传输数据时,发送方要先发一个起始位告知对方我要发数据了,这样接收方才知道发送方要发数据了,就准备接收数据。起始位为一位二进制位,规定逻辑“0”为起始位。
3) 数据位
当发送一个起始位之后,接收方知道发送方要发送数据了,准备好之后,发送方就开始发数据了,数据位可以是5个二进制位或者6/7/8个二进制位,我们常取8个二进制位作为数据位,发送数据时,先发送低位后发送高位。
4) 校验位
数据发送完之后,需要判断数据是否和发送方发送的一致,用一个二进制位表示校验位,常用的校验方式有奇校验、偶校验等,奇校验要求数据位和校验位“1”的个数为奇数,偶校验要求数据位和校验位“1”的个数为偶数,校验位可一定程度解决数据出错问题,但也会有误差,当然也可以不设定校验位。
5) 结束位
当一次数据发送完毕后,发送方需要告知接收方这一次数据发送完毕,常用1位或2位二进制位表示,用逻辑“1”表示停止位。
当双方达成约定后,两个设备就可以进行数据传输了。
-
单片机学习之基础篇
①寄存器
寄存器,是集成电路中非常重要的一种存储单元,在集成电路设计中,寄存器可分为电路内部使用的寄存器和充当内外部接口的寄存器这两类。内部寄存器不能被外部电路或软件访问,只是为内部电路的实现存储功能或满足电路的时序要求。而接口寄存器可以同时被内部电路和外部电路或软件访问,CPU中的寄存器就是其中一种,作为软硬件的接口,为广泛的通用编程用户所熟知。
我们举例STM32的一个寄存器:
1. 关于学习资料
对于学习一款单片机而言,资料不需要很多,最主要的还是芯片的手册,手册里面会体现出你想要的,有的人忽视手册,想要通过某种捷径直接去跳过这一步,在这里我想说,除非你有一定的了解,有信心前面的过程不会出现问题,否则还是老老实实的去啃手册,下面我们来介绍下学习STM32F373CCT6这款单片机我们用到的文档:
1.《STM32F373_Reference_Manual》是 ST 出的官方资料,有 STM32F3的详细介绍,包括了STM32F3 的各种寄存器定义以及功能等,是学习 STM32F3 的必备资料之一。
2.《STM32F373xx》也是 ST 出的官方资料,有关于芯片引脚定义,模块功能概括,以及芯片的电气属性等介绍,是学习 STM32F3 的必备资料之一。
3. 《STM32F3 与F4 系列 Cortex M4 内核编程手册》 则是对 上述资料的补充, 很多关于 CortexM4 内核的介绍(寄存器等) ,都可以在这个文档找到答案,该文档同样是 ST 的官方资料,专门针对 ST 的 Cortex M4 产品。4. 《Cortex M3 与 M4 权威指南》则针对 Cortex M4 内核进行了详细介绍,并配有简单实例,对于想深入了解 Cortex M4 内核的朋友,此文档是非常好的参考资料。
2. 关于学习方法
首先你要有个实验平台,不管资料再多这些资料的来源依据还是数据手册,一定要自己去对着数据手册仔细研读,自己独立动手配置,在平台上去做实验。只有不断的实验你才能理解。当遇到问题时不要在第一时间去网上搜索答案,再去研读数据手册,解决不了的时候,再试图去网上参考借鉴一下其他朋友的案例。
3. MDK5软件的使用
3.1 STM32 官方 标准 固件库简介
ST(意法半导体)为了方便用户开发, 提供了一套丰富的 STM32 固件库。 到底什么是固件库?它与操作寄存器有什么区别?这一节,我们将讲解 STM32 固件库相关的基础知识, 希望能够让大家对 STM32固件库有一个初步的了解。
3.1.1 库开发与寄存器开发的关系
固件库就是函数的集合,固件库函数的作用是向下负责与寄存器直接打交道,向上提供用户函数调用的接口(API) ,最终操作的还是寄存器。
在 51 的开发中我们常常的做法是直接操作寄存器,比如要控制某些 IO 口的状态,我们直接操作寄存器:
我们写成十六进表示:P0 = 0xAA;
而在 STM32 的开发中,我们同样可以操作寄存器:
GPIOA->BSRR=0x0001;
为了省却麻烦, ST(意法半导体)推出了官方固件库, 固件库将这些寄存器底层操作都封装起来, 提供一整套接口 (API)。
比如上面的控制 BSRR 寄存器实现对某一引脚的电平控制,官方库封装了一个函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BSRR = GPIO_Pin;
}
②位操作
C 语言位操作相信学过 C 语言的人都不陌生了,简而言之, 就是对基本类型变量可以在位级别进行操作。这里简单再复习一下。我们讲解几种位操作符,然后讲解位操作使用技巧。
运算符
含义
运算符
含义
&
按位与
~
取反
|
按位或
<<
左移
^
按位异或
>>
右移
1. 不改变其他位的值的情况下,对某几个位进行清0
GPIOA-> BSRR = GPIOA-> BSRR & 0XFF0F; //将第 4-7 位清 0
不改变其他位的值的状况下,对某几个位进行置1
GPIOA-> BSRRL = GPIOA-> BSRRL | 0X0040; //设置相应位第6位的值,不改变其他位的
2. 移位操作
GPIOx->ODR = 1 << 2; //1<<2 = 4 相当于把4赋给了ODR寄存器
GPIOx->ODR = 4 >> 2; //4<<2 = 1 相当于把1赋给了ODR寄存器
~取反操作 //用来对一个二进制数按位取反,即0变1,1变0。
GPIOx->ODR = ~GPIOx->ODR;
3. ^按位异或 //若参加运算的两个二进制位值相同则为0,否则为1.
GPIOx->ODR = 0x05^GPIOx->ODR;
以上就是做单片机控制的基础知识,踏实学了可以节省时间和精力去做应用模块的逻辑和算法了。基础扎实了会让产品更稳定,不能模棱两可的设计,否则故障会意想不到的出现。