简体   繁体   English

STM32F103 GPIO 端口

[英]STM32F103 GPIO Ports

I have a STM32F103C8 MCU, and I want to control GPIO registers without Cube MX.我有一个STM32F103C8 MCU,我想在没有Cube MX的情况下控制GPIO寄存器。 The MCU has an embedded LED and I want control it. MCU 有一个嵌入式 LED,我想控制它。 I'm currently using CubeMX and IAR Software, and I make the pin an output (in CubeMX) with this code:我目前正在使用 CubeMX 和 IAR 软件,并使用以下代码将引脚设为输出(在 CubeMX 中):

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

This works, but I want to do it without Cube and HAL library;这有效,但我想在没有 Cube 和 HAL 库的情况下做到这一点; I want to edit the register files directly.我想直接编辑寄存器文件。

Using GPIO using registers is very easy.通过寄存器使用 GPIO 非常容易。 You fo not have to write your own startup (as ion the @old_timer answer).您不必编写自己的启动程序(就像@old_timer 的答案一样)。 Only 2 steps are needed只需要2个步骤

you will need the STM provided CMSIS headers with datatypes declarations and human readable #defines and the reference manual您将需要 STM 提供的 CMSIS 标头以及数据类型声明和人类可读的 #defines 以及参考手册

  1. Enable GPIO port clock.使能 GPIO 端口时钟。 ecample: RCC -> APB2ENR |= RCC_APB2ENR_IOPAEN;营地: RCC -> APB2ENR |= RCC_APB2ENR_IOPAEN;
  2. Configure the pins using CRL/CRH GPIO registers使用 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. Manipulate the output操作输出
  /* to toggle */
  GPIOA -> ODR ^= (1 << pinNummer);
  /* to set */
  GPIOA -> BSRR = (1 << pinNummer);
  /* to reset */
  GPIOA -> BRR = (1 << pinNummer);
  //or
  GPIOA -> BSRR = (1 << (pinNummer + 16));

It is very good to know how to do bare metal without the canned libraries, and or to be able to read through those libraries and understand what you are getting yourself into by using them.知道如何在没有罐头库的情况下进行裸机处理,或者能够通读这些库并了解您通过使用它们会进入什么状态,这是非常好的。

This blinks port C pin 13 that is where you generally find the user led on the stm32 blue pill boards.这会使端口 C 引脚 13 闪烁,您通常会在 stm32 蓝色药丸板上找到用户引导的位置。 You can figure it out from here and the documentation for the STM32F103C8.您可以从这里和 STM32F103C8 的文档中找出答案。

flash.s 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 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);
}

flash.ld闪存文件

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

build建造

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 is IMO a highly recommended style of abstraction, decades of experience and it has many benefits over the volatile pointer or worse the misuse of unions thing that is the current FAD. PUT32/GET32 是 IMO 强烈推荐的抽象风格,有几十年的经验,它比 volatile 指针有很多好处,或者更糟的是,当前 FAD 滥用联合的东西。 Not meant to be a library but to show code that does not require any libraries, only the files provided are required.不是一个库,而是显示不需要任何库的代码,只需要提供的文件。

Most mcus you need to enable clocks to the peripheral before you can talk to it.大多数 mcus 需要先启用外设的时钟,然后才能与之交谈。 You can see the read-modify-write of an RCC register.您可以看到 RCC 寄存器的读取-修改-写入。

Most MCUs the GPIO pins reset to inputs so you need to set one to an output to drive/blink an led.大多数 MCU 的 GPIO 引脚重置为输入,因此您需要将其设置为输出以驱动/闪烁 LED。 Even within the STM32 world but certainly across brands/families the GPIO (and other) peripherals are not expected to be identical nor even compatible so you have to refer to the documentation for that part and it will show how to make a pin an output.即使在 STM32 世界中,但肯定跨品牌/系列,GPIO(和其他)外设预计不会相同甚至不兼容,因此您必须参考该部分的文档,它将展示如何使引脚成为输出。 very good idea to read-modify-write instead of just write, but since you are in complete control over the chip you can just write if you wish, try that later.读-修改-写而不是只写是个好主意,但是由于您可以完全控制芯片,因此您可以根据需要进行写操作,请稍后再试。

This chip has a nice register that allows us to change the output state of one or more but not necessarily all GPIO outputs in a single write, no read-modify-write required.该芯片有一个不错的寄存器,允许我们在一次写入中更改一个或多个但不一定是所有 GPIO 输出的输出状态,无需读取-修改-写入。 So I can set or clear pin 13 of GPIOC without affecting the state of the other GPIOC pins.因此,我可以设置或清除 GPIOC 的引脚 13,而不会影响其他 GPIOC 引脚的状态。

Some cortex-ms have a systick timer, for example not all cortex-m3s have to have one it is up to the chip folks usually and some cores may not have the option.一些 cortex-ms 有一个 systick 计时器,例如,并非所有 cortex-m3 都必须有一个,通常由芯片人员决定,有些内核可能没有选项。 This chip does so you can use it.这个芯片可以让你使用它。 In this example the timer is set to roll over every 1 million clocks, the delay function waits for N number of rollovers before returning.在此示例中,计时器设置为每 100 万个时钟翻转一次,延迟函数在返回之前等待 N 次翻转。 so 50,000,000 clocks between led state changes.所以 LED 状态变化之间有 50,000,000 个时钟。 since this code runs right from reset without messing with the clocking or other systems, the internal HSI 8MHz clock is used 50/8 = 6.25 seconds between led state changes.由于此代码直接从复位开始运行而不会干扰时钟或其他系统,因此在 LED 状态更改之间使用内部 HSI 8MHz 时钟 50/8 = 6.25 秒。 systick is very easy to use, but remember it is a 24 bit counter not 32 so if you want to do now vs then you must mask it. systick 非常易于使用,但请记住,它是一个 24 位计数器,而不是 32 位计数器,因此如果您现在想这样做,那么您必须屏蔽它。

