RISC-V MCU中文社区

RVMCU课堂「11」: 手把手教你玩转RVSTAR—GPIO使用篇

发表于 2021-04-16 15:35:01
3
10925
2

​GPIO是通用输入输出接口(General Purpose Input Output)的简称,是微控制器最基本也是最常用的外设,本期内容将介绍GPIO的基本原理,然后通过「点亮LED」「按键控制LED」两个小实验带领大家了解GPIO基本输出与输入功能的使用方法。


系统环境

Windows 10-64bit


软件平台

NucleiStudio IDE 202102版

或 PlatformIO IDE


硬件需求

RV-STAR开发板





GPIO基本原理


GPIO的全称是通用输入输出接口(General Purpose Input/Output),是很多外设能够正常工作的基础,MCU上除了一些特定功能的引脚(如电源引脚)外,其他的引脚基本都可以作为GPIO来使用。

GD32VF103的GPIO端口以“组”的形式工作,命名方式为Px(x=A, B, C, D, E···),每组配置有16个引脚。GPIO端口和其他的备用功能(AFs)共用引脚,在特定的封装下获得最大的灵活性。

每个GPIO引脚都可以通过软件配置为输出(推挽或开漏)、输入、外设备用功能或者模拟模式。每个GPIO引脚都可以配置为上拉、下拉或浮空。除模拟模式外,所有的GPIO引脚都具备大电流驱动能力。



(标准GPIO端口的基本结构)


GPIO具有下列特征:


  • 输入/输出方向控制

  • 施密特触发器输入功能使能控制

  • 每个引脚都具有弱上拉/下拉功能

  • 推挽/开漏输出使能控制

  • 置位/复位输出使能

  • 可编程触发沿的外部中断——使用EXTI配置寄存器

  • 模拟输入/输出配置

  • 备用功能输入/输出配置

  • 端口锁定配置






实验1:点亮LED


RVSTAR有一个板载的RGB LED,通过PA1、PA2、PA3三个引脚控制LED_G、LED_R、LED_B三个颜色,通过原理图可以得知:要点亮LED需要通过GPIO引脚输出一个低电平,当然也可以通过输出PWM控制三个颜色LED的亮度,进而组合产生不同的颜色。

下面将通过实验点亮LED_G,并令其以2秒为间隔闪烁:

(RGB LED原理图 - 1)

(RGB LED原理图 - 2)


首先参照之前的教程,使用NucleiStudio IDE或PlatformIO IDE创建工程,在main.c文件内编写代码如下:


/* 实验1:点亮LED */
#include "nuclei_sdk_hal.h"

void led_config();
void led_on();
void led_off();

int main(void)
{
                         // 首先对外设进行初始化操作
    led_config();

    while (1)
    {
        led_on();        // 点亮LED
        delay_1ms(2000); // 延时2秒

        led_off();       // 熄灭LED
        delay_1ms(2000); // 延时2秒
    }
}

void led_config()
{
    // 使能GPIOA端口的外设时钟
    rcu_periph_clock_enable(RCU_GPIOA);
    // 将PA1初始化为推挽输出模式
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
}

void led_on()
{
    // 将输出引脚置位为0,输出低电平,灯亮
    gpio_bit_reset(GPIOA, GPIO_PIN_1);
}

void led_off()
{
    // 将输出引脚置位为1,输出高电平,灯熄
    gpio_bit_set(GPIOA, GPIO_PIN_1);
}

然后将工程文件进行编译和上传,将可以观察到板载的绿色LED以2秒为间隔进行闪烁。
运行结果如下:






实验2:按键控制LED


实验1我们使用了GPIO的输出功能点亮了LED,实验2将带领大家使用GPIO的输入功能:用RVSTAR的板载按键控制LED的亮和灭,在使用它之前我们需要了解一点按键消抖的知识。



(按键的原理图)


RVSTAR有一个板载的按键,连接到了MCU的PA1引脚上,按键没按下时,PA1引脚通过一个下拉电阻接地,处于低电平状态,按键按下时PA1引脚被拉高到3.3V,处于高电平状态。由于按键是机械结构,在按下和弹起的瞬间会产生抖动,即引脚上的电平状态不会被立刻拉高或拉低到稳定状态,因此为了能够准确检测用户的按键操作往往需要通过一定的方法进行消抖处理




常用的按键消抖方法有硬件消抖软件消抖:硬件消抖是通过在按键两端增加电容的方式实现的,成本较高;而软件消抖是在第一次检测到电平变化后延时一定时间后再次检测电平状态,如果检测到仍处于按下时的电平状态,那么说明用户进行了完整的按键操作,这个延时时间跟具体的硬件有关,经测试,在RVSTAR的按键上大约需要是设置100毫秒的延时比较合适。

使用NucleiStudio IDE或PlatformIO IDE创建工程,在main.c文件内编写代码如下:


/* 实验2:按键控制LED */
#include "nuclei_sdk_hal.h"

