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

news2024/11/16 19:54:06

🐱作者:一只大喵咪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.coloradmin.cn/o/1243527.html

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

相关文章

数据结构(超详细讲解!!)第二十四节 二叉树(下)

1.遍历二叉树 在二叉树的一些应用中&#xff0c;常常要求在树中查找具有某种特征的结点&#xff0c;或者对树中全部结点逐一进行某种处理。这就引入了遍历二叉树的问题&#xff0c;即如何按某条搜索路径访问树中的每一个结点&#xff0c;使得每一个结点仅且仅被访问一次。 …

Vue3 响应式数据 reactive使用

ref 与 reactive 是 vue3 提供给我们用于创建响应式数据的两个方法。 reactive 常用于创建引用数据&#xff0c;例如&#xff1a;object、array 等。 reactive 则是通过 proxy 来实现的响应式数据&#xff0c;并配合 reflect 操作的源对象。 reactive 创建引用数据&#xff1…

外汇天眼:外汇市场中的点差是什么? 又该怎么计算呢?

今天为大家揭开外汇点差的神秘面纱&#xff0c;了解这一外汇交易的核心概念。 定义 外汇点差&#xff0c;简单来说&#xff0c;就是外汇市场上买卖双方报价的差异。 每一笔交易由买卖报价中高低不同的部分构成&#xff0c;高出的部分是买方的盈利&#xff0c;低出的部分则是卖…

docker部署微服务

目录 docker操作命令 镜像操作命令 拉取镜像 导出镜像 删除镜像 加载镜像 推送镜像 部署 pom文件加上 在每个模块根目录加上DockerFile文件 项目根目录加上docker-compose.yml文件 打包&#xff0c;clean&#xff0c;package 服务器上新建文件夹 测试docker-compo…

OpenStack云计算平台-Dashboard(图形化)

目录 一、安装和配置 1、安全并配置组件 2、完成安装 ​二、验证操作 一、安装和配置 1、安全并配置组件 安装软件包&#xff1a; yum install openstack-dashboard 编辑文件 vim /etc/openstack-dashboard/local_settings vim /etc/httpd/conf.d/openstack-dashboard.…

PPP/INS紧组合算法

前言&#xff1a;在学习紧组合之前学会GNSS/INS松组合是很有必要的&#xff0c;i2NAV团队开源的KF_GINS项目可以作为GNSS/INS松组合学习模板&#xff0c;本文章主要对武汉大学i2NAV发布的PPP/INS紧组合学习资源进行算法层面的总结&#xff0c;链接&#xff1a; 武汉大学多源智…

聚类笔记/sklearn笔记:Affinity Propagation亲和力传播

1 算法原理 1.1 基本思想 将全部数据点都当作潜在的聚类中心(称之为 exemplar )然后数据点两两之间连线构成一个网络( 相似度矩阵 )再通过网络中各条边的消息( responsibility 和 availability )传递计算出各样本的聚类中心。 1.2 主要概念 Examplar聚类中心similarity S(i…

软件工程——数据流图(20分把握在自己手里)【言简意赅】

数据流图【DFD -> Data Flow Diagram】 确定外部实体&#xff1a; 在一个对于某系统的描述中&#xff0c;我们需要分辨的是&#xff0c;该系统的使用人员(或外部设备)&#xff0c;以及系统所反馈的人员(或外部设备)是谁&#xff1f; 这就是外部实体&#xff01;与系统内部处…

新手必看!!附源码!!STM32通用定时器-比较输出PWM

一、什么是PWM? PWM&#xff08;脉冲宽度调制&#xff09;是一种用于控制电子设备的技术。它通过调整信号的脉冲宽度来控制电压的平均值。PWM常用于调节电机速度、控制LED亮度、产生模拟信号等应用。 二、PWM的原理 PWM的基本原理是通过以一定频率产生的脉冲信号&#xff0…

使用Wireshark提取流量中图片方法

0.前言 记得一次CTF当中有一题是给了一个pcapng格式的流量包&#xff0c;flag好像在某个响应中的图片里。比较简单&#xff0c;后来也遇到过类似的情况&#xff0c;所以总结和记录一下使用Wireshark提取图片的方法。 提取的前提是HTTP协议&#xff0c;至于HTTPS的协议需要导入服…

uni-app打包后,打开软件时使其横屏显示

找到page.json文件&#xff0c;在global加入以下代码&#xff1a; 这样就可以横屏显示了。

走迷宫(BFS宽度优先搜索)

给定一个 nm 的二维整数数组&#xff0c;用来表示一个迷宫&#xff0c;数组中只包含 0 或 1&#xff0c;其中 0 表示可以走的路&#xff0c;1 表示不可通过的墙壁。 最初&#xff0c;有一个人位于左上角 (1,1)处&#xff0c;已知该人每次可以向上、下、左、右任意一个方向移动…

机器学习实战-第2章 k-近邻算法

KNN 概述 k-近邻(kNN, k-NearestNeighbor)算法是一种基本分类与回归方法,我们这里只讨论分类问题中的 k-近邻算法。 一句话总结: 近朱者赤近墨者黑! k 近邻算法的输入为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多类。k 近邻算法假设给定一个训练数…

掌握高效性能测试技能:JMeter基础入门!

一、JMeter基础 A、JMeter介绍 Apache JMeter是Apache组织开发的基于Java的压力测试工具。 Apache JMeter may be used to test performance both on static and dynamic resources (files, Servlets, Perl scripts, Java Objects, Data Bases and Queries, FTP Servers and …

可视化NGINX管理平台Nginx Proxy Manager

# for CentOSyum install docker-compose -y# for Ubuntuapt-get install docker-compose -y 如果提示&#xff1a; 没有可用软件包 docker-compose&#xff0c; 错误&#xff1a;无须任何处理 通过 pip 安装 docker-compose # 添加企业版附加包 yum -y install epel-rel…

二次开发问题汇总【C#】

1未将对象引用到实例。 接口函数的参数不对。解决办法【用fixed去限制数组长度】 unsafe public struct VCI_BOARD_INFO {public UInt16 hw_Version;public UInt16 fw_Version;public UInt16 dr_Version;public UInt16 in_Version;public UInt16 irq_Num;public byte can_Num;…

【实战精选】掌握图像风格迁移:构建基于生成对抗网络的系统

1.研究背景与意义 随着计算机技术的不断发展&#xff0c;图像处理和计算机视觉领域取得了长足的进步。图像风格迁移是其中一个备受关注的研究方向&#xff0c;它可以将一幅图像的风格特征应用到另一幅图像上&#xff0c;从而创造出新的图像。这项技术具有广泛的应用前景&#…

分布式链路追踪入门篇-基础原理与快速应用

为什么需要链路追踪&#xff1f; 我们程序员在日常工作中&#xff0c;最常做事情之一就是修bug了。如果程序只是运行在单机上&#xff0c;我们最常用的方式就是在程序上打日志&#xff0c;然后程序运行的过程中将日志输出到文件上&#xff0c;然后我们根据日志去推断程序是哪一…

随笔记录-springmvc_ResourceHandlerRegistry+ResourceHttpRequestHandler

环境&#xff1a;springboot-2.7.5 配置文件配置静态资源映射 springboot配置静态资源映射方式是通过 WebMvcAutoConfiguration 实现的 spring: # resources: # # 自springboot 2.5.5之后&#xff0c;该属性已经被废弃&#xff0c;使用spring.web.resources.static-locat…