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

[电赛]MSPM0G3507学习笔记(二) GPIO:led与按键(流水灯、呼吸灯,短按长按与双击,ui预览)

电赛备赛中,打算系统过一遍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.代码使用方法

1.1led灯

我编写的代码,led的配置极其简单,如下文,仅需指定gpio组与端口号即可,比如下面就定义了两个led灯,分别对应B2和B3。
// LED配置LED_Config led1   = {GPIOB, DL_GPIO_PIN_2};LED_Config led2   = {GPIOB, DL_GPIO_PIN_3};

接下来可以快速实现开关led、流水灯、呼吸灯,调用方式如下:

功能: 点亮指定的LED

// 点亮led1
led_on(&led1);// 点亮led2  
led_on(&led2);

功能: 熄灭指定的LED

// 熄灭led1
led_off(&led1);// 熄灭led2
led_off(&led2);

功能: 切换LED状态(亮变灭,灭变亮)

// 切换led1状态
led_toggle(&led1);// 切换led2状态
led_toggle(&led2);

功能: 让LED闪烁指定次数
参数:

  • led: LED配置结构体指针,指向要闪烁的LED配置
  • times: 闪烁次数
  • delay_ms: 每次闪烁的间隔时间(毫秒)
// led1闪烁5次,每次间隔500毫秒
led_blink(&led1, 5, 500);// led2闪烁10次,每次间隔200毫秒
led_blink(&led2, 10, 200);

功能: 创建LED呼吸灯效果(渐亮渐暗)
参数:

  • led: LED配置结构体指针,指向要产生呼吸效果的LED配置
  • cycles: 呼吸循环次数
  • duration_ms: 每个呼吸周期的持续时间(毫秒)
// led1呼吸3个周期,每个周期2000毫秒
led_breathing(&led1, 3, 2000);// led2呼吸5个周期,每个周期1500毫秒
led_breathing(&led2, 5, 1500);

功能: 创建流水灯效果(多个LED依次点亮)
参数:

  • leds: LED配置结构体数组指针,包含多个LED的配置
  • count: LED数组中LED的数量
  • delay_ms: 流水灯切换间隔时间(毫秒)
// 使用led1和led2创建流水灯,切换间隔300毫秒
LED_Config led_array[] = {led1, led2};
led_water_flow(led_array, 2, 250);

1.2按键(非外部中断)

如下文,仅需指定端口号+按下是高电平还是低电平,你便配置了按键
// 按键配置 - 高电平有效KEY_Config key1 = {GPIOA, DL_GPIO_PIN_18, KEY_ACTIVE_HIGH};

以轮询的方式使用以下函数(放入while循环)即可完成短按、双击、长按的逻辑写作,细节无需关心,参数可以在key.h里面调节。

