嵌入式学习笔记——STM32的USART相关寄存器介绍及其配置
文章目录
- 前言
- USART的相关寄存器介绍
- 状态寄存器:USARTX->SR
- 具体位代表的含义
- 实际代码
- 数据寄存器 USARTX->DR
- 波特率寄存器 USARTX->BRR
- 控制寄存器 (USART_CR)
- 控制寄存器1(USART_CR1)
- 控制寄存器2(USART_CR2)
- GPIO的复用模式
- 查询对应GPIO管脚
- 配置为复用模式
- 编程实现串口收发一个字节
- 串口打印乱码
前言
上一篇中,对串口做了个概述,主要是介绍了串口通信的特征,异步串行全双工通信,然后就是结合串口的框图梳理了一下STM32中USART的配置流程以及发送接收数据的流程,本文将接着上篇的内容,对串口的寄存器做个介绍,然后实现一个简单的收发实验。
USART的相关寄存器介绍
根据之前GPIO的经验,咱们可以打开中文编程手册去找到对应的章节,然后依次看一下寄存器的每个位的功能。具体的位置在手册的第20章。

状态寄存器:USARTX->SR
首先第一个寄存器,就是状态寄存器,其作用就是用于描述USART的工作状态,为编程者提供一个串口的实时状态,前面分析框图的时候,有提到过,发送时需要判断上一帧有没有发送完毕;接收时需要判断一帧数据有没有接收完毕,当时说的是有内部的标志,这其中的标志就在此寄存器中。
状态寄存器是只读。

寄存器的访问方式,还是结构体指针指向成员的写法:
**USARTx->SR(x代表1、2、3、6)
或者
UARTx->SR(x代表4、5)**
具体位代表的含义
-
位 7 TXE:发送数据寄存器为空 (Transmit data register empty)
用于判断发送数据寄存器
当发送数据寄存器为空 1
当发送数据寄存器不为空 0
从发送数据寄存器发送到移位寄存器 这一位置1 -
位 6 TC:发送完成 (Transmission complete)
用于判断上一帧数据是否发送完成
发送完成 1
发送未完成 0
移位寄存器将数据发送完了,会将这一位置1;用于判断上一帧有么有发送完毕,没有就等待。
参照之前GPIO等待松手的写法,等待发送完成的代码应该是下面这个样子:
while( !(USART1->SR & 1<<6) ); /如果不为1 则一直等待
- 位 5 RXNE:读取数据寄存器不为空 (Read data register not empty)
判断数据接收结束了(接受完毕了CPU就可以将数据读取到变量)。
如果数据接收结束 1
如果数据没有接收结束 0 - 位 4 IDLE:检测到空闲线路 (IDLE line detected)
检测到空闲线路时,该位由硬件置 1。这位此文用不上,下一篇的串口中断中会应用到,想要了解可以自己去手册细看。
剩下的位,包括第0位的奇偶校验,一般都不做配置,所以在这也不看了。
实际代码
到这里关于发送和接收的代码编写思路其实已经有了,用串口1为例,代码如下:
//注意注释串口的发送字符数据函数
{等待上一帧数据发送完成发送数据
}
/*******************************************
*函数名 :Usart1_Send_Byte
*函数功能 :串口1发送一个字节函数
*函数参数 :u8 data
*函数返回值:无
*函数描述 :
发送一个U8类型的字符
*********************************************/
void Usart1_Send_Byte(u8 data)
{//等待之前的发送完成while(!(USART1->SR & (1<<6)));//将要发送的数据给数据寄存器USART1->DR = data;
}接收过程
{等待接收移位寄存器为满接收数据
}/*******************************************
*函数名 :Usart1_Receive_Byte
*函数功能 :串口1接收一个字节函数
*函数参数 :void
*函数返回值:u8 str
*函数描述 :
发送一个U8类型的字符
*********************************************/
u8 Usart1_Receive_Byte(void)
{u8 str;//等待接收完成while(!(USART1->SR & (1<<5)));//将数据寄存器的数据读取到str = USART1->DR;return str;
}
这个寄存器最大作用就是解决了上一篇中发送完成和接收完成的两个标志位的问题,完善了框图中发送和接收过程的标志判断问题。
注意上面的代码中,无论是写数据还是读数据都用到了一个USART1->DR的
寄存器,那么它的作用又是什么呢,接下里就对它来做个分析。
数据寄存器 USARTX->DR
注意手册中红框的表述,前面的框图中,发送和接收是两个数据寄存器,但实际在单片机内部是一个,这两个寄存器的唯一区别方法就是,执行写操作就是发送数据寄存器(TDR),执行读操作的时候就是接受数据寄存器(RDR)。这也就解释了为什么上面的代码中,读和写都是使用的DR寄存器。

