1. 程式人生 > >織女星開發板使用RISC-V核驅動GPIO

織女星開發板使用RISC-V核驅動GPIO

前言

織女星開發板是OPEN-ISA社群為中國大陸地區定製的一款體積小、功耗超低和功能豐富的 RISC-V評估開發板,基於NXP半導體四核異構RV32M1主控晶片。

  • 兩個RISC-V核:RI5CY + ZERO_RISCY。
  • 兩個ARM核: Cortex-M4F + Cortex-M0+ 。

4個核被分為兩個子系統,大核CM4F/RI5CY和小核CM0+/ZERO-RISCY,片上整合1.25 MB Flash 、384 KB SRAM,其中1 MB的Flash被大核所使用,起始地址0x0000_0000,另外的256 KB Flash被小核所使用,起始地址0x0100_0000。利用該開發板,使用者可以快速建立一個使用 RV32M1 的 RISC-V應用和演示系統。詳細的介紹可以參考: 真正的RISC-V開發板——VEGA織女星開發板開箱評測 ,本篇文章介紹如何基於RISC-V RI5CY/ZERO核心來點亮板載的RGB_LED/STS_LED、讀取按鍵輸入,演示GPIO的輸入輸出和外部中斷功能。

準備工作

在進行以下操作之前,要確保開發環境已經搭建完成,而且能正常下載除錯。

  • 織女星開發板RISC-V開發環境:Eclipse + riscv32 工具鏈 + OpenOCD除錯工具
  • 織女星開發板SDK包:rv32m1_sdk_riscv
  • 織女星開發板的原理圖
  • RV32M1參考手冊

以上資料的獲取、開發環境搭建和啟動模式修改等教程,可以到官方中文論壇查詢:www.open-isa.cn

或者是參考我分享的以下文章:

  • 真正的RISC-V開發板——VEGA織女星開發板開箱評測
  • 手把手教你搭建織女星開發板RISC-V開發環境
  • 織女星開發板啟動模式修改——從ARM M4核啟動
  • 織女星開發板偵錯程式升級為Jlink韌體
  • 織女星開發板RISC-V核心實現微秒級精確延時

暫存器簡介

根據RV32M1參考手冊GPIO章節的介紹,我們可以獲得關於GPIO相關暫存器資訊:

各GPIO組的基地址:

GPIOA——4802_0000h
GPIOB——4802_0040h
GPIOC——4802_0080h
GPIOD——4802_00C0h
GPIOE——4100_F000h

GPIO配置PCR暫存器

這是一個32位的暫存器,每一個引腳都有對應的一個PORTx_PCRn,用來配置GPIO的以下功能:

  • 上下拉配置
  • 翻轉速率控制
  • 開漏使能
  • 無源輸入濾波器
  • 暫存器鎖定
  • 複用功能設定

以PA0控制暫存器,PORTA_PCR0為例:

通過檢視參考手冊,可以瞭解到各Bit的功能:

  • ISF:1位,中斷狀態標誌
  • IRQC:4位,配置中斷方式和DMA功能
  • LK:1位,是否鎖定PCR寄存
  • MUX:3位,複用功能配置
  • ODE:1位,推輓開漏配置
  • PFE:1位,濾波器配置
  • SRE:1位,翻轉速率配置
  • PE:1位,上下拉使能
  • PS:1位,上下拉配置

詳細的配置介紹可以檢視參考手冊。官方庫fsl_port中的


PORT_SetPinConfig(PORT_Type *base, uint32_t pin, const port_pin_config_t *config)
PORT_SetPinMux(PORT_Type *base, uint32_t pin, port_mux_t mux)
PORT_SetPinInterruptConfig(PORT_Type *base, uint32_t pin, port_interrupt_t config)
PORT_SetPinDriveStrength(PORT_Type* base, uint32_t pin, uint8_t strength)

這些函式就是控制的這個PCR暫存器。

GPIO控制暫存器

主要包括控制GPIO輸入輸出控制,讀取輸入,控制輸出,方向控制等。

暫存器描述和地址偏移量:

