8051U深度入门到32位51大型实战教学的学习过程
第一集,序言。简要的介绍和8051单片机相对以往的产品的强大功能提升演示功能
TFT屏幕显示
手写识别
数字录放音
WS2812点阵时钟
声音频谱仪
屏幕动画播放
<p>第二集 工具箱硬件及工具介绍</p>
<p>PCB板正面硬件功能分区</p>
<p><img src="data/attachment/forum/202412/30/170350quxufsdciyihutsl.png" alt="image.png" title="image.png" /></p>
<p>PCB板背面芯片功能</p>
<p><img src="data/attachment/forum/202412/30/172341gbpnhtpzfzghlvpl.png" alt="image.png" title="image.png" /></p>
<p>单片机编程软件安装</p>
<p><img src="data/attachment/forum/202412/30/172439jdueooo46ocy88nt.png" alt="image.png" title="image.png" /></p>
<p>到这个网址下载C251程序 https://www.keil.com/download/product/ 点击这个按钮,然后进入另一个页面输入一些信息进行免费注册</p>
<p><img src="data/attachment/forum/202412/30/173539ofq0qg9e0f9p8uu7.png" alt="image.png" title="image.png" /></p>
<p>注册完成后另外一个页面,点击这个下载</p>
<p><img src="data/attachment/forum/202412/30/173441nw1ayyiarzrmlzil.png" alt="image.png" title="image.png" /></p>
<p>下载好的程序</p>
<p><img src="data/attachment/forum/202412/30/173840ciybrj31ujwnjkpn.png" alt="image.png" title="image.png" /></p>
<p>点击运行</p>
<p><img src="data/attachment/forum/202412/30/173916orzli3lerhn6uepc.png" alt="image.png" title="image.png" /></p>
<p>一路NEXT</p>
<p><img src="data/attachment/forum/202412/30/173948p053195c1ytv3399.png" alt="image.png" title="image.png" /></p>
<p>安装完成</p>
<p><img src="data/attachment/forum/202412/30/174110uz6gqifa6lb782f7.png" alt="image.png" title="image.png" /></p>
<p>下载 STC-ISP软件,添加头文件到KEIL中,这样在项目中才能找到STC单片机型号</p>
<p><img src="data/attachment/forum/202412/30/180215e3j05xjh0exgt0ex.png" alt="image.png" title="image.png" /></p>
<p>要选择KEIL安装的目录,ISP会把头文件分别加到各个类型中去(C51,C251是不同的编译环境)</p>
<p><img src="data/attachment/forum/202412/30/180434yr34cpcprprc931w.png" alt="image.png" title="image.png" /></p>
<p>下载KEIL中断拓展插件并安装</p>
<p><img src="data/attachment/forum/202412/30/180740jb0xey1fftthlhex.png" alt="image.png" title="image.png" /></p>
<p>打开跑马灯程序</p>
<p><img src="data/attachment/forum/202412/30/182108f8hizl8mnizfi8bi.png" alt="image.png" title="image.png" /></p>
<p>由于我手上只有擎天柱开发板,板上仅在P2口焊接了LED灯,因此把while()中的程序修改了一下,把P0x修改成P2x。</p>
<pre><code>while(1)
{
P20 = 0; //LED On
delay_ms(250);
P20 = 1; //LED Off
P21 = 0; //LED On
delay_ms(250);
P21 = 1; //LED Off
P22 = 0; //LED On
delay_ms(250);
P22 = 1; //LED Off
P23 = 0; //LED On
delay_ms(250);
P23 = 1; //LED Off
P24 = 0; //LED On
delay_ms(250);
P24 = 1; //LED Off
P25 = 0; //LED On
delay_ms(250);
P25 = 1; //LED Off
P26 = 0; //LED On
delay_ms(250);
P26 = 1; //LED Off
P27 = 0; //LED On
delay_ms(250);
P27 = 1; //LED Off
}
</code></pre>
<p>编译,按住P32键后,按一下POWER键进入HID1 USB-WRITER模式,下载程序,跑马灯成功运行。</p>
<video controls="controls" src="forum.php?mod=attachment&aid=78207"></video>
<p>第三集 点亮第一颗LED</p>
<p>新建一个KEIL项目,选择8051-32bit系列</p>
<p><img src="data/attachment/forum/202412/30/224126nefbdelixxhhojjh.png" alt="image.png" title="image.png" /></p>
<p>新建 main.c 文件,保存</p>
<p><img src="data/attachment/forum/202412/30/224430qpn3hntvsuhbbuhl.png" alt="image.png" title="image.png" /></p>
<p>将main.c文件添加到项目中,打开选项,在Target中设置cpu Mode,Memory Mode, Code Rom Size三项</p>
<p><img src="data/attachment/forum/202412/30/224808xfyuuymt3b3gh5j5.png" alt="image.png" title="image.png" /></p>
<p>在Output中设置 Creat HEX_File上打勾</p>
<p><img src="data/attachment/forum/202412/30/224901bkb91d0e6h3flaae.png" alt="image.png" title="image.png" /></p>
<p>在STC-ISP软件里面将头文件保存在自己的项目的目录中,命名为ai8051u.h</p>
<p><img src="data/attachment/forum/202412/30/225334pq5wjgl8ozo5v55k.png" alt="image.png" title="image.png" /></p>
<p>按教程输入代码,其中将P40改为P20,擎天柱板上只有P2有LED灯。编译下载代码,LED灯点亮。</p>
<p><img src="data/attachment/forum/202412/30/230450n0d0fmm0ifhzgfmu.png" alt="image.png" title="image.png" /></p>
<p>单片机IO口四种模式的设置,是初学者的难点之一。把0和1理解成开和关,把8位开关排成一排就是P0M0或P0M1,再把8位的开关排成上下两排就是设置IO口的模式(每个端口由上下两个开关设置模式,共有4种排列模式,即00,01,10,11,分别对应4种IO口模式)。把这些开关状态写出如0000 0000的形式,再把它们转换成16进制的数0x00赋值给P0M0和P0M1,就配置完一组8位IO口的模式配置了。</p>
<p><img src="data/attachment/forum/202412/30/231515cdxjjx6bl6xh38bw.png" alt="image.png" title="image.png" /></p>
<p>修改P2的值=0,让LED全点亮。</p>
<p>#include "ai8051u.h"//调用头文件</p>
<p>void main(void)//主函数<br />
{<br />
P0M0=0X00;P0M1=0X00;<br />
P2M0=0X00;P2M1=0X00;<br />
P2=0;<br />
while(1)<br />
{<br />
P00=0;<br />
}<br />
}</p>
<p><img src="data/attachment/forum/202412/30/232032rbka55r7k5r5vs2z.png" alt="image.png" title="image.png" /></p>
<p>实际项目中使用ISP软件配置端口更快,直接复制就行。</p>
<p><img src="data/attachment/forum/202412/30/232222mcf7ks5wdzzodcgh.png" alt="image.png" title="image.png" /></p>
<p>第四集,USB不停电下载</p>
<p><img src="data/attachment/forum/202412/30/232523kwwo1yodgwiyjty7.png" alt="image.png" title="image.png" /></p>
<p>首先在下载的USB库文件解压后选择32位-cdc模式-查询方式的库文件和头文件,复制到目标项目文件夹中,与main.c同一级目录。</p>
<p><img src="data/attachment/forum/202412/30/233343twow8accgp5px5kk.png" alt="image.png" title="image.png" /></p>
<p>将两个文件添加到项目中</p>
<p><img src="data/attachment/forum/202412/30/233642a8222pzvlppfgvbb.png" alt="image.png" title="image.png" /></p>
<p>在上节课代码的基础上,写入红框内的代码,增加USB不停电下载功能,当编译时出现较多57错误时,可在设置中的L251 Misc中的 disable Warning Number:框中输入57,将此警告屏蔽掉。</p>
<p><img src="data/attachment/forum/202412/30/235942gzvetonxe5x33xhr.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202412/30/235925romuk0q0lmtzq30z.png" alt="image.png" title="image.png" /></p>
<p>编译完成后,第一次下载代码需要手动按住P32再按一下POWER进入下载模式,下载一次代码。此后就可以使用ISP软件中的下载按键直接下载程序而不用再手动按P32和POWER键了。使用软件使硬件自动复位,大大减小了开发工作时的动手工作量,减小了不断动按键调试程序的体力劳动。</p>
<p><img src="data/attachment/forum/202412/31/000213kaqcinmymnyql8ac.png" alt="image.png" title="image.png" /></p>
<p>不停电下载功能确实很好用。以前我使用ARDUINO编程ESP8266或ESP32的时候要么是开发板要加上硬件下载电路(CH304+三极管)要么就要用CH340+手动按住GPIO0按钮再下载。不过这是有代价的,加载了这个USB库后编译的HEX文件体积加大到了21K多,而不加载这个库时的同样功能的HEX文件只有1K大小。所以也只能是8051U32K64这种大存储空间的单片机才能用这个库。</p>
<p><img src="data/attachment/forum/202412/31/001406onaw2ecq33dag7at.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202412/31/001327nkl3lwcc2lfhlhca.png" alt="image.png" title="image.png" /></p>
<p>第五集 C语言基础本集主要内容</p>
<p><img src="data/attachment/forum/202412/31/001745evllw953mxvmvvpp.png" alt="image.png" title="image.png" /></p>
<p>首先打开在stc32_stc8_usb.h文件中使能 PRINT_USB这个语句功能。</p>
<p><img src="data/attachment/forum/202412/31/002910x0los1zzgzolqqbs.png" alt="image.png" title="image.png" /></p>
<p>在自己写的程序中在 if(bUsbOutReady) 语句中增加一句 printf("STC YYDS\r\n"); 即当串口收到数据时均返回一句话STC YYDS并换行。</p>
<p><img src="data/attachment/forum/202412/31/003007db4y782byfuu7vtn.png" alt="image.png" title="image.png" /></p>
<p>printf函数的使用功能和方法</p>
<p><img src="data/attachment/forum/202412/31/003327eskg5oo66ykztyb6.png" alt="image.png" title="image.png" /></p>
<p>printf函数格式字符和转义字符的含义,要熟记,关于串口发送数据的程序经常要用到。</p>
<p><img src="data/attachment/forum/202412/31/003657nhpu0phz0i40e4aa.png" alt="image.png" title="image.png" /></p>
<p>32位单片机中可以使用64位的变量,这个知识点以前没学过。</p>
<p><img src="data/attachment/forum/202412/31/004108klmilii1dkfq8i3l.png" alt="image.png" title="image.png" /></p>
<p>常用运算符</p>
<p><img src="data/attachment/forum/202412/31/004342ly9zd1cmdf4w045d.png" alt="image.png" title="image.png" /></p>
<p>数值格式定义和设置输出格式</p>
<p><img src="data/attachment/forum/202412/31/005446reeynppze2z4oon0.png" alt="image.png" title="image.png" /></p>
<p>关系运算符</p>
<p><img src="data/attachment/forum/202412/31/005746lrizvtk2vsj6nnf6.png" alt="image.png" title="image.png" /></p>
<p>逻辑运算符和赋值运算符,是 重要的基础知识点</p>
<p><img src="data/attachment/forum/202412/31/005926rpexzx2eytxn0pwz.png" alt="image.png" title="image.png" /></p>
<p>要注意变量的定义类型和长度,否则计算时会溢出错误,初学都常犯这个错。比如两个8位整数相乘,结果仍然是一个8位整数,但这并不是我们想要的结果。</p>
<p><img src="data/attachment/forum/202412/31/011019g1vavipimpugoipv.png" alt="image.png" title="image.png" /></p>
<p>第六集 I/O输入输出</p>
<p>高低电平的概念</p>
<p><img src="data/attachment/forum/202412/31/091851vvcicc6v7zzzo8v1.png" alt="image.png" title="image.png" /></p>
<p>I/O口的四种工作模式选择</p>
<p><img src="data/attachment/forum/202412/31/092030by8g5pl1170yi55d.png" alt="image.png" title="image.png" /></p>
<p>作用在I/O口上高低电平的电压范围是有限制的</p>
<p><img src="data/attachment/forum/202412/31/092228xvdsnwp47piyv2di.png" alt="image.png" title="image.png" /></p>
<p>开发板上的按键电路,这有个知识点是IO口复位后的初始值是1,高电平状态。</p>
<p><img src="data/attachment/forum/202412/31/092536k94yw2w99q9r4o2j.png" alt="image.png" title="image.png" /></p>
<p>在代码中增加一部分实现按下P32时P20灯亮,放开灯灭的效果</p>
<p><img src="data/attachment/forum/202412/31/094202ib3d2vj5mp0p133v.png" alt="image.png" title="image.png" /></p>
<p>按键抖动的消除</p>
<p><img src="data/attachment/forum/202412/31/095244qy8bn1yy6k81155s.png" alt="image.png" title="image.png" /></p>
<p>增加读取P32按键状态,并增加防抖效果的程序,实现按一松开下灯灭,再按一下松开灯亮。红框中为本集新增语句</p>
<p><img src="data/attachment/forum/202412/31/102136yhfivojj9tliprai.png" alt="image.png" title="image.png" /></p>
<p>课后任务1:按一下P32灯亮,再按一下P33灯灭</p>
<p><img src="data/attachment/forum/202412/31/102859cu23sum72wkluupd.png" alt="image.png" title="image.png" /></p>
<video controls="controls" src="forum.php?mod=attachment&aid=78342"></video>
<p>课后任务2:按下亮1个灯,再按下亮2灯,直到全亮</p>
<p><img src="data/attachment/forum/202412/31/104550ujpqtaav6jpavx9j.png" alt="image.png" title="image.png" /></p>
<video controls="controls" src="forum.php?mod=attachment&aid=78345"></video>
<p>第七集 定时器中断</p>
<p><img src="data/attachment/forum/202412/31/104948qm7asgr7gaq7uguz.png" alt="image.png" title="image.png" /></p>
<p>在ISP软件中复制出定时器初始化函数,注意别忘了“使能定时器中断”打勾</p>
<p><img src="data/attachment/forum/202412/31/114144g3wcy3nqd39d33wr.png" alt="image.png" title="image.png" /></p>
<p>粘贴到main()前部,并在main函数中以 Timer0_Init(); 的形式调用,并在中断函数中写入改变标志位</p>
<p>状态和P20灯状态语句</p>
<p><img src="data/attachment/forum/202412/31/114354cf84raln24z58lad.png" alt="image.png" title="image.png" /></p>
<p>主函数中增加一个count变量和修改一下按键计数语句。</p>
<p><img src="data/attachment/forum/202412/31/114640q5lab00xs6kx1blh.png" alt="image.png" title="image.png" /></p>
<p>实现3秒亮灭一次和打印按键次数效果</p>
<video controls="controls" src="forum.php?mod=attachment&aid=78385"></video>
<p><img src="data/attachment/forum/202412/31/115121mooeeo8bolbgokz5.png" alt="image.png" title="image.png" /></p>
<p>定时器原理</p>
<p><img src="data/attachment/forum/202412/31/145409yj14vzkrz1nvkn4n.png" alt="image.png" title="image.png" /></p>
<p>定时时间的计算公式</p>
<p><img src="data/attachment/forum/202412/31/145927sw212jp2hw0fpwqw.png" alt="image.png" title="image.png" /></p>
<p>函数的定义,声明,调用</p>
<p><img src="data/attachment/forum/202412/31/150247sa7zo7myr4aazm42.png" alt="image.png" title="image.png" /></p>
<p>任务2:灯亮三秒后关闭</p>
<p><img src="data/attachment/forum/202412/31/151041dtc3v0ccpo8i2j03.png" alt="image.png" title="image.png" /></p>
<p>任务3:救护车灯控制器</p>
<p><img src="data/attachment/forum/202412/31/152246dh1nh200j5z9hozv.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202412/31/152332haa11u9lahlhhiga.png" alt="image.png" title="image.png" /></p>
<p>课后小练:功德箱程序</p>
<p>#include "ai8051u.h"//调用头文件<br />
#include "stc32_stc8_usb.h"<br />
#include "intrins.h"<br />
#define u8 unsigned char//定义8位无符号整数<br />
#define u16 unsigned int//定义16位无符号整数<br />
char *USER_DEVICEDESC=NULL; //自动下载<br />
char *USER_PRODUCTDESC=NULL;<br />
char *USER_STCISPCMD="@STCISP#";<br />
u8 state; //切换功德<br />
u16 count,gonde1; //计秒<br />
u8 run_state=0; //单双功德<br />
void Delay20ms(void) //@24.000MHz<br />
{<br />
unsigned long edata i;<br />
_nop_();<br />
_nop_();<br />
i = 119998UL;<br />
while (i) i--;<br />
}<br />
void Timer0_Init(void) //1秒@24.000MHz<br />
{<br />
TM0PS = 0x1E; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )<br />
AUXR &= 0x7F; //定时器时钟12T模式<br />
TMOD &= 0xF0; //设置定时器模式<br />
TL0 = 0xFC; //设置定时初始值<br />
TH0 = 0x03; //设置定时初始值<br />
TF0 = 0; //清除TF0标志<br />
TR0 = 1; //定时器0开始计时<br />
ET0=1; //使能定时器0中断<br />
}<br />
void Timer1_Init(void) //2秒@24.000MHz<br />
{<br />
TM0PS = 0x3D; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )<br />
AUXR &= 0x7F; //定时器时钟12T模式<br />
TMOD &= 0xF0; //设置定时器模式<br />
TL0 = 0xFC; //设置定时初始值<br />
TH0 = 0x03; //设置定时初始值<br />
TF0 = 0; //清除TF0标志<br />
TR0 = 1; //定时器0开始计时<br />
ET0 = 1; //使能定时器0中断<br />
}<br />
void Timer0_Isr(void) interrupt 1<br />
{<br />
P20=1;<br />
}<br />
void main(void)//主函数<br />
{<br />
WTST=0; //设置程序指令延时<br />
EAXFR=1; //扩展寄存器XFR访问使能<br />
CKCON=0; //提高访问XRAM速度<br />
P0M0=0X00;P0M1=0X00; //IO口4种模式设置<br />
P2M0=0X00;P2M1=0X00;<br />
P3M0=0X00;P3M1=0X00;<br />
P4M0=0X00;P4M1=0X00;<br />
P5M0=0X00;P5M1=0X00;<br />
usb_init();<br />
IE2 |=0X80; //使能USB中断<br />
Timer0_Init(); //定时器初始化<br />
EA=1; //IE|=0x80;<br />
while(DeviceState!=DEVSTATE_CONFIGURED);//等待USB完成配置<br />
while(1)<br />
{<br />
if(bUsbOutReady)<br />
{<br />
usb_OUT_done();<br />
}<br />
if(P32==0) //判断P32是否按下<br />
{<br />
Delay20ms(); //延时消抖<br />
if(P32==0) //再次检测P32状态,如果仍按下<br />
{<br />
state=!state; //功德状态改变<br />
while(P32==0); //执行到按键松开,才跳出循环<br />
if(state==1)<br />
{<br />
printf("双倍功德时间\r\n"); //打印双倍<br />
}else{<br />
printf("单倍功德时间\r\n"); //打印单倍<br />
}<br />
}<br />
}<br />
if(P33==0) //判断P33是否按下<br />
{<br />
Delay20ms(); //延时消抖<br />
if(P33==0) //再次检测P33状态,如果仍按下<br />
{<br />
while(P33==0); //执行到按键松开,才跳出循环<br />
if(state==1)<br />
{<br />
gonde1=gonde1+2;<br />
Timer1_Init();<br />
P20=0;<br />
}else{<br />
gonde1++;<br />
Timer0_Init();<br />
P20=0;<br />
}<br />
if(state==1)<br />
{<br />
printf("功德+2 当前功德%d\r\n",gonde1); //打印双倍<br />
}else{<br />
printf("功德+1 当前功德%d\r\n",gonde1); //打印单倍<br />
}<br />
}<br />
}<br />
}<br />
}</p>
<p><img src="data/attachment/forum/202412/31/162949igfmf3kffs3ggsry.png" alt="image.png" title="image.png" /></p>
<video controls="controls" src="forum.php?mod=attachment&aid=78495"></video>
<p>第八集 定时器周期性调度任务</p>
<p><img src="data/attachment/forum/202501/01/215223abns5so5i75ocbkj.png" alt="image.png" title="image.png" /></p>
<p>本集的难点在这个结构体数组的周期性任务调度。首先要学习定义结构体的过程,再将这个结构体实例化,其次编制一个任务标记处理回调函数(负责对各项周期性任务进行计时)和一个任务执行处理回调函数(负责执行各周期性任务程序),最后分别将这两个函数放在定时器中断函数中和主循环程序中就可以将各个不同周期的函数组织起来按顺序执行。</p>
<p><img src="data/attachment/forum/202501/01/215354x7ko204flkzh84hz.png" alt="image.png" title="image.png" /></p>
<p>本例中新的知识点较多,特别是最后的结构体程序计数和调用的过程不太容易理解,我也是反复看了这段视频几遍初步理解后,再把其中的代码全部手打一遍,最后逐个将编译出错的语句进行调试后才慢慢理解了程序的编制意图。学会了这个结构体周期任务调度程序才能让单片机发挥出它的最大效能。</p>
<p><img src="data/attachment/forum/202501/01/215948kcoo86183i8g6eqj.png" alt="image.png" title="image.png" /></p>
<p>第九集 数码管 数码管外形</p>
<p><img src="data/attachment/forum/202501/01/220730ekeew9af9ah95wkk.png" alt="image.png" title="image.png" /></p>
<p>数码管类型</p>
<p><img src="data/attachment/forum/202501/01/220937ick252j7y284y557.png" alt="image.png" title="image.png" /></p>
<p>实验箱上的8位数码管是用三根数据线通过两个74HC595移位寄存器芯片控制8位LED数码管的</p>
<p><img src="data/attachment/forum/202501/01/221524enwzlcudhqkww49o.png" alt="image.png" title="image.png" /></p>
<p>移位寄存器传输数据原理</p>
<p><img src="data/attachment/forum/202501/01/222244vvs818g1jaoj2ajl.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202501/01/222832jk1krpie0hl1g0ge.png" alt="image.png" title="image.png" /></p>
<p>添加74HC595传输数据语句,同时修改周期任务调度语句,将其它LED灯控制语句注释掉</p>
<p><img src="data/attachment/forum/202501/01/232638rjfskcs7sfkkkgfs.png" alt="image.png" title="image.png" /></p>
<p>数码管动态显示的原理的要求</p>
<p><img src="data/attachment/forum/202501/01/233813um96841zbwnyschb.png" alt="image.png" title="image.png" /></p>
<p>将程序集中在main.c中</p>
<p>//运行频率24Mhz,使用ai8051u实验箱,通过两个74hc595向8位数码管显示简易时钟<br />
#include "AI8051U.h"<br />
#include "usb.h" //USB调试及复位所需头文件<br />
#include "intrins.h"<br />
#define MAIN_Fosc 24000000L //定义主时钟<br />
#define u8 unsigned char //8位无符号数<br />
#define u16 unsigned int //16位无符号整数<br />
#define Dat P34 //向595传输数据的引脚编号<br />
#define RCK P35 //595锁存数据引脚编号<br />
#define SCK P32 //向595数据移位时钟信号引脚编号<br />
//USB调试及复位所需定义<br />
char *USER_DEVICEDESC = NULL;<br />
char *USER_PRODUCTDESC = NULL;<br />
char *USER_STCISPCMD = "@STCISP#";//设置自动复位到ISP区的用户接口命令<br />
bit state0,state1,state2,state3; //状态0~3<br />
u8 Second,Minute,Hour,Bit_Count=0; //秒,分,时,数码管位码控制<br />
u8 Seg_Table[]= //数码管段码表<br />
{<br />
0x3F, /*'0', 0*/<br />
0x06, /*'1', 1*/<br />
0x5B, /*'2', 2*/<br />
0x4F, /*'3', 3*/<br />
0x66, /*'4', 4*/<br />
0x6D, /*'5', 5*/<br />
0x7D, /*'6', 6*/<br />
0x07, /*'7', 7*/<br />
0x7F, /*'8', 8*/<br />
0x6F, /*'9', 9*/<br />
0x77, /*'A', 10*/<br />
0x7C, /*'B', 11*/<br />
0x39, /*'C', 12*/<br />
0x5E, /*'D', 13*/<br />
0x79, /*'E', 14*/<br />
0x71, /*'F', 15*/<br />
0x40, /*'-', 16*/<br />
0x00, /*' ', 17*/<br />
0x80, /*'.', 18*/<br />
};<br />
u8 Bit_Table[]= //数码管位码表(需取反)<br />
{<br />
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80<br />
};<br />
//-----------数码管显示程序--------------------<br />
void Init_595(void) //595初始化,引脚全0<br />
{<br />
Dat=0;SCK=0;RCK=0;<br />
}<br />
void SendByte(u8 Date) //发送1字节数据到595并移位<br />
{<br />
u8 i;<br />
for(i=0;i<8;i++)<br />
{<br />
Date<<=1; //数值左移1位<br />
Dat=CY; //将左移1位的数据(在CY中)放入595数据引脚<br />
SCK=1; //移位时钟线拉高<br />
SCK=0;<br />
}<br />
}<br />
void Seg(u8 Segmented,u8 Bit_Code)<br />
{<br />
SendByte(Segmented); //发送段码数据<br />
SendByte(Bit_Code); //发送位码数据<br />
RCK=1; //锁存器锁存<br />
RCK=0;<br />
}<br />
void Dis_Seg(void)<br />
{<br />
switch(Bit_Count)<br />
{<br />
case 0:<br />
Seg(Seg_Table,~Bit_Table);break; //输出0位数码管数字<br />
case 1:<br />
Seg(Seg_Table,~Bit_Table);break; //输出1位数码管数字<br />
case 2:<br />
Seg(Seg_Table,~Bit_Table);break;<br />
case 3:<br />
Seg(Seg_Table,~Bit_Table);break;<br />
case 4:<br />
Seg(Seg_Table,~Bit_Table);break;<br />
case 5:<br />
Seg(Seg_Table,~Bit_Table);break;<br />
case 6:<br />
Seg(Seg_Table,~Bit_Table);break;<br />
case 7:<br />
Seg(Seg_Table,~Bit_Table);break;<br />
}<br />
Bit_Count++;Bit_Count%=8; //位码自加1,且小于8<br />
}<br />
//---------各个定时执行程序--------------------<br />
void Led0(void) //4个led闪烁控制<br />
{<br />
state0=!state0;<br />
P00=state0;<br />
}<br />
void Led1(void)<br />
{<br />
state1=!state1;<br />
P01=state1;<br />
}<br />
void Led2(void)<br />
{<br />
state2=!state2;<br />
P02=state2;<br />
}<br />
void Led3(void)<br />
{<br />
state3=!state3;<br />
P03=state3;<br />
}<br />
void Time(void) //计时程序<br />
{<br />
Second++; //秒<br />
if(Second>59)<br />
{<br />
Second=0;Minute++;<br />
}<br />
if(Minute>59) //分<br />
{<br />
Minute=0;Hour++;<br />
}<br />
if(Hour>23) //时<br />
{<br />
Hour=0;<br />
}<br />
}<br />
//-----------------------周期任务调度-------------------------------<br />
typedef struct //定义周期任务控制结构体<br />
{<br />
u8 Run; //运行程序标志<br />
u16 Count; //计数值<br />
u16 Reload; //重载计数值<br />
void(*Task_Fuction)(void); //需要运行的程序<br />
}Task_Control;<br />
Task_Control Task_Com[]= //周期任务结构体实例化<br />
{ //运行标志/计数值/重载值/运行程序名<br />
{0, 1000, 1000,Led0}, //4个Led定时亮灭<br />
{0, 2000, 2000,Led1},<br />
{0, 4000, 4000,Led2},<br />
{0, 8000, 8000,Led3},<br />
{0, 10000,10000,Time}, //时钟计时<br />
{0, 10, 10,Dis_Seg}, //数码管显示<br />
};<br />
u8 Task_Num=sizeof(Task_Com)/sizeof(Task_Com); //计算要循环几次任务</p>
<p>void Task_Mark_Handler_Callback(void) //任务标记处理回调函数(在中断中)<br />
{<br />
u8 i;<br />
for(i=0;i<Task_Num;i++) //循环各个实例任务<br />
{<br />
if(Task_Com.Count) //如计数值非0<br />
{<br />
Task_Com.Count--; //计数值自减1<br />
if(Task_Com.Count==0) //如计数值为0<br />
{<br />
Task_Com.Count=Task_Com.Reload; //将重载值赋值给计数值<br />
Task_Com.Run=1; //运行程序标志置1<br />
}<br />
}<br />
}<br />
}<br />
void Task_Pro_Handler_Callback(void) //任务处理回调函数<br />
{<br />
u8 i;<br />
for(i=0;i<Task_Num;i++) //逐个循环结构体内各个任务<br />
{<br />
if(Task_Com.Run) //如果运行标志为1<br />
{<br />
Task_Com.Run=0; //运行标志置0<br />
Task_Com.Task_Fuction(); //运行相应位置的程序<br />
}<br />
}<br />
}<br />
//--------------------定时器--------------------------------------<br />
void Timer0_Isr(void) interrupt 1 //定时器0中断<br />
{<br />
Task_Mark_Handler_Callback(); //中断计数标记要执行的结构体程序<br />
}</p>
<p>void Timer0_Init(void) //100微秒@24.000MHz<br />
{<br />
AUXR |= 0x80; //定时器时钟1T模式<br />
TMOD &= 0xF0; //设置定时器模式<br />
TL0 = 0xA0; //设置定时初始值<br />
TH0 = 0xF6; //设置定时初始值<br />
TF0 = 0; //清除TF0标志<br />
TR0 = 1; //定时器0开始计时<br />
ET0 = 1; //使能定时器0中断<br />
}</p>
<p>/******************** 主函数 **************************/<br />
void main(void)<br />
{<br />
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快<br />
EAXFR = 1;//扩展寄存器(XFR)访问使能<br />
CKCON = 0;//提高访问XRAM速度</p>
<p>RSTFLAG |= 0x04; //软复位标志写1清零,设置硬件复位后需要检测P3.2的状态选择运行区域,硬件复位后进入系统ISP检测到P3.2为低电平,才会进入USB下载模式</p>
<p>P0M1 = 0x00; P0M0 = 0xff; //设置为推挽输出<br />
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口<br />
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口<br />
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口<br />
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口<br />
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口<br />
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口<br />
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口</p>
<p>usb_init(); //usb初始化<br />
EUSB = 1; //IE2相关的中断位操作使能后,需要重新设置EUSB<br />
Timer0_Init(); //定时器0初始化<br />
Init_595(); //74HC595初始化<br />
EA = 1; //总中断开关打开<br />
P40 = 0; //P0 LED Power On</p>
<p>while(1)<br />
{<br />
Task_Pro_Handler_Callback(); //当有标识时运行相应的程序<br />
}</p>
<p>}</p>
<p>程序运行效果</p>
<video controls="controls" src="forum.php?mod=attachment&aid=79127"></video>
<p>第十集 虚拟键盘LED和数码管</p>
<p>在ISP软件中进行设置调度接口到CDC。</p>
<p><img src="data/attachment/forum/202501/06/180506yl4pwqdhq9andqxq.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202501/06/182151r90o8x9zauz9gomn.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202501/06/225318vtodcjzn6d4t2z2t.png" alt="image.png" title="image.png" /></p>
<p>使用虚拟LED前要更新USB驱动库,更换库文件stc_usb_cdc_32.LIB 2024/11/29的,替换到原来项目内的库文件</p>
<p><img src="data/attachment/forum/202501/06/225509om0ybd0b0mqd0gy7.png" alt="image.png" title="image.png" /></p>
<p>把程序中的stc32_stc8_usb.h更换成AI_usb.h 这样新的库文件中才有虚拟LED的专用函数。</p>
<p><img src="data/attachment/forum/202501/06/225846e26ppun7mxa8xrss.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202501/06/230044uxfdsd8x9d9d9unr.png" alt="image.png" title="image.png" /></p>
<p>程序中要使用printf()函数的话,要把第6行前面的注释//去掉。</p>
<p><img src="data/attachment/forum/202501/06/230159q5mh3lb55bmm9pww.png" alt="image.png" title="image.png" /></p>
<p>在任务中增加上虚拟LED的命令,实现P2流水灯和P10闪烁的效果。</p>
<p><img src="data/attachment/forum/202501/06/230632xryz3zye4eiii0s4.png" alt="image.png" title="image.png" /></p>
<p>虚拟数码管</p>
<p><img src="data/attachment/forum/202501/06/230937mxu33egmgwezuukx.png" alt="image.png" title="image.png" /></p>
<p>新增虚拟数码管显示的代码并加入TASK.C中运行</p>
<p><img src="data/attachment/forum/202501/06/235331gl946hl446lzt44d.png" alt="image.png" title="image.png" /></p>
<p>这里要注意一下,视频教程中KEY_Count=KEY_Count1*10000+KEY_Count2;没有把KEY_Count1和KEY_Count2这两个变量前面加上(u32)强制转换成32位数,这会使得计算后的结果仍然为16位数,在数值超过65536后将产生溢出,因此该语句应改成KEY_Count=(u32)KEY_Count1*10000+(u32)KEY_Count2;</p>
<p><img src="data/attachment/forum/202501/06/235448hdqu9roqq9bol8sd.png" alt="image.png" title="image.png" /></p>
<p>虚拟键盘部分</p>
<p><img src="data/attachment/forum/202501/06/235603ks3q67o36s68l9i9.png" alt="image.png" title="image.png" /></p>
<p>一个新语句,在程序中调用外部程序的变量,使用extern语句声明该变量名称。后面不能跟=号,否则重复赋值</p>
<p><img src="data/attachment/forum/202501/07/001119g0e77v0l7svslklp.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202501/07/001458i40a434vukn73nlc.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202501/07/001558jm11tr2r3tk14r4s.png" alt="image.png" title="image.png" /></p>
<p>课后作业</p>
<p><img src="data/attachment/forum/202501/07/161658m6l2qblzzq87jyl6.png" alt="微信截图_20250107002119.png" title="微信截图_20250107002119.png" /></p>
<p>程序过程中调试了很久,主要的错误原因是自己对于数组的定义,调用,数值范围,数组大小的使用很不熟悉,一直编译出错。字符串处理复制strcpy()和比较命令strcmp()也是从网上找到例程套用的。</p>
<p>main()程序修改的部分</p>
<p><img src="data/attachment/forum/202501/07/162344ss4eb14n7i0zbaa9.png" alt="image.png" title="image.png" /></p>
<p><img src="data/attachment/forum/202501/07/162407hn7444bteeg7b4je.png" alt="image.png" title="image.png" /></p>
<p>task.c修改的部分</p>
<p><img src="data/attachment/forum/202501/07/162450xgzq4qjygqi7yky3.png" alt="image.png" title="image.png" /></p>
<p>io.c修改的部分</p>
<p><img src="data/attachment/forum/202501/07/162609eyinbicib7z7gbbp.png" alt="image.png" title="image.png" /></p>
<p>程序执行效果,这里”open“字符用“-16888-”代替。</p>
<video controls="controls" src="forum.php?mod=attachment&aid=79461"></video>
页:
[1]
2