繁体   English   中英

STM32F103 GPIO 端口

[英]STM32F103 GPIO Ports

我有一个STM32F103C8 MCU,我想在没有Cube MX的情况下控制GPIO寄存器。 MCU 有一个嵌入式 LED,我想控制它。 我目前正在使用 CubeMX 和 IAR 软件,并使用以下代码将引脚设为输出(在 CubeMX 中):

HAL_GPIO_TogglePin(Ld2_GPIO_Port,Ld2_Pin); 
HAL_Delay(1000); 

这有效,但我想在没有 Cube 和 HAL 库的情况下做到这一点; 我想直接编辑寄存器文件。

通过寄存器使用 GPIO 非常容易。 您不必编写自己的启动程序(就像@old_timer 的答案一样)。 只需要2个步骤

您将需要 STM 提供的 CMSIS 标头以及数据类型声明和人类可读的 #defines 以及参考手册

  1. 使能 GPIO 端口时钟。 营地: RCC -> APB2ENR |= RCC_APB2ENR_IOPAEN;
  2. 使用 CRL/CRH GPIO 寄存器配置引脚
#define GPIO_OUTPUT_2MHz (0b10)
#define GPIO_OUTPUT_PUSH_PULL (0 << 2)
  GPIOA -> CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
  GPIOA -> CRL |= GPIO_OUTPUT_2MHz | GPIO_OUTPUT_PUSH_PULL; 
  1. 操作输出
  /* to toggle */
  GPIOA -> ODR ^= (1 << pinNummer);
  /* to set */
  GPIOA -> BSRR = (1 << pinNummer);
  /* to reset */
  GPIOA -> BRR = (1 << pinNummer);
  //or
  GPIOA -> BSRR = (1 << (pinNummer + 16));

知道如何在没有罐头库的情况下进行裸机处理,或者能够通读这些库并了解您通过使用它们会进入什么状态,这是非常好的。

这会使端口 C 引脚 13 闪烁,您通常会在 stm32 蓝色药丸板上找到用户引导的位置。 您可以从这里和 STM32F103C8 的文档中找出答案。

flash.s

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop

.thumb_func
reset:
    bl notmain
    b loop
.thumb_func
loop:   b .

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

so.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );

#define GPIOCBASE 0x40011000
#define RCCBASE 0x40021000

#define STK_CSR 0xE000E010
#define STK_RVR 0xE000E014
#define STK_CVR 0xE000E018
#define STK_MASK 0x00FFFFFF

static int delay ( unsigned int n )
{
    unsigned int ra;

    while(n--)
    {
        while(1)
        {
            ra=GET32(STK_CSR);
            if(ra&(1<<16)) break;
        }
    }
    return(0);
}

int notmain ( void )
{
    unsigned int ra;
    unsigned int rx;

    ra=GET32(RCCBASE+0x18);
    ra|=1<<4; //enable port c
    PUT32(RCCBASE+0x18,ra);
    //config
    ra=GET32(GPIOCBASE+0x04);
    ra&=~(3<<20);   //PC13
    ra|=1<<20;      //PC13
    ra&=~(3<<22);   //PC13
    ra|=0<<22;      //PC13
    PUT32(GPIOCBASE+0x04,ra);

    PUT32(STK_CSR,4);
    PUT32(STK_RVR,1000000-1);
    PUT32(STK_CVR,0x00000000);
    PUT32(STK_CSR,5);

    for(rx=0;;rx++)
    {
        PUT32(GPIOCBASE+0x10,1<<(13+0));
        delay(50);
        PUT32(GPIOCBASE+0x10,1<<(13+16));
        delay(50);
    }
    return(0);
}

闪存文件

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

建造

arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding  -mthumb -c so.c -o so.o
arm-none-eabi-ld -o so.elf -T flash.ld flash.o so.o
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf so.bin -O binary

PUT32/GET32 是 IMO 强烈推荐的抽象风格,有几十年的经验,它比 volatile 指针有很多好处,或者更糟的是,当前 FAD 滥用联合的东西。 不是一个库,而是显示不需要任何库的代码,只需要提供的文件。

大多数 mcus 需要先启用外设的时钟,然后才能与之交谈。 您可以看到 RCC 寄存器的读取-修改-写入。

大多数 MCU 的 GPIO 引脚重置为输入,因此您需要将其设置为输出以驱动/闪烁 LED。 即使在 STM32 世界中,但肯定跨品牌/系列,GPIO(和其他)外设预计不会相同甚至不兼容,因此您必须参考该部分的文档,它将展示如何使引脚成为输出。 读-修改-写而不是只写是个好主意,但是由于您可以完全控制芯片,因此您可以根据需要进行写操作,请稍后再试。

