Skip to content
On this page

LED点灯实验(Coretex-A7)


标签:FSMP1A/🧪实验  

分析 LED1 灯的硬件原理图

  1. 在扩展板上找到 LED1 灯对应的位置,查看 LED1 灯的丝印为 LD1
  2. 打开扩展板的原理图,搜索 LD1,找到 LD1 灯对应的原理图,如下:

invert

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

invert

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

invert

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

invert

invert

INFO

通过对电路图的分析可以知道 LED1 灯接到了 SOCPE10 引脚;
编写程序让 PE10 引脚输出高低电平就可以控制 LED 灯亮或者灯灭。

  • PE10 的含义:
    • p ---> GPIO 通用输入输出引脚
    • E ---> GPIO 引脚所属的组(STM32MP1芯片的组,A-K,Z)
    • 10 ---> GPIOE 组的第 10 个引脚(每组有 16 个 GPIO 引脚,引脚的编号为 0-15)

三极管

  • 二极管:特性单向导电,正向导通,反向截止
  • 三极管:特性放大特性(模拟电路)和开关特性(数字电路)
  • 三极管的种类:NPNPNP

invert

INFO

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

如何驱动 PE10 引脚输出高低电平

invert

  • CPU 核不直接控制 GPIO 引脚,而是 CPU 核通过总线和外设进行通信,有外设控制器管理 GPIO 引脚。比如,PE10 引脚由 GPIOE 外设控制器进行管理。

CPU 指令

  • CPU 可以执行的指令的分类(参考ARM汇编指令):
    1. 数据操作指令
    2. 跳转指令
    3. 特殊功能寄存器操作指令
    4. 软中断指令
    5. Load/Store 内存读写指令

什么是特殊功能寄存器(SFR)?

  • 特殊功能寄存器就是 0-4G (物理内存)寻址空间上的一块特殊的内存空间,这块内存空间具有特殊的用途,这样的内存空间称为特殊功能寄存器。
  • 特殊功能寄存器的访问是通过地址进行访问的,每个特殊功能寄存器的大小为 4 字节。
  • 通过向特殊功能寄存器中写入或者读取特定的值就可以间接地控制外设控制器工作。

invert

软件编程控制硬件的思想?

  • 通过程序向特殊功能寄存器中读取或者写入特定的值就可以对应的外设控制器按照一定的要求工作,这就是软件编程控制硬件的思想。
  • 所有的处理器不管是单片机,还是高端处理器,软件编程控制硬件的思想都是同一的,都是向特殊功能寄存器中写入或者读取特定的值就可以控制外设控制器工作
  • 不一样的点:特殊功能寄存器的地址可能不同,特殊功能寄存器的名字不同

分析芯片手册的技巧?

  • 在芯片手册中每个外设控制器都有自己对应的章节进行描述如何使用这个外部控制器
  • 只要明确自己的实验使用的是哪个外设控制器,就可以知道芯片手册的那个章节,
  • 再根据软件编程控制影响思想的理解,重点分析手册中外设寄存器的描述

分析芯片手册的 GPIO 章节

invert

上拉电阻和下拉电阻

invert

N-MOS 和 P-MOS

invert

推挽输出

invert

开漏输出

invert

上述电路控制的寄存器

invert

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

GPIOx_MODER 寄存器

invert

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

GPIOx_OTYPER 寄存器

invert

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

GPIOx_OSPEEDR寄存器

invert

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

GPIOx_PUPDR寄存器

invert

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

GPIOx_IDR寄存器

invert

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

GPIOx_ODR寄存器

invert

  • GPIOE_ODRGPIO 端口输出数据寄存器
  • LED1 灯接到 PE10 引脚之上,需要配置 GPIOE_ODR 寄存器的 [10] 位,
  • 0b0 配置 PE10 引脚输出低电平,写 0b1 配置 PE10 引脚输出高电平,必须保证其他位不变,其他位管理其他引脚,有其他的用途

分析芯片手册的 RCC 章节

invert

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

invert

分析 GPIO,RCC 外设寄存器的基地址

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

invert

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

invert

汇编代码示例: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

.end
Makefile
#定义变量
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

.end

C 代码示例: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);

示例代码(C开发模板)

Last updated: