找回密码
 立即注册
楼主: dumon

跟 冲哥教学视频学习STC——记录每一天的成长| 必须立即送强大的实验箱支持啊 !

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:448
  • 最近打卡:2025-05-06 10:56:26

9

主题

85

回帖

1666

积分

金牌会员

积分
1666
发表于 2024-4-17 22:28:58 | 显示全部楼层
Mark一下,因为已经快要学习到ADC后面的课程,再用自己手搓的开发板不能做相应的实验,就想申请下冲哥同款实验箱。昨天刚申请通过,今天就收到了,还是寄的顺丰,速度好快,本来预计可能要周六或周日才能到的。收到实验箱的第一眼就喜欢上了。它非常超乎我的意料,制作精美,工艺精湛,后续可以继续跟着冲哥的视频学习后面的课程了。

其他的小伙伴也要加油,STC真是不错,每天打卡学习真的可以免费得到实验箱,工欲善其事,必先利其器,STC都把这么好的学习武器提供给我们了,我们还有什么理由不好好学习单片机知识呢。
永远相信美好的事情即将发生!
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:448
  • 最近打卡:2025-05-06 10:56:26

9

主题

85

回帖

1666

积分

金牌会员

积分
1666
发表于 2024-4-17 22:31:49 | 显示全部楼层
dum*** 发表于 2024-4-17 22:28
Mark一下,因为已经快要学习到ADC后面的课程,再用自己手搓的开发板不能做相应的实验,就想申请下冲哥同款 ...

太激动了,都忘记附上实验箱的照片了。
截图202404172231026109.jpg
永远相信美好的事情即将发生!
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:508
  • 最近打卡:2025-05-07 07:34:14
已绑定手机

1

主题

836

回帖

1521

积分

金牌会员

积分
1521
发表于 2024-4-18 08:11:06 | 显示全部楼层
哇,鸟枪换大炮了
靡不有初,鲜克有终
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:448
  • 最近打卡:2025-05-06 10:56:26

9

主题

85

回帖

1666

积分

金牌会员

积分
1666
发表于 2024-4-18 23:28:36 | 显示全部楼层
按键状态机


按键按状态来分:按键按下、按键松开
       按电平来分:低电平、高电平

其中按键按下时按低电平时长分为5种状态:
<30 ms     消抖
=30 ms     单击
<3000ms  单击结束
=3000ms  长按3秒
>3000ms  长按结束

可以看到时间是在一直累加的,当到达其中1个时间点时我们就执行这一步的程序,比如在30ms以内,我们认为按键是消抖状态,不进行任何操作,正好等于30ms时,执行单击程序,大于30ms小于3000ms这个区间是认为在长按过程,正好等于3000ms时认为达到长按的判定。超过3000ms,可以判定为长按结束。
这里引进了一个状态机的概念,状态机教程里说的是条件转移,即这个变量是按键按下后累计的时长ms,随着时间轴的进行,等于或满足哪个条件时就进行运行状态转移,就比如上面的消抖,单击,长按,他是要满足时间条件后才能运行对应的步。

下面分析下程序:
计时数组  u16 count[8] = {0,0,0,0,0,0,0,0};     //一个8位元素的数组,每个元素对应1个P口的8位IO口,用于记录每个按键按下后的时间,数据类型要为16位整型。比如count[0]用于记录P3.0按下的时长

u8 LastState = 0;   //8位整型变量用于记录上次哪个按键按下

检查按键状态函数,放在主程序中每10ms执行一次
void KEY_Deal(void)
{
    u8 i = 0;
    for(i=0; i<8; i++)       //循环8次,用于判定P30~P37有无按键按下
    {
        if(~P3 & (1<<i))     //假设i= 0时,P30有按下为低电平,~P30 = ^(1111 1110)=0000 0001,1<<0 = 0000 0001,两者相与后为0000 0001,如果持续有P30按下,这个条件是一直满足的。
        {
            if(count[i]<60000)   //当有按键按下时,并且count[i]的值小于60000时,对应的count[i]就会一直进行累加,用于记录对应的按键按下时间。
                count[i]++;
        }
        else            //当按键松开后,执行else里的语句
        {
            if(count[i]>0)    //当按键松开时,此时count[i]是大于0的,会把按键状态更新给LastState,LastState用来记录哪个按键按下了
            {
                LastState |= (1<<i);  //这个按键有按下过,假设是P35,i=5,LastState = 0000 0000,1<<5 = 0010 0000,更新后LastState = 0010 0000
             }
             else
             {
                 LastState &= ~(1<<i);  //两者相与,比如上面是P35按下,在i=0到4时,0010 0000 & 1111 1110,还是保持0010 0000不变,只有在i=5时才会将LastState清零,
             }                                     //所以在这段函数运行时,会记录下哪个按键有按下,按下了多长时间,并将按键状态传递给LastState。
             count[i] = 0;              //计数变量count[i]清零
         }
    }
}