波特率寄存器 USARTX->BRR
然后就是波特率寄存器,根据昨天的框图,在波特率的配置过程中,只用将计算的DIV结构写入一个寄存器即可。如下图:该寄存器的4-15位就是写入DIV的整数部分,0-3位就是写入DIV的小数部分。

至于怎么写,参照前面的经验,直接使用赋值语句即可。还是借用昨天的
使用串口1,最后一句就是写入过程。
波特率:115200 时钟大小:84000000 过采样:16
float USARTDIV;
unsigned int DIV_M;
unsigned int DIV_F;
USARTDIV=84000000/16/115200; // 45.57291666666667
DIV_M =(u32) USARTDIV;//读取整数部分
DIV_F = (USARTDIV- DIV_M)*16+0.5 f //考虑四舍五入USART1->BRR = DIV_M<<4 | DIV_F;//写入BRR寄存器
控制寄存器 (USART_CR)
前面三个寄存器已经解决了前面框图分析提到的发送接收以及波特率的配置,剩下的USART_CR1、USART_CR2、USART_CR3就是用来配置串口控制器的,可以预见的是,这三个寄存器绝对是与串口的剩下三要素紧密相光的。
控制寄存器1(USART_CR1)

如上图,控制寄存器1的高十六位是做保留的,只有低十六位用来做配置,这里还是先挑出今天需要使用的位,想要全面了解的可以自己去看数据手册哈。
-
位 15 OVER8:过采样模式 (Oversampling mode),过采样,很熟悉吧,上面一个寄存器中计算公式的8倍过采样还是16倍过采样就是通过这个寄存器来进行配置的,寄存器写1时是8倍过采样,写0时是16倍过采样。
-
位 13 UE:USART 使能 (USART enable)
片上外设使能----模块级使能
打开时钟 ----内核级使能
“注!!!:具备配置写保护,进行USART配置的时候要先关闭使能,其他的配置完才能打开使能,要放在串口配置的最后一个” -
位 12 M:字长 (Word length)
配置数据位,此位就是用来配置四要素中的数据位的,一般配置为8位数据位,也就是对其写0。

-
位 10 PCE:奇偶校验控制使能 (Parity control enable)
一般没有使用,所以直接配置为0,禁止奇偶校验。 -
位 3 TE:发送器使能 (Transmitter enable)
使能发送器配置为1即可。
-
位 2 RE:接收器使能 (Receiver enable)使能接收。

在控制寄存器1中我们需要操作的就是这些位,这里需要注意写法,控制寄存器1(USART_CR1),在代码中的写法是
USART1->CR[0]
控制寄存器2(USART_CR2)
可以发现,上面的控制寄存器1配置完后,四要素还有一个没有配置完毕,那就是停止位的配置,所以关于控制寄存器2,目前唯一用的上的就是第12
和13位。
一般配置为1个停止位,也就是写入00。
可以发现,到此,关于框图中的分析出来的,使能、四要素,发送,接收都齐活了,可是还有好几个寄存器没有露脸啊,这个后面遇到相关功能了再回来看,今天的功能确实只需要使用到上面的这些寄存器就够了。
关于控制寄存器的具体代码如下:
/*-----------------------------------------------------------------------*/ //Usart1初始化(四要素)RCC->APB2ENR |= (1<<4);//打开AHB2上的Usart_1时钟使能。
/* //CR1
// USART1->CR1 &= ~(1<<15); //16倍过采样
// USART1->CR1 &= ~(1<<12); //8位字长
// USART1->CR1 &= ~(1<<10); //无奇偶校验
// USART1->CR1 |= (1<<3); //发送使能
// USART1->CR1 |= (1<<2); //接收使能*/USART1->CR1 &=~ (0XB<<12);//清零(16倍过采样,8位字长)USART1->CR1 |= (3<<2); //配置CR1寄存器,无奇偶校验、发送使能接收使能 //CR2USART1->CR2 &= ~(3<<12); //1个停止位
/*-----------------------------------------------------------------------*/ //串口使能(最后开启使能,不然会锁住寄存器导致配置失败)USART1->CR1 |= (1<<13);
可以发现,到这里关于昨天框图的流程已经走完了,那么是不是将上面的这些代码放进工程就可以了呢,事实上是不行的,还漏了一个点,前面介绍GPIO的时候说过,任何需要CPU与外界进行数据交换的时间,都需要使用到GPIO,很明显,上面的步骤中,还没有对GPIO进行操作。根据GPIO那篇的介绍,现在这种情况属于GPIO的复用模式,那么该怎么配置呢,分析如下:
GPIO的复用模式
查询对应GPIO管脚
既然GPIO是唯一通道,那么串口的通道具体对应那几个GPIO口呢,根据前面的通信特征可以知道应该会用到两个GPIO口,一个是TX一个是RX那么具体的对应要怎么查询呢,在数数据手册的第三章最后一个表格中有具体的映射,这里还是以USART1来说,首先在上方找到USART1,然后向下找到USART1_TX和USART1_RX然后再向左寻找即可看见对应管脚


