单片机

今天分析一个经典的单片机供电电路,电路的原理图如下图所示:

“▲
▲ 开关电路简化后的电路

在电路上电之前。开关"TEST"断开,单片机也没有通过VCC加电。此时,T1的基极通过R9(100k)接地,处于截止状态。T3的基级电阻R7所连接的Test,T1都处于截止状态,所以T3也处于截止状态。

电源+9V被T3隔离,没有加载稳压芯片IC2上,IC2的输出VCC保持低电平。

“▲
▲ 电路关闭状态

按动按钮“TEST”启动电路,T3的基极通过R7,Test,T2的b-e接地,从而使得T3导通。此时+9V通过T3加到IC2稳压芯片。IC2输出VCC是加到单片机上。

单片机工作后,通过IO2输出高电压,通过R8使得T1导通。此时即使Test松开,T3的基极也可以通过R7,LED1,T1接地,实现电源自锁打开。

“▲
▲ 按动TEST,启动电路

“▲
▲ 电路启动后,由MCU提供T1基极电压,从而维持T3导通

之后,单片机软件可以来使得IO2端口重新变成低电平,使得T1截止,进而使得T3截止。

可以根据IO1端口,读取T2的开关状态,进而判断用户是否按动功能键。判断用户按动Test之后,等到用户释放Test之后,便可以将IO2置低电平。

也可以根据软件功能,实现自动延迟掉电,进而减少对供电电源的消耗。

来源:网络
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 65

单片机开发中,打印输出比较常见,也比较重要,今天就为大家分享一下常见的打印输出内容以及区别。

1、写在前面

在MCU项目中,printf主要用于打印输出一些调试信息。比如:程序执行出错,输出相关错误提示信息。

做的好的项目,会通过打印信息生成或保存日志信息,

2、printf输出方式

本教程主要是针对MCU的printf打印输出,常见的方式:

1)UART打印输出

2)仿真打印输出

3)SWO打印输出

4)JLink-RTT打印输出

除仿真之外,其它三种都是基于MCU硬件打印输出。

从打印效率来说:UART < SWO < JLink-RTT。

每一种printf打印输出方式应用场景不同,也各有各的特点。

3、实验现象

先让大家从实验现象了解一下printf的内容,后面文章讲述具体的配置。

3.1 UART打印输出

“单片机常见的打印输出方式及区别"

这种printf是最常见的使用UART串口输出方式,需要占用一个硬件UART串口。

3.2 仿真打印输出

“单片机常见的打印输出方式及区别"

只在集成开发环境中模拟printf仿真输出,不需要连接开发板(硬件MCU)即可实现。

3.3 SWO打印输出

SWO:Serial Wire Output,串行线输出

SWD:Serial Wire Debug,串行线调试

SWV:Serial Wire Viewer,串行线查看器

SWO输出,需要多一根SWO(引脚)线,同时需要借助SWV(查看器)查看数据,分享4种方法:

·基于Keil的『Debug(printf)Viewer』

·基于IAR的『Terminal IO』

·基于ST-LINK Utility的『Serial Wire Viewer』

·基于J-Link的『SWO Viewer』

1)基于Keil的『Debug(printf)Viewer』

“单片机常见的打印输出方式及区别"

<2)基于IAR的『Terminal IO』/font>

“单片机常见的打印输出方式及区别"

3)基于ST-LINK Utility的『Serial Wire Viewer』

“单片机常见的打印输出方式及区别"

4)基于J-Link的『SWO Viewer』

“单片机常见的打印输出方式及区别"

说明:前面2种可以分别基于ST-Link和J-link工具查看,第三种基于ST-Link查看,第四种基于J-link查看。

3.4 JLink-RTT打印输出

“单片机常见的打印输出方式及区别"

这种方式不需要使用UART串口,也不需要额外SWO引脚,但需要在代码中添加相关代码。

4、软、硬件工具及其它说明

该教程使用的软件及工具有点多,默认大家已经安装并掌握使用方法。

4.1 软件工具

1)STM32CubeMX

2)Keil MDK-ARM

3)IAR EWARM

4)STM32 ST-LINK Utility

5)J-Link / J-Trace

下载地址:

https://www.segger.com/downloads/jlink

“单片机常见的打印输出方式及区别"

4.2 硬件

1)MCU开发板

原则上Cortex-M3,M4都行,本教程基于STM32F0、F1、F4系列进行讲述。

<2)下载调试器/font>

  • ST-Link

  • J-Link

3)PC电脑

当然推荐Windows电脑,因为MDK-ARM只支持WIndows系统。

4.3 其它说明

1)源代码工程下载

为方便大家学习,本系列教程将提供对应源代码工程,供大家下载。公众号后台回复关键字“printf”获取。

2)关于仿真输出

现在开发板很便宜(相比以前),而且仿真和实际可能存在差异,故我不推荐大家软件仿真。

来源:strongerHuang
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 304

前言

单片机选型是一件重要而费心的事情,如果单片机型号选择合适,单片机应用系统经济性,可靠性较高;否则易造成经费高,系统性能到不到要求。因此掌握并正确运用单片机选型原则,选择出最能适用于应用系统的单片机,保证单片机应用系统有最高的可靠性,最优的性能价格比,最长的使用寿命和最好的升级换代的方案。

1、需求调研

选型前,首先对自己的需求有所了解,清楚自己需要哪些功能。具体便是确定电路板具有的硬件功能,如CAN、RS232、RS485、网口、USB通讯等,具体数量的IO口,具体类型的串口屏、LCD屏,板载FLASH,SD,音频输出等。

2、性能

如何选择单片机,首先也是最重要的一点就是考虑功能要求,即设计的对象是什么,要完成什么样的 任务,再根据设计任务的复杂程度来决定选择什么样的单片机。

