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

这篇具有很好参考价值的文章主要介绍了【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🐱作者:一只大喵咪1201
🐱专栏:《理解ARM架构》
🔥格言:你只管努力,剩下的交给时间!
【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide

🍠操作寄存器实现UART

🍟UART原理

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

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

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

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示,串口通信只需要三根线,发送(TXD)、接收(RXD)、地线(GND)。

  • 通信双方的TXD与对方的RXD相连。

串口发送数据是以帧格式一帧一帧来发的,帧格式由1bit起始位,8或9bit数据位,1或1.5或2bit校验位,1bit停止位组成。

  • 通常情况下都使用8bit数据位,不适用校验位,这样的一帧数据有10个bit。

校验位又叫奇偶校验位,如果8个数据位加校验位中比特为位1的个数是奇数,校验位就是1,否则就是0。

由于现在电子技术的逐渐成熟,串口通信很少出错,所以校验位使用的不多。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示是一帧数据传送时的逻辑电平示意图。

  • 发送方将自己的TXD线从高电平拉到低电平,保持一段时间,接收方读取到自己的RXD线由高到底以后就知道要接收数据了。
  • 发送方按照自己发送的这个字节,从低位开始,改变TXD线的电平,每改变一次保持一段时间,如此反复8次完成一字节数据的发送。
  • 接收方在自己RXD线上的电平保持期间的中间时刻,根据电平状态记录该比特位的值,最后组合成一字节数据。
  • 发送方将一字节数据发送完毕后,将自己的TXD线拉高方便下次发送数据,接收方在接收到8bit数据以后,并且检测到自己RXD线是高电平,就知道这一帧数据传送完毕了。

上面描述数据发送过程中电平维持的时间,就是根据波特率来确定的,一般选波特率都会有9600,19200,115200等选项。

  • 波特率:可以简单理解为,串口通信过程中1秒钟能发送的比特位个数。
  • 波特率是通信双方约定好的,一个按照这个速度发送数据,另一个按照这个速度接收数据。

逻辑电平:

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

如上图所示是本喵使用的ARM开发板串口发出的电平信号,在xV至5V之间,就认为是逻辑1,在0V至yV之间就为逻辑0,这叫做TTL/CMOS逻辑电平

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示是RS-232逻辑电平,在-12V至-3V之间,就认为是逻辑1,在+3V至+12V之间就为逻辑0,RS-232的电平比TTL/CMOS高,能传输更远的距离,在工业上用得比较多。

可以看到,RS-232与TTL/CMOS相同逻辑电平对应的真实电压正负是相反的。


【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示,ARM芯片上的串口都是TTL电平的,通过板子上或者外接的电平转换芯片,转成RS232接口,连接到电脑的RS232串口上,实现两者的数据传输。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示,现在的电脑越来越少有RS232串口的接口,但USB是几乎都有的。因此使用USB串口芯片将ARM芯片上的TTL电平转换成USB串口协议,即可通过USB与电脑数据传输。

  • 无论那种接口,板子上的芯片IO口输出的都是TTL/CMOS电平,我们在写程序时仅需要关心输出的逻辑电平即可。

🍟编程

一款ARM芯片上会有多个USART串口,一般UART1用来输出调试信息,这里本喵也使用USART1。

确定引脚:

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图,本喵使用的STMF103ZET6芯片上,USART1的USART1_RX、USART1_TX,接到了PA10、PA9。

将引脚配置为UART功能:

  • 使能GPIOA/USART1模块

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图是,RCC_APB2ENR寄存器,GPIOA模块、USART1模块的使能都是在这一个寄存器里实现。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图,从芯片手册中查看Reset and clock control RCC寄存器的基地址是0x40021000,再根据RCC_APB2ENR的偏移地址0x18得到该寄存器的绝对地址是0x40021000 + 0x18

将该寄存器的bit2和bit14写一,此时就使能了GPIOA和USART1模块。

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

  • 配置引脚功能

从上面的芯片原理图可以知道,PA9、PA10有三种功能:GPIO、USART1、TIMER1,所以这里要将其配置为USAT1功能。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示GPIOx_CRH寄存器,该寄存器的绝对地址是0x40010800 + 0x04,PA9配置为输出,所以将MODE9代表的bit4和bit5配置成01,将CNF9代表的bit6和bit7配置为10

PA10配置为输入,将MODE10代表的bit8和bit9配置为00,再将CNF10代表的bit10和bit11配置成01

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