RV32M1的GPIO共有6個32位的控制暫存器,從字面意思可以直接知道每個暫存器的功能:

  • PDOR:資料輸出暫存器,指定位寫入0/1,輸出0/1
  • PSOR:埠置位輸出暫存器,指定位寫1,置位輸出1,寫0狀態不變
  • PCOR:埠復位輸出暫存器,指定位寫1,復位輸出0,寫0狀態不變
  • PTOR:埠反轉輸出暫存器,指定位寫1,反轉輸出,寫0狀態不變
  • PDIR:埠輸入暫存器,讀取指定位輸入狀態
  • PDDR:埠方向配置暫存器,指定位寫0作為輸入,寫1作為輸出

官方庫中的fsl_gpio檔案中實現的函式就是控制的這幾個暫存器。


void GPIO_PinInit(GPIO_Type *base, uint32_t pin, const gpio_pin_config_t *config)
void GPIO_WritePinOutput(GPIO_Type *base, uint32_t pin, uint8_t output)
void GPIO_SetPinsOutput(GPIO_Type *base, uint32_t mask)
void GPIO_ClearPinsOutput(GPIO_Type *base, uint32_t mask)
void GPIO_TogglePinsOutput(GPIO_Type *base, uint32_t mask)

庫函式簡介

和其他的MCU一樣,由於RV32M1的暫存器眾多,為了方便使用,增強程式的可讀性,官方開發了庫函式,來實現對暫存器的控制,本質上還是操作的暫存器。GPIO控制的庫主要由fsl_gpio和fsl_port兩個檔案組成,其中fsl_gpio主要是對GPIO的控制,如讀取輸入,控制輸出,清除中斷標誌等,而fsl_port主要實現對GPIO工作的模式進行配置,如複用功能,上拉下拉,開漏推輓,中斷觸發方式,DMA功能等進行設定。

下面簡單介紹幾個常用的函式:

PORT_SetPinConfig

配置GPIO的複用功能,驅動能力,推輓開漏,上下拉,濾波器,翻轉速率等功能,基於PCR暫存器實現。


port_pin_config_t config;

config.driveStrength = kPORT_HighDriveStrength;     //驅動能力配置
config.mux = kPORT_MuxAsGpio;                       //通用GPIO
config.openDrainEnable = kPORT_OpenDrainDisable;    //推輓
config.passiveFilterEnable = kPORT_PassiveFilterDisable;//濾波器
config.pullSelect = kPORT_PullUp;                   //上拉
config.slewRate = kPORT_FastSlewRate;               //翻轉速率

PORT_SetPinConfig(PORTA, 22, &config);              //配置GPIOA22

PORT_SetPinMux

配置GPIO的複用功能,基於PCR暫存器實現。

//PA22作為普通GPIO使用
PORT_SetPinMux(PORTA, 22, kPORT_MuxAsGpio);

//PA25作為UART1_RX功能
PORT_SetPinMux(PORTA, 25, kPORT_MuxAlt2);

具體複用為哪種功能,不同的引腳有不同的複用功能,對應的ALTn,可以檢視參考手冊RV32M1 Pinout介紹。

PORT_SetPinConfig已經包含了PORT_SetPinMux的功能,可以只使用PORT_SetPinConfig來GPIO功能的配置。PORT_SetPinMux函式不推薦和PORT_SetPinsConfig函式一起使用:

This function is NOT recommended to use together with the PORT_SetPinsConfig, because the PORT_SetPinsConfig need to configure the pin mux anyway (Otherwise the pin mux is reset to zero : kPORT_PinDisabledOrAnalog). This function is recommended to use to reset the pin mux

GPIO_PinInit

控制GPIO的輸入輸出方式,及預設輸出電平,基於PDDR、PCOR、PSOR暫存器實現。


gpio_pin_config_t io_init;

//配置輸出/輸出模式
io_init.outputLogic = 0;    //預設輸出0
io_init.pinDirection = kGPIO_DigitalOutput; //數字輸出

GPIO_PinInit(LED_RGB_GPIO, LED_RED_Pin, &io_init);  //LED引腳配置

GPIO_WritePinOutput

指定引腳輸出高低電平,基於PCOR和PSOR暫存器實現。

GPIO_WritePinOutput(GPIOA, 22, 1);  //PA22輸出1

GPIO_TogglePinsOutput

指定引腳輸出翻轉,基於PTOR暫存器實現

GPIO_TogglePinsOutput(GPIOA, 1 << 22);  //PA22輸出翻轉

GPIO_ReadPinInput

讀取GPIO輸入狀態,基於PDIR暫存器實現

in = GPIO_ReadPinInput(GPIOA, 22);  //讀取PA22輸入狀態

GPIO操作的函式還有很多,詳細的介紹和實現可以直接檢視庫函式原始碼。

RGB LED的初始化

從原理圖中我們可以得知,織女星開發板上共有4個使用者可控制的LED,包括3個RGB LED和1個紅色LED,均採用MOS來驅動,引腳輸出高電平LED點亮,和GPIO的對應關係如下:

LED_RED——PTA24
LED_GREEN——PTA23
LED_BLUE——PTA22
LED_STS——PTE0

所以我們需要配置PTA22/PTA23/PTA24為普通推輓輸出方式,然後輸出高低電平就可以控制LED閃爍了。

led_driver.c檔案內容


#include "led_driver.h"

void LED_RGB_Init(void)
{
    gpio_pin_config_t io_init;
    port_pin_config_t config;

    //配置輸出/輸出模式
    io_init.outputLogic  = 0;
    io_init.pinDirection = kGPIO_DigitalOutput;

    config.driveStrength        = kPORT_HighDriveStrength;   //驅動能力
    config.lockRegister         = kPORT_LockRegister;        //PCR暫存器被鎖定,不能再次改變
    config.mux                  = kPORT_MuxAsGpio;           //通用GPIO
    config.openDrainEnable      = kPORT_OpenDrainDisable;    //推輓輸出
    config.passiveFilterEnable  = kPORT_PassiveFilterDisable;//濾波器
    config.pullSelect           = kPORT_PullUp;              //上拉
    config.slewRate             = kPORT_FastSlewRate;        //翻轉速率

    CLOCK_EnableClock(LED_RGB_Clk_Name);
    CLOCK_EnableClock(LED_STS_Clk_Name);        //GPIOE時鐘必須一直開啟
    CLOCK_EnableClock(kCLOCK_Rgpio1);           //GPIOE配置需要使能這個時鐘

    /*以下兩個函式都可以配置埠功能*/
    PORT_SetPinConfig(LED_RGB_Port, LED_RED_Pin, &config);      //配置功能更詳細
    PORT_SetPinConfig(LED_RGB_Port, LED_GREEN_Pin, &config);
    PORT_SetPinConfig(LED_RGB_Port, LED_BLUE_Pin, &config);
    PORT_SetPinConfig(LED_STS_Port, LED_STS_Pin, &config);

//  PORT_SetPinMux(LED_RGB_Port, LED_RED_Pin, kPORT_MuxAsGpio); //只能配置是否複用
//  PORT_SetPinMux(LED_RGB_Port, LED_GREEN_Pin, kPORT_MuxAsGpio);
//  PORT_SetPinMux(LED_RGB_Port, LED_BLUE_Pin, kPORT_MuxAsGpio);

//  CLOCK_DisableClock(LED_RGB_Clk_Name);       //可以在配置完成之後關閉時鐘,不影響使用

    GPIO_PinInit(LED_RGB_GPIO, LED_RED_Pin, &io_init);
    GPIO_PinInit(LED_RGB_GPIO, LED_GREEN_Pin, &io_init);
    GPIO_PinInit(LED_RGB_GPIO, LED_BLUE_Pin, &io_init);
    GPIO_PinInit(LED_STS_GPIO, LED_STS_Pin, &io_init);
}

要注意的是,時鐘使能要放在GPIO配置之前,否則不能訪問GPIO配置暫存器,在配置完成之後可以關閉時鐘,也可以一直開啟。其中GPIOE非常特殊,要想使用GPIOE,必須使能Rgpio1快速時鐘,其他的GPIO配置不需要,這是因為GPIOE屬於快速GPIO,和其他幾組GPIO不是同一個匯流排。


    CLOCK_EnableClock(kCLOCK_Rgpio1);           //GPIOE配置需要使能這個時鐘

