STM32技能综合巩固

一、深入理解ARMCPU架构及其指令格式、ARM汇编语言编程方法

1.汇编语言编程,实现LED灯

新建keil项目,选择芯片

选择运行环境以及配置

添加.s文件

汇编程序:

AREAMYDATA,DATA

AREAMYCODE,CODE

ENTRY

EXPORT__main

__main

MOVR0,#10

MOVR1,#11

MOVR2,#12

MOVR3,#13

;LDRR0,=func01

BL func01

;LDRR1,=func02

BL func02

BL func03

LDRLR,=func01

LDRPC,=func03

B.

func01

MOVR5,#05

BXLR

func02

MOVR6,#06

BXLR

func03

MOVR7,#07

MOVR8,#08

BXLR

点亮PC13程序:

LED0

EQU

0x4001100C

RCC_APB2ENR

EQU

0x40021018

GPIOC_CRH

EQU

0x40011004

Stack_Size

EQU

0x00000400

AREA STACK,NOINIT,READWRITE,ALIGN=3

Stack_Mem

SPACE Stack_Size

__initial_sp

AREA RESET,DATA,READONLY

__Vectors

DCD

__initial_sp

; Top of Stack

DCD

Reset_Handler

; Reset Handler

AREA |.text|, CODE, READONLY

THUMB

REQUIRE8

PRESERVE8

ENTRY

Reset_Handler

BL

LED_Init

MainLoop

BL LED_ON

BL Delay

BL LED_OFF

BL Delay

B MainLoop

LED_Init

PUSH {R0,R1,LR}

LDR R0,=RCC_APB2ENR

LDR R0,[R0]

ORR R0,R0,#0x10

LDR R1,=RCC_APB2ENR

STR R0,[R1]

LDR R0,=GPIOC_CRH

LDR R0,[R0]

BIC R0,R0,#0xF0000

ORR R0,R0,#0x30000

LDR R1,=GPIOC_CRH

STR R0,[R1]

MOV R0,#1

(LED灭)

LSL R0,R0,#13

LDR R1,=LED0

STR R0,[R1]

POP {R0,R1,PC}

LED_ON

PUSH {R0,R1,LR}

MOV R0,#0

LDR R1,=LED0

STR R0,[R1]

POP {R0,R1,PC}

LED_OFF

PUSH {R0,R1,LR}

MOV R0,#1

LSL R0,R0,#13

LDR R1,=LED0

STR R0,[R1]

POP {R0,R1,PC}

Delay

PUSH {R0-R2,LR}

MOVS R0,#0

MOVS R1,#0

MOVS R2,#0

DelayLoop0

ADDS R0,R0,#1

CMP R0,#330

BCC DelayLoop0

MOVS R0,#0

ADDS R1,R1,#1

CMP R1,#330

BCC DelayLoop0

MOVS R0,#0

MOVS R1,#0

ADDS R2,R2,#1

CMP R2,#15

BCC DelayLoop0

POP {R0-R2,PC}

END

2.对比C语言寄存器

C语言寄存器模式代码:

#include "stm32f10x.h"

void delay(u16 num)

{

u16 i,j;

for(i=0;i<num;i++)

for(j=0;j<0x800;j++);

}

int main(void)

{

RCC->APB2ENR = 0x0000001C;

while(1)

{

GPIOB->ODR &= ~(1 << 5);

delay(100);

GPIOB->ODR |= (1 << 5);

delay(200);

}

}

分析:

汇编语言的hex比C语言寄存器代码小。

汇编语言直接使用CPU指令操作寄存器,而C语言寄存器经过编译后会产生额外的指令。C代码编译后会自动添加一些启动和初始化代码。纯汇编需要手动定义程序入口和段属性。

二、熟悉ARM 汇编语言与C语言混合编程方式,掌握反汇编工具

1.在Keil中修改C程序的 主函数为其他名称

C程序主函数名并非必须为main。在ARM开发中,入口函数的名称可以自定义。

在 startup 文件中修改入口点声明

2.IDA Pro工具

加载hex/bin文件

使用 IDA Pro 对给定的 hex(bin)文件进行反汇编分析,找出控制LED 灯闪烁的相关代码段。这可能涉及对GPIO端口配置、延时函数以及LED状态 切换等相关代码的识别。在反汇编代码中,查找与(PB0)端口操作相关的指令, 如GPIO初始化、输出电平设置以及可能的延时循环等部分。

