找回密码
 立即注册
楼主: du***

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

[复制链接]
  • TA的每日心情
    开心
    昨天 08:26
  • 签到天数: 140 天

    [LV.7]常住居民III

    5

    主题

    56

    回帖

    433

    积分

    中级会员

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

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

    使用道具 举报

  • TA的每日心情
    开心
    昨天 08:26
  • 签到天数: 140 天

    [LV.7]常住居民III

    5

    主题

    56

    回帖

    433

    积分

    中级会员

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

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

    使用道具 举报

  • TA的每日心情
    开心
    12 小时前
  • 签到天数: 171 天

    [LV.7]常住居民III

    0

    主题

    464

    回帖

    565

    积分

    高级会员

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

    使用道具 举报

  • TA的每日心情
    开心
    昨天 08:26
  • 签到天数: 140 天

    [LV.7]常住居民III

    5

    主题

    56

    回帖

    433

    积分

    中级会员

    积分
    433
     楼主| 发表于 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]的值来判定程序应该执行到哪一步,满足了某个条件就进行转移,这就是状态机,这也是程序中一种常用用法。后面还会经常运用到。
    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 81 天

    [LV.6]常住居民II

    10

    主题

    138

    回帖

    206

    积分

    中级会员

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

    使用道具 举报

  • TA的每日心情
    开心
    昨天 08:26
  • 签到天数: 140 天

    [LV.7]常住居民III

    5

    主题

    56

    回帖

    433

    积分

    中级会员

    积分
    433
     楼主| 发表于 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, 下载次数: 9

    程序代码

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

    使用道具 举报

    该用户从未签到

    2

    主题

    22

    回帖

    64

    积分

    注册会员

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

    使用道具 举报

  • TA的每日心情
    开心
    昨天 08:26
  • 签到天数: 140 天

    [LV.7]常住居民III

    5

    主题

    56

    回帖

    433

    积分

    中级会员

    积分
    433
     楼主| 发表于 5 天前 | 显示全部楼层
    第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
    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    昨天 08:26
  • 签到天数: 140 天

    [LV.7]常住居民III

    5

    主题

    56

    回帖

    433

    积分

    中级会员

    积分
    433
     楼主| 发表于 5 天前 | 显示全部楼层
    dumon 发表于 2024-5-28 23:31
    第14集 矩阵按键

    1.矩阵按键是什么?

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

    使用道具 举报

  • TA的每日心情
    开心
    昨天 08:26
  • 签到天数: 140 天

    [LV.7]常住居民III

    5

    主题

    56

    回帖

    433

    积分

    中级会员

    积分
    433
     楼主| 发表于 4 天前 | 显示全部楼层
    矩阵键盘补充:
    准双向口的含义是:即可以写入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, 2024-6-2 12:37 , Processed in 0.374017 second(s), 68 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

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