(八)STM32 USART —— 串口通讯

目录

1. 串口通讯协议简介

1.1 物理层

1.1.1 电平标准

1)TTL 电平

2)RS-232 电平 

3)RS-485 电平

4)CAN 总线电平

1.1.2 USB 和 串口 的区分

1.1.3 RS-232 信号线

1.2 协议层 

1)波特率

2)通讯的起始和停止信号

3)有效数据

4)数据校验

2. 单片机常用的通信接口概况介绍

2.1 常用的通讯接口

2.2 串口通讯中什么是异步通讯?什么是同步通讯?

3. STM32 的 UART/USART 

3.1 UART/USART 简介

3.2 硬件电路

3.3 电平标准

3.4 串口参数

3.5 串口时序

3.6 USART 外设简介

3.7 USART 功能框图

1)功能引脚

2)数据寄存器

3)控制器

4)小数波特率生成

3.8 USART 基本结构

3.9 USART 初始化结构体详解

4. USART1 接发通信实验

4.1 硬件设计

4.2 软件设计


1. 串口通讯协议简介

        串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信。串行接口、并行接口是按照数据传输方式来划分的,串行接口是一大类接口。USB、RS232、SATA、PS/2、RS485等等,这些都属于串行接口,也就是说串口,串口是一大类接口,包括这些但不仅仅限于这些。

        串口通讯 (Serial Communication) 是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通讯方式输出调试信息
        在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设;STM32 标准库则是在寄存器与用户代码之间的软件层。对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的
特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、
解包标准。简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英
文来交流。
        下面我们分别对串口通讯协议的物理层及协议层进行讲解。

1.1 物理层

        串口通讯的物理层有很多标准及变种,我们主要讲解 RS-232 标准,RS-232 标准主要规定了信号的用途、通讯接口以及信号的电平标准。使用 RS-232 标准的串口设备间常见的通讯结构见图串口通讯结构图:

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

1.1.1 电平标准

        根据通讯使用的电平标准不同,串口通讯可分为 TTL 标准、RS-232 标准、RS485 标准、CAN 总线 标准。

1)TTL 电平
TTL 电平标准
通讯标准电平标准(发送端)
5V TTL

逻辑 1:2.4V-5V

逻辑 0:0V-0.5V

硬件框图,TTL 用于两个 MCU 之间的通讯:

0 和 1 表示如下图:

2)RS-232 电平 
RS-232 电平标准
通讯标准电平标准(发送端)
RS-232 

逻辑 1:-3V - -15V

逻辑 0:+3V - +15V

硬件框图,RS-232 用于 MCU 与 PC 机之间的通信:

0 和 1 表示如下图:

        因为控制器一般使用 TTL 电平标准,所以常常会使用电平转换芯片 MA3232 芯片对 TTL 及 RS-232 电平的信号进行互相转换。  

3)RS-485 电平
RS-485 电平标准
通讯标准电平标准(发送端)
RS-232 

逻辑 1:+2V - +6V

逻辑 0:-6V - -2V

这里的电平指AB两线间的电压差

硬件框图如下:

0 和 1 表示如下图:

4)CAN 总线电平
CAN 总线电平标准
通讯标准电平标准(发送端)
CAN 

逻辑 1:-1.5V - 0V

逻辑 0:+1.5V - +3V

这里的电平指CAN_High、CAN_Low 两线间的电压差

硬件框图如下:

0 和 1 表示如下图:

总结:

        从单片机软件编程的角度来讲,RS-232、RS-485 最终结果都是转换为 TTL 电平方式与单片机进行通信(CAN 收发器把差分信号转化为 TTL -> CAN 控制器(MCU))。其目的都是在硬件上实现提高通信质量,提高抗干扰能力。其实,这些电平标准就是在硬件上 信号的传输方式,是用 RS-232 标准,还是用 RS-485 标准,还是用 CAN 总线电平标准,且每种标准的硬件接口可能是不一样的,信号的电压是不一样的。

1.1.2 USB 和 串口 的区分

        COM口 ( cluster communication port ) 即 串行通讯端口,简称串口。微机上的串口通常是9针,也有25针的接口,最大速率115200bps。一般在台式计算机的机箱后边后 COM 口,也称DB9接口。但现在笔记本电脑上基本没有了。

        USB,是英文 Universal Serial Bus(通用串行总线)的缩写,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在PC领域的接口技术。

USB 和 串口 的区别
USB串口
通信方式使用复杂的通信协议,支持并行和串行通信,可同时连接多个设备使用简单的串行通信协议,一次只能连接一个设备
传输速率传输速度远快于串口,可达数百兆字节每秒,具体速度取决于 USB 的版本通常传输速率较慢,以波特率(比如115200波特)表示
接口形状与连接具有标准化的接口形状,易于插拔,支持即插即用功能接口形状各异,就比如台式计算机中一般会有 RS-232 标准的COM口(也称DB9接口),还有其他接口等
用途与应用领域广泛应用于外设连接,如鼠标、键盘、打印机等主要用于工业控制、嵌入式设备和一些旧式硬件的连接

常见的疑惑:

a. USB 和 串口 是否可以互相转换?

        是的,可以使用转换适配器(采用 CH340 模块 )将 USB 转换为串口,反之亦然。