在 IDA Pro 中对反汇编后的代码进行修改,将与 PC13 相关的操作改为针 对 PA1 的操作。修改完成后,使用 IDA Pro 的重新汇编功能或其他相关工具 将修改后的汇编代码转换为二进制文件。将生成的二进制文件下载到目标硬件 平台进行验证,观察 PA1 LED 灯是否按照预期进行闪烁。在验证过程中,可 使用示波器、逻辑分析仪等工具监测 PA1 端口的电平变化,确保程序功能修 改正确。

源代码:

IDA汇编:

三、掌握gcc编译工具集编译、链接源程序生成二进制应用程序

1.配置C/C++环境

安装gcc:sudo apt-get install gcc

安装g++:sudo apt-get install g++

安装gbd:sudo apt-get install gdb

2.安装、配置VScode

3.安装、配置arm-none-eabi-gcc 交叉编译工具链

下载解压完成后,进入 "arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi"文件夹进入"bin"文件夹。

打开profile 文件,在最后输入:

export PATH=$PATH:/home/yml/mondrian/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi/bin

执行:source profile

4.安装CubeMX

测试程序

四、了解ARM 应用程序的上电复位初始化、startup启动到跳转到C语言main 函数入库的过程

1.STM32的启动过程

复位后启动模式的选择

复位方式有三种:上电复位,硬件复位和软件复位。当产生复位,并且离开

复位状态后,CM3 内核做的第一件事就是读取下列两个32 位整数的值:

(1)从地址 0x00000000 处取出堆栈指针 MSP 的初始值,该值就是栈顶

地址。

(2)从地址 0x00000004 处取出程序计数器指针 PC 的初始值,该值指向

复位后执行的第一条指令。

内核在离开复位状态后会从映射的地址中取值给栈指针MSP及程序指针 PC,然后执行指令,我们一般以存储器的类型来区分自举过程,例如内部FLASH 启动方式、内部SRAM启动方式以及系统存储器启动方式。

内部FLASH启动方式:

当芯片上电后采样到BOOT0引脚为低电平时,0x00000000和0x00000004 地址被映射到内部FLASH的首地址0x08000000和0x08000004。因此,内核 离开复位状态后,读取内部FLASH的0x08000000地址空间存储的内容,赋值 给栈指针MSP,作为栈顶地址,再读取内部FLASH的0x08000004地址空间存 储的内容,赋值给程序指针PC,作为将要执行的第一条指令所在的地址。具备 这两个条件后,内核就可以开始从PC指向的地址中读取指令执行了。

内部SRAM启动方式:

当芯片上电后采样到BOOT0和BOOT1引脚均为高电平时, 0x00000000和0x00000004地址被映射到内部SRAM的首地址0x20000000和 0x20000004,内核从SRAM空间获取内容进行自举。 在实际应用中,由启动文件starttup_stm32f10x.s决定了0x00000000和 0x00000004地址存储什么内容,链接时,由分散加载文件(sct)决定这些内容的 绝对地址,即分配到内部FLASH还是内部SRAM。 这里的启动文件和分散加载文件有点不好理解,当说"由启动文件决定了 0x00000000和0x00000004地址存储什么内容"时,指的是启动文件定义了这两 个关键地址处的内容:一个是堆栈指针的初始值,另一个是指向复位处理程序的 指针。而"由分散加载文件决定这些内容的绝对地址"则意味着,尽管启动文件设 定了逻辑上的地址,但是具体的物理地址(即这些内容实际上位于FLASH还是 SRAM)是由分散加载文件来决定的。这允许开发者根据需要调整最终的内存布 局,同时保持启动代码的逻辑不变。

系统存储器启动方式:

当芯片上电后采样到BOOT0引脚为高电平,BOOT1为低电平时,内核将 从系统存储器的0x1FFFF000及0x1FFFF004获取MSP及PC值进行自举。系 统存储器是一段特殊的空间,用户不能访问,ST公司在芯片出厂前就在系统存 储器中固化了一段代码。因而使用系统存储器启动方式时,内核会执行该代码, 该代码运行时,会为ISP提供支持(InSystemProgram),如检测USART1/2、CAN2 及USB通讯接口传输过来的信息, 并根据这些信息更新自己内部FLASH的内 容,达到升级产品应用程序的目的,因此这种启动方式也称为ISP启动方式。

2.STM32的启动方式实验

代码:

#include "led.h"

#include "delay.h"

//#include"key.h"

#include "sys.h"

#include "usart.h"

#include <stdio.h>

#include <stdlib.h>

int k1= 1; //已初始化全局int型变量k1

int k2; //未初始化全局int型变量k2