led_driver.h檔案內容


#ifndef __LED_DRIVER_H__
#define __LED_DRIVER_H__

#include "fsl_gpio.h"
#include "fsl_port.h"
#include "fsl_clock.h"


/*
LED_RGB_BLUE    - A22
LED_RGB_GREEN   - A23
LED_RGB_RED     - A24
LED_STS         - E0
*/


#define LED_RED_Pin     24
#define LED_GREEN_Pin   23
#define LED_BLUE_Pin    22

#define LED_RGB_Port        PORTA
#define LED_RGB_GPIO        GPIOA
#define LED_RGB_Clk_Name    kCLOCK_PortA

#define LED_STS_Pin     0
#define LED_STS_Port        PORTE
#define LED_STS_GPIO        GPIOE
#define LED_STS_Clk_Name    kCLOCK_PortE

#define LED_STS_ON          GPIO_WritePinOutput(LED_STS_GPIO, LED_STS_Pin, 1)
#define LED_STS_OFF         GPIO_WritePinOutput(LED_STS_GPIO, LED_STS_Pin, 0)
#define LED_STS_TOGGLE      GPIO_TogglePinsOutput(LED_STS_GPIO, 1 << LED_STS_Pin)

#define LED_RED_ON          GPIO_WritePinOutput(LED_RGB_GPIO, LED_RED_Pin, 1)
#define LED_RED_OFF         GPIO_WritePinOutput(LED_RGB_GPIO, LED_RED_Pin, 0)
#define LED_RED_TOGGLE      GPIO_TogglePinsOutput(LED_RGB_GPIO, 1 << LED_RED_Pin)

#define LED_GREEN_ON        GPIO_WritePinOutput(LED_RGB_GPIO, LED_GREEN_Pin, 1)
#define LED_GREEN_OFF       GPIO_WritePinOutput(LED_RGB_GPIO, LED_GREEN_Pin, 0)
#define LED_GREEN_TOGGLE    GPIO_TogglePinsOutput(LED_RGB_GPIO, 1 << LED_GREEN_Pin)

#define LED_BLUE_ON         GPIO_WritePinOutput(LED_RGB_GPIO, LED_BLUE_Pin, 1)
#define LED_BLUE_OFF        GPIO_WritePinOutput(LED_RGB_GPIO, LED_BLUE_Pin, 0)
#define LED_BLUE_TOGGLE     GPIO_TogglePinsOutput(LED_RGB_GPIO, 1 << LED_BLUE_Pin)

void LED_RGB_Init(void);

#endif

標頭檔案中通過巨集定義的方式實現了LED的亮滅和翻轉控制。

板載按鍵初始化

按鍵部分硬體原理圖,按下為低電平。

button_driver.c檔案內容


#include "button_driver.h"
#include "delay.h"
#include "led_driver.h"