读取指定的按键 状态
/**************变量声明**************/
#define KEY_NOPRESS        0                // 按键未按下
#define KEY_FILCKER                1                // 消抖
#define KEY_PRESS                2                // 单击
#define KEY_PRESSOVER        3                // 单击结束
#define KEY_LONGPRESS        4                // 长按3秒
#define KEY_LONGOVER        5                // 长按结束
#define KEY_RELAX                6                // 按键松开

u8 KEY_Readstate(u8 keynum)    //keynum = 0~7;10ms执行一次
{
    if(coount[keynum]>0)   //按键有按下时,count[i]有计数,各种状态处理
   {
       if(count[keynum]<3)               //1~29ms按键消抖
      {    return KEY_FILCKER;  }
       else if (count[keynum]==3)     //超过30ms,返回单击
      {    return KEY_PRESS;  }

       else if (count[keynum]<300)    //31~2999ms,返回单击结束
      {    return KEY_PRESSOVER;  }


       else if (count[keynum]==300)  //正好3000ms时,返回长按
      {    return KEY_LONGPRESS;  }


       else                                        // 大于3000ms,返回长按结束
      {    return KEY_LONGOVER;  }

    }
   else                //按键松开后,进行判断按键之前有无按下过
   {
        if(LastState & (1<<keynum))    //假设之前P3.5有按下过,LastState会记忆P3.5的状态值,当P3.5再次按下,等其松开后就会返回”KEY_RELAX“,表示这个键上次有按下过
        {   return KEY_RELAX;  }
        else
        {   return KEY_NOPRESS;  }     //按键之前没有被按下过,比如上次是按的P3.5,这次是按的P3.0,两者不相等,就会返回这里语句的值。
    }
}

以上,是对第13集多任务处理中,按键状态机的程序解读,LastState这个变量是用于检查按键检查函数中用于区分”按键松开“与”没有按下“。

通过计数数组count[i]来统计按键按下时长,根据count[i]的值来判定程序应该执行到哪一步,满足了某个条件就进行转移,这就是状态机,这也是程序中一种常用用法。后面还会经常运用到。
永远相信美好的事情即将发生!
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:398
  • 最近打卡:2025-05-07 06:15:15
已绑定手机

17

主题

137

回帖

1543

积分

金牌会员

积分
1543
发表于 2024-4-19 19:50:42 | 显示全部楼层
学的真好啊
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:448
  • 最近打卡:2025-05-06 10:56:26

9

主题

85

回帖

1666

积分

金牌会员

积分
1666
发表于 2024-4-20 13:45:04 | 显示全部楼层
第13集 多任务处理的总结

附上开发板烧录程序后的演示视频
手写学习笔记



IMG_20240420_133719[1].jpg
IMG_20240420_133741[1].jpg
IMG_20240420_133757[1].jpg
IMG_20240420_133812[1].jpg

21. STC32G自制开发板 - 多任务处理.zip

330.8 KB, 下载次数: 181

程序代码

永远相信美好的事情即将发生!
回复 支持 反对

使用道具 举报 送花

2

主题

22

回帖

64

积分

注册会员

积分
64
发表于 2024-5-21 19:24:39 | 显示全部楼层
大哥,真乃大佬也,佩服!我学的没你认真,现在都开始遗忘了
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:448
  • 最近打卡:2025-05-06 10:56:26

9

主题

85

回帖

1666

积分

金牌会员

积分
1666
发表于 2024-5-28 23:31:49 | 显示全部楼层
第14集 矩阵按键

1.矩阵按键是什么?
相对于独立按键,每1个按键占用1个IO口,矩阵按键是将按键排列成矩阵排列的形式的按键排列。

课程里以P0口为例,P0.0-P0.3 4个IO为一组,P0.4-P0.7 4个IO为一组,可以组成4*4排列,通过8个IO口实现16个按键的输入。