void led_config();
void led_on();
void led_off();

void key_config();
bit_status key_get_status();

bit_status led_status = 0// 用来记录led的当前状态

int main(void)
{
    led_config();
    key_config();
    led_off();

    while (1)
    {
             // 第一次检测按键按下
        if(key_get_status() == SET)
        {
            // 软件延时100ms用以消除抖动
            delay_1ms(100);
            // 再次检测按键是否按下
            if(key_get_status() == SET)
            {
                switch (led_status)
                {
                case 0:
                    led_on();
                    break;
                case 1:
                    led_off();
                    break;
                default:
                    break;
                }
                led_status = !led_status; // 每次按键操作后切换led的状态
            }
        }
    }
}

void led_config()
{
    // 打开GPIOA端口的外设时钟
    rcu_periph_clock_enable(RCU_GPIOA);
    // 将PA1初始化为推挽输出模式
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
}

void led_on()
{
    // 将输出引脚置位为0,输出低电平,灯亮
    gpio_bit_reset(GPIOA, GPIO_PIN_1);
}

void led_off()
{
    // 将输出引脚置位为1,输出高电平,灯熄
    gpio_bit_set(GPIOA, GPIO_PIN_1);
}

void key_config()
{
    rcu_periph_clock_enable(RCU_GPIOA); // 开启外设时钟
    rcu_periph_clock_enable(RCU_AF); // 开启复用时钟
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_0); // 将PA0初始化为浮空输入模式
}

bit_status key_get_status()
{
    // 获得输入引脚的状态,其中bit_status是一个固件库定义的枚举类型
    return gpio_input_bit_get(GPIOA, GPIO_PIN_0);
}

然后将工程文件进行编译和上传,将可以观察到:上电后LED不亮,每次按下按键后LED的状态会发生切换。

运行结果如下:







实验完整资料

 

实验源码:
https://github.com/Nuclei-Software/nuclei-board-labs/tree/master/rvstar/gpio

    原理图:

https://www.rvmcu.com/quickstart-doc-u-pdf-id-235.html




喜欢2
用户评论 (3)
  • J

    2021-09-01 16:52:26 J 1#

    我是使用demosoc的SOC文件夹编译的话,很多像   rcu_periph_clock_enable    这样的函数找不到,请问在nuclei-sdk/demosoc文件夹中有同样功能的其他名字的函数呢?还是需要自己写出这些功能函数?

  • 创客大杨

    2021-07-23 10:13:55 创客大杨 2#

    按照实验2:按键控制LED的程序示例,长按会产生多次触发,出现LED闪烁。主程序中加上一个单次触发判断会解决这个问题。见以下字体加粗部分

    int main(void)
    {
        led_config();
        key_config();
        led_off();

        while (1)
        {
            // 检测按键为松开状态,确保按一次触发一次,避免长按产生多次触发
            if(key_get_status() == RESET)
            {
                delay_1ms(5);
                    // 第一次检测按键按下
                if(key_get_status() == SET)
                {
                    // 软件延时100用以消除抖动和防止误触碰,按下超过100ms或100ms内多次按下为明确按下
                    delay_1ms(5);
                    // 再次检测按键是否明确按下
                    if(key_get_status() == SET)
                    {
                        switch (led_status)
                        {
                        case 0:
                            led_G_on();
                            break;
                        case 1:
                            led_R_on();
                            break;
                        case 2:
                            led_B_on();
                            break;
                        default:
                            break;
                        }
                        led_status = (led_status+1) %3// 每次按键操作后切换led的状态
                    }
                }
            }   
        }
    }

    这种写法虽然避免了按下时的“闪烁”,但就无法检测用户的“长按”行为,实际上应该通过后沿检测用户的“短按”

  • 2021-07-22 22:45:26 3#

    按照实验2:按键控制LED的程序示例,长按会产生多次触发,出现LED闪烁。主程序中加上一个单次触发判断会解决这个问题。见以下字体加粗部分

    int main(void)
    {
        led_config();
        key_config();
        led_off();

        while (1)
        {
            // 检测按键为松开状态,确保按一次触发一次,避免长按产生多次触发
            if(key_get_status() == RESET)
            {
                delay_1ms(5);
                    // 第一次检测按键按下
                if(key_get_status() == SET)
                {
                    // 软件延时100用以消除抖动和防止误触碰,按下超过100ms或100ms内多次按下为明确按下
                    delay_1ms(5);
                    // 再次检测按键是否明确按下
                    if(key_get_status() == SET)
                    {
                        switch (led_status)
                        {
                        case 0:
                            led_G_on();
                            break;
                        case 1:
                            led_R_on();
                            break;
                        case 2:
                            led_B_on();
                            break;
                        default:
                            break;
                        }
                        led_status = (led_status+1) %3// 每次按键操作后切换led的状态
                    }
                }
            }   
        }
    }

Fish

Fish 实名认证

懒的都不写签名

积分
问答
粉丝
关注
专栏作者
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板