MSPM0G3507学习笔记(二) 超便捷配置led与按键

发布于:2025-06-18 ⋅ 阅读:(19) ⋅ 点赞:(0)

电赛备赛中,打算系统过一遍MSPM0G3507的各个部分,同时把过程记录下来。本系列以代码全部能用+复用性、可移植性高为目的。本系列所有的代码会开源至github,如果觉得有用,请点个赞/给我的github仓库点一颗star吧。

github地址:https://github.com/whyovo/MSPM0G3507Learning-Notes

(一)基础配置:https://blog.csdn.net/qq_23220445/article/details/148502065?spm=1001.2014.3001.5501

1.工程介绍

所有代码都在code中,device是设备相关代码,hardware是底层相关代码,ti是ti的库函数。

入口是main.c,general.h包含所有需要的头文件。

2.led与按键使用方法

在led.c里面添加led,key.c里面添加按键即可,像这样就添加了pb2与pb3这两个led,pa18这一个按键,按键需要指明按下时是高电平还是低电平(即active_level)

然后在main.c里面初始化即可直接使用

key_init();
led_init();

led支持开关、闪烁、呼吸灯与流水灯,分别调用以下函数即可:

void led_on(LED_Config *led);
void led_off(LED_Config *led);
void led_toggle(LED_Config *led);
void led_blink(LED_Config *led, uint32_t times, uint32_t delay_ms);
void led_breathing(LED_Config *led, uint32_t cycles, uint32_t duration_ms);
void led_water_flow(LED_Config *leds, uint8_t count, uint32_t delay_ms);

按键支持短按、长按与双击。

下面是一个综合案例,根据不同的按键操作触发不同的LED效果——短按让LED1闪烁3次,长按让LED1产生呼吸灯效果,双击触发两个LED的流水灯效果

main.c:

#include "general.h"
#include <stdio.h>

int main(void)
{
    SYSCFG_DL_init();
    key_init();  // 启用按键初始化
    led_init();  // 启用LED初始化
    while (1) {
        // 按键扫描并触发对应LED效果
        KEY_State key_state = key_scan(&key_configs[0]);
        
        switch (key_state) {
            case KEY_SHORT_PRESS:
                // 短按:LED1闪烁3次,每次闪烁间隔200ms
                led_blink(&led_configs[0], 3, 200);
                break;
                
            case KEY_LONG_PRESS:
                // 长按:LED1呼吸灯效果,2个周期,持续1000ms
                led_breathing(&led_configs[0], 2, 1000);
                break;
                
            case KEY_DOUBLE_CLICK:
                // 双击:两个LED流水灯效果
                led_water_flow(led_configs, 2, 300);
                break;

            default:
                // 无按键操作,不执行特殊效果
                break;
            
        }
        delay_ms(10);
    }
}

效果:

双击:流水灯效果

短按:LED1闪烁3次

长按:LED1呼吸灯2个周期,每个周期1秒

3.gpio使用方法

需要用gpio但不是led或者按键,用以下方法:

先在gpio.c里面定义引脚,说明是输出还是输入,上拉还是下拉,以及初始电平

然后直接在主函数调用gpio_init(),即可一键配置,然后直接调用函数即可:

// 函数声明
bool gpio_init_one(const GPIO_Config *config);
bool gpio_init_multiple(const GPIO_Config *configs, uint8_t count);
void gpio_set_pin(GPIO_Regs *port, uint32_t pin);
void gpio_clear_pin(GPIO_Regs *port, uint32_t pin);
void gpio_toggle_pin(GPIO_Regs *port, uint32_t pin);
void gpio_write_pin(GPIO_Regs *port, uint32_t pin, GPIO_Level level);
GPIO_Level gpio_read_pin(GPIO_Regs *port, uint32_t pin);
bool gpio_is_pin_set(GPIO_Regs *port, uint32_t pin);
void gpio_init(void);

4.部分代码参考

这里放led与key的代码,详细见github仓库里面的tjuwyh这个文件夹。

led.c

#include "led.h"
LED_Config led_configs[] = {
    {.port        = GPIOB,
     .pin         = DL_GPIO_PIN_2,
     .initialized = false}, // LED1
    {.port        = GPIOB,
     .pin         = DL_GPIO_PIN_3,
     .initialized = false}, // LED2
};

void led_init(void)
{
    for (int i = 0; i < sizeof(led_configs) / sizeof(led_configs[0]); i++) {
        // 创建临时GPIO配置结构体
        GPIO_Config gpio_config = {
            .port = led_configs[i].port,
            .pin = led_configs[i].pin,
            .dir = GPIO_DIR_OUTPUT,
            .pull = GPIO_PULL_NONE,
            .init_level = GPIO_HIGH  // 默认高电平
        };
        
        // 直接调用led_init_one初始化LED和GPIO
        if (!led_init_one(&led_configs[i], &gpio_config)) {
            // 初始化失败,可以添加错误处理
        }
    }
}

// LED初始化
bool led_init_one(LED_Config *led, const GPIO_Config *gpio_config)
{
    if (!led || !gpio_config) return false;

    // 检查GPIO配置是否为输出模式
    if (gpio_config->dir != GPIO_DIR_OUTPUT) {
        return false;
    }

    // 初始化GPIO
    if (!gpio_init_one(gpio_config)) {
        return false;
    }

    // 配置LED结构体
    led->port        = gpio_config->port;
    led->pin         = gpio_config->pin;
    led->initialized = true;

    return true;
}

// 检查LED是否已初始化
static bool led_is_initialized(LED_Config *led)
{
    return led && led->initialized;
}

void led_on(LED_Config *led)
{
    if (!led_is_initialized(led)) return;
    gpio_clear_pin(led->port, led->pin); // 共阴极LED,低电平点亮
}

void led_off(LED_Config *led)
{
    if (!led_is_initialized(led)) return;
    gpio_set_pin(led->port, led->pin); // 共阴极LED,高电平熄灭
}

void led_toggle(LED_Config *led)
{
    if (!led_is_initialized(led)) return;
    gpio_toggle_pin(led->port, led->pin);
}

void led_blink(LED_Config *led, uint32_t times, uint32_t delay_ms_val)
{
    if (!led_is_initialized(led)) return;

    for (uint32_t i = 0; i < times; i++) {
        led_on(led);
        delay_ms(delay_ms_val);
        led_off(led);
        delay_ms(delay_ms_val);
    }
}

void led_breathing(LED_Config *led, uint32_t cycles, uint32_t duration_ms)
{
    if (!led_is_initialized(led)) return;

    // 呼吸灯参数配置
    const uint32_t steps         = 100;                       // 每个方向的步数
    const uint32_t pwm_period_us = 1000;                      // PWM周期1ms
    const uint32_t step_delay_ms = duration_ms / (steps * 2); // 每步延时

    for (uint32_t cycle = 0; cycle < cycles; cycle++) {
        // 渐亮过程
        for (uint32_t i = 1; i <= steps; i++) {
            // 计算占空比 (1-100%)
            uint32_t duty_percent = (i * 100) / steps;
            uint32_t on_time_us   = (pwm_period_us * duty_percent) / 100;
            uint32_t off_time_us  = pwm_period_us - on_time_us;

            // PWM周期数,确保有足够的时间看到效果
            uint32_t pwm_cycles = (step_delay_ms * 1000) / pwm_period_us;
            if (pwm_cycles < 1) pwm_cycles = 1;

            // 执行PWM - 使用GPIO函数
            for (uint32_t j = 0; j < pwm_cycles; j++) {
                if (on_time_us > 0) {
                    gpio_clear_pin(led->port, led->pin); // LED点亮
                    delay_us(on_time_us);
                }
                if (off_time_us > 0) {
                    gpio_set_pin(led->port, led->pin); // LED熄灭
                    delay_us(off_time_us);
                }
            }
        }

        // 渐暗过程
        for (uint32_t i = steps; i >= 1; i--) {
            // 计算占空比 (100%-1%)
            uint32_t duty_percent = (i * 100) / steps;
            uint32_t on_time_us   = (pwm_period_us * duty_percent) / 100;
            uint32_t off_time_us  = pwm_period_us - on_time_us;

            // PWM周期数
            uint32_t pwm_cycles = (step_delay_ms * 1000) / pwm_period_us;
            if (pwm_cycles < 1) pwm_cycles = 1;

            // 执行PWM - 使用GPIO函数
            for (uint32_t j = 0; j < pwm_cycles; j++) {
                if (on_time_us > 0) {
                    gpio_clear_pin(led->port, led->pin); // LED点亮
                    delay_us(on_time_us);
                }
                if (off_time_us > 0) {
                    gpio_set_pin(led->port, led->pin); // LED熄灭
                    delay_us(off_time_us);
                }
            }
        }
    }

    // 确保最后LED是关闭状态
    led_off(led);
}

