Appearance
分析 LED1 灯的硬件原理图
- 在扩展板上找到 LED1 灯对应的位置,查看 LED1 灯的丝印为
LD1; - 打开扩展板的原理图,搜索
LD1,找到LD1灯对应的原理图,如下:

- 再在扩展板的原理图上搜索网络标号
LED1,如下图:

- 打开转接板的原理图,搜索网络标号
LED1,如下图所示:

- 打开主板的原理图,搜索网络标号
PE10,如下图所示:


INFO
通过对电路图的分析可以知道 LED1 灯接到了 SOC 的 PE10 引脚;
编写程序让 PE10 引脚输出高低电平就可以控制 LED 灯亮或者灯灭。
PE10的含义:p--->GPIO通用输入输出引脚E--->GPIO引脚所属的组(STM32MP1芯片的组,A-K,Z)10--->GPIOE组的第 10 个引脚(每组有 16 个 GPIO 引脚,引脚的编号为 0-15)
三极管
- 二极管:特性单向导电,正向导通,反向截止
- 三极管:特性放大特性(模拟电路)和开关特性(数字电路)
- 三极管的种类:
NPN和PNP

INFO
PE10 引脚输出高电平,NPN三极管导通,LED1灯亮;
PE10引脚输出低电平,NPN三极管截止,LED1灯灭
如何驱动 PE10 引脚输出高低电平

- CPU 核不直接控制 GPIO 引脚,而是 CPU 核通过总线和外设进行通信,有外设控制器管理 GPIO 引脚。比如,PE10 引脚由 GPIOE 外设控制器进行管理。
CPU 指令
- CPU 可以执行的指令的分类(参考ARM汇编指令):
- 数据操作指令
- 跳转指令
- 特殊功能寄存器操作指令
- 软中断指令
- Load/Store 内存读写指令
什么是特殊功能寄存器(SFR)?
- 特殊功能寄存器就是 0-4G (物理内存)寻址空间上的一块特殊的内存空间,这块内存空间具有特殊的用途,这样的内存空间称为特殊功能寄存器。
- 特殊功能寄存器的访问是通过地址进行访问的,每个特殊功能寄存器的大小为 4 字节。
- 通过向特殊功能寄存器中写入或者读取特定的值就可以间接地控制外设控制器工作。

软件编程控制硬件的思想?
- 通过程序向特殊功能寄存器中读取或者写入特定的值就可以对应的外设控制器按照一定的要求工作,这就是软件编程控制硬件的思想。
- 所有的处理器不管是单片机,还是高端处理器,软件编程控制硬件的思想都是同一的,都是向特殊功能寄存器中写入或者读取特定的值就可以控制外设控制器工作
- 不一样的点:特殊功能寄存器的地址可能不同,特殊功能寄存器的名字不同
分析芯片手册的技巧?
- 在芯片手册中每个外设控制器都有自己对应的章节进行描述如何使用这个外部控制器
- 只要明确自己的实验使用的是哪个外设控制器,就可以知道芯片手册的那个章节,
- 再根据软件编程控制影响思想的理解,重点分析手册中外设寄存器的描述
分析芯片手册的 GPIO 章节
- 实验使用的是芯片文档是:STM32MP157文档

上拉电阻和下拉电阻

N-MOS 和 P-MOS

推挽输出

开漏输出

上述电路控制的寄存器

分析特殊功能寄存器的文档
GPIOx_MODER 寄存器

- 该寄存器是 GPIO 端口模式寄存器
LED1灯接到PE10引脚上,所以按照上表其需要配置的是 GPIO_MODER 寄存器的[21:20]位- 写
Ob01配置PE10引脚为输出模式,必须保证其他位不变,其他位管理其他引脚,有其他的用途
GPIOx_OTYPER 寄存器

- 该寄存器设置 GPIO 端输出类型
LED1灯接到PE10引脚上,需要配置GPIOE_OTYTPER寄存器的[10]位- 写
0b0配置PE10引脚为推挽输出,必须保证其他位不变,其他位管理其他引脚,有其他用途
GPIOx_OSPEEDR寄存器