,细心的同学肯定会发现一个问题,在后面的续表中还有一组GPIO口对应值USART1的TX和RX,这时候到底要配置哪一组呢,解决方法就是看原理图的物理连接用的是什么。通过原理图可以发现使用的是PA9和PA10这一组,所以说,在配置过程中就要使用PA9和PA10管脚。

配置为复用模式
前面的按键输入和控制LED做了通用输入模式和通用输出模式的配置,这是第一次使用到复用模式的配置,那么关于复用模式具体怎么配置呢,之前在介绍GPIO的寄存的时候,有两个寄存器当时给略过了,现在就需要用到他们了,GPIO的复用功能寄存器,其中GPIO复用功能低寄存器对应控制的是0-7八个管脚的复用功能,GPIO复用功能高寄存器对应控制的是8-15这八个管脚的复用功能,这里使用的是PA9和PA10属于复用功能高位寄存器,这里和上面的串口控制寄存器一样需要注意寄存器具体的写法:
GPIO复用功能低位寄存器: GPIOx->AFR[0]
GPIO复用功能高位寄存器: GPIOx->AFR[1]

然后在上面的引脚映射表中可以看见有个AF7,这个AF7就可以让PA9PA10对应到USART1的TX和RX。注意到编程手册的描述,对应AF7需要写入的是0111,也就是说,需要对AFR9和AFR10中写入0111即可。

具体的配置代码:
RCC->AHB1ENR |= (1<<0); //打开AHB1上GPIOA端口GPIOA ->MODER &= ~(0xf<<18);//清0 GPIOA_MODER寄存器GPIOA ->MODER |= (0xA<<18); //GPIOA_MODER寄存器配置为复用模式GPIOA->AFR[1] &= ~((15<<4) | (15<<8)); //清零GPIOA ->AFR[1]|= (0X77<<4); //A9,A10配置为AF7(USART1的TX、RX)
编程实现串口收发一个字节
根据上面的介绍,我们将函数进行封装,串口配置部分就使用初始化函数,接收和发送就是用功能函数,同样的,这是一个新模块,还是需要新建两个文件用来存放源文件和头文件。
具体的代码:
#include "Usart1.h"/*******************************************
*函数名 :Usart1_Init
*函数功能 :串口1初始化函数
*函数参数 :u32 bps波特率
*函数返回值:无
*函数描述 :
PA9--------USART1_TX
PA10-------USART1_RX
*********************************************/
void Usart1_Init(u32 bps)
{float USARTDIV;unsigned int DIV_M;unsigned int DIV_F;//GPIOA9、GPIOA10的初始化RCC->AHB1ENR |= (1<<0); //打开AHB1上GPIOA端口GPIOA ->MODER &= ~(0xf<<18);//清0 GPIOA_MODER寄存器GPIOA ->MODER |= (0xA<<18); //GPIOA_MODER寄存器配置为复用模式GPIOA->AFR[1] &= ~((15<<4) | (15<<8)); //清零GPIOA ->AFR[1]|= (0X77<<4); //A9,A10配置为AF7(USART1的TX、RX)/*-----------------------------------------------------------------------*/ //Usart1初始化(四要素)RCC->APB2ENR |= (1<<4);//打开AHB2上的Usart_1时钟使能。
/* //CR1
// USART1->CR1 &= ~(1<<15); //16倍过采样
// USART1->CR1 &= ~(1<<12); //8位字长
// USART1->CR1 &= ~(1<<10); //无奇偶校验
// USART1->CR1 |= (1<<3); //发送使能
// USART1->CR1 |= (1<<2); //接收使能*/USART1->CR1 &=~ (0XB<<12);//清零(16倍过采样,8位字长)USART1->CR1 |= (3<<2); //配置CR1寄存器,无奇偶校验、发送使能接收使能 //CR2USART1->CR2 &= ~(3<<12); //1个停止位
/*-----------------------------------------------------------------------*/
//BRR波特率计算USARTDIV = 84000000/16/bps; // DIV_M =(u32)USARTDIV;DIV_F = (USARTDIV - DIV_M)*16+0.5f; //考虑四舍五入USART1->BRR = DIV_M<<4 | DIV_F;
/*-----------------------------------------------------------------------*/ //串口使能(最后开启使能,不然会锁住寄存器导致配置失败)USART1->CR1 |= (1<<13);
}/*******************************************
*函数名 :Usart1_Send_Byte
*函数功能 :串口1发送一个字节函数
*函数参数 :u8 data
*函数返回值:无
*函数描述 :
发送一个U8类型的字符
*********************************************/
void Usart1_Send_Byte(u8 data)
{//等待之前的发送完成while(!(USART1->SR & (1<<6)));//将要发送的数据给数据寄存器USART1->DR = data;
}/*******************************************
*函数名 :Usart1_Receive_Byte
*函数功能 :串口1接收一个字节函数
*函数参数 :void
*函数返回值:u8 str
*函数描述 :
发送一个U8类型的字符
*********************************************/
u8 Usart1_Receive_Byte(void)
{u8 str;//等待接收完成while(!(USART1->SR & (1<<5)));//将数据寄存器的数据读取到str = USART1->DR;return str;
}
#ifndef _USART1_H__
#define _USART1_H_
#include "stm32f4xx.h"void Usart1_Init(u32 bps);
void Usart1_Send_Byte(u8 data);
u8 Usart1_Receive_Byte(void);
#endif
然后在主函数调用初始化,编译