在单片机的性能上有很多要考虑的因素,比如中断源的数量和优先级、工作温度范围、有没有低电压检测功能、单片机内部有无时钟振荡器、有无上电复位功能等等

3、存储器

单片机的存储器可分为程序存储器(ROM)和数据存储器(RAM)。

程序存储器是专门用来存放程序和常数的,有MASK (掩模)ROM、OTPROM、EPROM、FlashROM 等类型。掩模这种形式的程序存储器适用成熟的和大批量生产的产品,如使用到彩色电视机等家电产品中的单片机就采用这种方式,只要用户把应用程序代码交给半导体制造厂家,在生产相应的单片机时将程序固化到芯片中,这种芯片一旦生产出来,程序就无法改变了。

采用EPROM 的单片机具有可以灵活修改程序的优点,但存在需要紫外线擦除、较费时间的缺点。在自己做试验或样机的研发阶段,推荐使用Flash 单片机,它有电写入、电擦除的优点,使得修改程序很方便,可以提高开发速度。对于初具规模的产品可选用OTP 单片机,它不但能免去较长的产品掩模时间,加快产品的上市时间,而且方便程序的修改,能够对产品进行及时的调整和升级。

程序存储器的容量可根据程序的大小确定。对于8位单片机片内程序存储器的最大容量能达到64KB,不够时还可以扩展。选用时程序存储器的容量只要够用就行了,不然会增加成本。

数据存储器是程序在运行中存放临时数据的,掉电后数据即丢失,现在有些型号的单片机提供了EEPROM,可用来存储掉电后需要保护的关键数据,如系统的一些设置参数。

4、运行速度

单片机的运行速度首先看时钟频率,一般情况对于同一种结构的单片机,时钟频率越高速度越快。

其次看单片机CPU 的结构;采用CISC 结构(集中指令集)比采用RISC 结构(精简指令集)的速度要慢。就 是同一种结构、同一种时钟频率的单片机,有时候速度也不一样,比如Winbond (华邦)公司的W77 系列 的51 单片机1个机器周期只要4 个时钟周期,而一般的51 单片机1个周期是12个时钟周期,前者的速度是后者的3倍。

在选用单片机时要根据需要选择速度,不要片面追求高速度,单片机的稳定性、抗干扰性等参数基本上是跟速度成反比的,另外速度快功耗也大。

5、 I/O (输入/输出)口

I/O 口的数量和功能是选用单片机时首先要考虑的问题之一,要根据实际需要确定I/O 口的数量,I/O口多余了不仅芯片的体积增大,也增加了成本。选用时还要考虑I/O 口的驱动能力,驱动电流大的单片机可以简化外围电路。

51等系列的单片机下拉(输出低电平)时驱动电流大,但上拉(输出高电平)时驱动电流很小。而PIC 和AVR 系列的单片机每个I/O 口都可以设置方向,当输出口使用时以推挽驱动的方式输出高、低电平,驱动能力强,也使得I/O 口资源灵活、功能强大、可充分利用。当然我们也可以根据I/O 口的功能来设计外围电路,例如用51 单片机驱动数码管,我们选用共阳的数码管就能发挥其输出口下拉驱动电流大的特点。

6、 定时/计数器(I/O)

大部分单片机提供2~3 个定时/计数器还具有输入捕获、输出比较和PWM (脉冲宽度调制)功能,如AVR 单片机。有的单片机还有专门的PCA (可编程计数器阵列)模块和CCP (输入捕获/输出比较/PWM)模 块,如PIC 和Philips 的部分中高档单片机。利用这些模块不仅可以简化软件设计,而且能少占用CPU 的 资源。

现在还有不少单片机提供了看门狗定时器(WDT),当单片机“死机”后可以复位。

选用时可根据自己的需要和编程要求进行选择,不要片面追求功能多,用不上的功能就等于金钱的浪费。

7、 串行接口

单片机常见的串行接口有:标准UART 接口、增强型UART 接口、I2C 总线接口、CAN 总线接口、SPI接 口、USB 接口等。大部分单片机有串行接口。在没有特别说明的情况下我们常说的串行接口,简称串口, 指的就是UART。

如果系统只用一个单片机芯片时,UART 接口或USB 接口通常用来和计算机或其他芯片通信,不需要和其他设备/芯片通信时可以不用。

SPI接口可用来进行ISP编程,当你没有编程器时,尽量选用带这种接口的单片机,当然SPI接口也能用来和其它外设进行高速串行通信。

I2C 总线是一种两线、双向、可多主机操作的同步总线,IC 总线是一种工业标准,被广泛应用在各种电子产品中,如现在的彩色电视机就采用IC 总线进行参数的设置。具有 IC 总线接口的单片机在使用AT24C01 等串行EEPROM 时可以简化程序设计。

通常情况下使用最多的是UART 接口,其它接口可根据你的需要选择。

8、 模拟电路功能

现在不少单片机内部提供了A/D 转换器、PWM 输出和电压比较器,也有少量的单片机提供了D/A 转换器。单片机在集成片内A/D 转换器的同时,还集成了采样/保持电路,使用户容易建立精密的数据采集系统。

PWM 输出模块可用来产生不同频率和占空比的脉冲信号。利用PWM 输出模块配合RC 滤波电路即可方便实现D/A 输出功能。PWM 输出模块也可以用来实现直流电机的调速等功能。

单片机内部集成的电压比较器可以实现多种功能,例如作阀值检测,实现低成本的A/D 转换器等。

9、 工作电压、功耗

单片机的工作电压最低可以达到1.8V,最高6V,常用的单片机工作电压为4.5V~5.5V,低电压系列为2.7V~5.0V或2.4V~3.6V。选用时根据供电方式确定。