I don't remember if it is an up counter我不记得是不是柜台

elapsed = (now - then) & 0x00FFFFFF; elapsed = (now - then) & 0x00FFFFFF;

or down或向下

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

(now = GET32(systick count register address)) (现在 = GET32(系统棒计数寄存器地址))

The systick timer is in the arm documentation not the chip documentation necessarily although sometimes ST produces their own version, you want the arm one for sure and maybe then the st one. systick 计时器在 arm 文档中,而不是在芯片文档中,尽管有时 ST 会生产自己的版本,但您肯定需要 arm 一个,然后可能是 st 一个。 infocenter.arm.com (you have to give up an email address or you can Google sometimes you get lucky, someone will illegally post them somewhere) this chip will tell you it uses a cortex-m3 so find the technical reference manual for the cortex-m3 in that you will find it is based on architecture armv7-m so under architecture find the armv7-m documentation, between these you see how the vector table works, the systick timer and its addresses, etc. infocenter.arm.com(你必须放弃一个电子邮件地址,或者你可以谷歌有时你很幸运,有人会非法将它们张贴在某个地方)这个芯片会告诉你它使用的是 cortex-m3,所以找到 cortex 的技术参考手册-m3 因为你会发现它基于架构 armv7-m 所以在架构下找到 armv7-m 文档,在这些文档之间你可以看到向量表是如何工作的,系统计时器及其地址等。

Examine vector table检查向量表

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>

The entry point code with our vector table which starts off with the value we would like to put in the stack pointer on reset should be the first thing, then vector tables which are the address of the handler ORRed with 1 (not as easy to find in the docs sometimes).我们的向量表的入口点代码以我们想在重置时放入堆栈指针的值开始,应该是第一件事,然后向量表是处理程序的地址 ORRed 与 1(不容易找到)有时在文档中)。 the disassembly of these addresses is because I used the disassembler to view them those are not actual instructions in the vector table it is a table of vectors.这些地址的反汇编是因为我使用反汇编器查看它们那些不是向量表中的实际指令,它是一个向量表。 the tool is just doing its best to disassemble everything, if you look at the rest of the output it also disassembles the ascii tables and other things which are also not code.该工具正在尽最大努力反汇编所有内容,如果您查看输出的其余部分,它还会反汇编 ascii 表和其他也不是代码的内容。

.data is not supported in this example a bunch more work would be required.在这个例子中不支持 .data 需要做更多的工作。

I recommend if/when you get yours working you then examine the HAL library sources to see that when you dig through layers of sometimes bloated or scary code, you will end up with the same core registers, they may choose to always configure all the gpio registers for example, speed and pull up/down, turn off the alternate function, etc. Or not.我建议如果/当你开始工作时,然后检查 HAL 库源,看看当你挖掘有时臃肿或可怕的代码层时,你最终会得到相同的核心寄存器,他们可能会选择始终配置所有 gpio寄存器例如速度和上拉/下拉,关闭交替功能等。或不。 the above knows it is coming out of reset and the state of the system so doesn't go to those lengths for some peripherals you can pop the reset for that peripheral and put it in a known state rather than try to make a library that anticipates it being left in any condition and trying to configure from that state.上面知道它是从重置和系统状态中出来的,所以对于某些外围设备,你可以弹出该外围设备的重置并将其置于已知状态,而不是尝试制作一个预期的库它处于任何状态并尝试从该状态进行配置。 YMMV.天啊。

It is good professionally to know how to work at this level as well as how to use libraries.了解如何在此级别工作以及如何使用库是专业的。 An MCU chip vendor will often have two libraries, certainly for older parts like these, the current library product and the legacy library product, when a new library comes out to keep it fresh and competitive (looking) the oldest one will drop off from support and you sometimes have current and prior.一个 MCU 芯片供应商通常会有两个库,当然对于像这样的旧部件,当前库产品和旧库产品,当一个新库出现以保持新鲜和竞争力(看起来)时,最旧的库将不再支持你有时有当前和以前。 depends on the vendor, depends on the part, depends on how they manage their software products (same goes for their IDEs and other tools).取决于供应商,取决于零件,取决于他们如何管理他们的软件产品(他们的 IDE 和其他工具也是如此)。

Most of the stm32 parts esp a blue pill and other boards you can get do not require the fancy IDEs to program but external hardware is sometimes required unless you get a NUCLEO or Discovery board then you have at least enough to program the part with free software not attached to ST.大多数 stm32 部件(尤其是蓝色药丸)和您可以获得的其他电路板不需要花哨的 IDE 进行编程,但有时需要外部硬件,除非您获得 NUCLEO 或 Discovery 板,那么您至少有足够的时间使用免费软件对部件进行编程不附属于 ST。 with a nucleo it is mbed style where you simply copy the .bin file to the virtual usb drive and the board takes care of programming the development MCU.使用 nucleo,它是 mbed 风格,您只需将 .bin 文件复制到虚拟 USB 驱动器,开发板就会负责对开发 MCU 进行编程。

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

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