电赛备赛中,打算系统过一遍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