GPIOE_OSPEEDR: GPIO端口速度寄存器LED1灯接到PE10引脚之上,需要配置GPIOE_OSPEEDR寄存器的[21:20]位,- 写
0b00配置PE10引脚为低速模式,必须保证其他位不变,其他位管理其他引脚,有其他的用途
GPIOx_PUPDR寄存器

GPIOE_PUPDR: GPIO 端口上拉下拉寄存器LED1灯接到PE10引脚之上,需要配置GPIOE_PUPDR寄存器的[21:20]位,- 写
0b00配置PE10引脚为禁止上拉和下拉,必须保证其他位不变,其他位管理其他引脚,有其他的用途
GPIOx_IDR寄存器

GPIOE_IDRR: GPIO 端口输入数据寄存器
GPIOx_ODR寄存器

GPIOE_ODR:GPIO端口输出数据寄存器LED1灯接到PE10引脚之上,需要配置GPIOE_ODR寄存器的[10]位,- 写
0b0配置PE10引脚输出低电平,写0b1配置PE10引脚输出高电平,必须保证其他位不变,其他位管理其他引脚,有其他的用途
分析芯片手册的 RCC 章节

- 配置
RCC_MP_AHB4ENSETR寄存器的第[4]位,使能GPIOE外设控制器的时钟。 - 本次使用操作的是Cortex-A7核,属于
MPU,因此分析MPU对应的寄存器 GPIOE外设控制器接到芯片内部的AHB4总线上,因此分析AHB4总线外设对应的寄存器- 因为要使能
GPIOE外设的使用,因此分析外设的时钟使能寄存器。

分析 GPIO,RCC 外设寄存器的基地址
- 上述的文档中,对寄存器地址的描述都是偏移量,基地址需要在手册中查找
- 分析手册的 2.5.2 章节,我们已经知道特殊功能寄存器是内存的预留区域,所以直接查找内存映射表:

- 在外设区域可以找到
GPIOE寄存器的基地址,如下图:

