TMS320F2812 DSP 通用I/O端口实验:线性键盘控制跑马灯 + 矩阵键盘控制LED
最近在《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开发板原理图
更多推荐
所有评论(0)