C与汇编深入分析

汇编怎么调用C函数

直接调用

BL main

传参数
在arm中有个ATPCS规则(ARM-THUMB procedure call standard)(ARM-Thumb过程调用标准)。

约定r0-r15寄存器的用途:

  • r0-r3:调用者和被调用者之间传递参数
  • r4-r11:函数可能被使用,所以在函数的入口保存它们,在函数的出口恢复它们。

在这里插入图片描述

int delay(unsigned int d)
{while(d--);return 0;
}
ldr r0,=1000000
dl delay
cmp r0,#0 

返回值保存在r0

A函数调用B函数
假设A函数里需要用到R4寄存器,B函数里也需要用到R4寄存器。
因此B函数就有会覆盖A函数R4寄存器的值。

于是需要在B函数执行前,保存R4-R11寄存器的值在A的栈中 。
B函数执行完后,返回A函数前,要从A的栈中恢复R4-R11寄存器。

C函数的反汇编码阅读

要解决几个问题:

  • 为什么调用C函数前要设置栈?栈的作用是?
  • C函数传参
  • C函数执行过程

在这里插入图片描述

反汇编示例

在这里插入图片描述
地址0x08000010,Flash上烧写20010000。

启动流程

上电后:

  • 设置栈:CPU会从0x08000000读取值,用来设置SP(我们的程序里再次设置了SP)。
  • 跳转:CPU会从0x08000004得到地址值,根据它的BIT0切换到ARM状态或Thumb状态,然后跳转。

对于Cortex M3/M4,它只支持Thumb状态,所以0x08000004上的值bit0必定是1。
对0x08000004上的值=Reset_Handler+1。
从Reset_Handler上继续执行

纯汇编点灯

在这里插入图片描述
在这里插入图片描述

写程序

寄存器操作
对于寄存器的操作,主要涉及读、修改、写。

  • 读可以使用LDR指令,代码为LDR R1,[R0]
  • 写使用STR指令,代码为STR R1,[R0]
  • 修改:清除位使用BIC或AND指令,设置位使用ORR指令
LDR R0, =(1<<20) | (1<<21)
BIC R1,R1,R0 ;清除R1的bit20,bit21
LDR R0, =(1<<20)
ORR R1,R1,R0 ;设置R1的bit20
  • 函数里的条件判断
    比如减1操作,代码为SUB R0,R0,#1
    顺便使用减1后的结果影响程序状态寄存器,代码为SUBS R0,R0,#1
  • 程序的调用与返回值
    传参,代码为LDR R0,=VAL
    调用,代码为BL delay,它顺便把下一条指令的地址保存在LR寄存器了。
    返回,代码为MOV PC,LR

使用按键控制LED

在这里插入图片描述
先看原理图,我们使用KEY1来控制红色LED,按下KEY1在灯亮,松开后灯灭。
KEY1的引脚是PA0

使能GPIOA模块,RCC_APB2ENR:0x40021000+0x18

串口的硬件介绍

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

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

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

通过三根线即可,发送、接收、地线。
在这里插入图片描述
TXD线把PC机要发送的信息发送给ARM开发板。
最下面的地线统一参考地。

串口的参数

  • 波特率:一般选波特率都会有9600、19200、115200等选项。其实意思就是每秒传输这么多个比特位数(bit)。
  • 起始位:先发出一个逻辑0的信号,表示传输数据的开始。
  • 数据位:可以是5~8位逻辑0或1.如ASCII码(7位)、扩展BCD码(8位)。小端传输。
  • 校验位:数据位加上这一位后,使得1的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。
  • 停止位:它是一个字符数据的结束标志。

怎么发送一字节数据,比如A?
'A’的ASCII值是0x41,二进制就是0x01000001,怎么把这8位数据发送给PC机呢?

  • 双方约定好波特率(每一位占据的时间)
  • 规定传输协议
    原来是高电平,ARM拉低电平,保持1bit时间。
    PC在低电平处开始计时。
    ARM根据数据依次驱动TXD电平,同时PC依次读取RXD电平,获得数据。
    PC机在数据位的中间读取引脚状态。

在这里插入图片描述
RS-232的电平比TTL/CMOS高,能传输更远的距离,在工业上用得比较多。

