找回密码
 立即注册
查看: 666|回复: 11

《Ai8051U教学视频》学习心得

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-1 14:26:30 | 显示全部楼层 |阅读模式
开篇从1月1日开始


爱电子技术的人大抵是热爱学习的。做为一个从小就对电子有些许喜好而从事的工作与电子无关的人,应该只能被定义为电子爱好者。那是23年,也许是为了使生活有更多的期许,也许是儿时梦想的延续,开始自学STC单片机。在一次不经意打开STC-ISP准备下载程序的时候,我偶然间发现了STC官方赠送的屠龙刀开发板活动。免费获得STC32G12K128,这对于才刚刚完成STC89C52点灯的老初学者来说诱惑实在太大。但是我既不是学生,又不是从业人员,如果贸然申请会被通过吗?不会是被人歧视吧。尽管有种种顾虑,但还是鼓起勇气联系了STC客服经理。出乎意料的是,客服经理竟然非常友好地通过了我的申请。那一刻,我深刻感受到了电子行业人谦逊、好学乐于助人


着学习的深入,我逐渐意识到,电子工程师的世界是一个充满挑战与机遇的天地。他们最关注的是自身技术的进步,最开心的事情是解决一个困扰自己多时的问题。在这个领域里,没有人会因为你的身份或背景而轻视你,只要你愿意学习、愿意分享,你就会受到大家的尊重和欢迎。

得知《Ai8051U教学视频》这一优质学习资源时,我毫不犹豫地加入了学习计划。这套视频不仅内容详实、讲解清晰,而且还赠送实验箱。这怎么能错过。


2025年伊始,开始新的学习篇章,开贴记之。期待与坛友共同学习,共同进步,也鞭策自己可以坚持下去。

目  录
1.《第三集 点亮第一颗LED》学习

2.《第四集 USB不停机下载》学习

3.《第六集 I/O输入输出》学习

4.《第七集 定时器中断》学习

5. 《第八集 定时器周期性调度任务》学习

6.《第九集 数码管》学习


7.《第十集 虚拟键盘LED和数码管》学习

8《第十一集 矩阵按键》学习







1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
  • +1

    楼主威武~

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-1 15:06:41 | 显示全部楼层
学习单片机必须要上手,所以从点灯开始
《第三集 点亮第一颗LED》学习



为了点亮一个LED,我们需要做什么?
1、从硬件上确定使用哪个IO
2、配置IO
3、为IO设置电平、延时程序


一、硬件上确定使用哪个IO
根据开发板实际情况,查看管脚定义,确认使用的LED已经与之相邻的IO。我使用的P46。
1.jpg

二、配置IO
对于89C51入门级的51单片机来说,是不用配置IO的,可以直接使用。因为系统已经缺省做好了配置。
但是Ai8051U需要先做配置再使用,这部分涉及寄存器的位操作,幸好STC-ISP提供了IO配置工具可以勾选一下就获得配置程序:
1.png
获得的程序片段:
  1.     P4M0 = 0x40; P4M1 = 0x00;
复制代码


三、点灯之旅
1、延时函数
用STC-ISP提供的软件延时计算器生成延时1ms函数
2.png
生成的函数,及写个延时ms函数:
  1. void Delay1ms(void)        //@24.000MHz
  2. {
  3.         unsigned long edata i;
  4.         _nop_();
  5.         _nop_();
  6.         _nop_();
  7.         i = 5998UL;
  8.         while (i) i--;
  9. }
  10. void delay_ms(unsigned short ms)
  11. {
  12.         unsigned short i;
  13.         for(i=0;i<ms;i++) Delay1ms();
  14. }
复制代码


2、main函数
  1. void main(void)
  2. {
  3.           P4M0 = 0x40; P4M1 = 0x00;
  4.        
  5.         while(1)
  6.         {
  7.                 P46=1;
  8.                 delay_ms(1000);
  9.                 P46=0;
  10.                 delay_ms(1000);
  11.         }
  12. }
复制代码
P46=1;//熄灭

P46=0;//低电平点灯


3、包含头文件
#include "stc8051u.h"  //我手头的是STC8051U
#include "INTRINS.h"  //_nop_()函数需要


四、运行效果