//按鍵使用普通輸入GPIO方式
void Button_Init(void)
{
    gpio_pin_config_t io_init;
    port_pin_config_t config;

    io_init.outputLogic  = 0;
    io_init.pinDirection = kGPIO_DigitalInput;

    config.mux                  = kPORT_MuxAsGpio;              //通用GPIO
    config.lockRegister         = kPORT_LockRegister;           //PCR暫存器被鎖定,不能再次改變
    config.pullSelect           = kPORT_PullUp;                 //上拉
    config.slewRate             = kPORT_FastSlewRate;           //翻轉速率
    config.lockRegister         = kPORT_LockRegister;           //PCR暫存器被鎖定,不能再次改變
    config.passiveFilterEnable  = kPORT_PassiveFilterEnable;    //濾波器

    CLOCK_EnableClock(BTN_SW2_Clk_Name);
    CLOCK_EnableClock(BTN_SW3_Clk_Name);
//  CLOCK_EnableClock(BTN_SW4_Clk_Name);
//  CLOCK_EnableClock(BTN_SW5_Clk_Name);
    CLOCK_EnableClock(kCLOCK_Rgpio1);           //GPIOE配置需要使能這個時鐘

    //以下兩個函式功能一樣
    PORT_SetPinConfig(BTN_SW2_Port, BTN_SW2_Pin, &config);
    PORT_SetPinConfig(BTN_SW3_Port, BTN_SW3_Pin, &config);
    PORT_SetPinConfig(BTN_SW4_Port, BTN_SW4_Pin, &config);
    PORT_SetPinConfig(BTN_SW5_Port, BTN_SW5_Pin, &config);

//  PORT_SetPinMux(BTN_SW2_Port, BTN_SW2_Pin, kPORT_MuxAsGpio); //設定IO模式為通用GPIO
//  PORT_SetPinMux(BTN_SW3_Port, BTN_SW3_Pin, kPORT_MuxAsGpio); //設定IO模式為通用GPIO
//  PORT_SetPinMux(BTN_SW4_Port, BTN_SW4_Pin, kPORT_MuxAsGpio); //設定IO模式為通用GPIO
//  PORT_SetPinMux(BTN_SW5_Port, BTN_SW5_Pin, kPORT_MuxAsGpio); //設定IO模式為通用GPIO

    GPIO_PinInit(BTN_SW2_GPIO, BTN_SW2_Pin, &io_init);
    GPIO_PinInit(BTN_SW3_GPIO, BTN_SW3_Pin, &io_init);
    GPIO_PinInit(BTN_SW4_GPIO, BTN_SW4_Pin, &io_init);
    GPIO_PinInit(BTN_SW5_GPIO, BTN_SW5_Pin, &io_init);
}

//按鍵使用外部中斷初始化函式
void ButtonInterruptInit(void)
{
    gpio_pin_config_t io_init;
    port_pin_config_t config;

    io_init.outputLogic  = 0;
    io_init.pinDirection = kGPIO_DigitalInput;

    config.mux                  = kPORT_MuxAsGpio;              //通用GPIO
    config.lockRegister         = kPORT_LockRegister;           //PCR暫存器被鎖定,不能再次改變
    config.pullSelect           = kPORT_PullUp;                 //上拉
    config.slewRate             = kPORT_FastSlewRate;           //翻轉速率
    config.lockRegister         = kPORT_LockRegister;           //PCR暫存器被鎖定,不能再次改變
    config.passiveFilterEnable  = kPORT_PassiveFilterEnable;    //濾波器

    CLOCK_EnableClock(BTN_SW2_Clk_Name);
    CLOCK_EnableClock(BTN_SW3_Clk_Name);
//  CLOCK_EnableClock(BTN_SW4_Clk_Name);
//  CLOCK_EnableClock(BTN_SW5_Clk_Name);

    CLOCK_EnableClock(kCLOCK_Rgpio1);           //GPIOE配置需要使能這個時鐘

    //以下兩個函式功能一樣
    PORT_SetPinConfig(BTN_SW2_Port, BTN_SW2_Pin, &config);
    PORT_SetPinConfig(BTN_SW3_Port, BTN_SW3_Pin, &config);
    PORT_SetPinConfig(BTN_SW4_Port, BTN_SW4_Pin, &config);
    PORT_SetPinConfig(BTN_SW5_Port, BTN_SW5_Pin, &config);

    //設定中斷觸發方式
    PORT_SetPinInterruptConfig(BTN_SW2_Port, BTN_SW2_Pin, kPORT_InterruptFallingEdge);  //下降沿觸發中斷
    PORT_SetPinInterruptConfig(BTN_SW3_Port, BTN_SW3_Pin, kPORT_InterruptFallingEdge);
    PORT_SetPinInterruptConfig(BTN_SW4_Port, BTN_SW4_Pin, kPORT_InterruptFallingEdge);
    PORT_SetPinInterruptConfig(BTN_SW5_Port, BTN_SW5_Pin, kPORT_InterruptFallingEdge);

#if defined(CPU_RV32M1_ri5cy)
    //RI5CY Core GPIOE需要使能以下兩個函式, ZERO Core不用
    INTMUX_Init(INTMUX0);
    INTMUX_EnableInterrupt(INTMUX0, 0, PORTE_IRQn);
#endif

    EnableIRQ(BTN_SW2_IRQ);
    EnableIRQ(BTN_SW3_IRQ);
//  EnableIRQ(BTN_SW4_IRQ);
//  EnableIRQ(BTN_SW5_IRQ);

    GPIO_PinInit(BTN_SW2_GPIO, BTN_SW2_Pin, &io_init);
    GPIO_PinInit(BTN_SW3_GPIO, BTN_SW3_Pin, &io_init);
    GPIO_PinInit(BTN_SW4_GPIO, BTN_SW4_Pin, &io_init);
    GPIO_PinInit(BTN_SW5_GPIO, BTN_SW5_Pin, &io_init);
}