市面上大多数ARM芯片都不止一个串口,一般使用串口0来调试,其它串口来外接模块。

在这里插入图片描述
115200,8n1。
一秒钟可以传输115200个bit,传送一个字节需要:1个起始位+8个数据位+1个停止位=10bit。
所以一秒钟可以传输11520个字节。

UART编程

看原理图确定引脚

  1. 有很多串口,使用哪一个?看原理图确定。
  2. 配置引脚为UART功能
    至少用到发送、接收引脚:txd、rxd
    需要把这些引脚配置为UART功能,并使能UART模块
  3. 设置串口参数
  4. 根据状态寄存器读写数据
    肯定有一个数据寄存器,程序把数据写入,即可通过串口向外发送数据。
    肯定有一个数据寄存器,程序读取这个寄存器,就可以获得先前接收到的数据。
    很多有状态寄存器:判断数据是否发送出去?是否发送成功?判断是否接收到了数据?
    在这里插入图片描述
    需要使能GPIOA/USART1模块
    需要设置GPIOA的寄存器,选择引脚功能:所以要使能GPIOA模块。
    GPIOA模块、USART1模块的使能都是在同一个寄存器里实现。

在这里插入图片描述
在这里插入图片描述
USART寄存器,用结构体来表示比较方便:

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 
}USART_TypeDef;
USART_TypeDef *usart1 = (USART_TypeDef *)0x40013800;

在C语言中,编译器通常会对代码进行各种优化,以提高程序的执行效率。这些优化包括重新排序指令、删除看似无用的变量读取等。

然而,在与硬件通信的情景下,有些变量的值可能会被硬件或者其他事件异步地修改,而这种修改通常是在程序控制流之外发生的。

假设有一个代表硬件寄存器的变量,如果没有使用’volatile’关键字,编译器可能会进行一些优化,认为这个变量的值在程序的某个地方被设置后就不再改变。这就可能导致一些问题,因为实际上这个变量的值可能会在程序的其它地方被异步地修改。

通过在变量声明中加入’volatile’关键字,告诉编译器不要对这个变量进行过多的优化,以确保每次访问这个变量时都从内存中读取最新的值,而不是使用之前缓存的值。
这对于与硬件直接交互的变量,比如你提供的USART通信寄存器,非常重要,因为这些寄存器的值可能会在程序的正常流程之外被外部事件改变。

#ifndef __UART_H
#define __UART_Hvoid uart_init();
char getchar();
char putchar(char c);#endif

这段代码是一种常见的C/C++预处理器约定,用于防止头文件(header file)被多次包含,避免引起重定义错误。

  1. #ifndef:这是预处理指令,表示if not defined,即如果之前没有定义过指定的标识符,那么执行接下来的代码。
  2. __UART_H:这是一个宏标识符,通常是头文件名的大写形式,加上双下划线前缀以确保标识符的唯一性。在这里,它用于作为一个宏,防止头文件被多次包含。
  3. #define __UART_H:如果之前没有定义过__UART_H,那么用这个#define指令定义它。这样,当头文件再次被引用时,#ifndef检查将会失败,因为__UART_H已经被定义了。
  4. #endif:表示条件编译的结束。如果之前#ifndef条件成立,那么在这里结束条件编译块。

这种写法的目的是确保一个头文件只会被编译一次,即使它在多个地方被引用。这可以防止由于头文件被重复引用而导致的重定义错误。这是一种预防措施,用于提高代码的可维护性和可移植性。

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

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

相关文章

2023网络钓鱼状况报告:ChatGPT等工具致网络钓鱼电子邮件数量激增1265%

近日&#xff0c;SlashNext发布了《2023年网络钓鱼状况报告》&#xff0c;报告显示&#xff1a;自ChatGPT于2022年11月推出以来&#xff0c;网络钓鱼电子邮件数量激增1265%&#xff0c;这标志着网络犯罪依托于人工智能进入了一个新的时代。 该报告深入分析了2022年第四季度至2…

【第2章 Node.js基础】2.4 Node.js 全局对象(二) process 对象

process对象是一个全局对象&#xff0c;提供当前Node.js 进程信息并对其进行控制。通常用于编写本地命令行程序。 1.进程事件 process对象是EventEmitter类的实例&#xff0c;因此可以使用事件的方式来处理和监听process对象的各种事件。以下是一些常用的process对象事件&…

使用 Stable Diffusion Img2Img 生成、放大、模糊和增强

在线工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 3D数字孪生场景编辑器 Stable Diffusion 2022.1 Img5Img 于 2 年发布&#xff0c;是一款革命性的深度学习模型&#xff0c;正在重新定义和推动照片级真实…

FD-Align论文阅读

FD-Align: Feature Discrimination Alignment for Fine-tuning Pre-Trained Models in Few-Shot Learning&#xff08;NeurIPS 2023&#xff09; 主要工作是针对微调的和之前的prompt tuining&#xff0c;adapter系列对比 Motivation&#xff1a; 通过模型对虚假关联性的鲁棒…

Adobe ME下载、Media Encoder下载

Media Encoder 2021 是一款可以帮助Adobepremiere pro和Adobe After Effects的用户使用集成视频编码器进行创作的视频和音频编码软件。Media Encoder 2021 mac新版本中针对上一个版本进行了多方面的改进与优化&#xff0c;提升了软件的性能与支持文件格式提升&#xff0c;有需要…

RabbitMQ 核心部分之简单模式和工作模式

文章目录 一、Hello World&#xff08;简单&#xff09;模式1.导入依赖2.消息生产者3.消息消费者 二、Work Queues&#xff08;工作&#xff09;模式1.抽取工具类2.启动两个工作线程3.启动一个发送线程4.结果 总结 一、Hello World&#xff08;简单&#xff09;模式 在下图中&…

深入了解鼠标光标的设置过程

有一位读者问了这样一个问题&#xff1a; “为什么鼠标光标的设定绑定在窗口类&#xff0c;而不是窗口上&#xff1f;” 这个问题隐含地假设了光标与窗口类相关联。虽然每个窗口类都有一个关联的光标&#xff0c;但决定使用哪个光标的是窗口。 光标设置过程在 WM_SETCURSOR 消…

Centos上删除文件及目录的命令积累

01-如果我想删除Centos上当前目录下的文件 test06-2023-11-14-01.sql 该怎么操作&#xff1f; 答&#xff1a;如果你想删除CentOS上当前目录下的文件 test06-2023-11-14-01.sql&#xff0c;可以使用 rm 命令。以下是删除文件的基本语法&#xff1a; rm test06-2023-11-14-01.s…

设计模式之模版方法(TemplateMethod)

模版方法 钩子函数 回调函数 在父类里面有一个模版方法&#xff0c;在这个方法里面调用了op1&#xff0c;op2&#xff0c;op3… 在子类里面如果想要改变父类的op1和op2 只需要重写op1和op2&#xff0c;那么这个重写之后的方法&#xff0c;可以在父类里面直接调用的到 例子: J…

观察者模式-C++

观察者模式&#xff08;Observer&#xff09;是一种行为型设计模式&#xff0c;它用于在对象之间建立一对多的依赖关系&#xff0c;当一个对象发生改变时&#xff0c;所有依赖它的对象都会收到通知进行相应的改变。 观察者模式中有两种核心角色&#xff1a; 观察者&#xff0…

ARPG----C++学习记录05 Section10 碰撞,重叠事件

collision碰撞 Query only 仅查询。包括请求&#xff0c;扫描和重叠&#xff0c;扫描两个物体知否存在重叠Physics Only 仅物理。重力&#xff0c;反弹等物理计算都开启 按下“~”输入show collision可以查看碰撞 给石块添加碰撞&#xff0c;Query可以阻挡人物过去&#xff0c;…

基于 HarmonyOS 的 HTTPS 请求过程开发示例(ArkTS)

介绍 本篇 Codelab 基于网络模块以及 Webview 实现一次 HTTPS 请求&#xff0c;并对其过程进行抓包分析。效果如图所示&#xff1a; 相关概念 ● Webview&#xff1a;提供 Web 控制能力&#xff0c;Web 组件提供网页显示能力。 ● HTTP数据请求&#xff1a;网络管理模块&am…