单片机的功耗参数主要是指正常模式、空闲模式、掉电模式下的工作电流,用电池供电的系统要选用电流小的产品,同时要考虑是否要用到单片机的掉电模式,如果要用的话必须选择有相应功能的单片机。

10、封装形式

单片机常见的封装形式有:DIP (双列直插式封装)、PLCC (PLCC 要对应插座)、QFP (四侧引脚扁平封装)、SOP (双列小外形贴片封装)等。

做实验时一般选用DIP 封装的,如果选用其它封装,用编程器编程时还配专用的适配器。如果对系统的体积有要求,如遥控器中用的单片机,往往选用QFP和SOP封装的。

11、抗干扰性能、保密性

选用单片机要选择抗干扰性能好的,特别是用在干扰比较大的工业环境中的尤应如此。单片机加密后的保密性能也要好,这样可保证你的知识产权不容易被侵犯。

12、单片机的可开发性

这也是一个十分重要的因素。所选择的单片机是否有足够的开发手段,直接影响到单片机能否顺利开发,以及开发的速度。对于被选择的单片机,应考虑下列问题。

13、开发工具、编程器

有没有集成的开发环境,在支持汇编语言的同时是否支持C 语言,使用C 语言可加快你的开发进度, 另外C语言的移值性也好。

你所选用的单片机有没有编程器支持,或能否采用ISP编程。

14、开发成本

你选择的单片机对应的编程器、仿真器价格是否高,是否要用专用设备,比如有时单片机需要选用专用的编程器,这样你的开发成本就高了。

15、开发人员的适用性

这也是一个很实际的问题,如果有两种单片机都能解决问题,当然选一种你熟悉的品种。在大多数情况下大家往往优先考虑选择51或STM32系列的单片机。

一般不选用类似小编在用的较冷门的瑞萨单片机,除了开发资料不够丰富外,厂家的技术支持服务也不给力。

16、技术支持和服务

技术支持和服务可以从下面几个方面进行考虑。

1、技术是否成熟

经大量使用被证明是成熟的产品你可以放心使用。

2、有无技术服务

国内有没有代理商和相应的技术支持,网站提供的资料是否丰富,包括芯片手册,应用指南,设计方案,范例程序等。

17、单片机的可购买性

单片机是否可直接购买到,这是指单片机能否直接从厂家或其代理商处买到,购买的途径是否顺畅。单片机是否有足够的供应量,以保证所选择的单片机能满足产品的生产需要。

选择单片机,还应注意选择那些仍然在生产中的型号,已经停产的单片机是不能使用的,因为它已无后续供货能力,直接影响到产品的继续生产和生命力。同时,也会给人以一种过时的感觉,从而影响产品的新颖性。

最好还要看一下所选用的单片机是否在改进之中,显然,对于准备推出新版本或有新版本的单片机,选择用于应用系统或产品具有较强的后劲。

18、产品价格

这也是一个重要的因素,在其它条件相当的情况下,当然选择价格低的产品,这样可以提高性价比。

19、总结

根据上面几个原则对单片机进行选择,就可以选择最能适用于你的应用系统的单片机,从而保证应用系统有最高的可靠性、最优的性价比、最长的使用寿命和最好的升级换代性。

来源:工程师看海
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 91

单片机的组成以及分类和指标

cathy的头像