tutieshi_480x270_4s.gif


回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-1 19:18:43 | 显示全部楼层
《第四集 USB不停机下载》学习


STC单片机需要复位才能下载程序:
STC单片机在启动时,会首先执行一段出厂时固化的程序。这段程序的主要作用是检测串口是否有下载程序的需求。
如果没有下载需求,单片机将继续执行内部的用户程序。这就是为什么在每次下载程序时需要进行复位。

一般STC单片机具有以下几种复位方式可以选择,每一种复位都可以用来触发下载程序。
1、上电复位
2、低压复位(掉电复位)
3、复位脚复位(RST)
4、看门狗复位
5、软件复位(寄存器IAP_CONTR的SWRST位)


  1. IAP_CONTR = 0x60;   //触发软件复位,从ISP开始执行
复制代码

3.png
但是随着单片机3.3v工作电压的普及,导致部分单片机可以通过串口供电而使复位变得困难;还有一些单片机取消了RST复位引脚,从而导致复位的手段越来越少,复位可靠性也较低,传统的下载方式不断接收挑战。
于是不停机下载应运而生。个人理解不停机下载主要是利用软件复位,在不手动接触开发板的情况下,通过各种手段发出软件复位命令,如串口、USB。

因为Ai8051U具有USB功能,可以将USB配置为USB CDC或USB HID进行下载。而CDC可以理解为串口,所以本质上就是ISP通过串口发送了”@STCISP#"指令,单片机串口接收处理函数判断接收成功后,触发软件复位,进而带动程序下载。

教程中给出的程序如下:

  1. #include "ai8051u.h"                        //调用头文件
  2. #include "stc32_stc8_usb.h"                //调用头文件
  3. char *USER_DEVICEDESC = NULL;
  4. char *USER_PRODUCTDESC = NULL;
  5. char *USER_STCISPCMD = "@STCISP#";
  6. void main(void)
  7. {
  8.     P_SW2 |= 0x80;                //B7位写1,使能访问XFR
  9.         
  10.     P0M1 = 0x00;   P0M0 = 0x00;
  11.     P1M1 = 0x00;   P1M0 = 0x00;
  12.     P2M1 = 0x00;   P2M0 = 0x00;
  13.     P3M1 = 0x00;   P3M0 = 0x00;
  14.     P4M1 = 0x00;   P4M0 = 0x00;
  15.     P5M1 = 0x00;   P5M0 = 0x00;
  16.     P6M1 = 0x00;   P6M0 = 0x00;
  17.     P7M1 = 0x00;   P7M0 = 0x00;
  18.         
  19.         usb_init();                                     //USB CDC 接口配置
  20.     IE2 |= 0x80;                                    //使能USB中断
  21.     EA = 1;                                                                                        //IE |= 0X80;
  22.         
  23.         while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置
  24.         
  25.         while(1)
  26.         {
  27.                
  28.         if (bUsbOutReady)
  29.         {
  30.             USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
  31.             
  32.             usb_OUT_done();
  33.         }
  34.                
  35.                 P40 = 0;        //P40端口输出0V
  36.                 P00 = 0;        //P00端口输出0V
  37.                 P02 = 0;        //P02端口输出0V
  38.                 //P01 = 0;        //P01端口输出0V
  39.                 //
  40.         }
  41. }
复制代码


char *USER_STCISPCMD = "@STCISP#";是可以修改的,对应STC-ISP如下位置也需要修改:


4.png


关于USB的驱动和不停机处理程序以静态库方式提供:
5.png

当然这个程序还提供串口接收转发的功能,从串口工具上发送什么内容,就接收到什么内容:

6.png


关于“当目标文件变化时自动装载并发送下载命令”,看个人喜好吧,因为并不是每次编译都要下载,有的时候会在编译的时候发现逻辑问题,可能不想直接下载。
7.png





回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-1 20:17:11 | 显示全部楼层
《第六集 I/O输入输出》学习



点灯和按键是单片机永恒的两大主题,IO输入输出是实现这个主题的具体实现方式

一、先定义一下用到的IO
  1. #define KEY1 P11
  2. #define KEY2 P12
  3. #define KEY3 P13
  4. #define LED2 P46
  5. #define LED3 P14
  6. #define LED4 P20