b. 为什么现代计算机上越来越少见串口?

        因为 USB 接口更灵活,传输速度更快,已逐渐取代串口在许多应用中的地位。

c. USB 和 串口 在数据传输中有什么安全性区别?

        USB 可能提供更多的安全性,但具体取决于设备和配置。串口通常用于封闭系统,安全性也可能足够。

d. 哪些场景下应优先选择 串口 而不是 USB ?

        在工业控制和嵌入式系统等需要简单、可靠连接的场景下,串口可能是更好的选择。

e. USB 的不同版本与串口相比有何优势?

        USB 的不同版本提供了不同的传输速率和功能,通常在速度和灵活性方面优于串口。

f. USB 和 串口 之间的联系

        所以,当我们的单片机与电脑进行通讯时,将单片机的串口与电脑的 USB 相接是需要 转换模块(CH340)的,CH340 是一个USB 总线的转接芯片,实现USB 转串口、USB 转IrDA 红外或者USB 转打印口。在串口方式下,CH340 提供常用的 MODEM 联络信号,用于为计算机扩展异步串口,或者将普通的串口设备直接升级到USB 总线。电脑端安装 CH340 驱动,就可以看到与从机通信的 COM 口。因为我们现在的电脑上,已经不存在串口(这里说的串口指的是 RS232 标准的串口),所以我们一般使用 USB 转串口芯片,目的只有一个,把电脑的 USB 口映射为串口用。

 USB 转 串口 电路:

1.1.3 RS-232 信号线

        在最初的应用中,RS-232 串口标准常用于计算机、路由与调制调解器 (MODEN,俗称“猫”) 之间的通讯,在这种通讯系统中,设备被分为 数据终端设备 DTE(计算机、路由)  和 数据通讯设备
DCE(调制调解器)。我们以这种通讯模型讲解它们的信号线连接方式及各个信号线的作用。在旧式的台式计算机中一般会有 RS-232 标准的 COM 口 (也称 DB9 接口),见图电脑主板上的COM 口及串口线。

        其中接线口以针式引出信号线的称为公头以孔式引出信号线的称为母头。在计算机中一般引出公头接口,而在调制调解器设备中引出的一般为母头,使用上图中的串口线即可把它与计算机连接起来。通讯时,串口线中传输的信号就是使用前面讲解的 RS-232 标准调制的。
        在这种应用场合下,DB9 接口中的公头及母头的各个引脚的标准信号线接法见图 DB9 标准的公头及母头接法 及表 DB9 信号线说明。 

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

        串口线中的 RTS、CTS、DSR、DTR 及 DCD 信号,使用逻辑 1 表示信号有效,逻辑 0 表示信号无效。例如,当计算机端控制 DTR 信号线表示为逻辑 1 时,它是为了告知远端的调制调解器,本机已准备好接收数据,0 则表示还没准备就绪。
        在目前的其它工业控制使用的串口通讯中,一般只使用 RXD、TXD 以及 GND 三条信号线,直接传输数据信号,而 RTS、CTS、DSR、DTR 及 DCD 信号都被裁剪掉了。 

1.2 协议层 

        串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由 启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据,其组成见图串口数据包的基本组成。

1)波特率

        串口异步通讯,异步通讯中由于没有时钟信号 (如前面讲解的 DB9 接口中是没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,图串口数据包的基本组成 中用虚线分开的每一格就是代表一个码元。常见的波特率为 4800、9600、115200 等。波特率的单位bps,指的是每秒可以发送多少比特位,115200bps,表示每秒可以发送115200个比特位。

2)通讯的起始和停止信号

        串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的 起始信号由一个逻辑 0 的数据位表示,而数据包的 停止信号可由 0.5、1、1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。

3)有效数据

        在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7 或 8 位长。一般都是8位,一个字节的长度。

4)数据校验

        在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验 (odd)、偶校验
(even)、0 校验 (space)、1 校验 (mark) 以及无校验 (noparity)。
       奇校验要求有效数据和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为01101001,此时总共有 4 个“1”,为达到奇校验效果,校验位为“1”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位。
       偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如一个 8 位长的有效数据11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为“0”。
        0 校验是不管有效数据中的内容是什么,校验位总为“0”。1 校验是校验位总为“1”。

2. 单片机常用的通信接口概况介绍

2.1 常用的通讯接口

名称引脚双工时钟电平设备
USARTTX(数据发送引脚)、RX(数据接收引脚)全双工异步单端点对点
I2CSCL(时钟引脚)、SDA(数据引脚)半双工同步单端多设备
SPISCLK(时钟)、MOSI、MISO、CS全双工同步单端多设备
CANCAN_H、CAN_L半双工异步差分多设备
USBDP(D+)、DM(D_)半双工异步差分点对点

引脚说明: 

  • MOSI:主机输出数据引脚;
  • MISO:主机输入数据引脚;
  • CS:片选,用于指定通信的对象;
  • CAN、USB 的两个引脚表示的是 差分数据;

半/全双工: 

  • 全双工:指通信双方能够同时进行双向通信;
  • 半双工:指通信双方在同一时刻只能有一方在传输数据,另一方在接收数据; 

时钟:

  • 同步:设备之间用同一根时钟线;
  • 异步:需要自己定义时钟,或者如USART定义波特率;

单端/差分:

  • 单端:他们的引脚的高低电平都是对 GND 的电压差,所以双方需要共地;
  • 差分:信号的传输是靠两条线的电压差来区分逻辑位的; 

