带按钮的 LED 灯闪烁

Led blinking with button

我正在对 F401RE 板进行编程,我试图通过按下板上的按钮来切换 LED ON/OFF。我正在使用低级方法,因为它是大学作业,我不能使用高级库。

这就是我所做的,但它不起作用,led 静止不动... 代码应该是正确的,我错过了什么?

#include "stm32f4xx.h"
int main(void){

    int flag=0;
    //PORT REGISTERS
    volatile uint32_t *GPIOA_MODER = (uint32_t*) (0x40020000 + 0x00);
    volatile uint32_t *GPIOA_ODR = (uint32_t*) (0x40020000 + 0x14);
    volatile uint32_t *GPIOC_MODER = (uint32_t*) (0x40020000 + 0x0800 + 0x00);
    volatile uint32_t *GPIOC_IDR = (uint32_t*) (0x40020000 + 0x0800 + 0x10);

    //CLOCK REGISTERS
    volatile uint32_t *RCC_AHB1ENR = (uint32_t*) (0x40023800 + 0x30);

    *RCC_AHB1ENR |= 0x05U;

    *GPIOA_MODER = *GPIOA_MODER | 0x400;

    *GPIOC_MODER = *GPIOC_MODER | 0x0C000000;

    *GPIOA_ODR = *GPIOA_ODR | 0x20;

    // Application code (Infinite loop)
    while (1){

        if((((*GPIOC_IDR>>13) & 0x01) == 0x01)){
            flag=1;
        }else if((((*GPIOC_IDR>>13) & 0x01) == 0x00)  && flag==1){
            *GPIOA_ODR = *GPIOA_ODR ^ 0x20;
            flag=0;
        }
    }
}

您打算使用端口引脚 PC13 作为按钮输入吗? ORing GPIOC_MODER 与 0x0C000000 将 PC13 设置为模拟模式。我想你想要输入模式而不是模拟模式。输入模式是 PC13 的默认模式。因此,请尝试仅注释掉以下行。

//    *GPIOC_MODER = *GPIOC_MODER | 0x0C000000;

警告:我不是这个特定 MCU 的专家。小心点

this 参考手册中提取的所有 MCU 寄存器信息。


无论如何,现在我已经完成了免责声明,让我们开始吧。

//Lines 16-20
...
*GPIOA_MODER = *GPIOA_MODER | 0x400;

*GPIOC_MODER = *GPIOC_MODER | 0x0C000000;

*GPIOA_ODR = *GPIOA_ODR | 0x20;
...

我认为这是包含错误的代码段。您设置 GPIOA_MODER5 使用通用输出模式,设置 GPIOC_MODER13 使用模拟模式。再一次,这里不是专家,但我相信这应该设置为使用输入模式。要么不做任何事情(因为端口 C 的重置值为 0x00000000),要么用 *GPIOC_MODER = *GPIOC_MODER & ~(0x0C000000);.

显式重置寄存器的那部分

在调试过程中,我重新格式化了您的代码以符合我大学的编码标准以及我的个人喜好。这里是。

#include "stm32f4xx.h"

//Magic numbers are BAD. Use macros instead.
#define GPIOA_MEMOFFSET 0x40020000
#define GPIOC_MEMOFFSET 0x40020800
#define CLOCK_MEMOFFSET 0x40023800

//more macros. These are bit offsets for various registers.
#define GPIOC_MODER13 26
#define GPIOA_MODER5 10
#define GPIOC_IDR13 13
#define GPIOA_ODR5 5
#define RCC_AHB1ENR_GPIOAEN 0
#define RCC_AHB1ENR_GPIOCEN 1

int main(void)
{
    //Different compilers have different lengths for ints.
    //uint8_t is always 8 bits long.

    uint8_t flag=0;

    //This area has been width formatted to be easier to read. 
    //The compiler doesn't care about whitespace. See the macros at work?

    //PORT REGISTERS
    volatile uint32_t *GPIOA_MODER = (uint32_t*) (GPIOA_MEMOFFSET + 0x00);
    volatile uint32_t *GPIOA_ODR   = (uint32_t*) (GPIOA_MEMOFFSET + 0x14);
    volatile uint32_t *GPIOC_MODER = (uint32_t*) (GPIOC_MEMOFFSET + 0x00);
    volatile uint32_t *GPIOC_IDR   = (uint32_t*) (GPIOC_MEMOFFSET + 0x10);
    //CLOCK REGISTERS
    volatile uint32_t *RCC_AHB1ENR = (uint32_t*) (CLOCK_MEMOFFSET + 0x30);

    //No, it's not as concise as your version. But since all of this is constant,
    //any GOOD compiler will literally turn all that into a 5. It makes debugging
    //for other programmers easier to see what's actually going on. 
    *RCC_AHB1ENR |= (1 << RCC_AHB1ENR_GPIOAEN) | (1<<RCC_AHB1ENR_GPIOCEN); 

    //I turned every assignment operator into its equivalent boolean assignment.
    *GPIOA_MODER |= (0b01 << GPIOA_MODER5);
    *GPIOA_MODER &= ~(0b10 << GPIOA_MODER5);

    *GPIOC_MODER &= ~(0b11 << GPIOC_MODER13);

    *GPIOA_ODR = *GPIOA_ODR | (1 << GPIOA_ODR5);

    // Application code (Infinite loop)
    while (1)
    {
        //Lots of work here. C standard defines false as 0 and true as !false
        //so you're able to turn 'if (xx!=0)' into just 'if (xx)'
        //...usually. The rules get iffy when you do weird things. 
        if (*GPIOC_IDR & (1<<GPIOC_IDR13)) //AND masking IDR13.
        {
            flag=1;
        }
        else if(!(*GPIOC_IDR & (1<<GPIOC_IDR13)) && flag) //AND masking IDR13 again, but also boolean inverting the result. 
        {
            *GPIOA_ODR ^= (1<<GPIOA_ODR5);
            flag=0;
        }
    }
}