复制代码


二、IO初始化
  1. P1M0 = 0x00; P1M1 = 0x00;
  2. P2M0 = 0x00; P2M1 = 0x00;
  3. P4M0 = 0x00; P4M1 = 0x00;
复制代码
都初始化为准双向口。

三、按键轮询控制LED翻转
  1. while(1)
  2. {
  3.       if(KEY1==0)
  4.      {
  5.           delay_ms(10);
  6.           if(KEY1==0)
  7.              LED2=~LED2;
  8.              while(KEY1==0);
  9.       }
  10.       if(KEY2==0)
  11.       {
  12.             delay_ms(10);
  13.             if(KEY2==0)
  14.                 LED3=~LED3;
  15.                 while(KEY2==0);
  16.       }
  17.       if(KEY3==0)
  18.       {
  19.              delay_ms(10);
  20.              if(KEY3==0)
  21.                  LED4=~LED4;
  22.             while(KEY3==0);
  23.        }
复制代码
按键消抖处理:判断低电平、延时、判断低电平、低电平循环、高电平退出。
四、实验效果
tutieshi_480x270_6s.gif


五、简单实现下按一键亮一灯

  1. if(KEY1==0)
  2.                 {
  3.                         delay_ms(10);
  4.                         if(KEY1==0)
  5.                         {
  6.                                 ledcount++;
  7.                                 if(ledcount>3){
  8.                                         ledcount=0;
  9.                                 }
  10.                                
  11.                                 if(ledcount==1)LED2=0;
  12.                                 if(ledcount==2)LED3=0;
  13.                                 if(ledcount==3)LED4=0;
  14.                                 if(ledcount==0){LED2=1;LED3=1;LED4=1;}
  15.                         }
  16.                         while(KEY1==0);
  17.                 }
复制代码


tutieshi_480x853_2s.gif



回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:155
  • 最近打卡:2025-05-06 16:02:38
已绑定手机

15

主题

630

回帖

734

积分

高级会员

积分
734
发表于 2025-1-1 22:29:43 来自手机 | 显示全部楼层
不错,自己焊接的板子?
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-2 12:45:12 | 显示全部楼层
《第七集 定时器中断》学习

为了实现练习,需要进一步实现串口驱动(我没有采用USB CDC方式,所以需要单独实现)

一、串口2驱动
本来想用AIapp-ISP中的串口波特率工具生成代码,但是怎么配置出的代码运行起来都是乱码,还是通过从例程中找了一段代码解决
1、主时钟和波特率定义
  1. #define MAIN_Fosc        24000000L   //定义主时钟
  2. #define Baudrate2   (65536 - MAIN_Fosc / 115200 / 4)
复制代码
2、UART2初始化
  1. void SetTimer2Baudraye(u32 dat)
  2. {
  3.     T2R = 0;                //Timer stop
  4.     T2_CT = 0;        //Timer2 set As Timer
  5.     T2x12 = 1;        //Timer2 set as 1T mode
  6.     T2H = (u8)(dat / 256);
  7.     T2L = (u8)(dat % 256);
  8.     ET2 = 0;    //禁止中断
  9.     T2R = 1;                //Timer run enable
  10. }
  11. void UART2_config(u8 brt)    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
  12. {
  13.     if(brt == 2)
  14.     {
  15.         SetTimer2Baudraye(Baudrate2);
  16.         S2CFG |= 0x01;     //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误
  17.         S2CON = (S2CON & 0x3f) | 0x40;    //UART2模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
  18.         ES2   = 1;         //允许中断
  19.         S2REN = 1;         //允许接收
  20.         S2_S  = 0;         //UART2 switch to: 0: P1.2 P1.3,  1: P4.2 P4.3
  21.         B_TX2_Busy = 0;
  22.     }
  23. }
复制代码
波特率:115200;串口引脚复用:P1.2\P1.3

3、串口中断
  1. void Uart2_Isr(void) interrupt 8
  2. {
  3.         if (S2CON & 0x02)        //检测串口2发送中断
  4.         {
  5.                 S2CON &= ~0x02;        //清除串口2发送中断请求位
  6.                  B_TX2_Busy = 0;
  7.         }
  8.         if (S2CON & 0x01)        //检测串口2接收中断
  9.         {
  10.                 S2CON &= ~0x01;        //清除串口2接收中断请求位
  11.         }
  12. }
复制代码
UART2中断向量为8,这个可以从头文件中查到:
8.png



4、printf重定向

  1. char putchar(char c)
  2. {
  3.         S2BUF = c;
  4.   B_TX2_Busy = 1;
  5.   while(B_TX2_Busy);
  6.         return c;
  7. }
复制代码


二、定时器驱动

1、TIMER0初始化
  1. void Timer0_Init(void)                //500毫秒@24.000MHz
  2. {
  3.         TM0PS = 0xB7;                        //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
  4.         AUXR |= 0x80;                        //定时器时钟1T模式
  5.         TMOD &= 0xF0;                        //设置定时器模式
  6.         TL0 = 0x3F;                                //设置定时初始值
  7.         TH0 = 0x01;                                //设置定时初始值
  8.         TF0 = 0;                                //清除TF0标志
  9.         TR0 = 1;                                //定时器0开始计时
  10.         ET0 = 1;                                //使能定时器0中断
  11. }
复制代码
2、TIMER0中断处理
  1. void Timer0_Isr(void) interrupt 1
  2. {
  3.         out_put_flag=1;
  4.         Timer0_count++;
  5. }
复制代码


三、main函数
  1. Timer0_Init();
  2.         UART2_config(2);
  3.         EA=1;
  4.         while(1)
  5.         {
  6.                
  7.                 if(out_put_flag==1)
  8.                 {
  9.                         out_put_flag=0;
  10.                         printf("定时器500ms触发一次串口输出,中断触发次数:%d\r\n",Timer0_count);
  11.                 }
复制代码


四、运行
9.png
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-2 15:58:23 | 显示全部楼层
《第八集 定时器周期性调度任务》学习


不得不为了这一集拍案叫绝,也许我们都使用过实时操作系统,freertos、rethread、调度器等等,但真正愿意深入了解原理的并不多,本集看完有豁然开朗的感觉。

调度任务可以理解为将MCU的时间进行分时处理,对于优先级高的任务,优先执行。这样使每个任务都可以被mcu执行,而又不独占系统。这样程序也优雅起来了。

一、任务注册
  1. typedef struct
  2. {
  3.         u8 Run;               //任务状态:Run/Stop
  4.         u16 TIMCount;         //定时计数器
  5.         u16 TRITime;          //重载计数器
  6.         void (*TaskHook) (void); //任务函数
  7. } TASK_COMPONENTS;
复制代码
首先定义了一个任务结构体,用于记录任务的状态;执行倒计时(定时);倒计时重新设定值(可以理解为优先级,重载计数器越小优先权越高);任务函数,这是一个函数指针,根据任务声明执行不同的任务函数。

  1. static TASK_COMPONENTS Task_Comps[]=
  2. {
  3. //状态  计数  周期  函数
  4.         
  5. {0, 300,   300,   LED0_Blink},      /* task 1 Period: 300ms */
  6. {0, 600,   600,   LED1_Blink},      /* task 1 Period: 600ms */
  7. {0, 900,   900,   LED2_Blink},      /* task 1 Period: 600ms */  
  8. {0, 10,    10,    KEY_Task},      /* task 1 Period: 600ms */         
  9. };
复制代码
定义任务数组,可以根据需要添加。这里注册了4个任务。

二、轮询调度
  1. void Task_Marks_Handler_Callback(void)
  2. {
  3.     u8 i;
  4.     for(i=0; i<Tasks_Max; i++)
  5.     {
  6.         if(Task_Comps[i].TIMCount)      /* If the time is not 0 */
  7.         {
  8.             Task_Comps[i].TIMCount--;   /* Time counter decrement */
  9.             if(Task_Comps[i].TIMCount == 0) /* If time arrives */
  10.             {
  11.                 /*Resume the timer value and try again */
  12.                 Task_Comps[i].TIMCount = Task_Comps[i].TRITime;  
  13.                 Task_Comps[i].Run = 1;      /* The task can be run */
  14.             }
  15.         }
  16.     }
  17. }
复制代码
此函数会在定时器中断回调中调用,依次减少每个任务的定时倒计时,当倒计时到0时,设置任务run状态为1。

三、调度执行函数
此函数需要在main函数while(1)中调用,当任何一个任务处于run==1状态,执行任务中的注册函数
  1. void Task_Pro_Handler_Callback(void)
  2. {
  3.     u8 i;
  4.     for(i=0; i<Tasks_Max; i++)
  5.     {
  6.         if(Task_Comps[i].Run) /* If task can be run */
  7.         {
  8.             Task_Comps[i].Run = 0;      /* Flag clear 0 */
  9.             Task_Comps[i].TaskHook();   /* Run task */
  10.         }
  11.     }
  12. }
复制代码


四、main

  1. void main(void)
  2. {
  3.         Sys_init();                                                                                //系统初始化
  4.         usb_init();                                     //USB CDC 接口配置
  5.     IE2 |= 0x80;                                    //使能USB中断
  6.         Timer0_Init();                                                                        //定时器初始化
  7.     EA = 1;                                                                                        //IE |= 0X80;
  8.         
  9.         P40 = 0;
  10.         
  11.         while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置
  12.         
  13.         while(1)
  14.         {
  15.                
  16.         if (bUsbOutReady)                                                        //如果接收到了数据
  17.         {
  18.             //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
  19.                         
  20.             usb_OUT_done();                                                        //
  21.         }
  22.                 Task_Pro_Handler_Callback();                                //执行功能函数
  23.         }
  24. }
  25. void Timer0_Isr(void) interrupt 1                //1MS执行一次
  26. {
  27.         Task_Marks_Handler_Callback();                                        //系统计时
  28. }
复制代码


至此,以后增加功能就是增加一个TASK。




回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-2 21:37:26 | 显示全部楼层
《第九集 数码管》学习

因为手头没有595驱动的数码管,用595驱动的8x8点阵屏代替
课后练习完成如下,不知如何@冲哥

tutieshi_480x853_24s.gif


一、倒计时任务
  1. void wash_Task(void)
  2. {
  3.         if(washing_machine_status==2)
  4.         {
  5.                 washing_machine_times--;                //倒计时显示
  6.                 if(washing_machine_times==0) washing_machine_status=0;
  7.         }
  8. }
复制代码
二、显示任务

  1. void matrix_Task(void)
  2. {
  3.         //show_matrix8x8(&zimo[zimo_no*8]);
  4.         
  5.         
  6.         clear_matrix8x8();
  7.         if(washing_machine_status==0){
  8.                 set_matrix8x8(0,1);                //设置显示1,清洗模式
  9.         }else if(washing_machine_status==1){
  10.                 if(washing_machine_mode!=0) set_matrix8x8(4,washing_machine_mode);        //设置显示模式选择
  11.         }else if(washing_machine_status==2){
  12.                 set_matrix8x8(4,washing_machine_times%10);                //设置显示时间倒计时
  13.                 set_matrix8x8(0,washing_machine_times/10);
  14.         }
  15.         show_matrix8x8(matrix1);                //显示
  16. }
复制代码


三、key任务

  1. void KEY_Task(void)
  2. {
  3.         
  4.         if(KEY1==0)
  5.                 {
  6.                         delay_ms(10);
  7.                         if(KEY1==0){
  8.                                 LED2=~LED2;
  9.                                 washing_machine_status=0;
  10.                         }
  11.                         while(KEY1==0);
  12.                 }
  13.                
  14.                 if(KEY2==0)
  15.                 {
  16.                         delay_ms(10);
  17.                         if(KEY2==0){
  18.                                 LED3=~LED3;
  19.                                 washing_machine_status=1;                //mode选择
  20.                                 washing_machine_mode++;
  21.                                 if(washing_machine_mode>5)washing_machine_mode=1;
  22.                         }
  23.                         while(KEY2==0);
  24.                 }
  25.                
  26.                 if(KEY3==0)
  27.                 {
  28.                         delay_ms(10);
  29.                         if(KEY3==0){
  30.                                 LED4=~LED4;
  31.                                 washing_machine_times=washing_machine_times_by_mode[washing_machine_mode-1];
  32.                                 washing_machine_status=2;                //确认开始清洗
  33.                         }
  34.                         while(KEY3==0);
  35.                 }
  36.         
  37. }
复制代码

四、任务注册

  1. static TASK_COMPONENTS Task_Comps[]=
  2. {
  3. //状态  计数  周期  函数
  4. {0, 500,   500, wash_Task},
  5. {0, 10,    10,    KEY_Task},      /* task 1 Period: 600ms */
  6. {0, 200,   200,    matrix_Task},         
  7. };
复制代码

文字很少,相信代码自己会说话

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-3 09:45:08 | 显示全部楼层
《第十集 虚拟键盘LED和数码管》学习



虚拟键盘LED和数码管是Aiapp-ISP提供的功能,可以通过如下菜单打开。
10.png


工具很贴心提供了帮助信息
11.png


一、实验一:通过串口工具发送数码管协议指令显示数字
1、注册一个CDC_Task任务用于处理串口收发
  1. static TASK_COMPONENTS Task_Comps[]=
  2. {
  3. //状态  计数  周期  函数
  4. {0, 200,   200, CDC_Task},
  5. {0, 500,   500, wash_Task},
  6. {0, 10,    10,    KEY_Task},      /* task 1 Period: 600ms */
  7. {0, 200,   200,    matrix_Task},         
  8. };
复制代码
2、CDC_Task任务处理函数
  1. void CDC_Task(void)
  2. {
  3.         if (bUsbOutReady)                                                        //如果接收到了数据
  4.   {
  5.       USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
  6.             usb_OUT_done();                                                        //
  7.   }
  8. }
复制代码
将USB配置为CDC虚拟串口,串口在收到数据后,原路返回。

3、按照数码管串口数据协议发送数据

功能1:
在数码管上显示字符串
命令格式:
37H 53H 45H 47H 53H 00H 00H 00H s1 s2 s3 s4 ...
命令说明:

第1~4字节:
命令头
第5字节:
功能选择(53H显示字符串)
第6~8字节:
保留
第9~n字节:
字符的ASCII码,字符串必须以‘\0’结尾
示例:
发送37H 53H 45H 47H 53H 00H 00H 00H 31H 32H 2EH 33H 00H

在数码管上会显示“12.3”
库函数声明:
int SEG7_ShowString(const char *fmt, ...);
库函数调用:
SEG7_ShowString("%08lx", 0x1234abcdL);

12.png
1)通过串口工具先MCU发送指令37H 53H 45H 47H 53H 00H 00H 00H 31H 32H 2EH 33H 00H,
2)MCU指令数据后,原路返回
3)窗口工具在收到指令数据后,判定为虚拟数码管驱动指令,解析后在虚拟数码管上显示12.3。