2.2 串口通讯中什么是异步通讯?什么是同步通讯?

        串口通讯中,异步通讯和同步通讯是两种不同的数据传输方式,它们在数据帧传输时的时间同步方式上有所不同:
        ①异步通讯(Asynchronous Communication):

  • 异步通讯是一种不要求发送端和接收端时钟同步的通讯方式。
  • 数据帧在传输时包含起始位(Start Bit)、数据位、校验位(可选)、停止位(Stop Bit)。
  • 发送端和接收端都约定好一个特定的波特率(波特率表示每秒传输的比特数),通常是常见的波特率之一,例如 9600 bps(bits per second)。
  • 发送端和接收端在传输数据前都要遵循相同的波特率,数据位数,校验方式和停止位数。然后发送端按照这些设置构建数据帧,并在每个数据帧之间插入停止位,以确保接收端能够正确解析数据。

        ②同步通讯(Synchronous Communication):

  • 同步通讯是一种需要发送端和接收端时钟同步的通讯方式。
  • 数据帧通常没有明确的起始位和停止位,而是按照时钟信号连续传输。
  • 通信双方必须共享一个共同的时钟源,以确保数据的传输和接收在时钟的同步下进行。
  • 同步通讯通常用于高速数据传输,如以太网通信等。

        总的来说,异步通讯是串口通讯中更常见的方式,因为它不要求发送端和接收端的时钟严格同步,使其在各种不同的硬件和应用中都能使用。同步通讯则需要更严格的时钟同步,通常用于特定高速数据传输的场合。选择哪种通讯方式通常取决于应用的要求和硬件支持。

3. STM32 的 UART/USART 

3.1 UART/USART 简介

       串行通信一般是以帧格式传输数据,即一帧一帧的传输,每帧包含起始信号、数据信息、停止信号,可能还有校验信息。UART 和 USART 都是用于串行通信的通信接口,它们之间有一些共同之处,但也有一些关键的区别:

1)通用异步收发器 UART (Universal Asynchronous Receiver and Transmitter)

  • UART 是一种通用异步收发器/发射器,它是一种硬件接口,通常用于串行通信。
  • UART 通信是异步的,这意味着数据帧之间没有共享时钟信号。通信双方必须事先协商好通信参数,如波特率、数据位、停止位和奇偶校验等,以确保数据正确传输。
  • UART 可以用于单向通信或双向通信,因为它包含了一个接收器和一个发射器。

2)通用同步/异步收发器 USART (Universal Synchronous Asynchronous Receiver and Transmitter)

  • USART 是一种通用同步/异步收发器/发射器,也是一种串行通信接口。
  • USART 可以支持异步通信(与 UART 类似)和同步通信。这意味着 USART 具有一个可选的同步模式,其中通信双方共享一个时钟信号,从而提供了更可靠的同步通信。
  • USART 通常具有更多的功能和灵活性,可以支持不仅仅是异步通信,还可以适应更多的通信需求。

        UART是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。或者把 USART 当 UART 来用。

USART 的优点:
        USART 满足外部设备对工业标准 NRZ 异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。USART 支持同步单向通信和半双工单线通
信;还支持局域互连网络 LIN、智能卡 (SmartCard) 协议与 lrDA(红外线数据协会) SIR ENDEC 规
范。
        USART 支持使用 DMA,可实现高速数据通信,有关 DMA 具体应用将在 DMA 章节作具体讲解。USART 在 STM32 应用最多莫过于 “打印”程序信息,一般在硬件设计时都会预留一个 USART
通信接口连接电脑,用于在调试程序是可以把一些调试信息“打印”在电脑端的串口调试助手工具上,从而了解程序运行是否正确、如果出错哪具体哪里出错等等。

3.2 硬件电路

  • 简单的双向串口通信有 2 根通信线(发送端 TX 和接收端 RX)
  • TX 与 RX 要交叉连接
  • 当只需单向的数据传输时,可以只接一根通信线
  • 当电平标准不一致,需加电平转换芯片

3.3 电平标准

        电平标准是数据 1 和数据 0 的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

        ① TTL电平:+3.3v/5v 表示 逻辑 1,0v 表示 逻辑 0

        ② RS232电平:-3v~-15v 表示逻辑 1,+3v~+15v 表示 逻辑 0

        ③ RS485电平:两线压差+2v~+6v 表示逻辑 1,-2v~-6v 表示 逻辑 0(差分信号)

3.4 串口参数

        波特率:串口通信的速率(通信双方约定速率)

        起始位:标志一个数据帧的开始,串口数据帧的起始位固定为低电平

        数据位:数据帧的有效载荷,1 为高电平,0 为低电平,低位先行

        检验位:用于数据验证,根据数据位计算得来

        停止位:用于数据帧间隔,串口数据帧的停止位固定为高电平

3.5 串口时序

在示波器上查看串口的数据传输过程:

        Tx 引脚输出定时翻转的高低电平

        Rx 引脚定时读取引脚的高低电平 

3.6 USART 外设简介

        USART 通用同步/异步收发器。

        USART/UART 是 STM32 内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从 Tx 引脚发送出去,也可自动接收 Rx 引脚的数据帧时序,拼接为一个字节数据存在数据寄存器里。

        自带波特率发生器(相当于分频器)

        可配置数据位长度(8/9),停止位长度(0.5-2)

        可选检验位(无校验/奇校验/偶校验)

        支持同步模式(clk 时钟输出),硬件流控制(防止接收端 处理慢而导致数据丢失的问题)

3.7 USART 功能框图

1)功能引脚

TX发送数据输出引脚;
RX接收数据输入引脚;
SW_RX数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚;
nRTS请求以发送 (Request To Send),n 表示低电平有效。如果使能 RTS 流控制,当 USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制;
nCTS清除以发送 (Clear To Send),n 表示低电平有效。如果使能 CTS 流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制;
SCLK发送器时钟输出引脚。这个引脚仅适用于同步模式。

         USART 引脚在 STM32F103ZET6 芯片具体分布见表 :

        STM32F103ZET6 系统控制器有三个 USART 和两个 UART(STM32F103C8T6只有三个USART)其中 USART1 时钟来源于 APB2 总线时钟,其最大频率为 72MHz,其他四个的时钟来源于 APB1 总线时钟,其最大频率为 36MHz。UART 只是异步传输功能,所以没有 SCLK、nCTS 和 nRTS 功能引脚。

2)数据寄存器

        USART 数据寄存器 (USART_DR) 只有低 9 位有效,并且第 9 位数据是否有效要取决于 USART控制寄存器 1(USART_CR1) 的 M 位设置,当 M 位为 0 时表示 8 位数据字长,当 M 位为 1 表示 9位数据字长,我们一般使用 8 位数据字长。
        USART_DR 包含了已发送的数据或者接收到的数据。USART_DR 实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送操作时,往 USART_DR 写入数据会自动存储在 TDR 内;当进行读取操作时,向 USART_DR 读取数据会自动提取 RDR 数据。TDR 和 RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。
        USART 支持 DMA 传输,可以实现高速数据传输,具体 DMA 使用将在 DMA 章节讲解。

3)控制器

        USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。使用
USART 之前需要向 USART_CR1 寄存器的 UE 位置 1 使能 USART,UE 位用来开启供给给串口的时钟。发送或者接收数据字长可选 8 位或 9 位,由 USART_CR1 的 M 位控制。

发送器
        当 USART_CR1 寄存器的发送使能位 TE 置 1 时,启动数据发送,发送移位寄存器的数据会在 TX 引脚输出,低位在前,高位在后。如果是同步模式 SCLK 也输出时钟信号。
        一个字符帧发送需要三个部分:起始位 + 数据帧 + 停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据帧就是我们要发送的 8 位或 9 位数据,数据是从最低位开始传输的;停止位是一定时间周期的高电平。
        停止位时间长短是可以通过 USART 控制寄存器 2(USART_CR2) 的 STOP[1:0] 位控制,可选 0.5个、1 个、1.5 个和 2 个停止位。默认使用 1 个停止位。2 个停止位适用于正常 USART 模式、单线模式和调制解调器模式。0.5 个和 1.5 个停止位用于智能卡模式。当选择 8 位字长,使用 1 个停止位时,具体发送字符时序图见图字符发送时序图。

        当发送使能位 TE 置 1 之后,发送器开始会先发送一个空闲帧 (一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状态寄存器 (USART_SR) 的 TC 位为 1,表示数据传输完成,如果 USART_CR1 寄存器的 TCIE 位置1,将产生中断。 

在发送数据时,编程的时候有几个比较重要的标志位我们来总结下。

名称描述
TE发送使能
TXE发送寄存器为空,发送单个字节的时候使用
TC发送完成,发送多个字节数据的时候使用
TXIE发送完成中断使能

接收器 

        如果将 USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。接收完成
后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置 1,同时如果
USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断。

在接收数据时,编程的时候有几个比较重要的标志位我们来总结下。

名称描述
RE接收使能
RXNE读数据寄存器非空
RXNEIE接收完成中断使能

4)小数波特率生成

        波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,单位为波特。比特率指单位时间内传输的比特数,单位 bit/s(bps)。对于 USART 波特率与比特率相等,以后不区分这两个概念。波特率越大,传输速率越快

USART 的发送器和接收器使用相同的波特率。计算公式如下:

        其中,fPLCK 为 USART 时钟,USARTDIV 是一个存放在波特率寄存器 (USART_BRR) 的一个无符号定点数。其中 DIV_Mantissa[11:0] 位定义 USARTDIV 的整数部分,DIV_Fraction[3:0] 位定义USARTDIV 的小数部分。 

例如:DIV_Mantissa=24(0x18),DIV_Fraction=10(0x0A),此时 USART_BRR 值为 0x18A;那么 USARTDIV 的小数位 10/16=0.625;整数位 24,最终 USARTDIV 的值为 24.625。

如果知道 USARTDIV 值为 27.68,那么 DIV_Fraction=16*0.68=10.88,最接近的正整数为 11,所
以 DIV_Fraction[3:0] 为 0xB;DIV_Mantissa= 整数 (27.68)=27,即为 0x1B。

        波特率的常用值有 2400、9600、19200、115200。下面以实例讲解如何设定寄存器值得到波特率的值。

        我们知道 USART1 使用 APB2 总线时钟,最高可达 72MHz,其他 USART 的最高频率为 36MHz。我们选取 USART1 作为实例讲解,即 fPLCK=72MHz。为得到 115200bps 的波率,此时:

解得 USARTDIV=39.0625,可算得 DIV_Fraction=0.0625*16=1=0x01,DIV_Mantissa=39=0x27,即应该设置 USART_BRR 的值为 0x271。 

        这里我们直接调用库函数,设置结构体参数,输入我们想要的波特率数值即可,无需像以上这样复杂计算。

3.8 USART 基本结构

3.9 USART 初始化结构体详解

        标准库函数对每个外设都建立了一个初始化结构体,比如 USART_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如 USART_Init() 调用,这些设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。

        初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f10x_usart.h 文件中,初始化库函数定义在 stm32f10x_usart.c 文件中,编程时我们可以结合这两个文件内注释使用。

USART 初始化结构体

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;
USART_BaudRate波特率设置。一般设置为 2400、9600、19200、115200。标准库函数会根据设定值计算得到 USARTDIV 值,从而设置USART_BRR寄存器值。
USART_WordLength数据帧字长,可选 8 位或 9 位。它设定 USART_CR1 寄存器的 M 位的值。如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇偶校验则一般设置为 9 数据位。
USART_StopBits停止位设置,可选 0.5 个、1 个、1.5个和2个停止位,它设定 USART_CR2
寄存器的 STOP[1:0] 位的值,一般我们选择 1 个停止位。
USART_Parity奇偶校验控制选择,可选 USART_Parity_No(无校验)、USART_Parity_Even(偶
校验) 以及 USART_Parity_Odd(奇校验),它设定 USART_CR1 寄存器的 PCE 位和 PS 位的值。
USART_ModeUSART 模式选择,有 USART_Mode_Rx 和 USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。
USART_HardwareFlowControl硬件流控制选择,只有在硬件流控制模式才有效,可选有 使能RTS、使能CTS、同时使能 RTS 和 CTS、不使能硬件流

USART 时钟初始化结构体

typedef struct 
{
    uint16_t USART_Clock;    // 时钟使能控制
    uint16_t USART_CPOL;     // 时钟极性
    uint16_t USART_CPHA;     // 时钟相位
    uint16_t USART_LastBit;  // 最尾位时钟脉冲
} USART_ClockInitTypeDef;
USART_Clock同步模式下SCLK引脚上时钟输出使能控制,可选禁止时钟输出 (USART_Clock_Disable) 或开启时钟输出 (USART_Clock_Enable);如果使用同步模式发送,一般都需要开启时钟。它设定 USART_CR2 寄存器的 CLKEN 位的值。
USART_WordLength数据帧字长,可选 8 位或 9 位。它设定 USART_CR1 寄存器的 M 位的值。如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇偶校验则一般设置为 9 数据位。
USART_StopBits停止位设置,可选 0.5 个、1 个、1.5个和2个停止位,它设定 USART_CR2
寄存器的 STOP[1:0] 位的值,一般我们选择 1 个停止位。
USART_Parity奇偶校验控制选择,可选 USART_Parity_No(无校验)、USART_Parity_Even(偶
校验) 以及 USART_Parity_Odd(奇校验),它设定 USART_CR1 寄存器的 PCE 位和 PS 位的值。
USART_ModeUSART 模式选择,有 USART_Mode_Rx 和 USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。
USART_HardwareFlowControl硬件流控制选择,只有在硬件流控制模式才有效,可选有 使能RTS、使能CTS、同时使能 RTS 和 CTS、不使能硬件流

        一般我们用的最多的就是 USART 的异步通讯,同步通讯,利用时钟的实验和项目不多。 

4. USART1 接发通信实验

        USART 只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留 USART 接口来实现与其他模块或者控制器进行数据传输,比如 GSM 模块,WIFI 模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”
       我们经常使用 USART 来实现控制器与电脑之间的数据传输。这使得我们调试程序非常方便,比如我们可以把一些变量的值、函数的返回值、寄存器标志位等等通过 USART 发送到串口调试助手,这样我们可以非常清楚程序的运行状态,当我们正式发布程序时再把这些调试信息去除即可。

        我们不仅仅可以将数据发送到串口调试助手,我们还可以在串口调试助手发送数据给控制器,控制器程序根据接收到的数据进行下一步工作。

       首先,我们来编写一个程序实现开发板与电脑通信,在开发板上电时通过 USART 发送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑。

4.1 硬件设计

        为利用 USART 实现开发板与电脑通信,需要用到一个 USB 转 USART 的 IC,我们选择 CH340G 芯片来实现这个功能,CH340G 是一个 USB 总线的转接芯片,实现 USB 转 USART、USB 转 lrDA红外或者 USB 转打印机接口,我们使用其 USB 转 USART 功能。具体电路设计见图USB 转串口硬件设计 _。
       我们将 CH340G的 TXD引脚与 USART1 的 RX 引脚连接,CH340G 的 RXD 引脚与 USART1 的TX 引脚连接。CH340G 芯片集成在开发板上,其地线 (GND) 已与控制器的 GND 连通。

①第一种

第二种

还有第三种,根上边的 1.1.2 中的电路原理图是一样的。都可以实现将 USB 转为 USART。

4.2 软件设计

参考正点原子的程序设计,所有的注释说明都在代码里体现了。

main.c