单片机又称单片微控制器,它把一个计算机系统集成到一块芯片上,主要包括微处理器(CPU)、存储器(随机访问存储器RAM、只读存储器ROM)和各种输入/输出接口(包括定时器/计数器、并行I/O接口、串行口、A/D转换器以及脉冲宽度调制(PWM)等。

本文介绍如何使用带FIFO的串口来减少接收中断次数,通过一种自定义通讯协议格式,给出帧打包方法;之后介绍一种特殊的串口数据发送方法,可在避免使用串口发送中断的情况下,提高系统的响应速度。

1. 简介

串口由于使用简单,价格低廉,配合RS485芯片可以实现长距离、抗干扰能力强的局域网络而被广泛使用。随着产品功能的增多,需要处理的任务也越来越复杂,系统任务也越来越需要及时响应。

绝大多数的现代单片机(ARM7、Cortex-M3)串口都带有一定数量的硬件FIFO,本文将介绍如何使用硬件FIFO来减少接收中断次数,提高发送效率。在此之前,先来列举一下传统串口数据收发的不足之处:

(1)每接收一个字节数据,产生一次接收中断。不能有效的利用串口硬件FIFO,减少中断次数。

(2)应答数据采用等待发送的方法。由于串行数据传输的时间远远跟不上CPU的处理时间,等待串口发送完当前字节再发送下一字节会造成CPU资源浪费,不利于系统整体响应(在1200bps下,发送一字节大约需要10ms,如果一次发送几十个字节数据,CPU会长时间处于等待状态)。

(3)应答数据采用中断发送。增加一个中断源,增加系统的中断次数,这会影响系统整体稳定性(从可靠性角度考虑,中断事件应越少越好)。

(4)针对上述的不足之处,将结合一个常用自定义通讯协议,提供一个完整的解决方案。

2.串口FIFO

串口FIFO可以理解为串口专用的缓存,该缓存采用先进先出方式。数据接收FIFO和数据发送FIFO通常是独立的两个硬件。

串口接收的数据,先放入接收FIFO中,当FIFO中的数据达到触发值(通常触发值为1、2、4、8、14字节)或者FIFO中的数据虽然没有达到设定值但是一段时间(通常为3.5个字符传输时间)没有再接收到数据,则通知CPU产生接收中断;发送的数据要先写入发送FIFO,只要发送FIFO未空,硬件会自动发送FIFO中的数据。

写入发送FIFO的字节个数受FIFO最大深度影响,通常一次写入最多允许16字节。上述列举的数据跟具体的硬件有关,CPU类型不同,特性也不尽相同,使用前应参考相应的数据手册。

3.数据接收与打包

FIFO可以缓存串口接收到的数据,因此我们可以利用FIFO来减少中断次数。以NXP的lpc1778芯片为例,接收FIFO的触发级别可以设置为1、2、4、8、14字节,推荐使用8字节或者14字节,这也是PC串口接收FIFO的默认值。

这样,当接收到大量数据时,每8个字节或者14个字节才会产生一次中断(最后一次接收除外),相比接收一个字节即产生一个中断,这种方法串口接收中断次数大大减少。

将接收FIFO设置为8或者14字节也十分简单,还是以lpc1778为例,只需要设置UART FIFO控制寄存器UnFCR即可。

接收的数据要符合通讯协议规定,数据与协议是密不可分的。通常我们需要将接收到的数据根据协议打包成一帧,然后交由上层处理。下面介绍一个自定义的协议帧格式,并给出一个通用打包成帧的方法。
自定义协议格式如图3-1所示。

“单片机串口发送数据很慢?这种方法帮助你提高!"
  • 帧首:通常是3~5个0xFF或者0xEE
  • 地址号:要进行通讯的设备的地址编号,1字节
  • 命令号:对应不同的功能,1字节
  • 长度:数据区域的字节个数,1字节
  • 数据:与具体的命令号有关,数据区长度可以为0,整个帧的长度不应超过256字节
  • 校验:异或和校验(1字节)或者CRC16校验(2字节),本例使用CRC16校验

下面介绍如何将接收到的数据按照图3-1所示的格式打包成一帧。

3.1 定义数据结构

typedef struct 
{  
    uint8_t * dst_buf;                  //指向接收缓存  
    uint8_t sfd;                        //帧首标志,为0xFF或者0xEE  
    uint8_t sfd_flag;                   //找到帧首,一般是3~5个FF或EE  
    uint8_t sfd_count;                  //帧首的个数,一般3~5个  
    uint8_t received_len;               //已经接收的字节数  
    uint8_t find_fram_flag;             //找到完整帧后,置1  
    uint8_t frame_len;                  //本帧数据总长度,这个区域是可选的  
}find_frame_struct;

3.2 初始化数据结构,一般放在串口初始化中

/** 
* @brief    初始化寻找帧的数据结构 
* @param    p_fine_frame:指向打包帧数据结构体变量 
* @param    dst_buf:指向帧缓冲区 
* @param    sfd:帧首标志,一般为0xFF或者0xEE 
*/  
void init_find_frame_struct(find_frame_struct * p_find_frame,uint8_t *dst_buf,uint8_t sfd)  
{  
    p_find_frame->dst_buf=dst_buf;  
    p_find_frame->sfd=sfd;  
    p_find_frame->find_fram_flag=0;  
    p_find_frame->frame_len=10;       
    p_find_frame->received_len=0;  
    p_find_frame->sfd_count=0;  
    p_find_frame->sfd_flag=0;  
} 

3.3 数据打包程序

/** 
* @brief    寻找一帧数据  返回处理的数据个数 
* @param    p_find_frame:指向打包帧数据结构体变量 
* @param    src_buf:指向串口接收的原始数据 
* @param    data_len:src_buf本次串口接收到的原始数据个数 
* @param    sum_len:帧缓存的最大长度 
* @return   本次处理的数据个数 
*/  
uint32_t find_one_frame(find_frame_struct * p_find_frame,const uint8_t * src_buf,uint32_t data_len,uint32_t sum_len)  
{  
    uint32_t src_len=0;  
    while(data_len--)  
    {  
        if(p_find_frame ->sfd_flag==0)                        
        {   //没有找到起始帧首  
            if(src_buf[src_len++]==p_find_frame ->sfd)  
            {  
                p_find_frame ->dst_buf[p_find_frame ->received_len++]=p_find_frame ->sfd;  
                if(++p_find_frame ->sfd_count==5)          
                {  
                    p_find_frame ->sfd_flag=1;  
                    p_find_frame ->sfd_count=0;  
                    p_find_frame ->frame_len=10;  
                }  
            }  
            else  
            {  
                p_find_frame ->sfd_count=0;   
                p_find_frame ->received_len=0;   
            }  
        }  
        else   
        {   //是否是"长度"字节? Y->获取这帧的数据长度  
            if(7==p_find_frame ->received_len)                
            {  
                p_find_frame->frame_len=src_buf[src_len]+5+1+1+1+2; //帧首+地址号+命令号+数据长度+校验       
                if(p_find_frame->frame_len>=sum_len)  
                {   //这里处理方法根据具体应用不一定相同  
                    MY_DEBUGF(SLAVE_DEBUG,("数据长度超出缓存!\n"));  
                    p_find_frame->frame_len= sum_len;       
                }  
            }  
              
            p_find_frame ->dst_buf[p_find_frame->received_len++]=src_buf[src_len++];                
            if(p_find_frame ->received_len==p_find_frame ->frame_len)                  
            {  
                p_find_frame ->received_len=0;              //一帧完成    
                p_find_frame ->sfd_flag=0;  
                p_find_frame ->find_fram_flag=1;                   
                return src_len;  
            }  
        }  
    }  
    p_find_frame ->find_fram_flag=0;  
    return src_len;  
} 

使用例子:

定义数据结构体变量:

find_frame_struct slave_find_frame_srt;

定义接收数据缓冲区:

#define SLAVE_REC_DATA_LEN  128
uint8_t slave_rec_buf[SLAVE_REC_DATA_LEN];

在串口初始化中调用结构体变量初始化函数:

init_find_frame_struct(&slave_find_frame_srt,slave_rec_buf,0xEE);

在串口接收中断中调用数据打包函数:

find_one_frame(&slave_find_frame_srt,tmp_rec_buf,data_len,SLAVE_REC_DATA_LEN);

其中,rec_buf是串口接收临时缓冲区,data_len是本次接收的数据长度。

4.数据发送

前文提到,传统的等待发送方式会浪费CPU资源,而中断发送方式虽然不会造成CPU资源浪费,但又增加了一个中断源。在我们的使用中发现,定时器中断是几乎每个应用都会使用的,我们可以利用定时器中断以及硬件FIFO来进行数据发送,通过合理设计后,这样的发送方法即不会造成CPU资源浪费,也不会多增加中断源和中断事件。

需要提前说明的是,这个方法并不是对所有应用都合适,对于那些没有开定时器中断的应用本方法当然是不支持的,另外如果定时器中断间隔较长而通讯波特率又特别高的话,本方法也不太适用。

公司目前使用的通讯波特率一般比较小(1200bps、2400bps),在这些波特率下,定时器间隔为10ms以下(含10ms)就能满足。如果定时器间隔为1ms以下(含1ms),是可以使用115200bps的。

本方法主要思想是:定时器中断触发后,判断是否有数据要发送,如果有数据要发送并且满足发送条件,则将数据放入发送FIFO中,对于lpc1778来说,一次最多可以放16字节数据。之后硬件会自动启动发送,无需CPU参与。

下面介绍如何使用定时器发送数据,硬件载体为RS485。因为发送需要操作串口寄存器以及RS485方向控制引脚,需跟硬件密切相关,以下代码使用的硬件为lpc1778,但思想是通用的。

4.1 定义数据结构

/*串口帧发送结构体*/  
typedef struct 
{  
    uint16_t send_sum_len;          //要发送的帧数据长度  
    uint8_t  send_cur_len;          //当前已经发送的数据长度  
    uint8_t  send_flag;             //是否发送标志  
    uint8_t * send_data;            //指向要发送的数据缓冲区  
}uart_send_struct;  

4.2 定时处理函数

/** 
* @brief    定时发送函数,在定时器中断中调用,不使用发送中断的情况下减少发送等待 
* @param    UARTx:指向硬件串口寄存器基地址 
* @param    p:指向串口帧发送结构体变量 
*/  
#define FARME_SEND_FALG 0x5A          
#define SEND_DATA_NUM   12  
static void uart_send_com(LPC_UART_TypeDef *UARTx,uart_send_struct *p)  
{  
    uint32_t i;  
    uint32_t tmp32;  
      
    if(UARTx->LSR &(0x01<<6))                      //发送为空  
    {         
        if(p->send_flag==FARME_SEND_FALG)  
        {                          
            RS485ClrDE;                             // 置485为发送状态  
              
            tmp32=p->send_sum_len-p->send_cur_len;  
            if(tmp32>SEND_DATA_NUM)                 //向发送FIFO填充字节数据  
            {  
                for(i=0;i<SEND_DATA_NUM;i++)  
                {  
                    UARTx->THR=p->send_data[p->send_cur_len++];  
                }  
            }  
            else  
            {  
                for(i=0;i<tmp32;i++)  
                {  
                    UARTx->THR=p->send_data[p->send_cur_len++];  
                }  
                p->send_flag=0;                      
            }  
        }  
        else  
        {  
            RS485SetDE;  
        }  
    }  
}  

其中,RS485ClrDE为宏定义,设置RS485为发送模式;RS485SetDE也为宏定义,设置RS485为接收模式。

使用例子:

定义数据结构体变量:

uart_send_struct uart0_send_str;

定义发送缓冲区:

uint8_t uart0_send_buf[UART0_SEND_LEN];

根据使用的硬件串口,对定时处理函数做二次封装:

void uart0_send_data(void)
{
 uart_send_com(LPC_UART0,&uart0_send_str);
}

将封装函数uart0_send_data();放入定时器中断处理函数中;

在需要发送数据的地方,设置串口帧发送结构体变量:

uart0_send_str.send_sum_len=data_len;      //data_len为要发送的数据长度
uart0_send_str.send_cur_len=0;             //固定为0
uart0_send_str.send_data=uart0_send_buf;   //绑定发送缓冲区
uart0_send_str.send_flag=FARME_SEND_FALG;  //设置发送标志

5. 总结

本文主要讨论了一种高效的串口数据收发方法,并给出了具体的代码实现。在当前处理器任务不断增加的情况下,提供了一个占用资源少,可提高系统整体性能的新的思路。

来源:嵌入式资讯精选(作者:张巧龙)
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 57

零、写在前面

串口是单片机重要的片上资源,通过串口搭配不同的电平转换芯片,可以实现不同的通讯协议/接口,如RS232、RS485等,通过与模组的AT指令还可以实现蓝牙、wifi等通讯,所以学会单片机的串口很重要。

一、什么是串口通讯?

串行通讯是指仅用一根接收线和一根发送线就能将数据以位进行传输的一种通讯方式。尽管串行通讯的比按字节传输的并行通信慢,但是串口可以在仅仅使用两根线的情况下就能实现数据的传输。

典型的串口通信使用3根线完成,分别是地线、发送、接收。由于串口通信是异步的,所以端口能够在一根线上发送数据同时在另一根线上接收数据。串口通信最重要的参数是波特率、数据位、停止位和奇偶的校验。对于两个需要进行串口通信的端口,这些参数必须匹配,这也是能够实现串口通讯的前提。

“图1:串行通讯示数据传输意图"
图1:串行通讯示数据传输意图

二、串口通讯的通讯协议?

最初数据是模拟信号输出简单过程量,后来仪表接口出现了RS232接口,这种接口可以实现点对点的通信方式,但这种方式不能实现联网功能,这就促生了RS485。

我们知道串口通信的数据传输都是0和1,在单总线、I2C、UART中都是通过一根线的高低电平来判断逻辑1或者逻辑0,但这种信号线的GND再与其他设备形成共地模式的通信,这种共地模式传输容易产生干扰,并且抗干扰性能也比较弱。所以差分通信、支持多机通信、抗干扰强的RS485就被广泛的使用了。

RS485通信最大特点就是传输速度可以达到10Mb/s以上,传输距离可以达到3000米左右。大家需要注意的是虽然485最大速度和最大传输距离都很大,但是传输的速度是会随距离的增加而变慢的,所以两者是不可以兼得的。

三、串口通讯的物理层

串口通讯的物理层有很多标准,例如上面提到的,我们主要讲解RS-232标准,RS-232标准主要规定了信号的用途、通讯接口以及信号的电平标准。

“单片机的串口通信,简单明了好理解"

在上面的通讯方式中,两个通讯设备的"DB9接口"之间通过串口信号线建立起连接,串口信号线中使用"RS-232标准"传输数据信号。由于RS-232电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个"电平转换芯片"转换成控制器能识别的"TTL校准"的电平信号,才能实现通讯。

下图为DB9标准串口通讯接口:

“单片机的串口通信,简单明了好理解"

DB9引脚说明:

“单片机的串口通信,简单明了好理解"

上表中的是计算机端的DB9公头标准接法,由于两个通讯设备之间的收发信号(RXD与TXD)应交叉相连,所以调制调解器端的DB9母头的收发信号接法一般与公头的相反,两个设备之间连接时,只要使用"直通型"的串口线连接起来即可。

“单片机的串口通信,简单明了好理解"

串口线中的RTS、CTS、DSR、DTR及DCD信号,使用逻辑 1表示信号有效,逻辑0表示信号无效。例如,当计算机端控制DTR信号线表示为逻辑1时,它是为了告知远端的调制调解器,本机已准备好接收数据,0则表示还没准备就绪。

四、波特率

波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变的次数来表示;

“单片机的串口通信,简单明了好理解"

比如波特率为9600bps;代表的就是每秒中传输9600bit,也就是相当于每一秒中划分成了9600等份。

因此,那么每1bit的时间就是1/9600秒=104.1666...us。约0.1ms。既然是9600等份,即每1bit紧接着下一个比特,不存在额外的间隔。两台设备要想实现串口通讯,这收发端设置的波特率必须相同,否则是没办法实现通讯的。

收发波特率一致可以实现通讯:

“单片机的串口通信,简单明了好理解"

收发波特率不一致,导致RX端不能正常接收:

“单片机的串口通信,简单明了好理解"

五、串口通讯的数据结构

“单片机的串口通信,简单明了好理解"

起始位: 起始位必须是持续一个比特时间的逻辑0电平,标志传输一个字符的开始,接收方可用起始位使自己的接收时钟与发送方的数据同步。

数据位: 数据位紧跟在起始位之后,是通信中的真正有效信息。数据位的位数可以由通信双方共同约定。传输数据时先传送字符的低位,后传送字符的高位。

奇偶校验位: 奇偶校验位仅占一位,用于进行奇校验或偶校验,奇偶检验位不是必须有的。如果是奇校验,需要保证传输的数据总共有奇数个逻辑高位;如果是偶校验,需要保证传输的数据总共有偶数个逻辑高位。

停止位: 停止位可以是是1位、1.5位或2位,可以由软件设定。它一定是逻辑1电平,标志着传输一个字符的结束。

空闲位: 空闲位是指从一个字符的停止位结束到下一个字符的起始位开始,表示线路处于空闲状态,必须由高电平来填充。

六、单双工通讯

单工: 数据传输只支持数据在一个方向上传输;

半双工: 允许数据在两个方向上传输,但某一时刻只允许数据在一个方向上传输,实际上是一种切换方向的单工通信,不需要独立的接收端和发送端,两者可合并为一个端口;

全双工: 允许数据同时在两个方向上传输,因此全双工通信是两个单工方式的结合,需要独立的接收端和发送端。

“单片机的串口通信,简单明了好理解"

七、STM32中的串口通讯

STM32串口通信接口有两种,分别是:UART(通用异步收发器)、USART(通用同步异步收发器),对于大容量STM32F10x系列芯片,分别由3个USART和两个UART。

“单片机的串口通信,简单明了好理解"

TXD:数据发送引脚;RXD:数据输入引脚

对于两芯片的间的连接,两个芯片GND共地,同时TXD和RXD交叉连接,这样两个芯片间可进行TTL电平通信。

但如果对于芯片和PC机相连,除了共地条件外,不能使用如上的直接交叉连接,虽然两者都有TXD和RXD引脚,但通常PC机使用的是RS232接口(9针),通常是TXC和RXD经过电平转换得到,故如果要使芯片与PC机的RS232接口直接通信,需要将芯片的输入输出端口也电平转换为RS232类型,再交叉连接,二者的电平标准不同:

单片机的点评标准(TTL电平):+5V表示1,0V表示0;

RS232电平标准:+15/+13V表示0,-15/-13表示1。

“单片机的串口通信,简单明了好理解"

因此单片机与PC机进行串口通信应该遵循:在单片机串口与上位机给出的RS232口之间,通过电平转换电路实现TTL电平与RS232电平间的转换.

如果使用USB转串口也可以实现串口通讯,USB转串口电路图如下所示

“单片机的串口通信,简单明了好理解"

STM32串口通讯代码

STM32中串口通讯已经给大家建好了相应的库函数,大家在使用和配置串口的时候直接进行调用库函数和配置就行了,请大家参照一下代码:

1、初始化结构体代码

typedef struct {
 uint32_t USART_BaudRate; // 波特率
 uint16_t USART_WordLength; // 字长
 uint16_t USART_StopBits; // 停止位
 uint16_t USART_Parity; // 校验位
 uint16_t USART_Mode; // USART 模式
 uint16_t USART_HardwareFlowControl; // 硬件流控制
 } USART_InitTypeDef;

2、NVIC配置中断优先级

NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

3、USART配置函数

void DEBUG_USART_Config(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;
 USART_InitTypeDef USART_InitStructure;
 
 /* 第一步:初始化GPIO */
  // 打开串口GPIO的时钟
 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
  // 将USART Tx的GPIO配置为推挽复用模式
 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure); 
 
 /* 第二步:配置串口的初始化结构体 */
  // 打开串口外设的时钟
 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
 // 配置串口的工作参数
 // 配置波特率
 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
 // 配置 针数据字长
 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
 // 配置停止位
 USART_InitStructure.USART_StopBits = USART_StopBits_1;
 // 配置校验位
 USART_InitStructure.USART_Parity = USART_Parity_No ;
 // 配置硬件流控制
 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
 // 配置工作模式,收发一起
 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 // 完成串口的初始化配置
 USART_Init(DEBUG_USARTx, &USART_InitStructure);

/*--------------------------------------------------------*/
 // 串口中断优先级配置
 NVIC_Configuration();
 
 // 使能串口接收中断
 USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
/*--------------------------------------------------------*/
 
 /* 第三步:使能串口 */ 
  // 使能串口
 USART_Cmd(DEBUG_USARTx, ENABLE); 
}

