【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令
🐱作者:一只大喵咪1201
🐱专栏:《理解ARM架构》
🔥格言:你只管努力,剩下的交给时间!
目录
- 🍠操作寄存器实现UART
- 🍟UART原理
- 🍟编程
- 🍠段的概念
- 🍠IDE背后的命令
- 🍠总结
🍠操作寄存器实现UART
🍟UART原理
UART的全称是Universal Asynchronous Receiver and Transmitter,即异步发送和接收。
串口在嵌入式中用途非常的广泛,主要的用途有:
- 打印调试信息;
- 外接各种模块:GPS、蓝牙;
串口因为结构简单、稳定可靠,广受欢迎。
如上图所示,串口通信只需要三根线,发送(TXD)、接收(RXD)、地线(GND)。
- 通信双方的TXD与对方的RXD相连。
串口发送数据是以帧格式一帧一帧来发的,帧格式由1bit起始位,8或9bit数据位,1或1.5或2bit校验位,1bit停止位组成。
- 通常情况下都使用8bit数据位,不适用校验位,这样的一帧数据有10个bit。
校验位又叫奇偶校验位,如果8个数据位加校验位中比特为位1的个数是奇数,校验位就是1,否则就是0。
由于现在电子技术的逐渐成熟,串口通信很少出错,所以校验位使用的不多。
如上图所示是一帧数据传送时的逻辑电平示意图。
- 发送方将自己的TXD线从高电平拉到低电平,保持一段时间,接收方读取到自己的RXD线由高到底以后就知道要接收数据了。
- 发送方按照自己发送的这个字节,从低位开始,改变TXD线的电平,每改变一次保持一段时间,如此反复8次完成一字节数据的发送。
- 接收方在自己RXD线上的电平保持期间的中间时刻,根据电平状态记录该比特位的值,最后组合成一字节数据。
- 发送方将一字节数据发送完毕后,将自己的TXD线拉高方便下次发送数据,接收方在接收到8bit数据以后,并且检测到自己RXD线是高电平,就知道这一帧数据传送完毕了。
上面描述数据发送过程中电平维持的时间,就是根据波特率来确定的,一般选波特率都会有9600,19200,115200等选项。
- 波特率:可以简单理解为,串口通信过程中1秒钟能发送的比特位个数。
- 波特率是通信双方约定好的,一个按照这个速度发送数据,另一个按照这个速度接收数据。
逻辑电平:
如上图所示是本喵使用的ARM开发板串口发出的电平信号,在xV至5V之间,就认为是逻辑1,在0V至yV之间就为逻辑0,这叫做TTL/CMOS逻辑电平。
如上图所示是RS-232逻辑电平,在-12V至-3V之间,就认为是逻辑1,在+3V至+12V之间就为逻辑0,RS-232的电平比TTL/CMOS高,能传输更远的距离,在工业上用得比较多。
可以看到,RS-232与TTL/CMOS相同逻辑电平对应的真实电压正负是相反的。
如上图所示,ARM芯片上的串口都是TTL电平的,通过板子上或者外接的电平转换芯片,转成RS232接口,连接到电脑的RS232串口上,实现两者的数据传输。
如上图所示,现在的电脑越来越少有RS232串口的接口,但USB是几乎都有的。因此使用USB串口芯片将ARM芯片上的TTL电平转换成USB串口协议,即可通过USB与电脑数据传输。
- 无论那种接口,板子上的芯片IO口输出的都是TTL/CMOS电平,我们在写程序时仅需要关心输出的逻辑电平即可。
🍟编程
一款ARM芯片上会有多个USART串口,一般UART1用来输出调试信息,这里本喵也使用USART1。
确定引脚:
如上图,本喵使用的STMF103ZET6芯片上,USART1的USART1_RX、USART1_TX,接到了PA10、PA9。
将引脚配置为UART功能:
- 使能GPIOA/USART1模块
如上图是,RCC_APB2ENR
寄存器,GPIOA模块、USART1模块的使能都是在这一个寄存器里实现。
如上图,从芯片手册中查看Reset and clock control RCC
寄存器的基地址是0x40021000
,再根据RCC_APB2ENR
的偏移地址0x18
得到该寄存器的绝对地址是0x40021000 + 0x18
。
将该寄存器的bit2和bit14写一,此时就使能了GPIOA和USART1模块。
- 配置引脚功能
从上面的芯片原理图可以知道,PA9、PA10有三种功能:GPIO、USART1、TIMER1,所以这里要将其配置为USAT1功能。
如上图所示GPIOx_CRH
寄存器,该寄存器的绝对地址是0x40010800 + 0x04
,PA9配置为输出,所以将MODE9
代表的bit4和bit5配置成01
,将CNF9
代表的bit6和bit7配置为10
。
PA10配置为输入,将MODE10
代表的bit8和bit9配置为00
,再将CNF10
代表的bit10和bit11配置成01
。
由于这里仅使能了USART1,没有使能定时器,所以PA9和PA10的默认复用功能就是USART1,使用默认值即可。
设置串口参数:
- 设置波特率
如上图所示是波特率的计算公式,USARTDIV由整数部分、小数部分组成,USARTDIV = DIV_Mantissa + (DIV_Fraction / 16)
。fck是内部时钟频率,这里就使用默认值,是8MHZ。
如上图USART_BRR
寄存器,DIV_Mantissa表示整数部分,占用该寄存器的bit4~bit15
,DIV_Fraction表示小数部分,占用该寄存器的bit0~bit3
。
以常用的波特率115200为例,来计算该寄存器的值:
设置波特率* 115200 = 8000000/16/USARTDIV* USARTDIV = 4.34* DIV_Mantissa = 4* DIV_Fraction / 16 = 0.34* DIV_Fraction = 16*0.34 = 5
所以给USART_BRR
寄存器的bit4~bit15
赋值4,bit0~bit3
赋值5,根据这两个值再来倒推一下真实的波特率:
真实波特率:* DIV_Fraction / 16 = 5/16=0.3125* USARTDIV = DIV_Mantissa + DIV_Fraction / 16 = 4.3125* baudrate = 8000000/16/4.3125 = 115942
可以看到,虽然和115200有点差距,但是并不影响。
- 设置数据格式
如上图所示USART1_CR1
寄存器,本喵将帧格式设置为1个起始位,8个数据位,无校验位,1个停止位,所以将bit13
设置1,bit12
设置为0,bit10
设置为0,bit3
设置为1,bit2
设置为1。
但是此时并没有设置几个停止位,还需要设置另一个寄存器:
如上图所示USART_CR2
寄存器,将bit12~bit13
设置为00,表示1个停止位。
根据状态寄存器读写数据:
如上图所示串口模块结构图,发送有一个发送数据寄存器和发送移位寄存器,接收有一个接收数据寄存器和接收移位寄存器。
发送数据时,CPU将数据写入到发送数据寄存器,然后由发送移位寄存器一位一位将数据通过TXD线发送出去。
接收数据时,RXD线上的数据一位一位放入接收移位寄存器,该寄存器接收完毕后将整个字节数据放入到接收数据寄存器,CPU从接收数据寄存器中可以直接读取数据。
- 状态寄存器
如上图所示USART_SR
状态寄存器,TXE
表示发送数据寄存器是否为空,该位并不能说明数据已经发送完了,因为真正发送数据的是移位寄存器,只能说发送数据寄存器将数据给了移位寄存器,CPU可以再向数据寄存器中写数据了。
TC
表示发送数据完成,即发送数据寄存器和移位寄存器中的数据都发送完毕了。RXNE
表示接收数据寄存器中有数据了,说明已经接收到了数据,CPU可以来读取了。
- 数据寄存器
如上图所示USART_DR
寄存器,写、读这个寄存器,就可以发送、读取串口数据。
在配置完引脚和功能选择以后,本喵在介绍USART_XXX
寄存器的时候并没有说它的地址,因为无论是设置波特率的USART_BRR
,还是设置数据格式的USART_CR1
,再或者状态寄存器USART_SR
,以及数据寄存器USART_DR
这些都是以USART
为基地址的。
如上图所示USART1
的基地址是0x40013800
,上面本喵提到的这些寄存器都是在这个基地址的基础上进行偏移,也就是说它们都属于USART1
模块中的寄存器。
typedef unsigned int uint32_t;
typedef struct
{volatile uint32_t SR; /*!< USART Status register, Address offset: 0x00 */volatile uint32_t DR; /*!< USART Data register, Address offset: 0x04 */volatile uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */volatile uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */volatile uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */volatile uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */volatile uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */
} USART_TypeDef;
如上面代码所示,用一个结构体来表示USART
模块,里面的成员变量表示各个寄存器,让它们在结构体中的偏移量和寄存器相对于USART
模块的偏移量相对应,此时就可以通过访问这个结构体访问到各个寄存器。
如上图所示是整个串口的初始化代码,其中配置波特率等参数使用的是结构体访问的寄存器,串口结构体是一个局部变量。
如上图所示,定义发送一个字符和接收一个字符的函数,通过判断状态寄存器的值,进而读写DR
寄存器,也是通过结构体访问的寄存器,结构体是一个局部变量。
如上图,此时将程序烧录到开发板以后,会通过串口发送Hello
字符串,在PC端发送一个字符,板子接收到以后返回该字符及下一个字符,此时我们的串口是配置好了。
问题:为什么每个函数中都得创建一个uart1结构体局部变量,而不是创建全局变量供这些函数使用呢?
🍠段的概念
如上图所示,增加三个函数,用来打印字符串及变量的地址。
如上图所示,创建四个全局变量,g_ConstChar
被const修饰,然后在mymain
中分别打印四个变量的地址及它们的值。
将程序编译后烧录到开发板中,通过串口工具来观察输出的内容。
如上图所示,来看这四个变量的地址,只有g_ConstChar
这个被const修饰的变量地址是位于Flash中的,其他几个变量都是位于RAM中。
如上图,keil中只能了Flash和RAM的起始地址,根据这两个参数很容易判断出这四个变量所处的位置。
如上图,再来看输出的这四个变量的值,可以看到,只有const修饰的g_ConstChar
变量输出了B
,其他几个变量都没有输出对应的则,而是奇怪的东西。
- 其他变量输出的奇怪值表明,这几个变量地址处的值是乱码。
g_ConstChar
变量位于Flash,也就是ROM,ROM是只读的,不能写,而其他三个变量位于RAM,RAM是可读可写的。
在编译的时候,编译器进行了判断处理,g_ConstChar
是只读的,不会写,所以把它放在Flash就可以。
- Flash上存放这种只读数据的区域叫做只读数据段。
其他三个变量会进行读和写的操作,所以编译器给了它们一个链接地址,这个地址对应在RAM上,方便CPU进行读写。
- RAM上存放这种可读可写全局变量的区域叫做可读可写数据段。
无论有没有被const修饰的变量,它们都有初始值A
或者B
,这个两个数值是不会变的,只是用来使用的,所以编译器将这两个值放在这两个变量位于Flash上的地址处(加载地址)。
- 有几个有初始值的全局变量,Flash中就会保存几个初始值。
- Flash以及内存中并没有变量名,只会在变量的地址处直接存放数值。
像char g_A = 0
这种初始值为0的全局变量,以及char g_B
这种没有初始值的全局变量,Flash上就没有必要存放它们的初始值。
假设初始值为0的变量有一万个,Flash中难道要存放1万个0吗?肯定不会的,这样浪费内存不说,还没有任何意义。对于没有初始值的全局变量Flash中更不会存放它的初始值了。
所以编译器在编译的时候,直接给这种初始值为0或者没有初始值的全局变量分配一个链接地址,位于RAM中,CPU直接去链接地址读写就可以了。
- 这种存放初始值为0或者没有初始值所在的RAM区域被叫做BSS段或者ZI段。
我们写的代码经过编译链接以后,会生成一个二进制可执行文件,里面全部都是机器码,这部分代码并不会改变,所以也存放到Flash上。
- 存放代码的Flash区域被叫做代码段。
至于栈以及堆本喵在前面的文章中就详细讲解过,这里就不再说了,有兴趣的小伙伴可以移步单片机中的C语言。
所以,程序分为这几个段:
- 代码段(RO-CODE):就是程序本身,不会被修改
- 可读可写的数据段(RW-DATA):有初始值的全局变量、静态变量,需要从ROM上复制到内存
- 只读的数据段(RO-DATA):可以放在ROM上,不需要复制到内存
- BSS段或ZI段:
- 初始值为0的全局变量或静态变量,没必要放在ROM上,使用之前清零就可以
- 未初始化的全局变量或静态变量,没必要放在ROM上,使用之前清零就可以
- 局部变量:保存在栈中,运行时生成
- 堆:一块空闲空间,使用malloc函数来管理它,malloc函数可以自己写
🍠IDE背后的命令
IDE指集成开发环境(Integrated Development Environment)。我们开发STM32F103等单片机程序时使用是keil5就是一种IDE。
使用IDE,很容易操作,点点鼠标就可完成,添加文件,指定文件路径(头文件路径、库文件路径),指定链接库,编译、链接,下载、调试等功能。
其实在我们点下某一个按钮以后,IDE的背后会执行一系列指令:
如上图,在keil5的Output选择中勾选Create Batch File
,然后重新全部编译。
如上图,此时在当前工程的Objects
目录下会多出上面红色框中的四个文件。
如上图所示分别是这几个文件中的内容,都是一系列的命令行指令,用来编译和链接文件的指令,具体怎么用不用管,只需要知道有这些东西。
start._ia
中的命令行就是在让start.s
汇编文件编译成start.o
目标文件。main._i
中的命令行就是在让main,c
源文件编译成main.o
目标文件。uart._i
中的命令行就是在让uart.c
源文件编译成uart.o
目标文件。led.linp
中的命令行就是把这几个.o
目标文件链接在一起形成一个二进制可执行文件led.axf
,我们烧录的就是这个文件。
当我们点下IDE上的编译选项时,IDE会自动执行上面四个文件中的内容,最后生成我们需要的东西。
🍠总结
虽然配置串口已经是一个老生常谈的问题了,但是相信大家很少直接使用寄存器地址来配置吧,这个过程中可以加深对ARM架构的理解。
串口配好后通过打印数据过程中出现的问题介绍了段的概念,编译器不同类型的变量放在内存中不同的位置。
要意识到,编译一个工程的背后没有那么简单。
相关文章:

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令
🐱作者:一只大喵咪1201 🐱专栏:《理解ARM架构》 🔥格言:你只管努力,剩下的交给时间! 目录 🍠操作寄存器实现UART🍟UART原理🍟编程 🍠…...
python 左值查找 右值查找
左值查找 在一组数据中查找出 数字x 在这组数据中第一次出现的索引并输出,没有找到则输出-1查找方式:二分查找 数据前提:一组数据要有序一组数据: arr [2, 3, 3, 3, 5, 7, 9, 11, 13, 15, 17]测试: 示例1ÿ…...
机器学习之自监督学习(四)MoCo系列翻译与总结(二)
MoCo中相关工作的对比分析 去噪自动编码器(Denoising Autoencoder)是一种用于学习数据表示的神经网络模型。它的主要目标是通过去除输入数据中的噪声,学习到输入数据的有用表示,从而提高模型对干净数据的鲁棒性。下面是对去噪自动…...
元宇宙企业3d数字展厅轻松低本搭建更全面、多元、趣味化的展览
对所有企业来说,拥有一个3D线上展厅是互联网营销必不可少的部分,但是3D线上展厅定制周期长费用高,让很多企业公司望而却步,web3d开发公司制作的3D线上企业展厅制作平台备导览地图、语音解说、交互热点、全景漫游、自主行走、链接跳…...
华为OD机试真题-开源项目热榜-2023年OD统一考试(C卷)
题目描述: 某个开源社区希望将最近热度比较高的开源项目出一个榜单,推荐给社区里面的开发者。对于每个开源项目,开发者可以进行关注(watch)、收藏(star)、fork、提issue、提交合并请求(MR)等。 数据库里面统计了每个开源项目关注、收藏、fork、issue、MR的数量,开源项目的热…...