二、实验二:密码锁
13.png
完成如下:




1、任务注册
  1. static TASK_COMPONENTS Task_Comps[]=
  2. {
  3. //状态  计数  周期  函数
  4. {0, 200,   200, CDC_Task},
  5. {0, 500,   500, SEG7_Task},
  6. {0, 10,    10,    KEY_Task},      /* task 1 Period: 600ms */
  7. };
复制代码

2、CDC_Task负责串口接收虚拟键盘指令
  1. void CDC_Task(void)
  2. {
  3.         if (bUsbOutReady)                                                        //如果接收到了数据
  4.   {
  5.       //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
  6.                         UART_NUM = UsbOutBuffer[5];
  7.                         UART_RX_FLAG=1;
  8.             usb_OUT_done();                                                        //
  9.   }
  10. }
复制代码

3、SEG7_Task用于发送SEG显示指令

  1. void SEG7_Task(void)
  2. {
  3.         u8 i;
  4.         if(UART_RX_FLAG==1)
  5.         {
  6.                 UART_RX_FLAG=0;
  7.                 SEG7_DATA[UART_CUR]=UART_NUM;
  8.                 UART_CUR++;
  9.                 if(UART_CUR==8){
  10.                         if(SEG7_DATA[0]==0x31&&SEG7_DATA[1]==0x32&&SEG7_DATA[2]==0x33&&SEG7_DATA[3]==0x34&&
  11.                                  SEG7_DATA[4]==0x35&&SEG7_DATA[5]==0x36&&SEG7_DATA[6]==0x37&&SEG7_DATA[7]==0x38)
  12.                         {
  13.                                 SEG7_status=2;
  14.                         }
  15.                         else{
  16.                                 SEG7_status=0;
  17.                                 for(i=0;i<8;i++)
  18.                                 {
  19.                                         SEG7_DATA[i]=' ';
  20.                                 }
  21.                         }
  22.                         UART_CUR=0;
  23.                 }else
  24.                 {
  25.                         SEG7_status=1;
  26.                 }
  27.                        
  28.                
  29.                 if(SEG7_status==0){                                //状态1:显示“--------”
  30.                         SEG7_show_one_line_char('-');
  31.                 }else if(SEG7_status==1){        //状态2:显示密码
  32.                         SEG7_ShowString("%s",(char*)SEG7_DATA);
  33.                 }else if(SEG7_status==2){        //状态3:密码对了显示OPEN
  34.                         SEG7_show_open();
  35.                 }
  36.         }else if(SEG7_status==0) SEG7_show_one_line_char('-');
  37. }