串口打印乱码
编译下载后发现,并没有按照我们的代码写的发送出‘A’到串口调试助手上,而是问号,仔细检查发现配置也没有问题,而且再林外一块板子上可以使用,那这是为什么呢。
后来,经过检查,是因为官方的时钟配置文件的分配是以25MHZ的晶振为基础来写的,而我的板子是8M晶振,造成了时序混乱,所以打印乱码了,修改为8后就可以了。

修改后,可以正常打印字符A了。

相关文章:
嵌入式学习笔记——STM32的USART相关寄存器介绍及其配置
文章目录前言USART的相关寄存器介绍状态寄存器:USARTX->SR具体位代表的含义实际代码数据寄存器 USARTX->DR波特率寄存器 USARTX->BRR控制寄存器 (USART_CR)控制寄存器1(USART_CR1)控制寄存器2(USART_CR2)GPIO…...
Android setContentView流程分析(一)
对于做Android App的小伙伴来说setContentView这个方法再熟悉不过了,那么有多少小伙伴知道它的调用到底做了多少事情呢?下面就让我们来看看它背后的故事吧? setContentView()方法将分为两节来讲: 第一节:如何获取De…...
doris数据库操作数字遇到的问题
关于doris数据库Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库,以极速易用的特点被人们所熟知,仅需亚秒级响应时间即可返回海量数据下的查询结果,不仅可以支持高并发的点查询场景,也能支持高吞吐的复杂分析场景。…...
3.13文件的IO操作
一.文件1.定义文件一般指的是存储在硬盘上的普通文件形如:txt.jpg.mp4,rar等这些文件在计算机中,文件可能是一个广义的概念,不仅可以包含普通文件,还可以包含目录(也就是文件夹.把目录称为目录文件)在操作系统中,还会用文件来描述一些其他的硬件设备或者软件资源比如网卡,显示器…...
ffmpeg使用
1 下载FFmpeg安装 官网地址:https://www.ffmpeg.org/download.html#build-windows 进入网址,点击下面红框部分 点击下面范围进行下载,下载速度有点慢,等等吧! 下载成功后,解压后,复制bin的路…...
spark中的并行度(分区数)/分区器如何确定
源头RDD有自己的分区计算逻辑,一般没有分区器,并行度是根据分区算法自动计算的,RDD的compute函数中记录了数据如何而来,如何分区的hadoopRDD,根据XxxinputFormat.getInputSplits()来决定,比如默认的TextInputFormat将文…...
00后女生“云摆摊”两周赚1.5万,实体店转战线上真的能赚钱吗?
最近,山东临沂的00后女生利用小程序在线上“云摆摊”卖水果,两周赚1.5万,引发网友热议。不少人发出质疑的声音:年轻人不要有稳定的工作不做,去摆摊;网上开店成本低,开实体店结果就难说了&#x…...
华为OD机试题 - 最优资源分配(JavaScript)| 机考必刷
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:最优资源分配题目输入输出描述备注示例一输入输出说明示例二输入…...
利用python判断字符串是否为回文
1 问题 如何用python判断字符串是否为回文。 2 方法 用两个变量left,right模仿指针(一个指向第一个字符,一个指向最后一个字符),每比对成功一次,left向右移动一位,right向左移动一位,…...
GDB 调用之ptype、set variable
今天在公司的时候,排查一个问题,创建l3 lif 失败,查看各种日志发现是用key去创建的 lif失败了,日志里指示key为空,导致的创建失败。原因为一个结构体比基线的多了一些东西,导致版本不对,既而计算…...
并发编程---阻塞队列(五)
阻塞队列一 阻塞队列1.1.阻塞队列概念1.2.阻塞队列API案例1.2.1. ArrayBlockingQueue1.2.1.1.抛出异常1.2.1.2.返回布尔1.2.1.3.阻塞1.2.1.4.超时1.2.2.SynchronousQueue二 阻塞队列应用---生产者消费者2.1.传统模式案例代码结果案例问题---防止虚假唤醒2.2.⽣产者消费者防⽌虚…...
本科课程【计算机组成原理】实验1 - 输出ABCD程序的生成
大家好,我是【1+1=王】, 热爱java的计算机(人工智能)渣硕研究生在读。 如果你也对java、人工智能等技术感兴趣,欢迎关注,抱团交流进大厂!!! Good better best, never let it rest, until good is better, and better best. 近期会把自己本科阶段的一些课程设计、实验报…...
Java并发编程(2) —— 线程创建的方式与原理
一、Java线程创建的三种方式 1. 继承Thread类并重写run()方法 ///方法一:使用匿名内部类重写Thread的run()方法Thread t1 new Thread() {Overridepublic void run() {try {sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}log.debug("…...
你写的js性能有多差你知道吗 | js性能优化
性能的计算⽅式 确认⾃⼰需要关注的指标 常⻅的指标有: ⻚⾯总加载时间 load⾸屏时间⽩屏时间 代码 尝试⽤⼀个指令, 挂载在重要元素上, 当此元素inserted就上报 各个属性所代表的含义 connectStart, connectEnd 分别代表TCP建⽴连接和连接成功的时间节点。如果浏…...
线程的状态、状态之间的相互转换
目录 一、线程的状态 1. NEW 2. TERMINATED 3. RUNNABLE 4. TIMED_WAITING 5. BLOCKED 6. WAITING 二、线程状态转换 1. 线程状态转换简图 一、线程的状态 线程的状态一共有 6 种: NEW:安排了工作,还未开始行动(调用 st…...
Java8使用Lambda表达式(流式)快速实现List转map 、分组、过滤等操作
利用java8新特性,可以用简洁高效的代码来实现一些数据处理。1 数据准备1.1 定义1个Fruit对象package com.wkf.workrecord.work;import org.junit.Test;import java.math.BigDecimal; import java.util.ArrayList; import java.util.List;/*** author wuKeFan* date …...
C++之深浅拷贝
一、浅拷贝 我们看下以下代码 Test.h 文件 #pragma once #include<iostream> using namespace std; class Student { public:Student(){}~Student(){if (m_Id ! nullptr){delete m_Id;m_Id nullptr;}}Student(int id, string strName){m_Id new int[id];m_strName s…...
CoreLocation的一切
Overview 概述 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pgnvehxf-1678717852996)(./blog_cover.png)] Core Location 提供的服务可以确定设备的地理位置、高度和方向,或者它相对于附近 iBeacon 设备的位置。 该框架使用设备上的所…...
HashMap原理
初始化 从HashMap 源码中我们可以发现,HashMap的初始化有一下四种方式 //HashMap默认的初始容量大小 16,容量必须是2的幂 static final int DEFAULT_INITIAL_CAPACITY 1 << 4; // HashMap最大容量 static final int MAXIMUM_CAPACITY 1 <&…...
STM32入门笔记(02):独立看门狗(IWDG)和窗户看门狗(WWDG)(SPL库函数版)
1.IWDG狗简介 除了始终控制器的RCC_CSR寄存器的父为标志位和备份区域中的寄存器以外,系统复位 将复位所有寄存器至它们的复位状态。 当发生以下任一事件时,产生一个系统复位: 1.NRST引脚上的 低 电平,即 外部复位;2…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