来源:玩转嵌入式
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 257

有读者问:GPIO配置不同输出速度会有什么影响?

1、写在前面

这个问题看起来比较简单,我相信很多人都能说出答案。

但是,很多人都只是停留在表面,没有在项目中经历过,也没有更深入的去了解。

“单片机GPIO配置不同输出速度有何区别?"

很早之前年的单片机可能没有输出速度这个配置选项,但是这后面的单片机基本都有配置速度的选项,下面结合STM32来简单介绍一下。

2、GPIO输出速度

不管标准外设库,还是STM32CubeMX配置GPIO输出引脚,都会有速度GPIO_InitStruct.Speed这个选项。

类似如下:

GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

根据不同MCU型号,速度选项,有些有3个,有些有4个。一般定义在xxx_gpio.h文件中。

#define GPIO_Speed_2MHz  GPIO_Speed_Level_1   /*!< I/O output speed: Low 2 MHz  */
#define GPIO_Speed_10MHz GPIO_Speed_Level_2   /*!< I/O output speed: Medium 10 MHz */
#define GPIO_Speed_50MHz GPIO_Speed_Level_3   /*!< I/O output speed: High 50 MHz */
#define  GPIO_SPEED_FREQ_LOW        (0x00000000u)  /*!< Low speed       */
#define  GPIO_SPEED_FREQ_MEDIUM     (0x00000001u)  /*!< Medium speed    */
#define  GPIO_SPEED_FREQ_HIGH       (0x00000002u)  /*!< High speed      */
#define  GPIO_SPEED_FREQ_VERY_HIGH  (0x00000003u)  /*!< Very high speed */