由于这里仅使能了USART1,没有使能定时器,所以PA9和PA10的默认复用功能就是USART1,使用默认值即可。

设置串口参数:

  • 设置波特率

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示是波特率的计算公式,USARTDIV由整数部分、小数部分组成,USARTDIV = DIV_Mantissa + (DIV_Fraction / 16) 。fck是内部时钟频率,这里就使用默认值,是8MHZ。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图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有点差距,但是并不影响。

  • 设置数据格式

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示USART1_CR1寄存器,本喵将帧格式设置为1个起始位,8个数据位,无校验位,1个停止位,所以将bit13设置1,bit12设置为0,bit10设置为0,bit3设置为1,bit2设置为1。

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

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示USART_CR2寄存器,将bit12~bit13设置为00,表示1个停止位。

根据状态寄存器读写数据:

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示串口模块结构图,发送有一个发送数据寄存器和发送移位寄存器,接收有一个接收数据寄存器和接收移位寄存器。

发送数据时,CPU将数据写入到发送数据寄存器,然后由发送移位寄存器一位一位将数据通过TXD线发送出去。

接收数据时,RXD线上的数据一位一位放入接收移位寄存器,该寄存器接收完毕后将整个字节数据放入到接收数据寄存器,CPU从接收数据寄存器中可以直接读取数据。

  • 状态寄存器

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示USART_SR状态寄存器,TXE表示发送数据寄存器是否为空,该位并不能说明数据已经发送完了,因为真正发送数据的是移位寄存器,只能说发送数据寄存器将数据给了移位寄存器,CPU可以再向数据寄存器中写数据了。

TC表示发送数据完成,即发送数据寄存器和移位寄存器中的数据都发送完毕了。RXNE表示接收数据寄存器中有数据了,说明已经接收到了数据,CPU可以来读取了。

  • 数据寄存器

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示USART_DR寄存器,写、读这个寄存器,就可以发送、读取串口数据。


在配置完引脚和功能选择以后,本喵在介绍USART_XXX寄存器的时候并没有说它的地址,因为无论是设置波特率的USART_BRR,还是设置数据格式的USART_CR1,再或者状态寄存器USART_SR,以及数据寄存器USART_DR这些都是以USART为基地址的。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示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模块的偏移量相对应,此时就可以通过访问这个结构体访问到各个寄存器。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示是整个串口的初始化代码,其中配置波特率等参数使用的是结构体访问的寄存器,串口结构体是一个局部变量。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示,定义发送一个字符和接收一个字符的函数,通过判断状态寄存器的值,进而读写DR寄存器,也是通过结构体访问的寄存器,结构体是一个局部变量。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图,此时将程序烧录到开发板以后,会通过串口发送Hello字符串,在PC端发送一个字符,板子接收到以后返回该字符及下一个字符,此时我们的串口是配置好了。

问题:为什么每个函数中都得创建一个uart1结构体局部变量,而不是创建全局变量供这些函数使用呢?

🍠段的概念

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示,增加三个函数,用来打印字符串及变量的地址。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示,创建四个全局变量,g_ConstChar被const修饰,然后在mymain中分别打印四个变量的地址及它们的值。

将程序编译后烧录到开发板中,通过串口工具来观察输出的内容。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示,来看这四个变量的地址,只有g_ConstChar这个被const修饰的变量地址是位于Flash中的,其他几个变量都是位于RAM中。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图,keil中只能了Flash和RAM的起始地址,根据这两个参数很容易判断出这四个变量所处的位置。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图,再来看输出的这四个变量的值,可以看到,只有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的背后会执行一系列指令:

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图,在keil5的Output选择中勾选Create Batch File,然后重新全部编译。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图,此时在当前工程的Objects目录下会多出上面红色框中的四个文件。

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令,理解ARM架构,arm开发,架构,ide
如上图所示分别是这几个文件中的内容,都是一系列的命令行指令,用来编译和链接文件的指令,具体怎么用不用管,只需要知道有这些东西。

  • 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架构的理解。

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

要意识到,编译一个工程的背后没有那么简单。文章来源地址https://www.toymoban.com/news/detail-754619.html