KEY_State key_state = key_scan(&key1);switch (key_state) {case KEY_SHORT_PRESS:led_blink(&led1, 3, 200); // 短按逻辑break;case KEY_LONG_PRESS:led_breathing(&led1, 2, 1000); // 长按逻辑break;case KEY_DOUBLE_CLICK:led_water_flow(leds, 2, 300);// 双击逻辑break;default:break;}

2.综合使用示例

main.c实现了一个基于按键控制的LED灯效果演示程序:程序初始化后点亮两个LED灯(B2和B3端口),然后持续扫描A18端口的按键输入,根据不同的按键操作触发不同的LED效果——**短按让LED1闪烁3次,长按让LED1产生呼吸灯效果,双击触发两个LED的流水灯效果**。
#include "general.h"int main(void)
{SYSCFG_DL_init();// LED配置LED_Config led1   = {GPIOB, DL_GPIO_PIN_2};LED_Config led2   = {GPIOB, DL_GPIO_PIN_3};LED_Config leds[] = {led1, led2};// 按键配置 - 高电平有效KEY_Config key1 = {GPIOA, DL_GPIO_PIN_18, KEY_ACTIVE_HIGH};// 初始化时点亮LEDled_on(&led1);led_on(&led2);while (1) {KEY_State key_state = key_scan(&key1);switch (key_state) {case KEY_SHORT_PRESS:led_blink(&led1, 3, 200); // 短按:LED1闪烁3次break;case KEY_LONG_PRESS:led_breathing(&led1, 2, 1000); // 长按:LED1呼吸灯2个周期,每个周期1秒break;case KEY_DOUBLE_CLICK:// 双击:流水灯效果led_water_flow(leds, 2, 300);break;default:break;}delay_ms(1);}
}

效果如下:

双击:流水灯效果

短按:LED1闪烁3次

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

3.gui预览

感觉ti的gui不是那么好用,刚好最近学了点前端vue3,就大致搓了个demo,但是功能还没有完全实现。后端用的python的flask,http协议连接。后续有时间会完善。

4.代码开源

led.c
#include "led.h"void led_on(LED_Config *led)
{DL_GPIO_setPins(led->port, led->pin);
}void led_off(LED_Config *led)
{DL_GPIO_clearPins(led->port, led->pin);
}void led_toggle(LED_Config *led)
{DL_GPIO_togglePins(led->port, led->pin);
}void led_blink(LED_Config *led, uint32_t times, uint32_t delay_ms_val)
{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)
{// 计算每个呼吸周期的步数和每步的延时uint32_t steps         = 100;                                // 每个方向100步uint32_t step_delay_us = (duration_ms * 1000) / (steps * 2); // 每步的微秒延时for (uint32_t cycle = 0; cycle < cycles; cycle++) {// 渐亮for (uint32_t i = 0; i < steps; i++) {uint32_t on_time  = (i * 10 * duration_ms) / 1000; // 根据时长调整亮度时间uint32_t off_time = step_delay_us - on_time;if (on_time > 0) {led_on(led);delay_us(on_time);}if (off_time > 0) {led_off(led);delay_us(off_time);}}// 渐暗for (uint32_t i = steps; i > 0; i--) {uint32_t on_time  = (i * 10 * duration_ms) / 1000; // 根据时长调整亮度时间uint32_t off_time = step_delay_us - on_time;if (on_time > 0) {led_on(led);delay_us(on_time);}if (off_time > 0) {led_off(led);delay_us(off_time);}}}
}void led_water_flow(LED_Config *leds, uint8_t count, uint32_t delay_ms_val)
{for (uint8_t i = 0; i < count; i++) {led_on(&leds[i]);delay_ms(delay_ms_val);led_off(&leds[i]);}
}

led.h

#ifndef __LED_H
#define __LED_H#include "ti_msp_dl_config.h"
#include "delay.h"typedef struct {GPIO_Regs *port;uint32_t pin;
} LED_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);#endif

delay.c