对于普通输出GPIO,使用STM32CubeMX配置,默认配置低:

“单片机GPIO配置不同输出速度有何区别?"

当然,如果配置成其他模式,有可能是中,或高。

比如:配置UART、CAN引脚,速度会是高。

提问:你想过为什么会是低、高吗?

3、实验:测量GPIO输出波形

不知道大家用示波器测量过GPIO输出波形没有,特别是在高速(单位M)的时候。

我以前经常测量MCO引脚输出时钟,测量过的人应该都知道,如果输出速度高于配置速度,会明显看到波形不正常。

波形会出现不完整,幅度低等失真现象。

相信不用我说,有一定常识的人都能理解。

4、具体原因

速度的配置,就是决定IO口驱动电路的响应速度。

我们需要结合实际情况配置速度,不同速度会有不同的影响。

高低速差异:

配置高速:输出频率高,噪音大,功耗高,电磁干扰强;

配置低速:输出频率低,噪音小,功耗低,电磁干扰弱;提高系统EMI(电磁干扰)性能;

看到差异,相信很多人就能理解了。

实际情况中,比如:低功耗的产品,你会考虑功耗。

环境不好的场合,通信不稳定,你会考虑电磁干扰等。

举例:

如果你使用9600波特率UART通信,建议速度配置为低。

