最近学习STM32F103 I/O编程的过程中,尝试用按钮上拉输入来出发PORTC13的LED,根据芯片手册,开启PORTC时钟,选择PORTC13为最大速度2MHz的通用推挽输出模式(GPIOC_CRH 23:20位置为“0010”),测试Blink,成功。

进一步加入按键控制功能,选择PORTB11口(离GND比较近),开启PORTB始终,选择PORTB11为上拉输入模式(GPIOB_CRH 15:12位置为“1000”,GPIOB_ODR 11位置为“1”),烧录测试,PORTB11电压没有拉高,与GND短接无反应。

void led_init(){
    *(unsigned int *)0x40021018 |= ((1) << 4);//开启PORTC时钟
    *(unsigned int *)0x40011004 |= ((2) << (4*5));//配置PORTC13
}

void key_init(){
    *(unsigned int *)0x40021018 |= ((1) << 3);//开启PORTB时钟
    *(unsigned int *)0X40010C04 |= ((8) << 4*3);//配置PORTB11
    *(unsigned int *)0X40010C0C |= ((1) << 11);//配置PORTB11为上拉
}

检查编译无数次快要放弃的时候,偶然注意到芯片手册“5.2 GPIO寄存器描述”中“端口配置寄存器”的复位值为0x44444444,即GPIOx_CRH的四位从高到低为0100(浮空输入模式),与我们所,而代码中用|=配置并未清除默认配置,导致实际配置的功能与期望不符,我的做法是在配置功能前先清除复位默认配置,即在初始化代码中加入一行,编译烧录测试成功!

void led_init(){
    *(unsigned int *)0x40021018 |= ((1) << 4);//开启PORTC时钟
    *(unsigned int *)0x40011004 &= ~((15) << 4*5);//清除PORTC13复位默认配置
    *(unsigned int *)0x40011004 |= ((2) << (4*5));//配置PORTC13
}

void key_init(){
    *(unsigned int *)0x40021018 |= ((1) << 3);//开启PORTB时钟
    *(unsigned int *)0X40010C04 &= ~((15) << 4*3);//清除PORTB11复位默认配置
    *(unsigned int *)0X40010C04 |= ((8) << 4*3);//配置PORTB11
    *(unsigned int *)0X40010C0C |= ((1) << 11);//配置PORTB11为上拉
}

完整代码:

#include "stm32f10x.h"

void led_reverse(){
    *(unsigned int *)0x4001100C ^= ((1) << 13);
}

void delay(uint32_t nCount)
{
    nCount *= 10;
    for(; nCount != 0; nCount--);
}

void led_init(){
    *(unsigned int *)0x40021018 |= ((1) << 4);//开启PORTC时钟
    *(unsigned int *)0x40011004 &= ~((15) << 4*5);//清除PORTC13复位默认配置
    *(unsigned int *)0x40011004 |= ((2) << (4*5));//配置PORTC13
}

void key_init(){
    *(unsigned int *)0x40021018 |= ((1) << 3);//开启PORTB时钟
    *(unsigned int *)0X40010C04 &= ~((15) << 4*3);//清除PORTB11复位默认配置
    *(unsigned int *)0X40010C04 |= ((8) << 4*3);//配置PORTB11
    *(unsigned int *)0X40010C0C |= ((1) << 11);//配置PORTB11为上拉
}

int main()
{

    led_init();
    key_init();

    while(1)
    {
        if (((*(unsigned int *)0X40010C08) >> (11) & 1) == 0) 
        {
            led_reverse();
            while(((*(unsigned int *)0X40010C08) >> (11) & 1) == 0);
            delay(0xFFFF);//加入延时去抖动
        }
        else
            delay(0xFFFF);
    }
}