void main(void)
{
    u16 t;
    u16 len;
    u16 times = 0;

    delay_init();            //延时函数初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    usart1_init(115200);     //串口初始化为115200
    led_init();              //LED端口初始化
    key_init();              //初始化与按键连接的硬件接口

//printf函数和UsartPrintf函数的用法
//  printf("你个老六\r\n");
    delay_ms(50);
    UsartPrintf(USART1, "温度:%d.%d  湿度:%d.%d\r\n", 26, 26, 56, 56);//放在这里打印出错,是因为printf函数和UsartPrintf函数不能连着放在一起

    while (1)
    {

        if (USART_RX_STA1 & 0x8000) //括号里表示的是SAT的最高位bit15,所以这里判断的是STA的最高位
        {
            len = USART_RX_STA1 & 0x3fff; //得到此次接收到的数据长度
            printf("\r\n您发送的消息为:\r\n\r\n");
            for (t = 0; t < len; t++)
            {
                USART_SendData(USART1, USART1_RX_BUF[t]);//向串口1发送数据
                while (USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET); //等待发送结束
            }
            printf("\r\n\r\n");//插入换行
            USART_RX_STA1 = 0;
        }
        else//以下是一些提示信息
        {
            times++;
            if (times % 5000 == 0)
            {
                printf("\r\n精英STM32开发板 串口实验\r\n");
                printf("正点原子@ALIENTEK\r\n\r\n");
            }
            if (times % 200 == 0)
            {
                printf("请输入数据,以回车键结束\r\n");
//              UsartPrintf(USART1, "温度:%d.%d  湿度:%d.%d\r\n", 26, 26, 56, 56); // 这里两个函数不能放在一起
            }
            if (times % 30 == 0)LED1_G = !LED1_G; //闪烁LED,提示系统正在运行.
            delay_ms(10);
        }
    }
}

 usart.h

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 

#define USART_REC_LEN  			200  	    //定义最大接收字节数 200
#define EN_USART1_RX 			   1		//使能(1)/禁止(0)串口1接收

extern u8  USART1_RX_BUF[USART_REC_LEN];    //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符

extern u16 USART_RX_STA1;         		    //串口1接收状态标记

//如果想串口中断接收,请不要注释以下宏定义
void usart1_init(u32 bound);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);

void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...);

#endif

usart.c

#include "sys.h"
#include "usart.h"
#include "stdio.h"
#include "string.h"
#include "stdarg.h"
#include "stm32f10x_tim.h"

/*---------------------------------printf函数配置-----------------------------------*/