复制代码

4、显示OPEN
  1. void SEG7_show_open(void)
  2. {
  3.         BYTE cod[8];
  4.         cod[0] = 0x00;
  5.         cod[1] = 0x00;
  6.         cod[2] = 0x00;
  7.         cod[3] = 0x00;
  8.         cod[4] = 0x3F;
  9.         cod[5] = 0x73;
  10.         cod[6] = 0x79;
  11.         cod[7] = 0x37;
  12.         SEG7_ShowCode(cod);
  13. }
复制代码
5、显示一行指定字符
  1. void SEG7_show_one_line_char(u8 c)
  2. {
  3.         u8 i;
  4.         u8 str[8];
  5.         for(i=0;i<8;i++) str[i]=c;
  6.         SEG7_ShowString("%s",str);
  7. }
复制代码


回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:512
  • 最近打卡:2025-05-06 08:04:19

17

主题

65

回帖

1670

积分

金牌会员

积分
1670
发表于 2025-1-3 13:32:51 | 显示全部楼层
《第十一集 矩阵按键》学习





一、注册任务
  1. static TASK_COMPONENTS Task_Comps[]=
  2. {
  3. //状态  计数  周期  函数
  4. {0, 500,   500, wash_Task},
  5. {0, 10,    10,    KEY_Task},      
  6. {0, 10,   10,    KEYBOARD_Task},         /*矩阵键盘*/
  7. {0, 200,   200,    matrix_Task},         
  8. };
