电赛ti板例程10+8学习

📅 2026/7/20 15:54:06
电赛ti板例程10+8学习
2026-07-19 电赛TI板例程108学习从GPIO输入到定时器中断一、今日目标跑通例程10keyscan理解GPIO输入、按键消抖、轮询检测跑通例程8systick理解定时器中断、非阻塞延时、软件分频建立GPIO输出→输入→中断→PWM的闭环认知记录从会看代码到会改代码的完整过程二、环境确认项目状态备注Keil MDK✅ 正常已安装TI MSPM0支持包J-Link驱动✅ 正常SWD模式核心板连接✅ 正常四线连接例程10编译✅ 0 Error10_keyscan.uvprojx例程8编译✅ 0 Error8_systick.uvprojx硬件无名创新24年电赛H题小车套件MSPM0G3507核心板屏幕SPI接口OLED7针但例程8代码是I2C驱动暂时跳过显示三、例程10keyscan核心代码分析3.1 main.c 完整结构#includeti_msp_dl_config.h#defineDELAY(16000000)// 0.5s当前代码未使用voiddelay_ms(uint16_tms){while(ms--)delay_cycles(CPUCLK_FREQ/1000);// 32MHz/1000 32000周期/ms}intmain(void){SYSCFG_DL_init();// 一键初始化while(1){// S1按键检测高电平有效if(DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN)){delay_ms(10);// 消抖if(DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN))// 再次确认{while(DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN));// 等待释放DL_GPIO_togglePins(PORTB_PORT,PORTB_RGB_R_PIN);// 翻转红灯}}// S2按键检测低电平有效注意取反!if(!DL_GPIO_readPins(PORTB_PORT,PORTB_S2_PIN)){delay_ms(10);if(!DL_GPIO_readPins(PORTB_PORT,PORTB_S2_PIN)){while(!DL_GPIO_readPins(PORTB_PORT,PORTB_S2_PIN));DL_GPIO_togglePins(PORTB_PORT,PORTB_RGB_G_PIN);// 翻转绿灯}}delay_ms(100);DL_GPIO_togglePins(GPIO_LEDS_PORT,GPIO_LEDS_USER_TEST_PIN);// 测试灯闪烁}}3.2 代码元素逐行拆解代码元素作用对应STM32标准库关键理解DL_GPIO_readPins(port, pin)读取引脚电平GPIO_ReadInputDataBit()返回值是掩码不是0/1DL_GPIO_initDigitalInputFeatures()初始化输入上拉/下拉GPIO_Init()配置输入模式需要配置上拉电阻delay_ms(10)软件消抖自定义或HAL_Delay机械按键抖动5-20mswhile(readPins(...))等待按键释放无手动实现防止按键按住时重复触发!DL_GPIO_readPins(...)低电平检测!GPIO_ReadInputDataBit()S2硬件是上拉输入3.3 返回值陷阱核心认知// ❌ 错误返回值不一定是1if(DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN)1)// ✅ 正确判断大于0高电平if(DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN)0)// ✅ 正确判断等于0低电平if(DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN)0)// ✅ 最精确与掩码比较if(DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN)KEY_S1_PIN)原因DL_GPIO_readPins返回的是对应位的掩码值如0x00200000第21位为1而不是简单的1。3.4 三层检测模型第一层if(检测到电平) ← 初步判断 ↓ 第二层delay_ms(10) ← 消抖等待 if(再次确认电平) ← 确认是真的按下 ↓ 第三层while(等待释放) ← 等待按键松开 执行动作翻转LED四、例程10拓展练习拓展1统一按键检测逻辑失败→成功目标把S1也改成低电平触发和S2统一风格。修改// 修改前S1高电平触发S2低电平触发if(DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN))// S1if(!DL_GPIO_readPins(PORTB_PORT,PORTB_S2_PIN))// S2// 修改后统一低电平触发if(!DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN)){delay_ms(10);if(!DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN)){while(!DL_GPIO_readPins(KEY_PORT,KEY_S1_PIN));DL_GPIO_togglePins(PORTB_PORT,PORTB_RGB_R_PIN);}}结果S2失效程序卡在S1的while循环里。原因分析S1硬件是下拉输入未按下时低电平按下后变高电平改成低电平检测后!0永远为真while死循环主循环被卡死执行不到下面的S2检测教训两个按键硬件连接方式不同代码逻辑本来就该不同。强行统一会导致逻辑错误。恢复S1保持高电平检测S2保持低电平检测。拓展2状态机切换LED模式成功目标短按S1切换LED模式灭→常亮→闪烁→灭。代码#defineMODE_OFF0#defineMODE_ON1#defineMODE_BLINK2uint8_tled_modeMODE_OFF;// 按键处理led_mode(led_mode1)%3;// 主循环根据模式执行switch(led_mode){caseMODE_OFF:DL_GPIO_clearPins(PORTB_PORT,PORTB_RGB_R_PIN);break;caseMODE_ON:DL_GPIO_setPins(PORTB_PORT,PORTB_RGB_R_PIN);break;caseMODE_BLINK:DL_GPIO_togglePins(PORTB_PORT,PORTB_RGB_R_PIN);delay_ms(300);// 300ms周期肉眼可见闪烁break;}遇到的问题直接复制代码红灯常亮而不是闪烁。原因delay_ms(300)在switch内部但while循环顶部还有delay_ms(100)循环周期约400ms。blink_cnt需要50次循环才翻转周期太长。解决直接用delay_ms(300)控制闪烁周期不用计数器。验证每按一次S1红灯切换一种模式灭→亮→闪烁→灭。五、例程8systick核心代码分析5.1 main.c 完整结构#includeti_msp_dl_config.h#includesystick.h#includedrv_oled.h#includestdio.h#defineDELAY(3200000)// 未使用systimemain_t,timer_t;intmain(void){SYSCFG_DL_init();oled_init();// OLED初始化SPI屏代码是I2C跳过NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);// 使能定时器中断DL_TimerG_startCounter(TIMER_0_INST);// 启动定时器while(1){// OLED显示跳过delay_ms(200);DL_GPIO_togglePins(USER_GPIO_PORT,USER_GPIO_LED_PA0_PIN);// PA0闪烁get_systime(main_t);}}voidTIMER_0_INST_IRQHandler(void){switch(DL_TimerG_getPendingInterrupt(TIMER_0_INST)){caseDL_TIMERG_IIDX_ZERO:get_systime(timer_t);DL_GPIO_togglePins(USER_GPIO_PORT,USER_GPIO_RED_PIN);// 红灯闪烁break;default:break;}}5.2 关键配置SysConfig截图参数值说明Timer Clock Prescaler10032MHz / 100 320kHzTimer Clock Frequency320.00 kHz定时器实际时钟Desired Timer Period100ms中断周期Actual Timer Period100.00 ms实际中断周期Enable InterruptsZero event计数到零触发中断Start Timer未勾选代码中手动启动5.3 中断 vs 轮询对比维度轮询例程10按键中断例程8定时器CPU行为主动查询忙等被动响应做其他事效率低CPU空转高CPU解放响应延迟取决于查询间隔硬件级微秒级适用场景简单、低频事件定时、高频事件代码复杂度简单需要中断服务函数5.4 遇到的问题红灯不亮排查过程PA0闪烁正常 → 程序在跑检查中断函数名 → 正确TIMER_0_INST_IRQHandler检查SysConfig → 中断已使能Zero event检查引脚定义 → 发现USER_GPIO_RED_PIN和GPIO_GRP_0_PIN_R_PIN不是同一个引脚原因USER_GPIO_RED_PIN GPIOA.26例程8的独立红灯GPIO_GRP_0_PIN_R_PIN GPIOB.26例程10的RGB红灯硬件上不是同一个LED解决把中断函数里的红灯改为GPIO_GRP_0_PIN_R_PIN。验证PA0每200ms闪烁主循环RGB红灯每100ms闪烁中断频率不同证明中断独立运行。六、例程8拓展练习拓展1改变定时器周期成功操作修改SysConfig中Desired Timer Period原值新值效果100ms10ms红灯快闪50Hz100ms500ms红灯慢闪1Hz100ms1000ms每秒闪一次理解Period越大中断越慢LED闪得越慢。拓展2非阻塞延时失败→成功目标用定时器中断做时间基准替代delay_ms()的阻塞等待。第一次尝试失败volatileuint32_ttick_ms0;// 中断中每100ms1// 主循环if(tick_ms-last_tick200)// 以为是200ms{last_ticktick_ms;DL_GPIO_togglePins(...);}结果LED不闪烁。原因中断周期是100mstick_ms每100ms才1。tick_ms - last_tick 200实际是200 × 100ms 20秒解决统一时间单位代码中按中断次数计算// 中断周期100mstick_cnt每100ms1volatileuint32_ttick_cnt0;// 每2个中断周期200ms翻转if(tick_cnt-last_tick2){last_ticktick_cnt;DL_GPIO_togglePins(...);}验证PA0每200ms闪烁主循环中没有delay_msCPU一直在运行。拓展3多个定时任务成功volatileuint32_ttick_cnt0;// 任务1每100ms1个中断翻转PA0if(tick_cnt-last_tick_11){last_tick_1tick_cnt;DL_GPIO_togglePins(USER_GPIO_PORT,USER_GPIO_LED_PA0_PIN);}// 任务2每500ms5个中断翻转红灯if(tick_cnt-last_tick_55){last_tick_5tick_cnt;DL_GPIO_togglePins(GPIO_GRP_0_PORT,GPIO_GRP_0_PIN_R_PIN);}验证PA0每100ms闪快红灯每500ms闪慢互不干扰。拓展4按键控制定时器启停成功if(timer_running)DL_TimerG_startCounter(TIMER_0_INST);// 启动elseDL_TimerG_stopCounter(TIMER_0_INST);// 停止验证上电红灯不闪按S1开始闪再按S1停止。拓展5按键动态改变周期失败→成功目标按S1切换红灯闪烁频率快/中/慢。第一次尝试失败DL_TimerG_setPeriod(TIMER_0_INST,3200);// 报错没有这个函数原因无名科技提供的库版本没有DL_TimerG_setPeriod。解决不用硬件修改用软件计数实现调速uint8_tspeed_mode1;// 0快(100ms) 1中(500ms) 2慢(1000ms)// 根据速度模式决定计数目标uint8_ttarget_cnt;switch(speed_mode){case0:target_cnt1;break;// 100ms 1个中断case1:target_cnt5;break;// 500ms 5个中断case2:target_cnt10;break;// 1000ms 10个中断}if(tick_cnt-last_ticktarget_cnt){last_ticktick_cnt;DL_GPIO_togglePins(GPIO_GRP_0_PORT,GPIO_GRP_0_PIN_R_PIN);}验证按S1切换红灯闪烁速度100ms→500ms→1000ms→100ms。拓展6状态机中断成功#defineSTATE_IDLE0#defineSTATE_RUN1#defineSTATE_FAST2#defineSTATE_SLOW3uint8_tsystem_stateSTATE_IDLE;// 按键切换状态system_state(system_state1)%4;// 根据状态执行不同动作switch(system_state){caseSTATE_IDLE:/* 所有灯灭 */break;caseSTATE_RUN:/* 红灯500ms闪烁 */break;caseSTATE_FAST:/* 红灯100ms绿灯500ms */break;caseSTATE_SLOW:/* 红灯1000ms闪烁 */break;}验证按S1循环切换四种模式。七、关键陷阱汇总陷阱现象解决readPins返回值陷阱判断条件永远为假用0或0判断不要用1按键硬件差异统一逻辑后某个按键失效确认上拉/下拉配置不要强行统一中断函数名错误程序卡死LED常亮必须和SysConfig生成的向量名一致引脚定义混淆LED不亮USER_GPIO_RED_PIN和GPIO_GRP_0_PIN_R_PIN不是同一个时间单位错误LED不闪烁实际很慢确认中断周期tick单位是中断次数不是mssetPeriod不存在编译报错用软件计数实现调速不修改硬件周期中断里阻塞程序卡死中断里不能用delay_ms共享变量不加volatile变量不更新中断和主循环共享的变量必须加volatile八、概念连接8.1 从例程10到例程8的演进例程1GPIO输出 例程10GPIO输入 例程8定时器中断 ↓ ↓ ↓ LED亮灭控制 按键检测消抖 定时触发非阻塞 ↓ ↓ ↓ 单一动作 条件判断 时间基准多任务 ↓ ↓ ↓ 阻塞延时 轮询检测 中断响应8.2 软件分频的理解硬件定时器100ms中断固定不变 ↓ 软件计数 tick_cnt % 1 0 → 100ms任务快 tick_cnt % 5 0 → 500ms任务中 tick_cnt % 10 0 → 1000ms任务慢 ↓ 一个硬件定时器 → 多个不同频率任务这是嵌入式中最常用的软件分频方法比动态修改硬件周期更稳定。8.3 向PWM衔接例程8 定时器中断通知型 ↓ 例程6/7 Timer PWM自动型 ↓ 区别 - 中断到点了告诉CPUCPU自己处理翻转LED - PWM到点了硬件自动改变引脚电平CPU完全解放输出方波九、今日问题清单序号问题描述当前状态优先级下一步行动1SPI OLED与I2C代码不匹配✅ 已解决高购买I2C OLED或改写SPI驱动2readPins返回值陷阱✅ 已理解高记住用0/0判断3按键硬件差异导致统一逻辑失败✅ 已理解中确认上拉/下拉后再写代码4引脚定义混淆USER_GPIO vs GPIO_GRP_0✅ 已解决高查ti_msp_dl_config.h确认5时间单位错误tick单位是中断次数✅ 已理解高确认中断周期后再写代码6DL_TimerG_setPeriod不存在✅ 已解决中用软件计数替代7中断函数里不能阻塞✅ 已理解高中断里只做简单操作8共享变量必须加volatile✅ 已理解高养成习惯9状态机从隐式到显式 已实践中继续练习10Keil调试Watch窗口 待学习中下次学习十、今日感悟与模式记录学习节奏“跑通例程→尝试修改→遇到问题→排查→理解原理→再修改→成功”这个循环比一次性看完所有代码效率高得多。每个陷阱都是一次深度理解的机会。关键认知更新返回值陷阱是TI的常态readPins返回掩码不是0/1必须养成检查返回值类型的习惯硬件决定软件逻辑按键的上拉/下拉配置决定了检测逻辑不能强行统一引脚定义必须确认USER_GPIO_RED_PIN和GPIO_GRP_0_PIN_R_PIN不是同一个查ti_msp_dl_config.h时间单位必须统一中断周期是100mstick单位是中断次数不是ms软件分频比硬件修改更稳定不用setPeriod用计数器实现多频率中断是效率的关键从轮询到中断CPU从忙等变成被动响应效率质变工具链使用工具今日用途效果Keil编译、烧录熟悉遇到delay_ms重复定义问题SysConfig配置定时器周期、中断理解Prescaler和Period的关系J-Link程序下载顺畅Kimi实时答疑、代码分析、概念连接长期陪伴理解上下文记录时间2026-07-19 16:15下次学习例程6/7Timer PWM让电机转起来