staticint k3 = 2;//已初始化静态全局int型变量k3

staticint k4; //未初始化静态全局int型变量k4

int main(void)

{

delay_init(); //延时函数初始化

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优

先级,2位响应优先级

uart_init(115200); //串口初始化为115200

LED_Init(); //LED端口初始化

//KEY_Init(); //初始化与按键连接的硬件接口

while(1)

{

static int m1 = 2; //已初始化静态局部int型变量m1

static int m2; //未初始化静态局部int型变量m2

int i1; //未初始化局部int型变量i1

int i2; //未初始化局部int型变量i2

char *p; //未初始化局部char型指针变量p

char str[10] ="hello";//已初始化局部char型数组str

char *var1 = "123456"; //已初始化局部char型指针变量var1

char *var2 = "abcdef"; //已初始化局部char型指针变量var2

int *p1 =malloc(4); //已初始化局部int型指针变量p1

int *p2 =malloc(4); //已初始化局部int型指针变量p2

printf("栈区-变量地址\r\n");

printf("未初始化局部int型变量i1 :0x%p\r\n",&i1);

printf("未初始化局部int型变量i2 :0x%p\r\n",&i2);

printf("未初始化局部char型指针变量p :0x%p\r\n",&p);

printf("已初始化局部char型数组str :0x%p\r\n",str);

//test();

printf("\n堆区-动态申请地址\r\n");

printf("已初始化局部int型指针变量p1 :0x%p\r\n",p1);

printf("已初始化局部int型指针变量p2 :0x%p\r\n",p2);

printf("\n.bss段地址\r\n");

printf("未初始化全局int型变量k2 :0x%p\r\n",&k2);

printf("未初始化静态全局int型变量k4 :0x%p\r\n",&k4);

printf("未初始化静态局部int型变量m2 :0x%p\r\n",&m2);

printf("\n.data段地址\r\n");

printf("已初始化全局int型变量k1 :0x%p\r\n",&k1);

printf("已初始化静态全局int型变量k3 :0x%p\r\n",&k3);

printf("已初始化静态局部int型变量m1 :0x%p\r\n",&m1);

printf("\n常量区地址\r\n");

printf("已初始化局部char型指针变量var1:0x%p\r\n",var1);

printf("已初始化局部char型指针变量var2:0x%p\r\n",var2);

printf("\n代码区地址\r\n");

printf("程序代码区main函数入口地址:0x%p\r\n", &main);

free(p1);

free(p2);

}

}

串口输出结果:

从中可以分析,main函数入口地址、常量区及代码区的存储地址都在 0x8000000开始的,说明程序成功从FALSH运行;全局和静态变量、栈区的地 址也都分配在0x20000000储存地址(高地址)处,堆区的地址分配在0x00000000 处。

相关推荐
可待电子单片机设计定制(论文)18 分钟前
【STM32设计】数控直流稳压电源的设计与实现(实物+资料+论文)
stm32·嵌入式硬件·mongodb
march_birds1 小时前
FreeRTOS 与 RT-Thread 事件组对比分析
c语言·单片机·算法·系统架构
小麦嵌入式1 小时前
Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践
linux·c语言·驱动开发·stm32·嵌入式硬件·物联网·ubuntu
触角010100013 小时前
STM32F103低功耗模式深度解析:从理论到应用实践(上) | 零基础入门STM32第九十二步
驱动开发·stm32·单片机·嵌入式硬件·物联网
昊虹AI笔记3 小时前
使用STM32CubeMX和Keil在STM32上创建并运行一个简单的FreeRTOS多任务程序
stm32·单片机·嵌入式硬件
王光环4 小时前
单片机使用printf,不用微库
单片机·嵌入式硬件
LS_learner4 小时前
小智机器人关键函数解析,Application::OutputAudio()处理音频数据的输出的函数
人工智能·嵌入式硬件
西城微科方案开发5 小时前
体重秤PCBA电路方案组成结构
单片机·嵌入式硬件
深圳市青牛科技实业有限公司5 小时前
「青牛科技 」GC4931P/4938/4939 12-24V三相有感电机驱动芯片 对标Allegro A4931/瑞盟MS4931
科技·单片机·扫地机器人吸尘·筋膜枪电机·驱动轮电机·服务机器人驱动轮电机·工业机器人减速电机
集和诚JHCTECH5 小时前
集和诚携手Intel重磅发布BRAV-7820边缘计算新品,为车路云一体化场景提供强大算力支撑
人工智能·嵌入式硬件·边缘计算