/* 使用microLib的方法,记得要在魔术棒哪里进行勾选 */
//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)//串口printf发送函数,若要改为其他串口直接修改USART1、2,然而这里修改串口又是可以的
{
    while ((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
    USART1->DR = (u8) ch;
    return ch;
}
//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
    /* 等待串口输入数据 */
    while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

    return (int)USART_ReceiveData(USART1);
}

/*另一种使用microLib的方法*/
/*
int fputc(int ch, FILE *f)
{
USART_SendData(USART2, (uint8_t) ch);

while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET) {}

   return ch;
}
int GetKey (void)  {

   while (!(USART2->SR & USART_FLAG_RXNE));

   return ((int)(USART2->DR & 0x1FF));
}
*/
/*------------------------------------------------------------------------------------*/


/*--------------------------------------串口1配置-------------------------------------*/
#if EN_USART1_RX   //如果使能了接收,若要修改串口,这里的宏定义必须也要修改
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART1_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节,这是个外部可调用的变量,需要在头文件中声明外部变量
//接收状态
//bit15,   接收完成标志
//bit14,   接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA1 = 0x0000;     //接收状态标记,这是个外部可调用的变量,需要在头文件中声明外部变量

void usart1_init(u32 bound)
{

    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 开启USART1,GPIOA的时钟 只有USART1挂载在APB2上,其他的串口都在APB1上*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    /* 复位串口1 */
    USART_DeInit(USART1);

    /*--------------------------GPIO端配置-----------------------------*/
    //USART1_TX   GPIOA.9   复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;// 这个是错误示例,必须使用 GPIO_Mode_AF_PP
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //USART1_RX   GPIOA.10  浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /*--------------------------串口中断优先级配置-----------------------------*/
    //USART1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;      //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器

    /*--------------------------串口的工作参数配置-----------------------------*/
    //USART 初始化设置
    USART_InitStructure.USART_BaudRate = bound;//串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    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(USART1, &USART_InitStructure); //初始化串口1
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口1接收中断
    USART_Cmd(USART1, ENABLE);                    //使能串口1
}

/**
 * @brief  USART1中断服务函数
 * @param  无
 * @retval 无
 */
void USART1_IRQHandler(void)//串口1接收中断服务程序
{
    u8 Res;
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {
        Res = USART_ReceiveData(USART1); //读取接收到的数据

        if ((USART_RX_STA1 & 0x8000) == 0) //判断USART_RX_STA的bit15,该位为0表示接收未完成
        {
            //用USART_RX_STA的bit14判断是否接收到回车符,如果接收到了回车符,再判断是否接收到了换行符
            if (USART_RX_STA1 & 0x4000)
            {
                if (Res != 0x0a)
                {
                    USART_RX_STA1 = 0;   //通过接收变量Res是否接收到换行符(\n)0x0A来对USART_RX_STA的bit15进行操作
                }
                else
                {
                    USART_RX_STA1 |= 0x8000;   //若接收到回车符紧接着又接收到换行符,表明这一组数据接收完成了
                }
            }
            else//如果USART_RX_STA的bit14还没被写1,表示还没收到回车符(\r)0x0D
            {
                if (Res == 0x0d) //电脑中换行符(\n)0x0A,回车符(\r)0x0D,空格符0x20。16进制中不区分大小写字母
                {
                    USART_RX_STA1 |= 0x4000;   //如果Res接收到回车符(\r)0x0D,把bit14写1
                }
                else
                {
                    USART1_RX_BUF[USART_RX_STA1 & 0X3FFF] = Res ; //在还没有接收到回车符(\r)0x0D之前,把接收到的数据放到数组里,数组从0位开始算
                    USART_RX_STA1++;//放一个,USART_RX_STA数据加一,因为USART_RX_STA的0-13位是记录数据个数的。这里是从0开始算的,所以要USART_REC_LEN要减一,
                    if (USART_RX_STA1 > (USART_REC_LEN - 1)) //200个数据,但USART_RX_STA是从0算起的
                    {
                        USART_RX_STA1 = 0; //如果接收到数据超过200个,则接收数据错误,重新开始接收
                        printf("\r\n您的输入错误\r\n\r\n");
                    }
                }
            }
        }
    }
}
#endif

其他串口配置亦是如此。这里在 usart.c 中还写了其他的可调用函数:

 发送一个字符函数:

/*****************  发送一个字符 **********************/
void Usart_SendByte(USART_TypeDef *pUSARTx, uint8_t ch)
{
    /* 发送一个字节数据到USART */
    USART_SendData(pUSARTx, ch);

    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

发送8位的数组:

/****************** 发送8位的数组 ************************/
void Usart_SendArray(USART_TypeDef *pUSARTx, uint8_t *array, uint16_t num)
{
    uint8_t i;

    for (i = 0; i < num; i++)
    {
        /* 发送一个字节数据到USART */
        Usart_SendByte(pUSARTx, array[i]);
    }
    /* 等待发送完成 */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}

发送字符串:

/*****************  发送字符串 **********************/
void Usart_SendString(USART_TypeDef *pUSARTx, char *str)
{
    unsigned int k = 0;
    do
    {
        Usart_SendByte(pUSARTx, *(str + k));
        k++;
    }
    while (*(str + k) != '\0');

    /* 等待发送完成 */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET)
    {}
}

发送一个16位数:

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord(USART_TypeDef *pUSARTx, uint16_t ch)
{
    uint8_t temp_h, temp_l;

    /* 取出高八位 */
    temp_h = (ch & 0XFF00) >> 8;
    /* 取出低八位 */
    temp_l = ch & 0XFF;

    /* 发送高八位 */
    USART_SendData(pUSARTx, temp_h);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

    /* 发送低八位 */
    USART_SendData(pUSARTx, temp_l);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

格式化打印:

/*
************************************************************
*	函数名称:	UsartPrintf
*
*	函数功能:	格式化打印
*
*	入口参数:	USARTx:串口组
*				fmt:不定长参
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
// 移植该函数的时候一定要加上头文件:#include "stdarg.h",va函数在头文件stdarg.h中,要使用其中的参数,必须包含此头文件#include "stdarg.h"
void UsartPrintf(USART_TypeDef *USARTx, char *fmt, ...)
{
    unsigned char UsartPrintfBuf[296];
    va_list ap;
    unsigned char *pStr = UsartPrintfBuf;

    va_start(ap, fmt);
    vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);                         //格式化
    va_end(ap);

    while (*pStr != 0)
    {
        USART_SendData(USARTx, *pStr++);
        while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/256403.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Arcgis中利用模型构建器统一栅格数据的行列号

1、统一&#xff08;X,Y) 方法&#xff1a;"数据管理工具箱"→"Projections and Transformations"→"Raster"→"Project Raster" 构建模型 这里以行列号最小的栅格&#xff08;X,Y&#xff09;为准&#xff08;其实也就是栅格数据的空…

数据可视化---离群值展示

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

全球通关第一人,分享阿里云新版ACE认证备考攻略~

2022.3月底阿里云针对老版ACE进行了改版&#xff0c;针对云计算技术的发展趋势&#xff0c;新增了云原生等热门技术&#xff0c;同时新版ACE认证新增了实验和面试&#xff0c;全面考查考生的动手能力和理论知识结构&#xff0c;含金量大大提升。 作为阿里云新版ACE全球通关第一…

【智慧之窗】AI驱动产品探索

一.初识 ChatGPT ChatGPT 是由 OpenAI 开发的自然语言处理&#xff08;NLP&#xff09;模型&#xff0c;基于 GPT&#xff08;Generative Pre-trained Transformer&#xff09;架构。GPT 系列的模型旨在理解和生成自然语言文本。ChatGPT 专注于支持对话性任务&#xff0c;即与…

【remb】twcc 与remb的切换测试

500000bps 70kBps 1 000 000 bps后&#xff0c;图像清晰些了&#xff0c;但在mesh下还是会牺牲了它的及时性&#xff1b;上面的几种情况的延时性很大啊&#xff0c;有流畅度&#xff0c;但延时太大 在twcc策略下&#xff0c;我们看到 220kBps时即大概1.6M时&#xff0c;视频才…

【Spring】09 BeanClassLoaderAware 接口

文章目录 1. 简介2. 作用3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 创建启动类3.4 启动 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点&#xff0c;其中之一就是 Bean 生命周期中的回调接口。本文将聚焦于其中的一个接口 BeanClassLoaderAware&#xff0c;介…

Day65力扣打卡

打卡记录 寻找峰值 II&#xff08;二分&#xff09; 链接 class Solution:def findPeakGrid(self, mat: List[List[int]]) -> List[int]:l, r 0, len(mat) - 1while l < r:mid (l r) // 2mx max(mat[mid])if mx > mat[mid 1][mat[mid].index(mx)]:r midelse:l…

外包干了6个月,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

xcrun: error: invalid active developer path

macOS升级完成后出现 xcrun: error: invalid active developer path问题。 xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun这是由于 Xcode command line tools 丢…

Web开发:如何在Visual Studio2022中使用Codeium(AI)编写代码

一、安装 【此款AI】免费&#xff01;免费&#xff01;免费&#xff01;使用时正常上网即可&#xff01; 1.前提条件 VS2022版本在17.5.5版本以上&#xff08;若版本不够则需要先更新&#xff09;安装和注册需要科学上网&#xff0c;使用则正常上网即可 2.开始下载扩展 方法…

springboot学习笔记(二)

1.Spring 和SpringBoot区别 2.Web开发入门 3.MVC模型 4.RequestMapping用法 5.RESTful 1.Spring 和SpringBoot区别 参考&#xff1a; 大家都懂Spring和SpringBoot的区别吗&#xff1f; - 知乎 https://www.zhihu.com/question/598494506/answer/3018702101 在学习了Spri…

现代化智慧档案馆八防十防建设调研样本

以下是一份完整的解决方案&#xff0c;包括方案介绍、方案优势、实施方案、预算和清单、实施时间和进度计划以及可能遇到的难点和解决方案。 方案介绍 档案库房八防十防环境一体化监控系统旨在为档案库房提供全方位的环境监控和管理&#xff0c;保障档案的安全、稳定和可靠。该…

CSS新手入门笔记整理:CSS3边框样式

圆角效果&#xff1a;border-radius 语法 元素{border-radius:取值;} border-radius 属性取值是一个长度值&#xff0c;单位可以是 px、em 和百分比等。 border-radius的四个取值 /*设置 1 个值,表示 4 个角的圆角半径都是 10px*/ border-radius:10px; /*设置 2 个值,表示…

mfc配置halcon环境

新建mfc窗体 选择基于对话框 打开项目属性 1、附加包含目录添加&#xff1a; $(HALCONROOT)\include;$(HALCONROOT)\include\halconcpp 2、链接器->常规->附加库目录 $(HALCONROOT)\lib\x64-win64 3、链接器->输入->附加依赖项 halcon.lib;halconcpp.lib 在对话…

山西电力市场日前价格预测【2023-12-20】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-12-20&#xff09;山西电力市场全天平均日前电价为473.70元/MWh。其中&#xff0c;最高日前电价为1292.75元/MWh&#xff0c;预计出现在08:15。最低日前电价为0.00元/MWh&#xff0c;预计出…

【ARM Trace32(劳特巴赫) 高级篇 21 -- Trace 系统性能分析 Performance Analyzer】

请阅读【Trace32 ARM 专栏导读】 文章目录 Performance AnalyzerPerf 操作步骤采样对象PC采样对象Memory采样对象 TaskPerformance Analyzer sample-based profiling 通常也叫做Trace32 的性能分析(Perf), 这个功能是通过周期性的采样来实现的。被采样到的数据可以被用于统计…

2023-12-18 最大二叉树、合并二叉树、二叉搜索树中的搜索、验证二叉搜索树

654. 最大二叉树 核心&#xff1a;记住递归三部曲&#xff0c;一般传入的参数的都是题目给好的了&#xff01;把构造树类似于前序遍历一样就可&#xff01;就是注意单层递归的逻辑&#xff01; # Definition for a binary tree node. # class TreeNode: # def __init__(se…

基于ssm高校宿舍管理系统的设计与开发论文

摘 要 本文是对高校宿舍管理系统的概括总结&#xff0c;主要从开题背景&#xff0c;课题意义&#xff0c;研究内容&#xff0c;开发环境与技术&#xff0c;系统分析&#xff0c;系统设计&#xff0c;系统实现这几个角度来进行本高校宿舍管理系统的阐述。 高校宿舍管理系统运用…

基于深度学习的图像去雾系统

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义&#xff1a; 随着计算机视觉和图像处理技术的不断发展&#xff0c;图像去雾成为了一个备受关注的研究领域。在自然环境中&#xff0c;由于大气中的颗粒物和水汽的…

vs code 如何配置配置自动编译将TypeScript代码编译成为JavaScript代码看这一篇就够了!!!

TS代码自动编译为JS代码 1. 检查是否有node环境2. 执行 tsc命令3. 监控编译 总结 本文介绍了vs code如何配置自动编译&#xff0c;将TypeScript代码编译成为JavaScript代码。 1. 检查是否有node环境 检查系统是否有node环境&#xff0c;命令如下 node -v npm -v如果没有安装…