到了这里,关于【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [C#] .NET8增加了Arm架构的多寄存器的查表函数(VectorTableLookup/VectorTableLookupExtension)

    作者: zyl910 发现.NET8增加了Arm架构的多寄存器的查表函数(VectorTableLookup/VectorTableLookupExtension),这给编写SIMD向量化算法带来了方便。 在学习Arm的AdvSimd(Neon)指令集时,发现它的Lookup(查表)功能,类似X86的Sse系列指令集中的字节Shuffle(换位。如 _mm_shuffle_epi8 )功能。 而

    2024年03月24日
    浏览(42)
  • 【ARM 嵌入式 C 入门及渐进 12 --寄存器位清0和置位函数实现】

    在 C 语言中,可以使用宏定义来创建用于清除(清零)或设置(置一)32位地址中特定位的函数。以下是两个宏定义的示例: 这里的 addr 是指向目标32位地址的指针, bit 是需要操作的位的索引(从0开始计数)。 示例使用方式 假设要操作的是一个具有可读写属性的寄存器,它

    2024年03月22日
    浏览(57)
  • Arm汇编---寄存器

    寄存器:r0~r15, sp, lr, sb, sl, fp, ip, pc 条件码:eq, ne, hs, lo, mi, pl, vs, vc, hi, ls, ge, lt, gt, le, al ------------------------------------------ 一、数据寄存器 --------------------------------------------- ------------------------------------------ 二、指针寄存器 --------------------------------------------- --------------------

    2024年02月02日
    浏览(47)
  • ARM 寄存器

    Cortex A 系列的 ARM 处理器共有 40 个 32 位寄存器,其中 33 个为通用寄存器,7 个为状态寄存器。用户模式和系统模式共用同一组寄存器。 一、未分组寄存器 R0~R7 有些寄存器是所有运行模式共用的,如 R0~R7,它们被称为未分组寄存器。 在所有运行模式下,未分组寄存器都指向同

    2024年02月02日
    浏览(47)
  • ARM寄存器组织

     ARM有37个32位长的寄存器: 1个用做PC(Program Counter); 1个用做CPSR(Current Program Status Register); 5个用做SPSR(Saved Program Status Registers); 30个通用寄存器。 ARM处理器共有37个寄存器,被分为若干个组(BANK),这些寄存器均为32位的寄存器。6个状态寄存器,用以标识CPU的工作状

    2024年02月01日
    浏览(78)
  • ARM寄存器组

    CM3 拥有通用寄存器 R0‐R15 以及一些特殊功能寄存器。 R0-R7也被称为低组寄存器,所有指令可以访问它们,它们的字长为32位,复位后的初始值是不可预料的。 R8-R12也被称为高组寄存器,所有指令可以访问它们,它们的字长为32位,复位后的初始值是不可预料的。 R13寄存器中

    2024年02月10日
    浏览(50)
  • ARM处理器有哪些工作模式和寄存器?各寄存器作用是什么?ARM异常中断处理流程?

    快速学习嵌入式开发其他基础知识? 返回专栏总目录 《嵌入式工程师自我修养/C语言》 Tip📌:鼠标悬停双虚线/句,可获得更详细的描述   ARM处理器有多种工作模式,如下表所示。应用程序正常运行时,ARM处理器工作在 用户模式(User mode) ,当程序运行出错或有中

    2024年02月21日
    浏览(133)
  • ARM中的寄存器

    ARM工作模式 ARM有8个基本的工作模式 User 非特权模式,一般在执行上层的应用程序时ARM处于该模式 FIQ 当一个高优先级中断产生后ARM将进入这种模式 IRQ 当一个低优先级中断产生后ARM将进入这种模式 SVC 当复位或执行软中断指令后ARM将进入这种模式 Abort 当产生存取异常时ARM将进

    2024年02月03日
    浏览(42)
  • 12.3 ARM寄存器组织

    目录 ARM寄存器组织(一) 寄存器 概念 作用 分类 ARM寄存器 ARM寄存器组织(二) 专用寄存器 R15(PC,Program Counter) R14(LR,Link Register) R13(SP,Stack Pointer) ARM寄存器组织(三) CPSR寄存器 ARM寄存器组织(一) 寄存器 概念 寄存器是处理器内部的存储器,没有地址 C语言中register存储在寄

    2024年02月13日
    浏览(40)
  • ARM编程模型-寄存器组

    Cortex A系列ARM处理器共有40个32位寄存器,其中33个为通用寄存器,7个为状态寄存器。usr模式和sys模式共用同一组寄存器。 通用寄存器包括R0~R15,可以分为3类: 未分组寄存器R0~R7 分组寄存器R8~R14、R13(SP) 、R14(LR) 程序计数器PC(R15)、R8_fiq-R12_fir为快中断独有 在不同模式下,名称相同的

    2024年02月10日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包