复制代码
KEYBOARD_Task是矩阵按键任务


二、矩阵按键处理函数
这部分基本都是抄冲哥的,只是改成了4*4矩阵按键
  1. void KEYBOARD_Task(void)
  2. {
  3. //①第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
  4.         KEY_COL1 = 0;
  5.         KEY_COL2 = 0;
  6.         KEY_COL3 = 0;
  7.         KEY_COL4 = 0;
  8.         KEY_ROW1 = 1;
  9.         KEY_ROW2 = 1;
  10.         KEY_ROW3 = 1;
  11.         KEY_ROW4 = 1;
  12.         
  13.         if(( KEY_ROW1 == 0 ) || ( KEY_ROW2 == 0 )|| ( KEY_ROW3 == 0 )|| ( KEY_ROW4 == 0 ))                //如果行按键有按下
  14.         {
  15.                 if(( KEY_ROW1 ==0 ) && ( KEY_ROW2 ==0 )&& ( KEY_ROW3 ==0 )&& ( KEY_ROW4 ==0 ))        //如果两行都有按键按下,不处理
  16.                 {
  17.                         
  18.                 }
  19.                 else if( KEY_ROW1==0||KEY_ROW2==0||KEY_ROW3 ==0||KEY_ROW4 ==0 )        //如果有按键按下,而且只有一颗
  20.                 {
  21.                         if( KEY_ROW1 ==0 )                                //判断哪一行,输出行开始的序号
  22.                                 key_num = 0;
  23.                         else if( KEY_ROW2 ==0 )
  24.                                 key_num = 4;
  25.                         else if( KEY_ROW3 ==0 )
  26.                                 key_num = 8;
  27.                         else if( KEY_ROW4 ==0 )
  28.                                 key_num = 12;
  29.                                 
  30.                         //②第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
  31.                         KEY_COL1 = 1;
  32.                         KEY_COL2 = 1;
  33.                         KEY_COL3 = 1;
  34.                         KEY_COL4 = 1;
  35.                         KEY_ROW1 = 0;
  36.                         KEY_ROW2 = 0;
  37.                         KEY_ROW3 = 0;
  38.                         KEY_ROW4 = 0;
  39.                         
  40.                         if( KEY_COL1 ==0 )                                //判断哪一列,叠加按键的序号
  41.                         {
  42. //                                key_num = key_num ;
  43.                         }
  44.                         else if( KEY_COL2 ==0 )
  45.                         {
  46.                                 key_num = key_num + 1;
  47.                         }
  48.                         else if( KEY_COL3 ==0 )
  49.                         {
  50.                                 key_num = key_num + 2;
  51.                         }
  52.                         else if( KEY_COL4 ==0 )
  53.                         {
  54.                                 key_num = key_num + 3;
  55.                         }
  56.                         
  57.         
  58.                 }
  59.                 KEY_COL1 = 0;
  60.                 KEY_COL2 = 0;
  61.                 KEY_COL3 = 0;
  62.                 KEY_COL4 = 0;
  63.                 KEY_ROW1 = 1;
  64.                 KEY_ROW2 = 1;               
  65.                 KEY_ROW3 = 1;
  66.                 KEY_ROW4 = 1;
  67.         }
  68.         else
  69.         {
  70.                 key_num = 0xff;
  71.         }
  72.         
  73.         //③第三步:行列组合一下就可以判断出是哪个按键按下了。
  74. }
