ti板例程1学习
📅 2026/7/19 3:39:58
👁️ 阅读次数
📝 编程学习
2026-07-18 电赛ti板例程1学习:empty_project_led
一、今日目标
- 跑通例程1(LED闪烁),验证Keil + J-Link + MSPM0G3507 链路
- 完成基础拓展(改延时、单独控制RGB三色)
- 尝试SysConfig换引脚,初步理解IOMUX复用机制
- 记录学习过程中的问题与概念连接
二、环境确认
| 项目 | 状态 | 备注 |
|---|---|---|
| Keil MDK | ✅ 正常 | 已安装TI MSPM0支持包 |
| J-Link驱动 | ✅ 正常 | 昨天更新过 |
| 核心板连接 | ✅ 正常 | SWD四线连接 |
| 例程工程编译 | ✅ 0 Error, 0 Warning | 直接打开1_empty_project_led.uvprojx |
硬件:无名创新24年电赛H题小车套件,MSPM0G3507核心板
三、例程1核心代码分析
3.1 main.c 完整结构
#include"ti_msp_dl_config.h"// SysConfig生成的配置头文件#defineDELAY(16000000)// 循环计数,决定延时长短intmain(void){SYSCFG_DL_init();// 一键初始化:时钟、GPIO、电源等while(1){delay_cycles(DELAY);// 空转延时DL_GPIO_togglePins(PORTB_PORT,PORTB_RGB_R_PIN|PORTB_RGB_G_PIN|PORTB_RGB_B_PIN);}}3.2 代码元素逐行拆解
| 代码元素 | 作用 | 对应STM32标准库 | 关键理解 |
|---|---|---|---|
#include "ti_msp_dl_config.h" | 包含SysConfig生成的配置头 | 无(直接写寄存器或用HAL) | TI把配置封装成宏,不用手动记寄存器地址 |
SYSCFG_DL_init() | 调用SysConfig生成的总初始化函数 | RCC_APB2PeriphClockCmd()+GPIO_Init() | 一键完成时钟使能+引脚复用+方向配置 |
delay_cycles(DELAY) | 阻塞式空转延时 | for(i=0; i<delay; i++); | 纯软件延时,CPU在此期间空转 |
DL_GPIO_togglePins() | 翻转引脚电平 | GPIO_WriteBit() | 高变低,低变高 |
PORTB_PORT | 宏定义,对应GPIOB | GPIOB | TI用PORTB_PORT代替GPIOB |
PORTB_RGB_R_PIN | 宏定义,对应PB26 | GPIO_Pin_26 | 具体引脚号封装在宏里 |
3.3 SysConfig生成的配置解读
ti_msp_dl_config.h关键宏:
#definePORTB_PORT(GPIOB)#definePORTB_RGB_R_PIN(DL_GPIO_PIN_26)#definePORTB_RGB_R_IOMUX(IOMUX_PINCM57)#definePORTB_RGB_G_PIN(DL_GPIO_PIN_27)#definePORTB_RGB_G_IOMUX(IOMUX_PINCM58)#definePORTB_RGB_B_PIN(DL_GPIO_PIN_22)#definePORTB_RGB_B_IOMUX(IOMUX_PINCM50)#defineCPCLK_FREQ(32000000)// 32MHz主频核心理解——IOMUX的作用:
物理引脚PB26 → IOMUX_PINCM57 → 可以连到GPIO/PWM/UART/ADC... ↑ SysConfig在这里配置Codex的原话:“如果你不理解IOMUX决定了一个物理引脚到底连到GPIO还是PWM/UART,那么你就无法解释:为什么把PB26改成PWM输出时,必须在SysConfig里重新配置,而不是只改代码里的API。”
四、拓展练习详细记录
拓展1:修改延时时间
操作:修改DELAY宏定义,重新编译烧录,观察LED闪烁频率
| DELAY值 | 理论计算 | 实际现象 | 验证结论 |
|---|---|---|---|
| 16000000 | 16000000 / 32MHz = 0.5s | LED约0.5秒闪烁一次 | ✅ 基准正确 |
| 8000000 | 8000000 / 32MHz = 0.25s | LED闪烁加快 | ✅ 时间减半 |
| 32000000 | 32000000 / 32MHz = 1s | LED闪烁变慢 | ✅ 时间翻倍 |
关键公式:
延时时间(秒) = DELAY / CPCLK_FREQ = DELAY / 32000000理解:delay_cycles()就是让CPU空转N个时钟周期。32MHz主频下,1600万个周期 = 0.5秒。
对比STM32:以前用for(i=0; i<0xFFFFF; i++);这种魔法数字,现在用delay_cycles(16000000),语义更清晰。
拓展2:单独控制RGB三色
目标:实现红灯亮0.5s → 绿灯亮0.5s → 蓝灯亮0.5s → 循环
代码:
while(1){// 状态1:红灯亮DL_GPIO_setPins(PORTB_PORT,PORTB_RGB_R_PIN);DL_GPIO_clearPins(PORTB_PORT,PORTB_RGB_G_PIN|PORTB_RGB_B_PIN);delay_cycles(DELAY);// 状态2:绿灯亮DL_GPIO_clearPins(PORTB_PORT,PORTB_RGB_R_PIN);DL_GPIO_setPins(PORTB_PORT,PORTB_RGB_G_PIN);DL_GPIO_clearPins(PORTB_PORT,PORTB_RGB_B_PIN);delay_cycles(DELAY);// 状态3:蓝灯亮DL_GPIO_clearPins(PORTB_PORT,PORTB_RGB_R_PIN|PORTB_RGB_G_PIN);DL_GPIO_setPins(PORTB_PORT,PORTB_RGB_B_PIN);delay_cycles(DELAY);}API对比表:
| 操作 | MSPM0 DriverLib | STM32标准库 |
|---|---|---|
| 置高电平 | DL_GPIO_setPins(port, pin) | GPIO_SetBits(GPIOx, GPIO_Pin) |
| 置低电平 | DL_GPIO_clearPins(port, pin) | GPIO_ResetBits(GPIOx, GPIO_Pin) |
| 翻转电平 | DL_GPIO_togglePins(port, pin) | GPIO_WriteBit(GPIOx, Pin, Bit_SET/RESET) |
| 读取电平 | DL_GPIO_readPins(port, pin) | GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) |
命名习惯差异:
- TI用
set/clear/read/toggle,动词在前 - ST用
SetBits/ResetBits/ReadInputDataBit/WriteBit,更强调寄存器操作
拓展3:SysConfig换引脚
操作步骤:
- 打开
empty_project_led.syscfg(SysConfig图形化配置界面) - 在GPIO配置面板中,找到RGB_R的配置
- 将引脚从PB26改为PA0(或其他可用引脚)
- 点击Save→ 自动生成
ti_msp_dl_config.c和ti_msp_dl_config.h - 回到Keil,编译 → 烧录 → 观察现象
遇到的问题:
| 现象 | 尝试的引脚 | 结果 |
|---|---|---|
| LED正常闪烁 | PB26(原配置) | ✅ 正常 |
| LED不亮 | PA0 | ❌ 不亮 |
| LED不亮 | 某些其他PB引脚 | ❌ 不亮 |
初步分析(待验证):
可能原因1:IOMUX未配置为GPIO模式
- 在SysConfig里选了PA0,但可能没把复用功能(MUX)设为GPIO
- 需要检查生成的代码中
DL_GPIO_initDigitalOutput()的参数
可能原因2:该引脚被其他功能占用
- MSPM0G3507某些引脚默认是调试口(SWD)、晶振口(XIN/XOUT)或电源脚
- 需要查数据手册确认引脚功能表
可能原因3:物理连接问题
- 核心板上PA0没有接LED,或者接了其他外设
- 需要用万用表量引脚电平确认
关键教训:
在MSPM0上,“选了引脚"不等于"能用GPIO”。必须同时确认:
- SysConfig里该引脚的IOMUX设为GPIO模式
- 该引脚物理上没有被其他功能(调试口、晶振等)占用
- 该引脚在硬件上连接了你想要的设备(LED)
五、概念连接:状态机
5.1 从例程1发现状态机
例程1的LED闪烁,如果显式写出状态,就是一个极简状态机:
uint8_tled_state=0;// 0=灭, 1=亮while(1){if(led_state==0){DL_GPIO_setPins(PORTB_PORT,PORTB_RGB_R_PIN);// 动作:亮led_state=1;// 转移:灭→亮}else{DL_GPIO_clearPins(PORTB_PORT,PORTB_RGB_R_PIN);// 动作:灭led_state=0;// 转移:亮→灭}delay_cycles(DELAY);// 条件:延时到期才转移}| 当前状态 | 触发条件 | 下一个状态 | 执行动作 |
|---|---|---|---|
| LED灭(0) | 延时到期 | LED亮(1) | setPins |
| LED亮(1) | 延时到期 | LED灭(0) | clearPins |
5.2 与上学期经验的连接
上学期循迹小车——隐式状态机:
// 没有状态变量,状态藏在条件判断里while(1){if(传感器==黑线)直走();elseif(传感器==偏左)右转();elseif(传感器==偏右)左转();else停止();}| 当前状态 | 谁决定的 | 状态转移条件 |
|---|---|---|
| 直走 | 传感器实时读数 | 传感器==偏左 → 右转 |
| 右转 | 传感器实时读数 | 传感器==黑线 → 直走 |
特点:无记忆性,当前状态完全由传感器实时决定,不需要记住"之前在哪"。
H题代码——显式状态机:
// duty1:从A点出发,循迹到B点,声光提示voidauto_drive_smartcar_duty1(void){staticuint8_tn=8;// 任务编号if(flight_subtask_cnt[n]==0){// 状态0:初始化speed_ctrl_mode=1;trackless_output.yaw_ctrl_mode=ROTATE;flight_subtask_cnt[n]=1;// 转移到状态1}elseif(flight_subtask_cnt[n]==1){// 状态1:行驶中distance_control_with_speed_limit(50);if(ABS(distance_ctrl.error)<distance_precision_cm){flight_subtask_cnt[n]=2;// 条件满足,转移到状态2}}else{// 状态2:到达,声光提示speed_expect[0]=0;// 停止电机speed_expect[1]=0;bling_set(&light_red,2000,500,0.5,0,0);// 红灯闪烁beep.reset=1;beep.times=2;}}| 状态值 | 含义 | 转移条件 | 动作 |
|---|---|---|---|
| 0 | 初始化 | 无条件 | 设置速度、启动循迹 → 进入状态1 |
| 1 | 行驶中 | distance_ctrl.error < 1cm | 距离控制、速度控制 → 到达后进入状态2 |
| 2 | 到达提示 | 无(结束) | 停止电机、红灯闪烁、蜂鸣器响 |
特点:有记忆性,必须记住"走到哪一步了",状态转移是单向的(0→1→2)。
5.3 隐式 vs 显式状态机对比
| 维度 | 隐式状态机(循迹小车) | 显式状态机(H题代码) |
|---|---|---|
| 状态记录 | 无状态变量,状态藏在if-else里 | flight_subtask_cnt[n]显式记录 |
| 状态转移 | 双向/多向,传感器变就跳 | 单向,0→1→2,不回头 |
| 记忆性 | 无记忆,只看当前输入 | 有记忆,必须知道"之前在哪" |
| 适用场景 | 响应式、实时反馈(循迹) | 流程式、时序控制(A→B→C) |
| 代码复杂度 | 状态少时简单 | 状态多时更清晰,易扩展 |
| 调试难度 | 难,不知道卡在哪个逻辑分支 | 易,看cnt值就知道在哪一步 |
5.4 为什么H题必须用显式状态机
| H题需求 | 隐式能否满足 | 显式如何解决 |
|---|---|---|
| "从A到B"是固定流程 | ❌ 不能靠传感器实时判断 | ✅cnt记录步骤,按顺序执行 |
| 需要记住"已经启动过" | ❌ 无记忆 | ✅cnt=1表示"已启动,正在行驶" |
| 多个任务(duty1~4)共享调度 | ❌ 每个任务逻辑混杂 | ✅ 每个任务有自己的cnt数组 |
| 非阻塞执行(不能卡死) | ❌while循环会阻塞 | ✅ 每个状态执行一次,立即返回 |
六、今日问题清单
| 序号 | 问题描述 | 当前状态 | 优先级 | 下一步行动 |
|---|---|---|---|---|
| 1 | 换引脚后LED不亮的具体原因? | 🔍 待验证 | 高 | 查MSPM0G3507数据手册引脚功能表;用万用表量引脚电平 |
| 2 | IOMUX_PINCMx和物理引脚号的对应关系? | 🔍 待理解 | 高 | 阅读数据手册IOMUX章节;对比SysConfig生成的前后代码 |
| 3 | 哪些引脚"确定可用"作GPIO输出? | 🔍 待整理 | 中 | 整理一份48引脚封装的安全GPIO清单 |
| 4 | delay_cycles的精确性如何?受中断影响吗? | 🔍 待验证 | 低 | 对比例程8的systick精准延时 |
| 5 | 状态机从隐式到显式的改写练习 | 📝 待做 | 中 | 把上学期循迹代码改成显式状态机版本 |
七、下一步学习计划
近期(下周)
| 优先级 | 任务 | 目标 | 预计时间 |
|---|---|---|---|
| P0 | 跑通例程10(keyscan) | 理解GPIO输入、按键消抖 | 1-2天 |
| P0 | 跑通例程8(systick) | 理解精准延时、替代delay_cycles | 1天 |
| P1 | 确认"换引脚不亮"原因 | 查数据手册,整理安全引脚清单 | 半天 |
| P1 | 跑通例程6(timer_period) | 理解定时器中断、周期触发 | 1-2天 |
中期(暑假)
| 里程碑 | 目标 | 标志 |
|---|---|---|
| M1 | 跑完前5个例程 | 1, 10, 8, 6, 7 |
| M2 | 让电机转起来 | 用例程7输出PWM,控制左右轮 |
| M3 | 建立速度闭环 | 例程14编码器测速 + 例程7 PWM |
| M4 | 接触H题代码骨架 | 提取电机驱动层,理解状态机调度 |
八、今日感悟与模式记录
学习节奏
“进度太快跟不上” → 主动刹车 → 记录消化 → 下周继续
这个节奏比"一天刷完5个例程但只记得最后一个"效率高得多。
验证ISTP学习模式:
- ✅ 边做边调整(跑例程→发现问题→定向查资料)
- ✅ 问题导向补理论(不前置灌输,遇到卡点再深入)
- ✅ 最小闭环验证(每个例程跑通+改参数+确认现象)
工具链使用
| 工具 | 今日用途 | 效果 |
|---|---|---|
| Keil | 编译、烧录、调试 | 熟悉,无卡点 |
| SysConfig | 图形化配置GPIO、IOMUX | 新接触,换引脚时遇到复用问题 |
| J-Link | 程序下载 | 昨天已配好,今日顺畅 |
| Codex | 扫描24个例程、生成学习路径 | 输出质量高,已建立信任 |
| Kimi | 实时答疑、概念连接、记录整理 | 长期陪伴,理解上下文 |
关键认知更新
- IOMUX是TI的灵魂:不是选了引脚就能用,必须理解复用配置
- 状态机不是新概念:上学期已经用过,只是不知道名字
- 显式状态机的价值:在"流程式任务"(A→B→C)中,记忆性是必须的
- Codex的边界:适合代码扫描、路径规划;Kimi适合实时对话、概念连接
记录时间:2026-07-18 17:53
下次学习:2026-07-21(周一)
编程学习
技术分享
实战经验