当前位置: 首页 > news >正文

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里面初始化即可直接使用

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次,每次闪烁间隔200msled_blink(&led_configs[0], 3, 200);break;case KEY_LONG_PRESS:// 长按:LED1呼吸灯效果,2个周期,持续1000msled_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和GPIOif (!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;}// 初始化GPIOif (!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周期1msconst 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初始化按键和GPIOif (!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;}// 初始化GPIOif (!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

相关文章:

  • linux thermal framework(5)_thermal core
  • 60days challenge day34
  • Vue3+TypeScript 导入枚举(Enum)最佳实践
  • Docker 镜像相关命令介绍与示例
  • 如何为你的工作站添加“一键切换显示器接口”功能?
  • 限流系列之三:TDMQ for Apache Pulsar 限流技术深度解析
  • 聊聊 Pulsar:Consumer 源码解析
  • Kafka 4.0.0集群部署
  • coze中怎么创建插件
  • KVM高级功能部署
  • 死锁、线程总结
  • 批量调用接口(ApiFox + Postman)
  • 【记录】Ubuntu挂载硬盘
  • ArcGIS Pro无插件加载(无偏移)天地图!一次添加长久使用
  • Langchain入门指南:一次讲清基本用法
  • jenkins打包问题jar问题
  • django 调用paramiko 向windows 上传文件
  • linux基础重定向及组合重定向
  • SCADA技术与市场全景解析
  • vue3 - 自定义hook
  • 泰安公司网站开发/简单的个人主页网站制作
  • 秦皇岛建设工程/站群优化公司
  • 学3dmax做效果图的网站/蔡甸seo排名公司
  • 做网站设计学那个专业好/啥都能看的浏览器
  • 有哪些网站可以做h5/网站网络推广优化
  • 网站建设建站知识/整站优化seo