void led_water_flow(LED_Config *leds, uint8_t count, uint32_t delay_ms_val)
{
    if (!leds) return;

    for (uint8_t i = 0; i < count; i++) {
        if (led_is_initialized(&leds[i])) {
            led_on(&leds[i]);
            delay_ms(delay_ms_val);
            led_off(&leds[i]);
        }
    }
}

led.h

#ifndef __LED_H
#define __LED_H

#if __has_include("gpio.h")
#include "gpio.h"

#else
// 如果没有定义GPIO模块,使用其他库,以HAL库为例
#include "stm32f1xx_hal.h" // 根据实际MCU型号调整

// 定义GPIO类型别名
typedef GPIO_TypeDef GPIO_Regs;

// 将自定义GPIO函数映射到HAL库函数
#define gpio_set_pin(port, pin)          HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET)
#define gpio_clear_pin(port, pin)        HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET)
#define gpio_toggle_pin(port, pin)       HAL_GPIO_TogglePin(port, pin)
#define gpio_write_pin(port, pin, level) HAL_GPIO_WritePin(port, pin, (level) ? GPIO_PIN_SET : GPIO_PIN_RESET)
#define gpio_read_pin(port, pin)         (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_SET ? 1 : 0)
#define gpio_is_pin_set(port, pin)       (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_SET)

// 定义GPIO配置结构体(简化版)
typedef struct {
    GPIO_Regs *port;
    uint32_t pin;
    uint8_t dir;        // 方向
    uint8_t init_level; // 初始电平
    uint8_t pull;       // 上拉下拉
} GPIO_Config;

// 定义枚举值
#define GPIO_DIR_INPUT                   0
#define GPIO_DIR_OUTPUT                  1
#define GPIO_LOW                         0
#define GPIO_HIGH                        1

// 提供兼容函数声明
static inline bool gpio_init_one(const GPIO_Config *config)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin              = config->pin;
    GPIO_InitStruct.Mode             = (config->dir == GPIO_DIR_OUTPUT) ? GPIO_MODE_OUTPUT_PP : GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull             = GPIO_NOPULL; // 简化处理
    GPIO_InitStruct.Speed            = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(config->port, &GPIO_InitStruct);

    if (config->dir == GPIO_DIR_OUTPUT && config->init_level == GPIO_HIGH) {
        HAL_GPIO_WritePin(config->port, config->pin, GPIO_PIN_SET);
    }
    return true;
}
#endif

#include "gpio.h"
#include "delay.h"

// LED配置结构体
typedef struct {
    GPIO_Regs *port;
    uint32_t pin;
    bool initialized; // 标记是否已初始化
} LED_Config;

// LED初始化和控制函数
bool led_init_one(LED_Config *led, const GPIO_Config *gpio_config);
void led_on(LED_Config *led);
void led_off(LED_Config *led);
void led_toggle(LED_Config *led);
void led_blink(LED_Config *led, uint32_t times, uint32_t delay_ms);
void led_breathing(LED_Config *led, uint32_t cycles, uint32_t duration_ms);
void led_water_flow(LED_Config *leds, uint8_t count, uint32_t delay_ms);
void led_init(void);

// 便捷宏定义
#define LED_ON(led)     led_on(led)
#define LED_OFF(led)    led_off(led)
#define LED_TOGGLE(led) led_toggle(led)

extern LED_Config led_configs[];

#endif

key.c

#include "key.h"

KEY_Config key_configs[] = {
    {.port         = GPIOA,
     .pin          = DL_GPIO_PIN_18,
     .active_level = KEY_ACTIVE_HIGH,
     .initialized  = false},          // KEY1
};

#define KEY_DEBOUNCE_TIME     20  // 消抖时间 20ms
#define KEY_LONG_PRESS_TIME   500 // 长按时间 500ms
#define KEY_DOUBLE_CLICK_TIME 300 // 双击间隔时间 300ms

// 按键状态定义
typedef enum {
    KEY_STATE_IDLE = 0,   // 空闲状态
    KEY_STATE_PRESSED,    // 按下状态
    KEY_STATE_RELEASED,   // 释放状态
    KEY_STATE_DOUBLE_WAIT // 双击等待状态
} KEY_StateMachine;

void key_init(void)
{
    for (int i = 0; i < sizeof(key_configs) / sizeof(key_configs[0]); i++) {
        // 根据按键有效电平选择合适的上拉/下拉配置
        GPIO_Pull pull_config;
        if (key_configs[i].active_level == KEY_ACTIVE_HIGH) {
            pull_config = GPIO_PULL_DOWN;  // 高电平有效时使用下拉电阻
        } else {
            pull_config = GPIO_PULL_UP;    // 低电平有效时使用上拉电阻
        }
        
        // 创建临时GPIO配置结构体
        GPIO_Config gpio_config = {
            .port = key_configs[i].port,
            .pin = key_configs[i].pin,
            .dir = GPIO_DIR_INPUT,
            .pull = pull_config,          // 根据按键有效电平配置上拉/下拉
            .init_level = GPIO_HIGH       // 对输入引脚无实际影响
        };
        
        // 直接调用key_init_one初始化按键和GPIO
        if (!key_init_one(&key_configs[i], &gpio_config, key_configs[i].active_level)) {
            // 初始化失败,可以添加错误处理
        }
    }
}   

// KEY初始化
bool key_init_one(KEY_Config *key, const GPIO_Config *gpio_config, KEY_ActiveLevel active_level)
{
    if (!key || !gpio_config) return false;

    // 检查GPIO配置是否为输入模式
    if (gpio_config->dir != GPIO_DIR_INPUT) {
        return false;
    }

    // 初始化GPIO
    if (!gpio_init_one(gpio_config)) {
        return false;
    }

    // 配置KEY结构体
    key->port         = gpio_config->port;
    key->pin          = gpio_config->pin;
    key->active_level = active_level;
    key->initialized  = true;

    // 初始化内部状态
    key->last_time       = 0;
    key->press_count     = 0;
    key->long_press_flag = 0;

    return true;
}

// 检查KEY是否已初始化
static bool key_is_initialized(KEY_Config *key)
{
    return key && key->initialized;
}

uint8_t key_read(KEY_Config *key)
{
    if (!key_is_initialized(key)) return 0;

    uint8_t pin_state = DL_GPIO_readPins(key->port, key->pin) ? 1 : 0;

    // 根据配置的有效电平返回按键状态
    if (key->active_level == KEY_ACTIVE_HIGH) {
        return pin_state; // 高电平有效
    } else {
        return !pin_state; // 低电平有效
    }
}