具体怎么实现的呢,有些是说明行扫描法或列扫描法,执行逻辑为:
1.先将P0.4-P0.7输出为低电平,P0.0-P0.3为高电平,当某个按键按下时,检测P0.0-P0.3哪个为低电平,用异或记下这时的P0口的值;
2.反转,将P0.0-P0.3输出为低电平,P0.4-P0.7输出为高电平,当同样的按键按下时,检测P0.4-P0.7哪个为低电平,用异或记下这时的P0口的值;
3.将2次P0口的值相或得到新的组合值,根据组合值计算出这是哪个按键被按下了。

这里的本质还是独立按键的工作原理,因为不便于描述,这里还是附上手写笔记,方便大家更好理解。

总的原则,矩阵按键也是由独立按键演化而来的,只是其中一组在充当GND,矩阵按键就是用行列来确定哪个按键按下,所以会有一外键值表,我们可以根据键值表来确定是哪个按键被按下。

矩阵按键极大缓解了单片机IO口不够用的情况。
IMG_20240528_232725.jpg
IMG_20240528_232736.jpg
IMG_20240528_232753.jpg
永远相信美好的事情即将发生!
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:448
  • 最近打卡:2025-05-06 10:56:26

9

主题

85

回帖

1666

积分

金牌会员

积分
1666
发表于 2024-5-28 23:37:51 | 显示全部楼层
dum*** 发表于 2024-5-28 23:31
第14集 矩阵按键

1.矩阵按键是什么?

非常抱歉,在上传图片时没有提前预览,导致图片旋转了180度,不便于大家观看,已重新上传。
IMG_20240528_233500.jpg
IMG_20240528_233512.jpg
永远相信美好的事情即将发生!
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:448
  • 最近打卡:2025-05-06 10:56:26

9

主题

85

回帖

1666

积分

金牌会员

积分
1666
发表于 2024-5-29 23:38:20 | 显示全部楼层
矩阵键盘补充:
准双向口的含义是:即可以写入0或1,又可读取P口的状态,但有一点要注意的是两者不能同时进行。同一时间只能进行一个读或写操作。


怎么对IO口进行读操作:2种方法
1)使用“==”号
if(P0 == 0xC0),这里程序就会对P0口进行读操作,并与0xC0进行判定是否等于
2)用变量转存Pn口的高低电平,用(8位二进制或2位16进制)
u8 key_state;
key_state = P0;


