目前共有3篇帖子。
【STM32入门级程序5】使用ODR和IDR寄存器直接操作I/O口
1樓 巨大八爪鱼 2016-5-14 19:16
在STM32中也可以像C51和AVR那样直接读取和操作I/O口。
其中,GPIOx->ODR几乎完全等价于C51中的Px和AVR中的PORTx,GPIOx->IDR则等价于AVR中的PINx(注:C51中读写I/O口都是用的Px寄存器)。唯一不同的是,Px和PORTx都是8位的寄存器,一次只能操作8个I/O口,而ODR和IDR都是16位的,一次可操作16个I/O口。
对于AVR中的DDRx寄存器(8位),STM32中也有类似的寄存器:CRL和CRH,这两个寄存器都是16位的,加起来就是32位,因此要比DDRx复杂很多。DDRx是每一位控制一个I/O口的输入输出方向,而CRL和CRH则是每4位控制一个I/O口的属性,包括输入输出方向,是否使用上拉电阻等等。并且,CRL控制Px0~Px7,CRH控制Px8~Px15。
不管工程是否引入了标准固件库,这几个寄存器都可以直接使用。当然,也可以用固件库中的相关函数代替。

【示例程序1:ODR寄存器的用法】
#include <stm32f10x.h>

#define _BV(n) (1 << (n))
#define BIT _BV

#define PORTB GPIOB->ODR // ODR寄存器完全等价于AVR中的PORTx寄存器
#define P1 PORTB // 同时也等价于C51中的P1寄存器

void delay()
{
    uint32_t i;
    for (i = 0; i < 400000; i++);
}

void init()
{
    int i;
    RCC->APB2ENR |= _BV(3); // 打开GPIOB的时钟
   
    // 把PB8~15都设置为输出模式
    // 这个相当于AVR中的DDRB,只不过操作起来更复杂一些
    for (i = 0; i < 8; i++)
        GPIOB->CRH |= _BV(4 * i + 1) | _BV(4 * i);
}

int main()
{
    init();
   
    PORTB = _BV(8); // 一开始只点亮PB8
    delay();

    while (1)
    {
        PORTB |= _BV(10); // 点亮PB10
        delay();

        PORTB &= ~_BV(10); // 熄灭PB10
        delay();

        PORTB |= _BV(12) | _BV(13); // 点亮PB12和PB13
        delay();

        PORTB &= ~(_BV(12) | _BV(13)); // 熄灭PB12和PB13
        delay();
    }
}

void SystemInit()
{
}

2樓 巨大八爪鱼 2016-5-14 19:17
【示例程序2:IDR寄存器的用法】
/*
程序功能:
    当按键K1(PB1)按下不放时PB8亮, 松开后PB8灭
    完整地按下并松开按键K1后,PB15的亮灭状态改变
*/
#include <stm32f10x.h>

#define _BV(n) (1 << (n))
#define BIT _BV

#define PORTB GPIOB->ODR
#define PINB GPIOB->IDR

#define K1 (PINB & _BV(1))

void delay()
{
    uint32_t i;
    for (i = 0; i < 40000; i++);
}

void init()
{
    int i;
    RCC->APB2ENR |= _BV(3); // 打开GPIOB的时钟

    // 把PB0~7都设置为输入模式
    for (i = 0; i < 8; i++)
    {
        GPIOB->CRL &= ~(_BV(4 * i + 1) | _BV(4 * i)); // MODE=00: 输入模式

        GPIOB->CRL |= _BV(4 * i + 3); // CNF=10: 上拉/下拉输入
        GPIOB->CRL &= ~_BV(4 * i + 2);
    }
   
    // 把PB8~15都设置为输出模式
    for (i = 0; i < 8; i++)
        GPIOB->CRH |= _BV(4 * i + 1) | _BV(4 * i); // MODE=11: 输出模式, 最大速度为50MHz
}

int main()
{
    init();
   
    PORTB = _BV(8); // 一开始只点亮PB8
    while (1)
    {
        // 当按键按下时,输入低电平
        // 当按键松开时,输入高电平
        if (!K1) // 当按键按下时,即PB1输入为低电平时
        {
            PORTB |= _BV(10); // 点亮PB10

            // 消抖处理
            delay();
            if (!K1)
            {
                PORTB ^= _BV(15); // 反转PB15
                while (!K1);
            }

            // 注意: 如果主程序是扫描数码管的程序scan_7seg的话
            //       就必须把delay换成scan_7seg,并且while(!K1)里面也要调用scan_7seg
            //       防止按键影响数码管的显示
        }
        else
            PORTB &= ~_BV(10); // 熄灭PB10
    }
}

void SystemInit()
{
}

3樓 巨大八爪鱼 2016-5-14 19:18
实际使用时一般不定义PORTB和PINB宏,这里只是为了作对比。

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
 
 
©2010-2024 Arslanbar [手機版] [桌面版]
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。