#include "delay.h"static volatile uint32_t tick_count = 0;void SysTick_Handler(void)
{tick_count++;
}uint32_t get_tick(void)
{return tick_count;
}void delay_ms(uint32_t ms)
{if (ms == 0) return;uint32_t start_tick = get_tick();while ((get_tick() - start_tick) < ms) {// 等待毫秒级延时__WFE(); // 等待事件,降低功耗}
}void delay_us(uint32_t us)
{if (us == 0) return;// 对于微秒延时,使用循环计数方式// 32MHz时钟,每个周期31.25ns,需要32个周期约等于1usuint32_t cycles = (CPUCLK_FREQ / 1000000) * us;// 考虑函数调用开销,进行补偿if (cycles > 20) {cycles -= 20; // 减去函数调用开销}delay_cycles(cycles);
}

delay.h

#ifndef __DELAY_H
#define __DELAY_H#include "ti_msp_dl_config.h"// 系统时钟频率宏定义
#ifndef CPUCLK_FREQ
#define CPUCLK_FREQ 32000000
#endifvoid delay_ms(uint32_t ms);
void delay_us(uint32_t us);
uint32_t get_tick(void);#endif

key.c

#include "key.h"#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;static KEY_StateMachine key_state  = KEY_STATE_IDLE;
static uint32_t press_start_time   = 0;
static uint32_t release_start_time = 0;uint8_t key_read(KEY_Config *key)
{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)
{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 "ti_msp_dl_config.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_Config key1 = {GPIOA, DL_GPIO_PIN_18, KEY_ACTIVE_HIGH};
typedef struct {GPIO_Regs *port;uint32_t pin;KEY_ActiveLevel active_level; // 按键有效电平uint32_t last_time;           // 默认为0uint8_t press_count;          // 默认为0uint8_t long_press_flag;      // 默认为0
} KEY_Config;KEY_State key_scan(KEY_Config *key);
uint8_t key_read(KEY_Config *key);#endif

ti_msp_dl_config.c

#include "ti_msp_dl_config.h"SYSCONFIG_WEAK void SYSCFG_DL_init(void)
{SYSCFG_DL_initPower();SYSCFG_DL_GPIO_init();SYSCFG_DL_SYSCTL_init();SYSCFG_DL_SYSTICK_init();
}SYSCONFIG_WEAK void SYSCFG_DL_initPower(void)
{DL_GPIO_reset(GPIOA);DL_GPIO_reset(GPIOB);DL_GPIO_enablePower(GPIOA);DL_GPIO_enablePower(GPIOB);delay_cycles(POWER_STARTUP_DELAY);
}SYSCONFIG_WEAK void SYSCFG_DL_GPIO_init(void)
{DL_GPIO_initDigitalOutput(LED1_PIN_2_IOMUX);DL_GPIO_initDigitalOutput(LED1_PIN_3_IOMUX);DL_GPIO_setPins(LED_PORT, LED1_PIN_2_PIN | LED1_PIN_3_PIN);DL_GPIO_enableOutput(LED_PORT, LED1_PIN_2_PIN | LED1_PIN_3_PIN);DL_GPIO_initDigitalInput(KEY1_PIN_18_IOMUX);DL_GPIO_setPins(KEY1_PORT, KEY1_PIN_18_PIN);
}SYSCONFIG_WEAK void SYSCFG_DL_SYSCTL_init(void)
{DL_SYSCTL_setBORThreshold(DL_SYSCTL_BOR_THRESHOLD_LEVEL_0);DL_SYSCTL_setSYSOSCFreq(DL_SYSCTL_SYSOSC_FREQ_BASE);DL_SYSCTL_disableHFXT();DL_SYSCTL_disableSYSPLL();
}SYSCONFIG_WEAK void SYSCFG_DL_SYSTICK_init(void)
{// 配置SysTick为1ms中断 (32MHz / 1000 = 32000)DL_SYSTICK_config(CPUCLK_FREQ / 1000);
}

ti_msp_dl_config.h

#ifndef ti_msp_dl_config_h
#define ti_msp_dl_config_h#define CONFIG_MSPM0G350X
#define CONFIG_MSPM0G3507#if defined(__ti_version__) || defined(__TI_COMPILER_VERSION__)
#define SYSCONFIG_WEAK __attribute__((weak))
#elif defined(__IAR_SYSTEMS_ICC__)
#define SYSCONFIG_WEAK __weak
#elif defined(__GNUC__)
#define SYSCONFIG_WEAK __attribute__((weak))
#endif#include <ti/devices/msp/msp.h>
#include <ti/driverlib/driverlib.h>
#include <ti/driverlib/m0p/dl_core.h>#ifdef __cplusplus
extern "C" {
#endif#define POWER_STARTUP_DELAY (16)#define CPUCLK_FREQ         32000000/* Port definition for Pin Group LED1 */
#define LED_PORT (GPIOB)/* Defines for PIN_2: GPIOB.2 with pinCMx 15 on package pin 14 */
#define LED1_PIN_2_PIN   (DL_GPIO_PIN_2)
#define LED1_PIN_2_IOMUX (IOMUX_PINCM15)/* Defines for PIN_3: GPIOB.3 with pinCMx 16 on package pin 15 */
#define LED1_PIN_3_PIN   (DL_GPIO_PIN_3)
#define LED1_PIN_3_IOMUX (IOMUX_PINCM16)/* Port definition for Pin Group KEY1 */
#define KEY1_PORT (GPIOA)/* Defines for PIN_18: GPIOA.18 with pinCMx 40 on package pin 37 */
#define KEY1_PIN_18_PIN   (DL_GPIO_PIN_18)
#define KEY1_PIN_18_IOMUX (IOMUX_PINCM40)/* clang-format on */void SYSCFG_DL_init(void);
void SYSCFG_DL_initPower(void);
void SYSCFG_DL_GPIO_init(void);
void SYSCFG_DL_SYSCTL_init(void);
void SYSCFG_DL_SYSTICK_init(void);#ifdef __cplusplus
}
#endif#endif /* ti_msp_dl_config_h */

general.h

#ifndef __GENERAL_H
#define __GENERAL_H// 系统配置文件
#include "ti_msp_dl_config.h"// 设备驱动头文件
#include "device/delay.h"
#include "device/led.h"
#include "device/key.h"#endif

5.不使用sysconfig配置gpio的方法

先删除.syscfg文件,同时删除keil的预编译过程,就是这一条指令,直接去掉前面的勾选框。

然后按以下步骤配置:

1. 配置文件结构

// 头文件定义 (ti_msp_dl_config.h)
#define LED_PORT (GPIOB)
#define LED1_PIN_2_PIN   (DL_GPIO_PIN_2)
#define LED1_PIN_2_IOMUX (IOMUX_PINCM15)// 实现文件 (ti_msp_dl_config.c)
void SYSCFG_DL_GPIO_init(void)
{DL_GPIO_initDigitalOutput(LED1_PIN_2_IOMUX);DL_GPIO_setPins(LED_PORT, LED1_PIN_2_PIN);DL_GPIO_enableOutput(LED_PORT, LED1_PIN_2_PIN);
}

2. 添加新GPIO配置

步骤1:在头文件中添加宏定义
// 添加新的LED配置(例如GPIOB.4)
#define LED2_PIN_4_PIN   (DL_GPIO_PIN_4)
#define LED2_PIN_4_IOMUX (IOMUX_PINCM17)  // 查阅数据手册获取正确的IOMUX// 添加新的按键配置(例如GPIOA.19)
#define KEY2_PIN_19_PIN   (DL_GPIO_PIN_19)
#define KEY2_PIN_19_IOMUX (IOMUX_PINCM41)  // 查阅数据手册获取正确的IOMUX
步骤2:在实现文件中添加初始化代码
SYSCONFIG_WEAK void SYSCFG_DL_GPIO_init(void)
{// 现有LED配置DL_GPIO_initDigitalOutput(LED1_PIN_2_IOMUX);DL_GPIO_initDigitalOutput(LED1_PIN_3_IOMUX);// 添加新LED配置DL_GPIO_initDigitalOutput(LED2_PIN_4_IOMUX);// 批量设置LED引脚DL_GPIO_setPins(LED_PORT, LED1_PIN_2_PIN | LED1_PIN_3_PIN | LED2_PIN_4_PIN);DL_GPIO_enableOutput(LED_PORT, LED1_PIN_2_PIN | LED1_PIN_3_PIN | LED2_PIN_4_PIN);// 现有按键配置DL_GPIO_initDigitalInput(KEY1_PIN_18_IOMUX);DL_GPIO_setPins(KEY1_PORT, KEY1_PIN_18_PIN);// 添加新按键配置DL_GPIO_initDigitalInput(KEY2_PIN_19_IOMUX);DL_GPIO_setPins(KEY1_PORT, KEY2_PIN_19_PIN);  // 设置上拉
}
步骤3:在电源管理中添加端口(如果是新端口)
SYSCONFIG_WEAK void SYSCFG_DL_initPower(void)
{DL_GPIO_reset(GPIOA);DL_GPIO_reset(GPIOB);// 如果添加了GPIOC端口,需要添加:// DL_GPIO_reset(GPIOC);DL_GPIO_enablePower(GPIOA);DL_GPIO_enablePower(GPIOB);// DL_GPIO_enablePower(GPIOC);delay_cycles(POWER_STARTUP_DELAY);
}

3. 删除GPIO配置

步骤1:从头文件删除宏定义
// 删除不需要的GPIO定义
// #define LED1_PIN_3_PIN   (DL_GPIO_PIN_3)
// #define LED1_PIN_3_IOMUX (IOMUX_PINCM16)
步骤2:从实现文件删除初始化代码
SYSCONFIG_WEAK void SYSCFG_DL_GPIO_init(void)
{DL_GPIO_initDigitalOutput(LED1_PIN_2_IOMUX);// 删除:DL_GPIO_initDigitalOutput(LED1_PIN_3_IOMUX);// 修改引脚掩码,移除删除的引脚DL_GPIO_setPins(LED_PORT, LED1_PIN_2_PIN);  // 移除 | LED1_PIN_3_PINDL_GPIO_enableOutput(LED_PORT, LED1_PIN_2_PIN);  // 移除 | LED1_PIN_3_PIN
}

4. 修改GPIO参数

修改引脚功能(输出→输入)
// 原来的输出配置
DL_GPIO_initDigitalOutput(LED1_PIN_2_IOMUX);
DL_GPIO_setPins(LED_PORT, LED1_PIN_2_PIN);
DL_GPIO_enableOutput(LED_PORT, LED1_PIN_2_PIN);// 修改为输入配置
DL_GPIO_initDigitalInput(LED1_PIN_2_IOMUX);
DL_GPIO_setPins(LED_PORT, LED1_PIN_2_PIN);  // 设置上拉
// 删除:DL_GPIO_enableOutput(LED_PORT, LED1_PIN_2_PIN);
修改引脚默认状态
// 输出低电平(LED熄灭)
DL_GPIO_clearPins(LED_PORT, LED1_PIN_2_PIN);// 输出高电平(LED点亮)
DL_GPIO_setPins(LED_PORT, LED1_PIN_2_PIN);
修改引脚上下拉配置
// 配置下拉电阻
DL_GPIO_initDigitalInputFeatures(LED1_PIN_2_IOMUX, DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_DOWN, DL_GPIO_HYSTERESIS_DISABLE);// 配置上拉电阻
DL_GPIO_initDigitalInputFeatures(LED1_PIN_2_IOMUX, DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP, DL_GPIO_HYSTERESIS_DISABLE);

5. 配置要点

  1. IOMUX查找:需要查阅MSPM0G3507数据手册确定正确的IOMUX_PINCMx值
  2. 批量操作:使用位或运算符组合多个引脚进行批量配置
  3. 电源管理:新增GPIO端口需在电源初始化中添加
  4. 弱符号:所有函数使用SYSCONFIG_WEAK,允许用户重写
  5. 分层配置:电源→GPIO→系统时钟→SysTick的初始化顺序

相关文章:

  • 你应该如何引入JavaScript
  • 再现重大BUG,微软紧急撤回Win 11六月更新
  • 力扣HOT100之技巧:31. 下一个排列
  • 学习笔记整理之状态图与状态图搜索
  • AI模型的泛化性的第一性原理是什么?
  • 解释器模式(Interpreter Pattern)
  • Spark on yarn的作业提交流程
  • AppInventor2原生进度条组件LinearProgress用法及注意点
  • 试过沃尔玛的无人机送货吗?今年覆盖范围将翻番
  • 傲火集团传媒基地武汉启幕 构建数字娱乐产业生态闭环
  • yolov5环境配置
  • 拉深工艺——有凸缘圆筒形件的拉深(实例分析)
  • slam--运动方程和观测方程
  • 【驱动设计的硬件基础】处理器的分类
  • 解决蓝牙MAC 地址倒序问题
  • 如何快速删除谷歌浏览器在mac启动台生成的网页图标
  • 从零开始学Python(3)——函数
  • python-76-基于uv的python虚拟环境和包管理工具
  • 基于大模型预测单纯性孔源性视网膜脱离的技术方案大纲
  • Makefile 学习笔记
  • 镇江网站建设找思创/沧州seo包年优化软件排名
  • 网站tdk优化/优化大师使用方法
  • 移动网络建设/seo引擎优化怎么做
  • 企业网站建设相关书籍在线阅读/怎么在腾讯地图上添加自己的店铺
  • 网站开发计算机配置/宁波最好的推广平台
  • 门户网站流程图/百度服务电话6988