找回密码
 立即注册
查看: 15|回复: 1

新手学STC单片机 实验七 按键控制16*16点阵(下)

[复制链接]
  • 打卡等级:偶尔看看III
  • 打卡总天数:30
  • 最近打卡:2025-12-16 16:50:01

20

主题

29

回帖

83

积分

注册会员

积分
83
发表于 昨天 16:50 | 显示全部楼层 |阅读模式
书接上回https://www.stcaimcu.com/thread-21629-1-1.html

实验测试
    连线:
    用 8P 杜邦线将 JP27 P0-P7 连接 JP18 QA-QH
    JP26 P9-P16 连接 JP20 QA-QH
    JP10-P12 连接 JP16 SER3,SRCLK,RCLK
    JP14-P16 连接 JP28 SER1,SRCLK,RCLK
    JP30-P33 连接 JP74 DK0-DK3
    JP19 SER4 连接 QH-3
    实验现象:
    按下 DK1 键 点阵显示字母 T,按下 DK2 键 点阵显示水平向右滚动的“特种兵”;
    按下 DK3 键 点阵显示向下移动的箭头,按下 DK4 键 点阵显示向上移动的箭头。

代码分析

SN74HC595 的使用
74HC595 串出一个 8 位的数据,其具体程序如下:

/*******************************************************
* 函数名称:HC595_send_byte_r()
* 函数功能:行方向上的HC595串出一个字节,从高位到低位串
* 入口参数:dat: 串入的字节
* 返回参数:无
********************************************************/
void HC595_send_byte_r(uchar dat)
{
    uchar i;
   
    for (i = 0; i < 8; i++)
    {
        HC595_data_r = dat & 0x80;   // 从高位到低位串出
        dat <<= 1;      
        HC595_clk_r = 0;             // HC595_clk_r上升沿将SER数据逐步传入到移位寄存器中
        HC595_clk_r = 1;
    }
}

    16*16 点阵扫描底层驱动就是不考虑显示缓冲区是什么内容,当定时器 1ms 一
到,就将当前缓冲区中相应列的段码显示出来。这种设计可以将底层驱动与上层程
序控制分开,更好的实现模块化,调试起来也比较方便。部分程序如下所示:

/*******************************************************
//函数名称:void display_io(void)
//函数功能:16 * 16点阵扫描底层驱动,1ms扫描一列
//入口参数:无
//返回参数:无
********************************************************/
void display_io(void)
{
    static uchar scan_line;     //16列扫描计数
    union display_data line;    //扫描位选值
    uchar temp1;
    uchar temp2;
   
    temp1 = ~display_buff[scan_line].data_8[0];   //扫描到当前列,显示缓冲区高8位
    temp2 = ~display_buff[scan_line].data_8[1];   //扫描到当前列,显示缓冲区低8位
   
    line.data_16 = scan_line_array[scan_line];     //给当前列即位选赋值
   
    HC595_send_byte_c(line.data_8[0]);             //串出列上的数据,位选
    HC595_send_byte_c(line.data_8[1]);
   
    HC595_send_byte_r(temp1);                      //串出行上的数据,段值
    HC595_send_byte_r(temp2);
   
    HC595_latch_r = 0;                             //行方向,HC595_latch_r上升沿将并出的数据锁存到存储寄存器中
    HC595_latch_r = 1;
   
    HC595_latch_c = 0;                             //列方向,HC595_latch_c上升沿将并出的数据锁存到存储寄存器中
    HC595_latch_c = 1;
   
    if (++scan_line >= 16) scan_line = 0;           //扫描完16列,回到第一列
}

    在本段代码中,用到了 union 数据结构,它的定义在 hal.h 文件中,这个数据
结构可以将 16 位数据的高八位和低八位直接取出使用,而不用通过左移操作实现,
更加简洁方便。

按键扫描底层驱动
    按键设计依然采用状态机的思想,现在有四个按键,所以需要确认那个键按下,