该芯片有一个不错的寄存器,允许我们在一次写入中更改一个或多个但不一定是所有 GPIO 输出的输出状态,无需读取-修改-写入。 因此,我可以设置或清除 GPIOC 的引脚 13,而不会影响其他 GPIOC 引脚的状态。

一些 cortex-ms 有一个 systick 计时器,例如,并非所有 cortex-m3 都必须有一个,通常由芯片人员决定,有些内核可能没有选项。 这个芯片可以让你使用它。 在此示例中,计时器设置为每 100 万个时钟翻转一次,延迟函数在返回之前等待 N 次翻转。 所以 LED 状态变化之间有 50,000,000 个时钟。 由于此代码直接从复位开始运行而不会干扰时钟或其他系统,因此在 LED 状态更改之间使用内部 HSI 8MHz 时钟 50/8 = 6.25 秒。 systick 非常易于使用,但请记住,它是一个 24 位计数器,而不是 32 位计数器,因此如果您现在想这样做,那么您必须屏蔽它。

我不记得是不是柜台

elapsed = (now - then) & 0x00FFFFFF;

或向下

elapsed = (然后 - 现在) & 0x00FFFFFF;

(现在 = GET32(系统棒计数寄存器地址))

systick 计时器在 arm 文档中,而不是在芯片文档中,尽管有时 ST 会生产自己的版本,但您肯定需要 arm 一个,然后可能是 st 一个。 infocenter.arm.com(你必须放弃一个电子邮件地址,或者你可以谷歌有时你很幸运,有人会非法将它们张贴在某个地方)这个芯片会告诉你它使用的是 cortex-m3,所以找到 cortex 的技术参考手册-m3 因为你会发现它基于架构 armv7-m 所以在架构下找到 armv7-m 文档,在这些文档之间你可以看到向量表是如何工作的,系统计时器及其地址等。

检查向量表

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000041    stmdaeq r0, {r0, r6}
 8000008:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 800000c:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000010:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000014:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000018:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 800001c:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000020:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000024:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000028:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 800002c:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000030:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000034:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000038:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 800003c:   08000047    stmdaeq r0, {r0, r1, r2, r6}

08000040 <reset>:
 8000040:   f000 f806   bl  8000050 <notmain>
 8000044:   e7ff        b.n 8000046 <loop>

08000046 <loop>:
 8000046:   e7fe        b.n 8000046 <loop>

我们的向量表的入口点代码以我们想在重置时放入堆栈指针的值开始,应该是第一件事,然后向量表是处理程序的地址 ORRed 与 1(不容易找到)有时在文档中)。 这些地址的反汇编是因为我使用反汇编器查看它们那些不是向量表中的实际指令,它是一个向量表。 该工具正在尽最大努力反汇编所有内容,如果您查看输出的其余部分,它还会反汇编 ascii 表和其他也不是代码的内容。

在这个例子中不支持 .data 需要做更多的工作。

我建议如果/当你开始工作时,然后检查 HAL 库源,看看当你挖掘有时臃肿或可怕的代码层时,你最终会得到相同的核心寄存器,他们可能会选择始终配置所有 gpio寄存器例如速度和上拉/下拉,关闭交替功能等。或不。 上面知道它是从重置和系统状态中出来的,所以对于某些外围设备,你可以弹出该外围设备的重置并将其置于已知状态,而不是尝试制作一个预期的库它处于任何状态并尝试从该状态进行配置。 天啊。

了解如何在此级别工作以及如何使用库是专业的。 一个 MCU 芯片供应商通常会有两个库,当然对于像这样的旧部件,当前库产品和旧库产品,当一个新库出现以保持新鲜和竞争力(看起来)时,最旧的库将不再支持你有时有当前和以前。 取决于供应商,取决于零件,取决于他们如何管理他们的软件产品(他们的 IDE 和其他工具也是如此)。

大多数 stm32 部件(尤其是蓝色药丸)和您可以获得的其他电路板不需要花哨的 IDE 进行编程,但有时需要外部硬件,除非您获得 NUCLEO 或 Discovery 板,那么您至少有足够的时间使用免费软件对部件进行编程不附属于 ST。 使用 nucleo,它是 mbed 风格,您只需将 .bin 文件复制到虚拟 USB 驱动器,开发板就会负责对开发 MCU 进行编程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM