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

电赛备赛中,打算系统过一遍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里面初始化即可直接使用

c 复制代码
key_init();
led_init();

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

c 复制代码
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:

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(),即可一键配置,然后直接调用函数即可:

c 复制代码
// 函数声明
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

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

c 复制代码
#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

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

c 复制代码
#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
相关推荐
Ccjf酷儿4 分钟前
操作系统 李治军 1 操作系统基础
笔记
Ghost-Silver6 分钟前
《星火》——关于Deepseek的进化速度
笔记·算法
古译汉书8 分钟前
嵌入式笔记(个人总结)
数据结构·笔记·stm32·单片机·嵌入式硬件
雍凉明月夜26 分钟前
视觉opencv学习笔记Ⅲ
笔记·opencv·学习
bai5459361 小时前
STM32 定时器(Timer)
stm32·单片机·嵌入式硬件
客梦1 小时前
Java 道路信息系统
java·笔记
一只侯子10 小时前
Face AE Tuning
图像处理·笔记·学习·算法·计算机视觉
whale fall13 小时前
【剑雅14】笔记
笔记
星空的资源小屋14 小时前
跨平台下载神器ArrowDL,一网打尽所有资源
javascript·笔记·django
Xudde.14 小时前
Quick2靶机渗透
笔记·学习·安全·web安全·php