在 read_key()函数中,在 state1 按键确认状态时还需要判断哪个键按下,并将返
回值赋给key_return,这样方便在主程序调用read_key()后就可将返回值赋给flag

标志位,从而方便进行相应的处理。部分程序如下所示:

/*******************************************************
//函数名称: uchar read_key(void)
//函数功能:按键扫描底层驱动,10ms扫描一次,利用状态机进行消抖
//入口参数:无
//返回参数:返回当前按键值
********************************************************/
uchar read_key(void)
{
    static uchar key_state = 0;
    uchar key_press, key_return = 0;
   
    P3 = 0x7f | P3;                     //读按键I/O电平,先写1
    key_press = P3 | 0xf0;              //将P3的高4位屏蔽掉,由低4位判断键值
   
    switch (key_state)
    {
    case key_state0:                    //按键初试态
        if (key_press != key_none_value)
        {
            key_state = key_state1;     //按键被按下状态转换到确认态
        }
        break;
        
    case key_state1:                    //按键确认态
        //按键仍按下状态转换成释放态
        if (key_press != key_none_value)
        {
            key_state = key_state2;
            switch (key_press)          //返回按键的值
            {
            case key1_value:
                key_return = 1;         //DK1键按下
                break;
            case key2_value:
                key_return = 2;         //DK2键按下
                break;
            case key3_value:
                key_return = 3;         //DK3键按下
                break;
            case key4_value:
                key_return = 4;         //DK4键按下
                break;
            }
        }
        else
            key_state = key_state0;     //否则按键在抖动,转换到按键初始态
        break;
        
    case key_state2:                    //按键释放态
        P3 = 0x7f | P3;                 //读按键I/O电平,先写1
        key_press = P3 | 0xf0;
        if (key_press == key_none_value)
        {
            key_state = key_state0;     //按键已释放,转换到按键初始态
        }
        break;
    }
   
    return key_return;
}

主程序流程图
    如图 7-9 所示,为 main 函数的程序流程图,本次实验软件设计由 3 个文件组
成,main.c、key_read.c、display_io.c。
    在 main.c 中进行系统的初始化和中断服务程序处理,选用定时器 0 进行 1ms
定时,在定时器 0 中断服务程序中,设置 1ms,10ms,150ms 标志位,用于 16*16
点阵扫描,驱动按键扫描与消抖以及更新显示缓冲区内容。
    需要注意的是,由于本实验全部采用列扫描方式,所以箭头上升与下降在更新
缓冲区内容时需要进行如下处理:
for (k0 = 0; k0 <= 15; k0++)
{
    tmp1 = display_buff[k0].data_8[1];  //行数据的低八位
   
    display_buff[k0].data_16 >>= 1;     //将16位数据右移一位
   
    if (tmp1 & 0x01)
    {
        display_buff[k0].data_16 |= 0x8000;  //低八位数据最后一位移位到高八位最高位
    }
}

截图202512161646264139.jpg

程序相关宏文件:

#define INT_GLOBAL_ENABLE(on) EA = (!!on)      //开全局中断

/********************************************************
// 功能:定时器运行操作控制
// 参数:num 计数器号(0-4) ;on 运行控制,1:启动,0停止
*********************************************************/
#define TIMER_RUN(num, on) \
    st( \
        if (num == 0) \
            TR0 = on; \
        else if (num == 1) \
            TR1 = on; \
        else if (num == 2) \
            (on) ? (AUXR |= BIT4) : (AUXR &= ~BIT4); \
        else if (num == 3) \
            (on) ? (T4T3M |= BIT3) : (T4T3M &= ~BIT3); \
        else if (num == 4) \
            (on) ? (T4T3M |= BIT4) : (T4T3M &= ~BIT4); \
    )

/********************************************************
// 功能:定时器定时操作控制
// 参数:num 计数器号(0-4)
*********************************************************/
#define TIMER_TIME(num) \
    st( \
        if (num == 0) \
            TMOD |= ~BIT2; \
        else if (num == 1) \
            TMOD |= ~BIT6; \
        else if (num == 2) \
            AUXR |= ~BIT3; \
        else if (num == 3) \
            T4T3M |= ~BIT2; \
        else if (num == 4) \
            T4T3M |= ~BIT6; \
    )