我在这节课学习时也学了好久,但通过原理图的分析加深自己的理解,最终把矩阵键盘搞定了。


  1. /**************************************************************************
  2.         “矩阵键盘:行列扫描得出按键键值,水平为列,垂直为行
  3.         先行写入低电平,列写入高电平,延时10ms后,再读取列电平状态,检测出哪列有低电平
  4.         再列写入低电平,行写入高电平,延时10ms后,再读取行电平状态,检测出哪行有低电平,
  5.         通过行列组合判断哪个按键按下。
  6.         这里相当于Arduino的digitalWrite和digitalRead,我想这就是准双向口的源来吧。
  7.        
  8.         第1步:P0.0-P0.3写入低电平,P0.4-P0.7写入高电平,为1111 0000,延时10ms,读取P0.4-P0.7哪个为低电平,则确认列数,
  9.         如P0.7读取为0,0111 0000^1111 0000=1000 0000,异或的目的是与上次比较哪个P口状态有变。此次为P0.7口有变
  10.         第2步:P0.0-P0.3写入高电平,P0.4-P0.7写入低电平,为0000 1111,延时10ms,读取P0.0-P0.3哪个为低电平,则确认行数,
  11.         如P0.0读取为0,0000 1110^0000 1111=0000 0001,此次为P0.0口有变。
  12.         第3步:将行与列进行逻辑或组合,1000 0000|0000 0001=1000 0001=0x81,P0.0+P0.7的交点为KEY13,
  13.        
  14.         下表为4x4矩阵键盘的键值
  15.        
  16. ---行--------- P0.0            P0.1        P0.2        P0.3
  17.                                  |                  |       |       |
  18. |                                 |                  |       |       |
  19. |        P0.4--------KEY1-----KEY2----KEY3----KEY4---------
  20. |                             0x11         0x12    0x14    0x18       
  21. |                                 |                  |       |       |       
  22. |        P0.5--------KEY5-----KEY6----KEY7----KEY8---------
  23. 列                             0x21         0x22    0x24    0x28
  24. |                                 |                  |       |       |       
  25. |        P0.6--------KEY9----KEY10---KEY11----KEY12--------
  26. |                             0x41         0x42    0x44    0x48
  27. |                                 |                  |       |       |       
  28. |        P0.7-------KEY13----KEY14---KEY15----KEY16--------
  29.                         0x81         0x82    0x84    0x88
  30. **************************************************************************/
  31. #include <STC32G.H>
  32. #include "comm/stc32_stc8_usb.h"
  33. #define MAIN_Fosc       24000000L   //定义主时钟
  34. char *USER_DEVICEDESC = NULL;
  35. char *USER_PRODUCTDESC = NULL;
  36. char *USER_STCISPCMD = "@STCISP#";
  37. #define MatrixKEY P0                                //定义P0为矩阵键盘
  38. void delay_ms(u16 ms)                //@24.000MHz
  39. {
  40.         u16 i;
  41.         do{
  42.                 i= MAIN_Fosc/6000;
  43.                 while(--i);
  44.         }        while(--ms);
  45. }
  46. u8 MatrixKEY_Read()
  47. {
  48.         u8 key_State;                                                                //用于存储按键状态
  49.         u8 key_value;                                                                //用于存储按键键值
  50.         //第一步,P0.0-P0.3给低电平,P0.6-P0.7给高电平,如果检测到P0.6-P0.7为低电平,则确认哪一列按下
  51.         MatrixKEY = 0xC0;                                                        //“这里的MatrixKEY表示写入电平”1100 0000,
  52.         delay_ms(10);                                                                //假设P0.7为0
  53.         key_State = (MatrixKEY^0xC0);                                //“这里的MatrixKEY表示读取电平” 0100 0000~1100 0000=“1000 0000”
  54.        
  55.         //第二步,P0.0-P0.3给高电平,P0.6-P0.7给低电平,如果检测到P0.0-P0.3为低电平,则确认哪一行按下
  56.         MatrixKEY = 0x0F;                                                        //0000 1111
  57.         delay_ms(10);                                                                //假设P0.0为0
  58.         key_State = key_State |(MatrixKEY^0x0F);        //0000 1110~0000 1111= “0000 0001”,1000 0000|0000 0001=1000 0001
  59.        
  60.         //第三步,行列组合,判断是哪个按键按下       
  61.         switch (key_State)
  62.     {
  63.             case 0x41:
  64.                         key_value = 1;
  65.                     break;
  66.             case 0x42:
  67.                         key_value = 2;
  68.                     break;
  69.             case 0x44:
  70.                         key_value = 3;
  71.                     break;
  72.             case 0x48:
  73.                         key_value = 4;
  74.                     break;
  75.             case 0x81:
  76.                         key_value = 5;
  77.                     break;
  78.             case 0x82:
  79.                         key_value = 6;
  80.                     break;
  81.             case 0x84:
  82.                         key_value = 7;
  83.                     break;
  84.             case 0x88:
  85.                         key_value = 8;
  86.                     break;               
  87.             default:key_value = 0;
  88.                     break;
  89.     }
  90.         printf("现在按下的是%2d\r\n", key_value);                //利用printf函数输出按键键值
  91.         return key_value;       
  92. }
  93. void sys_init()
  94. {
  95.     WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
  96.     EAXFR = 1; //扩展寄存器(XFR)访问使能
  97.     CKCON = 0; //提高访问XRAM速度
  98.     P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
  99.     P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
  100.     P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
  101.     P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
  102.     P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
  103.     P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
  104.     P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
  105.     P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
  106.     //====== USB 初始化 ======
  107.     P3M0 &= ~0x03;
  108.     P3M1 |= 0x03;
  109.    
  110.     IRC48MCR = 0x80;
  111.     while (!(IRC48MCR & 0x01));
  112. }
  113. void main()
  114. {
  115.     sys_init();  //系统初始化
  116.     usb_init();  //USB CDC 接口配置
  117.     EA = 1;                 //打开总中断
  118.    
  119.     while (1)
  120.     {
  121.                 if(bUsbOutReady)
  122.                 {
  123.                         USB_SendData(UsbOutBuffer,64);  //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
  124.                         usb_OUT_done();        //接收应答(固定格式)       
  125.                        
  126.                 }
  127.                 MatrixKEY_Read();
  128.     }
  129. }
复制代码



矩阵键盘原理图

矩阵键盘原理图

这张图可以理解矩阵按键为独立按键的转换图,当开关闭合时等效独立按键,10K为上拉5V电阻,开关闭合时,P0. ...

这张图可以理解矩阵按键为独立按键的转换图,当开关闭合时等效独立按键,10K为上拉5V电阻,开关闭合时,P0. ...
永远相信美好的事情即将发生!
回复 支持 反对

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-5-8 02:34 , Processed in 0.604465 second(s), 111 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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