深入探索Maven:优雅构建Java项目的新方式(一)
Maven高级 1,分模块开发1.1 分模块开发设计1.2 分模块开发实现 2,依赖管理2.1 依赖传递与冲突问题2.2 可选依赖和排除依赖方案一:可选依赖方案二:排除依赖 3,聚合和继承3.1 聚合步骤1:创建一个空的maven项目步骤2:将项目的打包方式改为pom步骤…...

Shopee如何入驻?如何防封?
Shopee作为东南亚领航电商平台,面向东南亚蓝海市场,近年来随着东南亚市场蒸蒸日上,虾皮也吸引了大批量的跨境商家入驻。那么接下来就给想要入驻的虾皮小白一个详细的安全入驻教程。 一、商家如何入驻 虾皮与LAZADA最大的区别就是商家即卖家&…...

2024年第十六届山东省职业院校技能大赛中职组 “网络安全”赛项竞赛正式卷任务书
2024年第十六届山东省职业院校技能大赛中职组 “网络安全”赛项竞赛正式卷任务书 2024年第十六届山东省职业院校技能大赛中职组 “网络安全”赛项竞赛正式卷A模块基础设施设置/安全加固(200分)A-1:登录安全加固(Windows, Linux&am…...

Python编程基础
Python是一种简单易学的编程语言,广泛应用于Web开发、数据分析、人工智能等领域。无论您是初学者还是有一定编程经验的人士,都可以从Python的基础知识开始建立自己的编程技能。 目录 理论Python语言的发展程序设计语言的分类静态语言与脚本语言的区别 代…...

python类和对象
1.使用对象组织数据 class Student:nameNone #记录名字 stu1Student() #创建对象 stu1.name"abc" #为对象属性赋值2.类的定义和使用 2.1成员方法的定义语法 传参的时候self是透明的,不用管 class Stu:nameNonedef sayHi(self):print(f"你好&#x…...

ubuntu操作系统中docker下Hadoop分布式前置环境配置实验
版本: centos7 hadoop 3.1.3 java JDK:1.8 集群规划: masterslave1slave2HDFS NameNode DataNode DataNode SecondryNameNode DataNode YARNNodeManager ResourceManage NodeManager NodeManager 1.docker容器: 把普通用户加入到docker组&am…...

【Linux学习笔记】protobuf 基本数据编码
https://zhuanlan.zhihu.com/p/557457644https://zhuanlan.zhihu.com/p/557457644 [新文导读] 从Base64到Protobuf,详解Protobuf的数据编码原理本篇将从Base64再到Base128编码,带你一起从底层来理解Protobuf的数据编码原理。本文结构总体与 Protobuf 官…...

OpenCV快速入门:图像分析——图像分割和图像修复
文章目录 前言一、图像分割1.1 漫水填充法1.1.1 漫水填充法原理1.1.2 漫水填充法实现步骤1.1.3 代码实现 1.2 分水岭法1.2.1 分水岭法原理1.2.2 分水岭法实现步骤1.2.3 代码实现 1.3 GrabCut法1.3.1 GrabCut法原理1.3.2 GrabCut法实现步骤1.3.3 代码实现 1.4 Mean-Shift法1.4.1…...

BART - 磁共振重建库 linux系统安装 MATLAB 使用
本文主要介绍如何在linux系统中安装伯克利大学的磁共振重建库BART 和在matlab中的配置使用。 安装必要的库 (linux 命令行) $ sudo apt-get install make gcc libfftw3-dev liblapacke-dev libpng-dev libopenblas-dev 下载编译BART 文件 (官网链接:BART Toolbox) 命令行下…...
在linux下在官网的nginxtar包 的安装方式并配置全局变量
在Linux系统中,Nginx是一种流行的Web服务器和反向代理服务器,它可以提供高性能的静态内容服务和动态内容服务。本文将详细介绍如何在Linux系统中安装Nginx,并配置全局变量。 安装Nginx 以下是在Linux系统中安装Nginx的步骤: 1.…...
Java中数据库查询方法MapListProcessor的应用
1.供应链系统的销售合同捉过了两个金额一样的,同一个项目 2.合同号也一样,oaid不一样,但是从OA前台只有一个 3.一个是建云的一个是泛微的 4.做下过滤,如果同一个合同编号,在泛微里面有的,建云的就不获取了 …...

一键换肤功能
使用css3的css变量来实现 示例: 代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name&qu…...

Oracle SQL 注入上的 Django GIS 函数和聚合漏洞 (CVE-2020-9402)
漏洞描述 Django 于2020年3 月4日发布了一个安全更新,修复了 GIS 函数和聚合中的 SQL 注入漏洞。 参考链接: Django security releases issued: 3.0.4, 2.2.11, and 1.11.29 | Weblog | Django 该漏洞要求开发者使用 JSONField/HStoreField;此外&…...
MYSQL 排序和分组怎么做?
文章目录 前言MySQL 排序语法在命令提示符中使用 ORDER BY 子句在PHP脚本中使用 ORDER BY 子句 MySQL 分组GROUP BY 语法实例演示使用 WITH ROLLUP 后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:Mysql 🐱👓博…...

ESP32 ESP-IDF5.1 在Visual Studio Code中自定义分区表与调整Flash大小
好记心不如烂笔头 使用ESP-IDF开发ESP32的时候,要是同时用到蓝牙和WIFI的话,很多时候会提示Flash不够, 我是照着这样解决的,存档记录 来源 : zaixingxing2539 大佬的 ESP32 ESP-IDF5.0 在VSCODE中自定义分区表 用Visual Studio Code自定义分区表 # ESP-IDF Partition Table…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...