最近在《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 扫描流程

  1. 所有列线输出高电平

  2. 依次将某一列置低,其余列保持高

  3. 读取三根行线状态,若某行为低,则记录行号

  4. 延时5ms消抖,再次确认该行仍为低

  5. 计算键值:key = row*3 + col + 1(范围1~9)

  6. 映射到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熄灭


五、实验总结与心得

  1. 寄存器保护:初次忘记 EALLOW 导致无法修改方向寄存器,排查后印象深刻。

  2. 按键消抖:机械按键必须加入软件消抖(延时+二次确认),否则会出现误触发或多重触发。

  3. GPxSET/GPxCLEAR:相比直接操作 GPxDAT,使用置位/清零寄存器可以避免读-改-写的风险,且不影响其他引脚状态。

  4. 矩阵扫描效率:列扫描法简单可靠,注意每次切换列后需要短暂延时等待电平稳定。

本实验为后续使用DSP驱动LCD、步进电机等外设打下了很好的GPIO操作基础。

六、代码获取

直接解压复制代码到您的CCS工程main.c文件中,注意包含必要的头文件即可。


七、参考资料

  • TMS320F2812 Datasheet & Technical Reference Manual

  • 《DSP原理与应用》课程讲义

  • SEED-DPS2812M开发板原理图

更多推荐