目前共有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許可協議進行許可。