《Ai8051U教学视频》学习心得
<p>《8051U深度入门到32位51大型实战教学视频》<br /><img src="data/attachment/forum/202412/21/144753wrrrmnl0ymvqrm95.png" alt="屏幕截图2024-12-21144743.png" title="屏幕截图 2024-12-21 144743.png" /></p>
第一集 序言
冲哥用Ai8051U实验箱展示了TFT屏幕显示,视频播放,录放音,频谱分析仪及手写计算器等例程,从这几个例程的演示效果可以看出AI8051U其强大的计算能力。AI8051U
1.不仅兼容8位8051指令集还兼容32位8051指令集
2.拥有32K的SRAM和64K的Flash
3.增加了采用独立时钟的MDU32和TFPU硬件单元,对32位数据处理的能力大大的提升
4.拥有专业级的内部复位电路,可以省去外部复位电路
5.拥有QSPI, i8080/M6800-TFT接口,而STM32F103C8T6没有
6.自带硬件USB,可以直接USB连接电脑进行仿真/下载,全球唯一
第二集 硬件及工具介绍
1.硬件介绍主要介绍了Ai8051U实验箱板子上一些元器件的功能及一些接口的用途
2.软件安装
C251、C51、MDK依次安装到同一目录,可解决这3个软件共存问题。
3.下载第一个程序
第三集 点亮第一颗LED
冲哥手把手教学如何新建一个AI8051U的工程模板,还介绍了单片机端口的模式配置(准双向口,推挽输出,高阻输入,开漏模式),并且演示了如何配置端口为准双向口,控制LED亮灭点亮LED
引入头文件->编写主函数->配置GPIO模式(要放在while循环外面)->使能连接LED的引脚
ISP的IO口配置工具选好需要的IO口选择模式直接就能生成配置代码,小CubeMX
冲哥的口头任务点亮全部LED
#include "ai8051u.h" //头文件
void main(void)
{
//GPIO初始化
P2M0 = 0x00; //P2端口配置为准双向口模式
P2M1 = 0x00;
while(1)
{
P2 = 0X00; //P2端口输出0v
}
}
第四集 USB不停电下载
实现AI8051U不断电下载需要导入并移植STC官网的USB库文件,使用Lib文件进行初始化P_SW2 |= 0x80; 设置P_SW2有效访问 EAXFR = 1 表示使能访问XFR
IE2 |= 0X80; 设置中断使能寄存器(IE2)EUSB = 1 表示允许USB中断
实现了AI8051U不断电下载功能
第五集 C语言基础
冲哥介绍了如何通过宏定义打卡USB-CDC串口的打印功能,详细讲解了printf函数的原理和用法。还讲解了变量的定义与运算符的使用,包括关系运算符,逻辑运算符,赋值运算符等。
最后强调了溢出的概念及其在编程中的重要性。
printf函数的实现
数的进制
数据基本类型
C语言常用运算符
第六集 I/O输入输出
什么是GPIO?GPIO:通用输入/输出口,可以输出高低电平或读取引脚状态
GPIO模式:准双向口,推挽输出,高阻输入,开漏模式
高电平:指接近于电源正极电压的电平也叫逻辑“1”
低电平:指接近于电源负极电压的电平也叫逻辑“0”
注:3.3V供电时
输出电压要大于1.18V才能打开施密特触发器,输出高电平
输出电压要小于0.99V才能打开施密特触发器,输出低电平
按键输入检测
任务1:按下P32按钮灯亮,松开P32按钮灯灭
if( P32 == 0 ) //判断P32按钮是否按下
{
P20 = 0;
}
else
{
P20 = 1;
}
任务2:按下P32按钮灯灭,松开P32按钮灯亮
if( P32 == 1 ) //判断P32按钮是否按下
{
P20 = 0;
}
else
{
P20 = 1;
}
任务3:按一下灯亮,按一下灯灭
if( P32 == 0 ) //判断P32按钮是否按下
{
Delay20ms(); //延时20ms消抖
if( P32 == 0 )
{
temp = !temp; //取反
P20 = temp;
}
}
注:机械按键里的金属触片按下松开时会有抖动,不处理的话会导致误判,所以需要消抖。
消抖可以分硬件消抖和软件消抖(一般要求不高都是用软件消抖)
软件消抖:本节使用的延时消抖就是软件消抖不过是阻塞式的,下一节学完之后可以用定时器实现一个非阻塞式延时
硬件消抖:如电容滤波、消抖芯片等。
电容滤波:在按键两端并连一个电容器利用电容的充放电特性来平滑电压的突变,从而实现消抖。
去抖动芯片:使用专门的去抖动芯片,如MAX6816、MAX6817和MAX6818,这些芯片为多输入系统提供了便利,能够有效地去除抖动。
课后练习
任务1:按一下P32灯亮,按一下P33灯灭
if( P32 == 0 ) //判断P32按钮是否按下
{
P20 = 0;
}
else if( P33 = 0 ) //判断P33按钮是否按下
{
P20 = 1;
}
任务2:按一下亮一颗,再按一下亮两颗,直到全亮
if( P32 == 0 ) //判断P32按钮是否按下
{
Delay20ms(); //延时20ms消抖
if( P32 == 0 )
{
i++;
if(i == 9)
{
i = 0;
}
P2 = (0XFF<<i);
while( P32 == 0 );
}
}
第七集 定时器中断
本节冲哥通过儿子给妈妈烧火的故事,引出了定时器的必要性。讲解了定时器的作用,包括计时和替代长时间的延时,以提高运行效率。演示通过ISP软件生成定时器函数,如何在代码中应用定时器中断,解决了主循环中无法响应按键问题的同时,实现了LED三秒闪烁及按键计数的功能。
强调了中断在程序执行中的重要性。还详细讲解了定时器中断的使用,包括判断按键是否按下,处理按键抖动,定时器的配置和使用。
然后,深度解析了TMOPS的计算方法,分析了TMOP的使用,讲解了CT等于0的含义,解释了定时器的时间,展示了定时器的工作流程。
计算公式
TIM0初始化
TM0PS = 0x5B;//设置定时器时钟预分频
AUXR &= 0x7F;//定时器时钟12T模式 AUXR最高位清零.
TMOD &= 0xF0;//低4位清零(设置T0为16位定时器)
TL0 = 0x3F;//设置定时初始值 0x013F=319 初始值65536-319
TH0 = 0x01;//设置定时初始值
TF0 = 0; //清除TF0标志 中断标识位置0
TR0 = 1; //定时器0开始计时,当T0_GATE=0时,TR0=1开启定时器,TR0=0时关闭定时器.
ET0 = 1; //使能定时器0中断
注:其他系列寄存器名称可能有所偏差,具体还是要看官方手册。
函数的定义、声明、调用
函数的定义:包含返回值类型,函数名和入口参数,并定义了函数具体功能。
返回值类型:就是调用完函数后会返回这个类型的参数,可用与赋值或判断
函数名称:描述函数功能,便于阅读理解。
函数入口参数:调用函数时需要传入的参数,比如前面几节课最常用的delay(入口参数)括号内的就是入口参数
声明:返回值类型 函数名(入口参数);在头文件或者被调用之前声明,注意末尾要加分号。
调用:函数名(入口参数);直接使用函数名,加上括号和分号。多个入口参数之间加逗号隔开。
注:函数名称不能与C语言的关键字同名。
课后作业
电子功德箱
#include <AI8051U.H>
#include "stc32_stc8_usb.h"
#include <intrins.h>
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
u8 Count_GongDe = 0; //功德计数
u8 State_GongDe = 0; //功德状态
u8 State_Delay = 1; //延时状态
void Delay20ms(void) //@24.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
}
void Timer1_Isr(void);
void Timer1_Init(void) //1秒@24.000MHz
{
TM1PS = 0x1E; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0xFC; //设置定时初始值
TH1 = 0x03; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}
void main() //按键1为P33按键,按键2为P35按键,在实验箱的右下角两个键。
{
EAXFR = 1; //打开扩展特殊功能寄存器,等同:P_SW2 |= 0x80;
CKCON = 0; //提高访问XRAM速度
WTST = 0; //设置程序指令延时参数
P0M0 = 0x00; P0M1 = 0x00; //初始化各个端口为准双向口模式
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
P6M0 = 0x00; P6M1 = 0x00;
P7M0 = 0x00; P7M1 = 0x00;
usb_init(); //USB口初始化配置
IE2 |= 0x80; //EUSB位置位,打开USB
EA = 1; //总中断置位,打开中断
P40 = 0; //使能LED总开关
while(DeviceState != DEVSTATE_CONFIGURED); //等待USB完成配置
while(1)
{
if(P33 == 0) //按下P33触发程序
{
Delay20ms(); //延时消抖
if(P33 == 0) //确实按下按钮
{
switch(State_GongDe) //判断功德状态 0=没有功德;1=单倍功德;2=双倍功德
{
case 0: //
case 1:
TR1 = 0; //停止定时器1计时
printf("单倍功德时间\r\n");
P00 = 0; //点亮P00
State_GongDe = 1; //功德状态设为1
Timer1_Init(); //开始延时
State_GongDe = 2; //功德状态设为2
break;
case 2:
TR1 = 0; //停止定时器1计时
printf("双倍功德时间\r\n");
P00 = 0; //点亮P00
Timer1_Init(); //开始延时
State_GongDe = 1; //功德状态设为1
break;
}
while(P33 == 0); //等待按键恢复
}
}
if(P35 == 0) //按下P35触发程序
{
Delay20ms(); //延时消抖
if(P35 == 0) //确实按下按钮
{
switch(State_GongDe) //判断功德状态
{
case 0:
printf("没有功德哟! 当前功德:%d\r\n",Count_GongDe);
break;
case 2: //注意这个模式2是指的P33按键进入单倍功德状态
Count_GongDe++; //功德计数加1
printf("功德+1 当前功德:%d\r\n",Count_GongDe);
break;
case 1: //注意这个模式1是指的P33按键进入双倍功德状态
Count_GongDe += 2; //功德计数加2
printf("功德+2 当前功德:%d\r\n",Count_GongDe);
break;
}
while(P35 == 0); //等待按键恢复
}
}
}
}
void Timer1_Isr(void) interrupt 3 //定时器1回调函数,每秒调用一次
{
switch(State_GongDe)
{
case 2: //注意这个模式2是指的P33按键进入单倍功德状态
P00 = 1; //关闭P00
TR1 = 0; //停止定时器1计时
State_GongDe = 0; //功德置0
break;
case 1: //注意这个模式1是指的P33按键进入双倍功德状态
if(State_Delay == 1) //判断是否是第一次循环
{
State_Delay++;
break;
}
else
{
P00 = 1; //关闭P00
TR1 = 0; //停止定时器1计时
State_GongDe = 0; //功德置0
State_Delay = 1; //重新置1
break;
}
}
}
第八集 定时器周期性调度任务
冲哥通过实例,展示了如何使用定时器实现LED的闪烁,具体包括LED0.3秒、0.6秒和0.9秒闪烁一次。通过定义计数变量,在定时器中断函数中不断累加,实现了定时器的周期性任务调度。接着,利用数组实现定时器周期性任务调度,通过定义个包含三个计时变是的数组,简化了代码的复杂性。
然后,介绍了如何通过for循环和使用数组实现固定次数的循环和流水灯的效果。还讲解了如何通过二进制和十六进制的转换,快速定义数组内容。
然后,如何使用结构体数组的周期性任务调度,以及如何创建和调用结构体数组,以便实现多个任务的周期性调度。
重点讲解了定时器在周期性任务调度中的应用,以及如何通过按键实现特定的任务。
注:用ISP软件的定时器计算器生成定时时间的时候,记得把使能定时器中断选项给勾上。
创建.c和.h(管理起来方便)
结构体
定义:它由一组称为成员(或域、元素)的不同数据组成,每个成员可以具有不同的类型。结构体通常用于表示类型不同但又相关的若干数据。
特点:结构体的成员可以包含其他结构体、指针、数组等复杂数据类型。其在内存中的布局是连续的,可以通过指针或数组等方式进行访问和操作。
应用场景:在操作系统内核中,结构体常用于表示进程、线程、任务等系统资源的状态和信息。
task.c
#include "task.h"
#include "io.h"
static TASK_COMPONENTS Task_Comps[]=
{
//状态计数周期函数
{0, 300, 300, LED0_Blink}, /* task 1 Period: 300ms */
{0, 600, 600, LED1_Blink}, /* task 1 Period: 600ms */
{0, 900, 900, LED2_Blink}, /* task 1 Period: 600ms */
{0, 10, 10, KEY_Task}, /* task 1 Period: 600ms */
};
u8 Tasks_Max = sizeof(Task_Comps)/sizeof(Task_Comps);
// 函数: Task_Handler_Callback
// 描述: 任务标记回调函数.
void Task_Marks_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps.TIMCount) /* If the time is not 0 */
{
Task_Comps.TIMCount--; /* Time counter decrement */
if(Task_Comps.TIMCount == 0) /* If time arrives */
{
/*Resume the timer value and try again */
Task_Comps.TIMCount = Task_Comps.TRITime;
Task_Comps.Run = 1; /* The task can be run */
}
}
}
}
// 函数: Task_Pro_Handler_Callback
// 描述: 任务处理回调函数.
void Task_Pro_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps.Run) /* If task can be run */
{
Task_Comps.Run = 0; /* Flag clear 0 */
Task_Comps.TaskHook(); /* Run task */
}
}
}
task.h
#ifndef __TASK_H
#define __TASK_H
#include "config.h" //调用头文件
typedef struct
{
u8 Run; //任务状态:Run/Stop
u16 TIMCount; //定时计数器
u16 TRITime; //重载计数器
void (*TaskHook) (void); //任务函数
} TASK_COMPONENTS;
void Task_Marks_Handler_Callback(void);
void Task_Pro_Handler_Callback(void);
#endif
第九集 数码管
冲哥介绍了数码管的种类和接线原理图,通过595芯片将单片机的三个引脚转化为16个端口输出。还详细讲解了595芯片的工作原理,以及如何通过单片机的三个引脚控制数码管的显示。
展示了如何使用AI8051U单片机进行数码管显示,包括静态和动态显示。
最后,使用Ai8051U单片机实现数码管的时间显示,以及通过虚拟接口进行实验。
数码管显示原理(跟点灯差不多)
数码管静态显示
数码管动态显示
ISP数码管工具
//定义一个数组储存段码
u8 SEG_NUM[]=
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x40, /*'-', 16*/
0x00, /*' ', 17*/
0x80, /*'.', 18*/
};
void PLED_40(void)
{
u8 cod;
cod = 0x0f; //表示开启P0-P3
cod = 0X01; //P0端口
cod = 0X01; //P1
cod = ~T_NUM; //P2
cod = 0X01; //P3
LED40_SendData( cod,5 );
P2 = ~T_NUM;
state_now++;
if( state_now>7 )
state_now = 0;
}
void SEG_PC( void )
{
u8 cod;
cod = SEG_NUM; //小时的十位数的数码管段码
cod = SEG_NUM;
cod =SEG_NUM; //数码管刷段码和位码
cod = SEG_NUM; //分钟
cod = SEG_NUM;
cod =SEG_NUM; //数码管刷段码和位码
cod = SEG_NUM; //分钟
cod = SEG_NUM;
SEG7_ShowCode(cod);
}
虚拟显示
页:
[1]
2