汇编代码示例:LED1 闪烁
使用的是:A7 核 SDCARD 模式
asm
. ext
.global _start
_start:
/* 设置RCC寄存器使能 */
/* GPIOEEN --> RCC_MP_AHB4ENSET[4] */
LDR R0,=0X50000A28
LDR R1,[R0]
ORR R1,R1,#(0X1<<4)
STR R1,[R0]
/* 设置 PE10 管脚为输出模式 */
/* GPIOE_MODER --> GPIOE_MODER[21:20] */
LDR R0,=0X50006000
LDR R1,[R0]
BIC R1,R1,#(0X3<<20) @先清零
ORR R1,R1,#(0X1<<20) @再设置位
STR R1,[R0]
/* 设置 PE0 为推挽输出 */
/* GPIOE_OTYPER --> GPIOE_OTYPER[10] */
LDR R0,=0X50006004
LDR R1,[R0]
BIC R1,R1,#(0X1<<10)
STR R1,[R0]
/* 设置 PE10 速度为低速 */
/* GPIOE_OSPEEDR --> GPIOE_OSPEEDR[21:20] */
LDR R0,=0X50006008
LDR R1,[R0]
BIC R1,R1,#(0X3<<20)
STR R1,[R0]
/* 不设置上拉下拉电阻 */
/* GPIOE_PUPDR --> GPIOE_PUPDR[21:20] */
LDR R0,=0X5000600C
LDR R1,[R0]
BIC R1,R1,#(0X3<<20)
STR R1,[R0]
/* 循环输出高低电平 */
/* GPIOE_ODR --> GPIOE_ODR[10] */
LDR R0,=0X50006014
loop:
@亮一秒
LDR R1,[R0]
ORR R1,R1,#(0X1<<10)
STR R1,[R0]
bl delay_1s
@灭一秒
LDR R1,[R0]
BIC R1,R1,#(0X1<<10)
STR R1,[R0]
bl delay_1s
b loop
/* 1s左右的延时函数 */
delay_1s:
mov r3, #0x10000000
mm:
cmp r3, #0
subne r3, r3, #1
bne mm
mov pc, lr
.endMakefile
#定义变量
NAME=asm-led
# 定义交叉编译器的前缀的变量
CROSS_COMPILE = arm-linux-gnueabihf-
# arm-linux-gnueabihf-gcc : 交叉编译器
CC = $(CROSS_COMPILE)gcc
# arm-linux-gnueabihf-ld : 链接器
LD = $(CROSS_COMPILE)ld
# arm-linux-gnueabihf-objcopy : 格式化工具
OBJCOPY = $(CROSS_COMPILE)objcopy
# arm-linux-gnueabihf-objdump : 反汇编的工具
OBJDUMP = $(CROSS_COMPILE)objdump
#目标:依赖
# (tab键)命令
all:
@# arm-linux-gnueabihf-gcc : 编译器,编译源文件
@# -O0 : 代码的优化等级,不优化
@# -g : 添加gdb的调试信息
@# -c : 只编译不链接
$(CC) -O0 -g -c $(NAME).S -o $(NAME).o
@# arm-linux-gnueabihf-ld : 链接器,将.o文件链接生成.elf文件
@# -Ttext=0xC0008000 :代码段中第一条指令的地址为0xc0008000
$(LD) -Ttext=0xC0008000 $(NAME).o -o $(NAME).elf
@# arm-linux-gnueabihf-objcopy : 格式化工具,将elf文件转换为bin文件
@# bin文件是一个纯粹的二进制文件
@# -O binary : 输出二进制文件
$(OBJCOPY) -O binary $(NAME).elf $(NAME).bin
@# arm-linux-gnueabihf-objdump : 反汇编的命令, 将elf文件转换为.dis反汇编文件
$(OBJDUMP) -D $(NAME).elf > $(NAME).dis
clean:
rm -rf *.elf *.bin *.o *.dis
install:
@# 将.bin拷贝到共享文件夹中,需要修改为自己的共享文件夹的路径
sudo cp $(NAME).bin /mnt/hgfs/Ubuntu_Share汇编代码示例:LED1~3 流水线闪烁
txt
LED2->PF10 基地址:0X50007000
GPIOF_MODER[21:20]:01 //输出
GPIOF_OTYPER[10]:0 //推挽输出
GPIOF_OSPEEDR[21:20]:00 //低速输出
GPIOF_PUPDR[21:20]:00 //没有上拉下拉电阻
GPIOF_ODR[10]: 0(输出低电平) 1(输出高电平)
LED3->PE8
GPIOE_MODER[17:16]:01 //输出
GPIOE_OTYPER[8]:0 //推挽输出
GPIOE_OSPEEDR[17:16]:00 //低速输出
GPIOE_PUPDR[17:16]:00 //没有上拉下拉电阻
GPIOE_ODR[8]: 0(输出低电平) 1(输出高电平)asm
/*
LED1接到PE10引脚,对应的总线为AHB4
*/
.text
.global _start
_start:
/* LED1的初始化(PE10) */
/* 1.1 使能GPIOE外设控制器的时钟
RCC_MP_AHB4ENSETR[4] 0x50000000+0xA28=0x50000A28 写0b1 */
LDR R0, =0x50000A28
LDR R1, [R0]
ORR R1, R1, #(0x1<<4)
STR R1, [R0]
/* 1.2 设置PE10引脚为输出模式
GPIOx_MODER[21:20] 0x50006000+0x00=0x50006000 写0b01 */
LDR R0, =0x50006000
LDR R1, [R0]
@写0b01:先清零,后置1
AND R1, R1, #(~(0x3 << 20)) @清零
ORR R1, R1, #(0x1 << 20) @置1
STR R1, [R0]
/* 1.3 设置PE10引脚为推挽输出
GPIOx_OTYPER[10] 0x50006000+0x04=0x50006004 写0b0 */
LDR R0, =0x50006004
LDR R1, [R0]
AND R1, R1, #(0x1 << 10)
STR R1, [R0]
/* 1.4 设置PE10引脚为低速模式
GPIOx_OSPEEDR[21:20] 0x50006000+0x08=0x50006008 写0b00 */
LDR R0, =0x50006008
LDR R1, [R0]
AND R1, R1, #(~(0x3 << 20))
STR R1, [R0]
/* 1.5 设置PE10引脚禁止上拉和下拉电阻
GPIOx_PUPDR[21:20] 0x50006000+0x0C=0x5000600C 写0b00 */
LDR R0, =0x5000600C
LDR R1, [R0]
AND R1, R1, #(~(0x3 << 20))
STR R1, [R0]
/* LED1的初始化完成 */
/* LED2的初始化(PF10) */
/* 1.1 使能GPIOF外设控制器的时钟
RCC_MP_AHB4ENSETR[5] 0x50000000+0xA28=0x50000A28 写0b1 */
LDR R0, =0x50000A28
LDR R1, [R0]
ORR R1, #(0X1 << 5)
STR R1, [R0]
/* 1.2 设置PF10引脚为输出模式
GPIOx_MODER[21:20] 0x50007000+0x00=0x50007000 写0b01 */
LDR R0, =0x50007000
LDR R1, [R0]
@写0b01:先清零,后置1
AND R1, R1, #(~(0x3<<20)) @清零
ORR R1, R1, #(0x1 << 20) @置1
STR R1, [R0]
/* 1.3 设置PF10引脚为推挽输出
GPIOx_OTYPER[10] 0x50007000+0x04=0x50007004 写0b0 */
LDR R0, =0x50007004
LDR R1, [R0]
AND R1, R1, #(~(0x1 << 10))
STR R1, [R0]
/* 1.4 设置PF10引脚为低速模式
GPIOx_OSPEEDR[21:20] 0x50007000+0x08=0x50007008 写0b00 */
LDR R0, =0x50007008
LDR R1, [R0]
AND R1, R1, #(~(0x3 << 20))
STR R1, [R0]
/* 1.5 设置PF10引脚禁止上拉和下拉电阻
GPIOx_PUPDR[21:20] 0x50006000+0x0C=0x5000700C 写0b00 */
LDR R0, =0x5000700C
LDR R1, [R0]
AND R1, R1, #(~(0x3 << 20))
STR R1, [R0]
/* LED2的初始化完成 */
/* LED3的初始化(PE8) */
/* 1.1 使能GPIOE外设控制器的时钟
RCC_MP_AHB4ENSETR[4] 0x50000000+0xA28=0x50000A28 写0b1 */
@ LDR R0, =0x50000A28
@ LDR R1, [R0]
@ ORR R1, R1, #(0x1 << 4)
@ STR R1, [R0]
/* 1.2 设置PE8引脚为输出模式
GPIOx_MODER[17:16] 0x50006000+0x00=0x50006000 写0b01 */
LDR R0, =0x50006000
LDR R1, [R0]
@写0b01:先清零,后置1
AND R1, R1, #(~(0x3 << 16))
ORR R1, R1, #(0x1 << 16)
STR R1, [R0]
/* 1.3 设置PE8引脚为推挽输出
GPIOx_OTYPER[8] 0x50006000+0x04=0x50006004 写0b0 */
LDR R0, =0x50006004
LDR R1, [R0]
AND R1, R1, #(~(0x1 << 8))
STR R1, [R0]
/* 1.4 设置PE8引脚为低速模式
GPIOx_OSPEEDR[17:16] 0x50006000+0x08=0x50006008 写0b00 */
LDR R0, =0x50006008
LDR R1, [R0]
AND R1, R1, #(~(0x3 << 16))
STR R1, [R0]
/* 1.5 设置PE8引脚禁止上拉和下拉电阻
GPIOx_PUPDR[17:16] 0x50006000+0x0C=0x5000600C 写0b00 */
LDR R0, =0x5000600C
LDR R1, [R0]
AND R1, R1, #(~(0x3 << 16))
STR R1, [R0]
/* LED3的初始化完成 */
/* 裸机程序必须有一个死循环,进行事件的遍历
如果没有死循环,程序就会出错跑飞 */
loop:
/* 设置LED1(PE10)引脚输出高电平,点亮LED1灯
GPIOx_ODR[10] 0x50006000+0x14=0x50006014 写0b1 */
LDR R0, =0x50006014
LDR R1, [R0]
ORR R1, R1, #(0x1 << 10)
STR R1, [R0]
BL delay_1s
/* 设置LED1(PE10)引脚输出低电平,熄灭LED1灯
GPIOx_ODR[10] 0x50006000+0x14=0x50006014 写0b0 */
LDR R0, =0x50006014
LDR R1, [R0]
AND R1, R1, #(~(0x1 << 10))
STR R1, [R0]
/* 设置LED2(PF10)引脚输出高电平,点亮LED2灯
GPIOx_ODR[10] 0x50007000+0x14=0x50007014 写0b1 */
LDR R0, =0x50007014
LDR R1, [R0]
ORR R1, R1, #(0x1 << 10)
STR R1, [R0]
BL delay_1s
/* 设置LED2(PF10)引脚输出低电平,熄灭LED2灯
GPIOx_ODR[10] 0x50007000+0x14=0x50007014 写0b0 */
LDR R0, =0x50007014
LDR R1, [R0]
AND R1, R1, #(~(0x1 << 10))
STR R1, [R0]
/* 设置LED3(PE8)引脚输出高电平,点亮LED3灯
GPIOx_ODR[10] 0x50006000+0x14=0x50006014 写0b1 */
LDR R0, =0x50006014
LDR R1, [R0]
ORR R1, R1, #(0x1 << 8)
STR R1, [R0]
BL delay_1s
/* 设置LED3(PE8)引脚输出低电平,熄灭LED3灯
GPIOx_ODR[10] 0x50006000+0x14=0x50006014 写0b0 */
LDR R0, =0x50006014
LDR R1, [R0]
AND R1, R1, #(~(0x1 << 8))
STR R1, [R0]
b loop
delay_1s:
mov r3, #0x01000000
mm:
cmp r3, #0
subne r3, r3, #1
bne mm
mov pc, lr
.endC 代码示例:LED1 点灯
代码模板
txt
.
├── common 存放了预先封装号的函数和函数声明
├── include 存放自己写的 .h 头文件
├── main.c 工程的主函数
├── Makefile 指定编译规则
├── map.lds 链接脚本,指定不同的内容在内存中的位置
├── src 存放自己封装的 .c 文件
└── start 存放一些机器相关的初始化文件设置特殊功能寄存器的方法
c
// GPIOE_MODER
(*(unsigned int *)0X50006000) &= (~(0X3<<20))//先清0
(*(unsigned int *)0X50006000) |= (0X1<<20)使用宏定义封装
c
// GPIOE_MODER
#define GPIOE_MODER (*(unsigned int *)0X50006000)
#define GPIOE_OTYPER (*(unsigned int *)0X50006004)
// #include "gpio.h"
#define RCC (*(volatile unsigned int *)0X50000A28)
#define GPIOE_MODER (*(volatile unsigned int *)0X50006000)
#define GPIOE_OTYPER (*(volatile unsigned int *)0X50006004)
#define GPIOE_OSPEEDR (*(volatile unsigned int *)0X50006008)
#define GPIOE_ODR (*(volatile unsigned int *)0X50006014)
#define GPIOE_PUPDR (*(volatile unsigned int *)0X5000600C)使用结构体成员的形式访问寄存器
c
typedef struct{
unsigned int moder;
unsigned int otyper;
unsigned int ospeedr;
unsigned int pupdr;
unsigned int idr;
unsigned int odr;
}gpio_t;
#define GPIOE (gpio_t *)0X50006000
#define GPIOF (gpio_t *)0X50007000
GPIOE->moder &= (0X3<<20);
GPIOE->moder |= (0x1<<20);