void PORTA_IRQHandler(void)
{
    GPIO_ClearPinsInterruptFlags(BTN_SW2_GPIO, 1U << BTN_SW2_Pin);
    LED_STS_TOGGLE;
    LOG("sw2 is pressed \r\n");
}

//GPIOE外部中斷函式
void PORTE_IRQHandler(void)
{
    uint32_t flag;

    flag = GPIO_GetPinsInterruptFlags(BTN_SW3_GPIO);

    GPIO_ClearPinsInterruptFlags(BTN_SW3_GPIO, 1U << BTN_SW3_Pin);
    GPIO_ClearPinsInterruptFlags(BTN_SW4_GPIO, 1U << BTN_SW4_Pin);
    GPIO_ClearPinsInterruptFlags(BTN_SW5_GPIO, 1U << BTN_SW5_Pin);

    if(flag & (1 << BTN_SW3_Pin))   //SW3產生中斷
    {
        LED_RED_TOGGLE;
        LOG("sw3 is pressed \r\n");
    }
    else if(flag & (1 << BTN_SW4_Pin))
    {
        LED_GREEN_TOGGLE;
        LOG("sw4 is pressed \r\n");
    }
    else if(flag & (1 << BTN_SW5_Pin))
    {
        LED_BLUE_TOGGLE;
        LOG("sw5 is pressed \r\n");
    }
}

//輪詢方式獲取按鍵狀態
uint8_t GetKey(void)
{
    uint8_t key = 1;
    //按鍵按下為0
    if(BTN_SW2_IN && BTN_SW3_IN && BTN_SW4_IN && BTN_SW5_IN)
    {
        Delay_ms(10);
        if(!BTN_SW2_IN)
            key = 2;
        else if(!BTN_SW3_IN)
            key = 3;
        else if(!BTN_SW4_IN)
            key = 4;
        else if(!BTN_SW5_IN)
            key = 5;
        while(!(BTN_SW2_IN && BTN_SW3_IN && BTN_SW4_IN && BTN_SW5_IN));
    }
    return key;
}

按鍵配置為上拉輸入模式,同樣如果使用GPIOE作為通用GPIO輸入,還需要使能Rgpio1時鐘:


    CLOCK_EnableClock(kCLOCK_Rgpio1);           //GPIOE配置需要使能這個時鐘

如果使用GPIOE的外部中斷功能,還需要使能INTMUX:


#if defined(CPU_RV32M1_ri5cy)
    //RI5CY Core GPIOE需要使能以下兩個函式, ZERO Core不用
    INTMUX_Init(INTMUX0);
    INTMUX_EnableInterrupt(INTMUX0, 0, PORTE_IRQn);
#endif

button_driver.h檔案內容


#ifndef __BUTTON_DRIVER_H__
#define __BUTTON_DRIVER_H__

#include "fsl_gpio.h"
#include "fsl_port.h"
#include "fsl_intmux.h"

/*
 * SW2 - A0
 * SW3 - E12
 * SW4 - E8
 * SW5 - E9
 * */

//按下為低電平

#define BTN_SW2_GPIO    GPIOA
#define BTN_SW3_GPIO    GPIOE
#define BTN_SW4_GPIO    GPIOE
#define BTN_SW5_GPIO    GPIOE

#define BTN_SW2_Pin     0
#define BTN_SW3_Pin     12
#define BTN_SW4_Pin     8
#define BTN_SW5_Pin     9