所以,如果你想使产品更加完美,速度配置也是关键的一项。

来源:strongerHuang
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 370

单片机是控制电子产品的大脑

现如今,我们生活中的许多电器都使用了单片机。例如:手机、电视机、冰箱、洗衣机、以及按下开关,LED就闪烁的儿童玩具。那么,单片机在这些电器中究竟做了些什么呢?

单片机是这些电器动作的关键,是指挥硬件运行的。例如:接收按钮或按键的输入信号,按照事先编好的程序,指挥马达和LCD的外围功能电路动作。

那么,单片机是如何构成的呢?如图1所示。

单片机是由CPU、内存、外围功能等部分组成的。如果将单片机比作人,那么CPU是负责思考的,内存是负责记忆的,外围功能相当于视觉的感官系统及控制手脚动作的神经系统。

“图1:单片机的构成要素"
图1:单片机的构成要素

尽管我们说CPU相当于人的大脑,但是它却不能像人的大脑一样,能有意识的、自发的思考。CPU只能依次读取并执行事先存储在内存中的指令组合(程序)。当然CPU执行的指令并不是“走路”、“讲话”等高难度命令,而是一些非常简单的指令,象从内存的某个地方“读取数据”或把某个数据“写入”内存的某个地方,或做加法、乘法和逻辑运算等等。然而这些简单指令的组合,却能实现许多复杂的功能。