复制代码
三、在点阵屏上显示

  1. void matrix_Task(void)
  2. {
  3.     static u8 num=0;
  4.         
  5.     if(key_num!=0xff) num=key_num;
  6.         
  7.     clear_matrix8x8();
  8.         
  9.     if(num!=0xff){
  10.         set_matrix8x8(4,num%10);
  11.         set_matrix8x8(0,num/10);
  12.     }
  13.     show_matrix8x8(matrix1);                //显示
  14. }
复制代码


下面是595的驱动控制:
  1. void show_matrix8x8(u8 *pBuff)
  2. {
  3.     unsigned char k;
  4.     unsigned int m,n;
  5.        for(m=0;m<32;m++)    //为移动预留
  6.        {
  7.            
  8.                for(k=0;k<8;k++)   //行扫描
  9.                {
  10.                                                                  for(n=0;n<32;n++)//控制显示速度,防止闪烁
  11.                                                                 {
  12.                    HC595(~pos[k],0);
  13.                    HC595(pBuff[k],0);
  14.                    //delay_us(10);
  15.                    LED_RCK_CLR;
  16.                    LED_RCK_SET;    //并行输出
  17.                                                                          delay_us(5);
  18.                 }
  19.                                                         
  20.             }
  21.        }
  22. }
复制代码

  1. void HC595(u8 c,u8 PN)
  2. {
  3.     u8 i;
  4.     for(i=0;i<8;i++){
  5.         if(PN==0){                       //正常显示
  6.             if((c>>i) & 0x01) LED_SER_SET;
  7.             else LED_SER_CLR;
  8.         }
  9.         else{                                //反显
  10.             if((c>>i) & 0x01) LED_SER_CLR;
  11.             else LED_SER_SET;
  12.         }
  13.         LED_SRCK_CLR;
  14.         LED_SRCK_SET;     // 上升沿进行一次数据移入
  15.     }
  16. }
复制代码

  1. void set_matrix8x8(u8 startPos,u8 num)
  2. {
  3.         u8 i;
  4.         for(i=0;i<3;i++)
  5.         {
  6.                 matrix1[i+startPos]=NUM[i+num*3];
  7.         }
  8. }
  9. void clear_matrix8x8()
  10. {
  11.         u8 i;
  12.         for(i=0;i<8;i++)
  13.         {
  14.                 matrix1[i]=0x00;
  15.         }
  16. }
复制代码


全貌
2.jpg

回复 支持 反对

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-5-6 16:25 , Processed in 0.659901 second(s), 107 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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