#define BTN_SW2_Port    PORTA
#define BTN_SW3_Port    PORTE
#define BTN_SW4_Port    PORTE
#define BTN_SW5_Port    PORTE

#define BTN_SW2_IRQ     PORTA_IRQn
#define BTN_SW3_IRQ     PORTE_IRQn
#define BTN_SW4_IRQ     PORTE_IRQn
#define BTN_SW5_IRQ     PORTE_IRQn

#define BTN_SW2_Clk_Name    kCLOCK_PortA
#define BTN_SW3_Clk_Name    kCLOCK_PortE
#define BTN_SW4_Clk_Name    kCLOCK_PortE
#define BTN_SW5_Clk_Name    kCLOCK_PortE

#define BTN_SW2_IN  GPIO_ReadPinInput(BTN_SW2_GPIO, BTN_SW2_Pin)
#define BTN_SW3_IN  GPIO_ReadPinInput(BTN_SW3_GPIO, BTN_SW3_Pin)
#define BTN_SW4_IN  GPIO_ReadPinInput(BTN_SW4_GPIO, BTN_SW4_Pin)
#define BTN_SW5_IN  GPIO_ReadPinInput(BTN_SW5_GPIO, BTN_SW5_Pin)

/*
#define BTN_SW2_IN  ReadGPIO(BTN_SW2_GPIO, BTN_SW2_Pin)
#define BTN_SW3_IN  ReadGPIO(BTN_SW3_GPIO, BTN_SW3_Pin)
#define BTN_SW4_IN  ReadGPIO(BTN_SW4_GPIO, BTN_SW4_Pin)
#define BTN_SW5_IN  ReadGPIO(BTN_SW5_GPIO, BTN_SW5_Pin)
*/

void Button_Init(void);
uint8_t GetKey(void);
void ButtonInterruptInit(void);

#endif

通過GPIO讀取函式來獲取按鍵輸入狀態,或者是通過中斷標誌來判斷輸入狀態。

主函式應用

使用外部中斷方式讀取按鍵輸入狀態。


#include "main.h"

extern uint32_t SystemCoreClock;

int main(void)
{
    BOARD_BootClockRUN();   //ϵͳʱ֓Ťփ

    UART0_Init();
    Delay_Init();

    LOG("SystemCoreClock: %ld \r\n", SystemCoreClock);

#if defined(CPU_RV32M1_ri5cy)
    LOG("RV32M1 RISC-V RI5CY Core Demo \r\n");
#elif defined(CPU_RV32M1_zero_riscy)
    LOG("RV32M1 RISC-V ZERO Core Demo \r\n");
#endif

    LED_RGB_Init();
//  Button_Init();
    ButtonInterruptInit();
    // LPMTR2_Init();
    // LPIT1_CH3_Init();

    while (1)
    {

    }
}

程式碼下載

織女星開發板VEGA_Lite支援從4個核啟動,所以在進行程式下載之前,要確認當前的啟動模式和當前的工程是對應的。如當前工程是使用RISC-V RI5CY核來驅動GPIO,那麼就需要配置晶片啟動模式為RI5CY核啟動。否則會不能下載。關於啟動模式的修改可以參考:織女星開發板啟動模式修改

  • RI5CY驅動GPIO原始碼下載:RI5CY_GPIO_Demo.rar
  • ZERO驅動GPIO原始碼下載:ZERO_GPIO_Demo.rar

總結

RV32M1晶片的GPIOE與其他幾組GPIO配置方法稍有不同,使用時要特別注意。

參考資料

  • MCUXpresso SDK API參考手冊
  • RV32M1_Vega_Develop_Environment_Setup.pdf
  • RV32M1資料手冊
  • RV32M1參考手冊
  • 織女星開發板快速入門指南.pdf

推薦閱讀

  • 真正的RISC-V開發板——VEGA織女星開發板開箱評測
  • 手把手教你搭建織女星開發板RISC-V開發環境
  • 織女星開發板啟動模式修改——從ARM M4核啟動
  • 織女星開發板偵錯程式升級為Jlink韌體
  • 織女星開發板RISC-V核心實現微秒級精確延時

  • 個人部落格:www.wangchaochao.top
  • 我的公眾號:mcu149