KEY_State key_scan(KEY_Config *key)
{
    if (!key_is_initialized(key)) return KEY_NONE;

    static KEY_StateMachine key_state  = KEY_STATE_IDLE;
    static uint32_t press_start_time   = 0;
    static uint32_t release_start_time = 0;
    static uint32_t last_scan_time     = 0;

    uint32_t current_time = get_tick();

    // 每1ms扫描一次
    if (current_time - last_scan_time < 1) {
        return KEY_NONE;
    }
    last_scan_time = current_time;

    uint8_t key_pressed           = key_read(key);
    static uint8_t last_key_state = 0;

    // 消抖处理
    if (key_pressed != last_key_state) {
        delay_ms(KEY_DEBOUNCE_TIME);
        if (key_read(key) != key_pressed) {
            return KEY_NONE; // 抖动,忽略
        }
        last_key_state = key_pressed;
    }

    switch (key_state) {
        case KEY_STATE_IDLE:
            if (key_pressed) {
                key_state        = KEY_STATE_PRESSED;
                press_start_time = current_time;
            }
            break;

        case KEY_STATE_PRESSED:
            if (!key_pressed) {
                // 按键释放,判断按下时间
                uint32_t press_duration = current_time - press_start_time;

                if (press_duration >= KEY_LONG_PRESS_TIME) {
                    // 长按事件,直接回到空闲状态
                    key_state = KEY_STATE_IDLE;
                    return KEY_LONG_PRESS;
                } else {
                    // 进入释放状态,等待可能的双击
                    key_state          = KEY_STATE_RELEASED;
                    release_start_time = current_time;
                }
            }
            break;

        case KEY_STATE_RELEASED:
            if (key_pressed) {
                // 在释放状态内再次按下,进入双击等待状态
                key_state        = KEY_STATE_DOUBLE_WAIT;
                press_start_time = current_time;
            } else if (current_time - release_start_time >= KEY_DOUBLE_CLICK_TIME) {
                // 超时未再次按下,判定为单击
                key_state = KEY_STATE_IDLE;
                return KEY_SHORT_PRESS;
            }
            break;

        case KEY_STATE_DOUBLE_WAIT:
            if (!key_pressed) {
                // 双击的第二次按下释放
                key_state = KEY_STATE_IDLE;
                return KEY_DOUBLE_CLICK;
            } else if (current_time - press_start_time >= KEY_LONG_PRESS_TIME) {
                // 如果第二次按下时间过长,按长按处理
                key_state = KEY_STATE_IDLE;
                return KEY_LONG_PRESS;
            }
            break;
    }

    return KEY_NONE;
}

key.h

#ifndef __KEY_H
#define __KEY_H

#include "sys_init.h"
#include "gpio.h"
#include "delay.h"

typedef enum {
    KEY_NONE = 0,
    KEY_SHORT_PRESS,
    KEY_LONG_PRESS,
    KEY_DOUBLE_CLICK
} KEY_State;

typedef enum {
    KEY_ACTIVE_LOW  = 0, // 按键按下为低电平
    KEY_ACTIVE_HIGH = 1  // 按键按下为高电平
} KEY_ActiveLevel;

// KEY配置结构体
typedef struct {
    GPIO_Regs *port;
    uint32_t pin;
    KEY_ActiveLevel active_level; // 按键有效电平
    bool initialized;             // 标记是否已初始化

    // 内部状态变量
    uint32_t last_time;      // 上次扫描时间
    uint8_t press_count;     // 按键计数
    uint8_t long_press_flag; // 长按标志
} KEY_Config;

// 函数声明
bool key_init_one(KEY_Config *key, const GPIO_Config *gpio_config, KEY_ActiveLevel active_level);
KEY_State key_scan(KEY_Config *key);
uint8_t key_read(KEY_Config *key);

// 便捷宏定义
#define KEY_IS_PRESSED(key)  (key_read(key) == 1)
#define KEY_IS_RELEASED(key) (key_read(key) == 0)
void key_init(void);

extern KEY_Config key_configs[];

#endif

网站公告

今日签到

点亮在社区的每一天
去签到