/********************************************************
// 功能:定时器中断使能操作控制
// 参数:num 计数器号(0-4) ;on 使能控制,1:允许,0禁止 //1溢出中断允许
*********************************************************/
#define TIMER_INT_EN(num, on) \
    if (num == 0) \
        ET0 = on; \
    else if (num == 1) \
        ET1 = on; \
    else if (num == 2) \
        (on) ? (IE2 |= BIT3) : (IE2 &= ~BIT3); \
    else if (num == 3) \
        (on) ? (IE2 |= BIT5) : (IE2 &= ~BIT5); \
    else if (num == 4) \
        (on) ? (IE2 |= BIT6) : (IE2 &= ~BIT6);

/********************************************************
// 功能:定时器工作模式选择
// 参数:num 计数器号(0-1) ;sel模式选择(0-3)
0:16位重载 1:16位计数器 2:8位重载 3:计数器无效
*********************************************************/
#define TIMER_MODE(num, sel) \
    if (sel < 4) \
    { \
        if (num == 0) \
        { \
            TMOD &= ~0x03; \
            TMOD |= sel; \
        } \
        else if (num == 1) \
        { \
            TMOD &= ~0x30; \
            TMOD |= sel << 3; \
        } \
    }

/********************************************************
// 功能:定时器定时时间分频选择
// 参数:num 计数器号(0-4); sel分频选择; 1:1分频,0-12分频
*********************************************************/
#define TIMER_CLK_DIV(num, sel) \
    st( \
        if (num == 0) \
            (sel == 1) ? (AUXR |= BIT7) : (AUXR &= ~BIT7); \
        else if (num == 1) \
            (sel == 1) ? (AUXR |= BIT6) : (AUXR &= ~BIT6); \
        else if (num == 2) \
            (sel == 1) ? (AUXR |= BIT2) : (AUXR &= ~BIT2); \
        else if (num == 3) \
            (sel == 1) ? (T4T3M |= BIT1) : (T4T3M &= ~BIT1); \
        else if (num == 4) \
            (sel == 1) ? (T4T3M |= BIT5) : (T4T3M &= ~BIT5); \
    )

/*************************************
// 功能:清除定时器中断标志 //清除溢出中断
// 参数:num 计数器号(0-1)
*********************************************************/
#define CLR_TIMER_FLAG(num) \
    st( \
        if (num == 0) \
            TF0 = 0; \
        else if (num == 1) \
            TF0 = 0; \
    )







回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-12-16 09:25:23

15

主题

64

回帖

802

积分

版主

积分
802
发表于 昨天 17:00 | 显示全部楼层
要 做到 USB不停电下载;
要 尝试 AiCube 图形化自动配置生成程序工具;
推荐优先看的:  
printf_usb("Hello World !\r\n")及
USB不停电下载, 演示视频链接:
https://www.stcaimcu.com/thread-19077-1-1.html

下载 最新的 AiCube-ISP-V6.96J 或以上版本软件 !
深圳国芯人工智能有限公司-工具软件

下载 最新的 USB库函数,永远用最新的 USB库函数 !
深圳国芯人工智能有限公司-库函数
下载 最新的 用户手册 !
下载 最新的 上机实践指导书 !

下载 最新的 Ai8051U 用户手册
https://www.stcaimcu.com/data/download/Datasheet/AI8051U.pdf

下载 最新的 Ai8051U 实验指导书,
有 AiCube 图形化自动配置生成程序工具使用说明
https://www.stcaimcu.com/data/do ... %AF%BC%E4%B9%A6.pdf

推荐优先看的 printf_usb("Hello World !\r\n")及usb不停电下载, 演示视频链接


回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-12-17 07:45 , Processed in 0.113887 second(s), 52 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表