【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令

🐱作者:一只大喵咪1201
🐱专栏:《理解ARM架构》
🔥格言:你只管努力,剩下的交给时间!
图

目录

  • 🍠操作寄存器实现UART
    • 🍟UART原理
    • 🍟编程
  • 🍠段的概念
  • 🍠IDE背后的命令
  • 🍠总结

🍠操作寄存器实现UART

🍟UART原理

UART的全称是Universal Asynchronous Receiver and Transmitter,即异步发送和接收。
串口在嵌入式中用途非常的广泛,主要的用途有:

  • 打印调试信息;
  • 外接各种模块:GPS、蓝牙;

串口因为结构简单、稳定可靠,广受欢迎。

tu
如上图所示,串口通信只需要三根线,发送(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相同逻辑电平对应的真实电压正负是相反的。


tu
如上图所示,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功能。

tu
如上图所示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,使用默认值即可。

设置串口参数:

  • 设置波特率

tu
如上图所示是波特率的计算公式,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。

但是此时并没有设置几个停止位,还需要设置另一个寄存器:

tu
如上图所示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模块的偏移量相对应,此时就可以通过访问这个结构体访问到各个寄存器。

tu
如上图所示是整个串口的初始化代码,其中配置波特率等参数使用的是结构体访问的寄存器,串口结构体是一个局部变量。

图
如上图所示,定义发送一个字符和接收一个字符的函数,通过判断状态寄存器的值,进而读写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的背后会执行一系列指令:

tu
如上图,在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架构的理解。

串口配好后通过打印数据过程中出现的问题介绍了段的概念,编译器不同类型的变量放在内存中不同的位置。

要意识到,编译一个工程的背后没有那么简单。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python——第十二天

内置模块或者其他模块学习方式&#xff1a; dir help os模块负责程序与操作系统的交互&#xff0c;提供了访问操作系统底层的接口&#xff1b;即os模块提供了非常丰富的方法用来处理文件和目录。 os&#xff1a; os.path 遍历C盘代码 import os from os import path def …

修改YOLOv5的模型结构第三弹

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 文章目录 任务任务拆解 开始修改C2模块修改yolo.py修改模型配置文件 模型训练 上次已…

【工具使用】Keil工具的使用——常用配置介绍

Keil调试具体教程学习 目录 ​​​​​​​Keil调试具体教程学习 常用功能总结 &#xff08;2&#xff09;目标设置&#xff08;Target&#xff09; ①设置晶振频率 ②跨模块优化选项 ③微库选项 &#xff08;3&#xff09;输出设置&#xff08;Output&#xff09; ①…

插入排序(形象类比)

最近在看riscv手册的时候&#xff0c;里面有一段代码是插入排序&#xff0c;但是单看代码的时候有点迷&#xff0c;没看懂咋操作的&#xff0c;后来又查资料复习了一下&#xff0c;最终才把代码看明白&#xff0c;所以写篇博客记录一下。 插入排序像打扑克牌 这是我听到过比较形…

list的总结

目录 1.什么是list 1.1list 的优势和劣势 优势&#xff1a; 劣势&#xff1a; 2.构造函数 2.1 default (1) 2.2 fill (2) 2.3 range (3) 2.4 copy (4) 3.list iterator的使用 3.1. begin() 3.2. end() 3.3迭代器遍历 4. list容量函数 4.1. empty() 4.2. siz…

语音合成综述Speech Synthesis

一、语音合成概述 语音信号的产生分为两个阶段&#xff0c;信息编码和生理控制。首先在大脑中出现某种想要表达的想法&#xff0c;然后由大脑将其编码为具体的语言文字序列&#xff0c;及语音中可能存在的强调、重读等韵律信息。经过语言的组织&#xff0c;大脑通过控制发音器…

qRT-PCR相对定量计算详解qPCR相对定量计算方式——2^-(∆∆Ct) deta t

做完转录组分析之后&#xff0c;一般都要求做qRT-PCR来验证二代测序得到的转录本表达是否可靠。荧光定量PCR是一种相对表达定量的方法&#xff0c;他的计算方法有很多&#xff0c;常用的相对定量数据分析方法有双标曲线法&#xff0c;ΔCt法&#xff0c;2^-ΔΔCt法(Livak法)&a…

顺序表基本操作全面解析

文章目录 1.线性表2.顺序表分类2.1 静态顺序表2.2 动态顺序表 3. 顺序表各接口实现1. 定义结构体(Seqlist)2. 结构体初始化(SLInit)3.检查容量 (SLCheckCapacity)4.打印数据 (SLPrintf)5.插入操作5.1 从数据头部插入(SLPushFront)5.2 从数据尾部插入(SLPushBack)5.3 从任意下标…

GEE:基于 Landst 遥感数据计算的 kNDVI 下载 APP

作者&#xff1a;CSDN _养乐多_ 本文记录了在Google Earth Engine&#xff08;GEE&#xff09;平台中&#xff0c;使用 Landsat 遥感数据计算并且下载 kNDVI 的应用 APP 链接&#xff0c;并介绍该 APP 的使用方法和步骤。该APP可以为用户展示 NDVI 和 kNDVI 的遥感影像&#…

抽象类, 接口, Object类 ---java

目录 一. 抽象类 1.1 抽象类概念 1.2 抽象类语法 1.3 抽象类特性 1.4 抽象类的作用 二. 接口 2.1 接口的概念 2.2 语法规则 2.3 接口的使用 2.4 接口间的继承 2.5 抽象类和接口的区别 三. Object类 3.1 toString() 方法 3.2 对象比较equals()方法 3.3 hash…

免费获取GPT-4的五种工具

不可否认&#xff0c;由OpenAI带来的GPT-4已是全球最受欢迎的、功能最强大的大语言模型&#xff08;LLM&#xff09;之一。大多数人都需要使用ChatGPT Plus的订阅服务去访问GPT-4。为此&#xff0c;他们通常需要每月支付20美元。那么问题来了&#xff0c;如果您不想每月有这笔支…

基于JavaWeb+SpringBoot+Vue医院管理系统小程序的设计和实现

基于JavaWebSpringBootVue医院管理系统小程序的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏[Java 源码获取 源码获取入口 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.1微信小程序 2 2.2 …

井盖位移传感器厂家批发,守护井盖安全

窨井盖广泛分布于城市街道&#xff0c;其管理效果直接反映了城市治理的现代化程度。根据住房和城乡建设部发布的《关于进一步加强城市窨井盖安全管理的通知》&#xff0c;全国各地需加强窨井盖的安全管理。作为市政基础设施的一个重要的组成部分&#xff0c;井盖的管理工作不仅…

去水印网站哪个好?试试这个去水印软件!

在工作中&#xff0c;我们都曾遇到过图片水印的困扰。在众多的在线水印去除工具中&#xff0c;虽然选择看似丰富&#xff0c;但往往很难找到完全满足我们需求的那一个。有些工具操作过程繁复&#xff0c;有些工具在处理复杂水印时力不从心&#xff0c;还有些工具在去水印的过程…

【Spring日志】

一.日志作用 1.定位和发现问题 这是日志的主要用途,通过查看日志,我们可以定位问题发生的位置,从而快速的发现问题,分析问题. 2.系统监控 监控几乎是一个成熟系统的标配,我们可以通过日志记录这个系统的运行状态,比如记录方法的响应时间,响应状态,通过设置不同的规则,超过阈值就…

葡萄酒按酒体如何分类,都有什么特点?

葡萄酒的酒体是指酒液在口腔中的饱满度和分量感&#xff0c;品酒者常用“轻盈”“厚重”“适中”等词汇来形容。所以&#xff0c;云仓酒庄的品牌雷盛红酒分享在葡萄酒分类中还有一个类型&#xff0c;就是按照酒体进行分类。一般分为轻盈型、中等型、饱满型。 轻盈型&#xff1…

海外https代理ip如何保障信息安全?该怎么选择?

海外https代理ip是指通信协议为https的海外真实网络地址ip&#xff0c;通常应用在各种跨境业务中。 一、什么是HTTPS协议 HTTP协议是一个应用层协议&#xff0c;通常运行在TCP协议之上。它是一个明文协议&#xff0c;客户端发起请求&#xff0c;服务端给出响应的响应。由于网…

表单邮箱密码登录 原生+Jquery实现

文章目录 效果代码邮箱验证正则表达式HTMLCSS JS 效果 正确密码为&#xff1a;123456 点击登录按钮校验。 代码 表单校验 - CodeSandbox 邮箱验证正则表达式 /(?:[a-z0-9!#$%&*/?^_{|}~-](?:\.[a-z0-9!#$%&*/?^_{|}~-])*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1…

Excel表中合并两个Sheet的方法?

按AltF11&#xff0c;调出Visual Basic 界面。 在左侧窗口中&#xff0c;右键选择“插入”—“模块”&#xff1a; 将如下代码粘贴进去&#xff0c;点击运行按钮&#xff0c;完成数据表合并。 Sub MergeAllSheetsInThisWorkbook() On Error Resume Next Application.ScreenU…

JOSEF约瑟 热过载保护继电器 JR36-160,整定值100-160A

系列型号 JR36-20 1.0-1.6A热继电器 JR36-20 0.25-0.35A热继电器 JR36-20 0.32-0.5A热继电器 JR36-20 0.45-0.72A热继电器 JR36-20 0.68-1.1A热继电器 JR36-20 1.5-2.4A热继电器 JR36-20 2.2-3.5A热继电器 JR36-20 3.2-5A热继电器 JR36-20 4.5-7.2A热继电器 JR36-20 …