最近在《DSP原理与应用》课程中完成了基于TMS320F2812的GPIO实验,内容包括线性键盘控制跑马灯方向与启停,以及矩阵键盘独立控制8个LED。在这里把实验过程和代码整理出来,希望对学习DSP的小伙伴有所帮助。
一、实验概述
1.1 实验目的
掌握TMS320F2812 GPIO寄存器的配置方法(复用功能、方向控制、数据读写)
理解线性键盘与矩阵键盘的硬件连接及扫描识别原理
学习利用GPIO控制LED实现流水灯及独立点亮
巩固C语言及CCS开发环境的使用
1.2 实验环境
硬件:SEED-DPS2812M开发板(TMS320F2812)、XDS510仿真器、PC
软件:Windows + Code Composer Studio (CCS)
二、GPIO寄存器基础
TMS320F2812的GPIO通过以下几类寄存器配置:
| 寄存器 | 功能说明 |
|---|---|
GPxMUX | 复用控制:0→GPIO功能,1→外设功能 |
GPxDIR | 方向控制:0→输入,1→输出 |
GPxDAT | 数据寄存器:读引脚状态 / 写输出电平 |
GPxSET | 置位寄存器:写1将对应输出引脚置高 |
GPxCLEAR | 清零寄存器:写1将对应输出引脚拉低 |
GPxTOGGLE | 翻转寄存器:写1使输出引脚电平翻转 |
注意:
GPxMUX和GPxDIR受EALLOW保护,修改前需执行EALLOW,修改后执行EDIS。
三、任务1:线性键盘控制跑马灯方向及启停
3.1 硬件连接
LED输出:GPIOA0~A7 → 8个LED(D08~D15)
按键输入:SW1→GPIOB5、SW4→GPIOB4、SW7→GPIOB3
扫描输出:GPIOB2 输出低电平作为公共扫描线
线性键盘原理:公共端(B2)输出低电平,分别读取三个输入引脚状态,低电平表示该按键被按下。
3.2 软件设计思路
初始化:所有LED熄灭,GPIOB2=0,方向初始为从右向左,起始点亮最右边LED
主循环:若
run_flag==1则点亮当前LED → 延时并扫描按键 → 熄灭当前LED → 根据方向更新下一个LED按键扫描:检测SW1/SW4/SW7,加入消抖,改变方向/启停标志
3.3 关键代码
// 线性键盘按键对应的 GPIO 引脚(输入) #define KEY_SW1 GpioDataRegs.GPBDAT.bit.GPIOB5 // SW1 #define KEY_SW4 GpioDataRegs.GPBDAT.bit.GPIOB4 // SW4 #define KEY_SW7 GpioDataRegs.GPBDAT.bit.GPIOB3 // SW7 // 全局变量 Uint32 current_led; // 当前点亮的LED位 Uint32 direction; // 0:从右向左, 1:从左向右 Uint32 run_flag; // 1:流动, 0:停止 // 线性键盘扫描函数 void Scan_Key(void) { Uint32 i; if(KEY_SW1 == 0) { for(i=0; i<10000; i++); // 消抖 if(KEY_SW1 == 0) { direction = 0; // 从右向左 current_led = 0x0080; // 最右边LED run_flag = 1; while(KEY_SW1 == 0); // 等待释放 } } else if(KEY_SW4 == 0) { // 类似处理,direction=1,current_led=0x0001 } else if(KEY_SW7 == 0) { // 停止:run_flag=0,但保持当前LED点亮 } }3.4 GPIO配置(关键部分)
c
void Gpio_select(void) { EALLOW; GpioMuxRegs.GPAMUX.all = 0x0000; // GPIOA全部为GPIO GpioMuxRegs.GPBMUX.all = 0x0000; GpioMuxRegs.GPADIR.all = 0xFFFF; // GPIOA全部输出(LED) // GPIOB: B0~B1输出,B2输出(扫描线),B3~B5输入(按键) GpioMuxRegs.GPBDIR.all = 0xFFC7; // 1111 1111 1100 0111 EDIS; }3.5 实验现象
按下SW1:跑马灯从右向左循环流动
按下SW4:方向立即切换为从左向右
按下SW7:跑马灯停止,当前LED保持点亮;再次按SW1/SW4恢复流动
四、任务2:矩阵键盘控制LED独立点亮
4.1 硬件连接
LED输出:GPIOA0~A7(与任务1相同)
矩阵键盘:
行输入:GPIOB5(行0)、GPIOB4(行1)、GPIOB3(行2)
列输出:GPIOB2(列0)、GPIOB1(列1)、GPIOB0(列2)
采用3×3矩阵键盘(SW1~SW9),使用列扫描法。
4.2 扫描流程
所有列线输出高电平
依次将某一列置低,其余列保持高
读取三根行线状态,若某行为低,则记录行号
延时5ms消抖,再次确认该行仍为低
计算键值:
key = row*3 + col + 1(范围1~9)映射到LED:键值1~8点亮对应LED(GPIOA0~A7),键值9熄灭所有LED
4.3 关键代码
// 行输入定义 #define ROW0 GpioDataRegs.GPBDAT.bit.GPIOB5 #define ROW1 GpioDataRegs.GPBDAT.bit.GPIOB4 #define ROW2 GpioDataRegs.GPBDAT.bit.GPIOB3 // 列输出定义 #define COL0 GpioDataRegs.GPBDAT.bit.GPIOB2 #define COL1 GpioDataRegs.GPBDAT.bit.GPIOB1 #define COL2 GpioDataRegs.GPBDAT.bit.GPIOB0 Uint16 Scan_Key(void) { Uint16 col, row; // 所有列输出高 COL0 = 1; COL1 = 1; COL2 = 1; Delay_us(20); for(col = 0; col < 3; col++) { // 当前列置低 if(col == 0) { COL0 = 0; COL1 = 1; COL2 = 1; } else if(col == 1) { COL0 = 1; COL1 = 0; COL2 = 1; } else { COL0 = 1; COL1 = 1; COL2 = 0; } Delay_us(50); // 读取行状态 if(ROW0 == 0) row = 0; else if(ROW1 == 0) row = 1; else if(ROW2 == 0) row = 2; else continue; // 消抖确认 Delay_ms(5); // 再次设置当前列并读取行 if( (row==0 && ROW0==0) || (row==1 && ROW1==0) || (row==2 && ROW2==0) ) { return row*3 + col + 1; // 返回键值1~9 } } return 0; // 无按键 } void Set_LED(Uint16 key) { if(key >= 1 && key <= 8) { Uint16 led_bit = 1 << (key - 1); GpioDataRegs.GPASET.all = led_bit; // 只点亮对应LED } else if(key == 9) { GpioDataRegs.GPACLEAR.all = 0x00FF; // 熄灭所有LED } }4.4 实验现象
按下SW1~SW8:对应编号的LED独立点亮(互不影响)
按下SW9:所有LED熄灭
五、实验总结与心得
寄存器保护:初次忘记
EALLOW导致无法修改方向寄存器,排查后印象深刻。按键消抖:机械按键必须加入软件消抖(延时+二次确认),否则会出现误触发或多重触发。
GPxSET/GPxCLEAR:相比直接操作
GPxDAT,使用置位/清零寄存器可以避免读-改-写的风险,且不影响其他引脚状态。矩阵扫描效率:列扫描法简单可靠,注意每次切换列后需要短暂延时等待电平稳定。
本实验为后续使用DSP驱动LCD、步进电机等外设打下了很好的GPIO操作基础。
六、代码获取
直接解压复制代码到您的CCS工程main.c文件中,注意包含必要的头文件即可。
七、参考资料
TMS320F2812 Datasheet & Technical Reference Manual
《DSP原理与应用》课程讲义
SEED-DPS2812M开发板原理图