会思考的CPU

让我们从CPU的构成来了解它的作用吧,如图2所示。

“图2:CPU的作用"
图2:CPU的作用

◇程序计数器

CPU读取指令时需要知道要执行的指令保存在内存的什么位置,这个位置信息称为地址(相当于家庭住址)。程序计数器(PC)就是存储地址的寄存器。通常,PC是按1递增设计的,也就是说,当CPU执行了0000地址中的指令后,PC会自动加1,变成0001地址。每执行一条指令PC都会自动加1,指向下一条指令的地址。可以说,PC决定了程序执行的顺序。

◇指令解码电路

指令解码电路是解读从内存中读取的指令的含义。运算电路是根据解码结果操作的。确切地讲,指令解码电路就是我们在“数字电路入门(2)”中学过的解码电路,只不过电路结构稍微复杂些,所以,指令解码电路的工作原理就是从被符号化(被加密)的指令中,还原指令。

◇运算电路

运算电路也称为ALU(Arithmetic and Logic Unit),是完成运算的电路。能进行加法、乘法等算术运算、也能进行AND、OR 、BIT-SHIFT等逻辑运算。运算是在指令解码电路的控制下进行的。通常运算电路的构成都比较复杂。

◇CPU内部寄存器

CPU内部寄存器是存储临时信息的场所。有存储运算值和运算结果的通用寄存器,也有一些特殊寄存器,比如存储运算标志的标志寄存器等。也就是说,运算电路进行运算时,并不是在内存中直接运算的,而是将内存中的数据复制到通用寄存器,在通用寄存器中进行运算的。

CPU的工作原理

让我们通过一个具体运算3+4,来说明CPU的操作过程吧。

假设保存在内存中的程序和数据如下。

“为什么很多电器设备都要使用单片机?"

◇步骤1:当程序被执行时,CPU就读取当前PC指向的地址0000中的指令(该操作称为指令读取)。经过解码电路解读后,这条指令的意思是“读取0100地址中的内容,然后,保存到寄存器1”。于是CPU就执行指令,从0100地址中读取数据,存入寄存器1。

寄存器1:0→3(由0变为3)

由于执行了1条指令,因此,PC的值变为0001

◇步骤2:由于PC的值为0001,因此CPU就读取0001地址中的指令,经解码电路解码后,CPU执行该指令。然后PC再加1。

寄存器2:0→4(由0变为4)
PC:0001→0000

◇步骤3:由于PC的值为0002,因此CPU从0002地址中读取指令,送给指令解码电路。解码结果是:将寄存器1和寄存器2相加,然后将结果存于寄存器1。

寄存器1:3→7
PC:2→3

于是3+4的结果7被存于寄存器1,加法运算结束。CPU就是这样,依次处理每一条简单的指令。

能记忆的内存

内存是单片机的记忆装置,主要记忆程序和数据,大体上分为ROM和RAM两大类。

◇ROM

ROM(Read Only Memory)是只读内存的简称。保存在ROM中的数据不能删除,也不会因断电而丢失。ROM主要用于保存用户程序和在程序执行中保持不变的常数。

大多数瑞萨(Renesas)的单片机都用闪存作为ROM。这是因为闪存不仅可以象ROM一样,即使关机也不会丢失数据,而且还允许修改数据。

◇RAM

RAM(Random Access Memory)是可随机读/写内存的简称。可以随时读写数据,但关机后,保存在RAM中的数据也随之消失。主要用于存储程序中的变量。

在单芯片单片机中(*1),常常用SRAM作为内部RAM。SRAM允许高速访问,但是,内部结构太复杂,很难实现高密度集成,不适合用作大容量内存。

除SRAM外,DRAM也是常见的RAM。DRAM的结构比较容易实现高密度集成,因此,比SRAM的容量大。但是,将高速逻辑电路和DRAM安装于同一个晶片上较为困难,因此,一般在单芯片单片机中很少使用,基本上都是用作外围电路。

(*1)单芯片单片机是指:将CPU,ROM,RAM,振荡电路,定时器和串行I/F等集成于一个LSI的微处理器。单芯片单片机的基础上再配置一些系统的主要外围电路,而形成的大规模集成电路称为系统LSI。

“为何要使用单片机?”

为什么很多电器设备都要使用单片机呢?

让我们用一个点亮LED的电路为例,来说明。如图3所示,不使用单片机的电路是一个由LED,开关和电阻构成的简单电路。

“图3:不安装单片机的LED电路"
图3:不安装单片机的LED电路

使用单片机的电路如图4所示。

“图4:安装单片机的LED电路图"
图4:安装单片机的LED电路图

很显然,使用单片机的电路要复杂得多,而且设计电路还要花费精力与财力。好象使用单片机并没有什么优点。但是,现在下结论还为时尚早。

如果我们让这个电路做一些比较复杂的操作,会怎么样呢。例如:如果希望LED在按下开关后,经过一段时间再点亮或熄灭,那么,对于安装有单片机的电路来说,只需更改单片机中的程序就可以了,并不需更改原电路。另一方面,对于没有单片机的电路来说,就必须在元电路中加入定时器IC,或者用标准逻辑IC和FPGA构成逻辑电路,才能实现这个功能。

也就是说,在更改和添加新功能时,带有单片机的电路显然更加容易实现。这正是电器设备使用单片机的原因。单片机可真是个方便的东西哦!

来源:STM32嵌入式开发
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 58

页面

订阅 RSS - 单片机