找回密码
 立即注册
查看: 670|回复: 10

8051U深度入门到32位51大型实战教学的学习过程

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2024-12-30 16:49:15 | 显示全部楼层 |阅读模式
第一集,序言。简要的介绍和8051单片机相对以往的产品的强大功能提升
演示功能

TFT屏幕显示
手写识别
数字录放音
WS2812点阵时钟
声音频谱仪
屏幕动画播放
截图202412301655272672.jpg
截图202412301656037376.jpg
截图202412301656295572.jpg
截图202412301657053849.jpg
截图202412301657238330.jpg
截图202412301658159906.jpg
截图202412301658574185.jpg
截图202412301659192778.jpg
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2024-12-30 17:03:10 | 显示全部楼层

第二集 工具箱硬件及工具介绍

PCB板正面硬件功能分区

image.png

PCB板背面芯片功能

image.png

单片机编程软件安装

image.png

到这个网址下载C251程序 https://www.keil.com/download/product/ 点击这个按钮,然后进入另一个页面输入一些信息进行免费注册

image.png

注册完成后另外一个页面,点击这个下载

image.png

下载好的程序

image.png

点击运行

image.png

一路NEXT

image.png

安装完成

image.png

下载 STC-ISP软件,添加头文件到KEIL中,这样在项目中才能找到STC单片机型号

image.png

要选择KEIL安装的目录,ISP会把头文件分别加到各个类型中去(C51,C251是不同的编译环境)

image.png

下载KEIL中断拓展插件并安装

image.png

打开跑马灯程序

image.png

由于我手上只有擎天柱开发板,板上仅在P2口焊接了LED灯,因此把while()中的程序修改了一下,把P0x修改成P2x。

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
}

编译,按住P32键后,按一下POWER键进入HID1 USB-WRITER模式,下载程序,跑马灯成功运行。

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2024-12-30 23:23:02 | 显示全部楼层

第三集 点亮第一颗LED

新建一个KEIL项目,选择8051-32bit系列

image.png

新建 main.c 文件,保存

image.png

将main.c文件添加到项目中,打开选项,在Target中设置cpu Mode,Memory Mode, Code Rom Size三项

image.png

在Output中设置 Creat HEX_File上打勾

image.png

在STC-ISP软件里面将头文件保存在自己的项目的目录中,命名为ai8051u.h

image.png

按教程输入代码,其中将P40改为P20,擎天柱板上只有P2有LED灯。编译下载代码,LED灯点亮。

image.png

单片机IO口四种模式的设置,是初学者的难点之一。把0和1理解成开和关,把8位开关排成一排就是P0M0或P0M1,再把8位的开关排成上下两排就是设置IO口的模式(每个端口由上下两个开关设置模式,共有4种排列模式,即00,01,10,11,分别对应4种IO口模式)。把这些开关状态写出如0000 0000的形式,再把它们转换成16进制的数0x00赋值给P0M0和P0M1,就配置完一组8位IO口的模式配置了。

image.png

修改P2的值=0,让LED全点亮。

#include "ai8051u.h" //调用头文件

void main(void) //主函数
{
P0M0=0X00;P0M1=0X00;
P2M0=0X00;P2M1=0X00;
P2=0;
while(1)
{
P00=0;
}
}

image.png

实际项目中使用ISP软件配置端口更快,直接复制就行。

image.png

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2024-12-31 00:16:15 | 显示全部楼层

第四集,USB不停电下载

image.png

首先在下载的USB库文件解压后选择32位-cdc模式-查询方式的库文件和头文件,复制到目标项目文件夹中,与main.c同一级目录。

image.png

将两个文件添加到项目中

image.png

在上节课代码的基础上,写入红框内的代码,增加USB不停电下载功能,当编译时出现较多57错误时,可在设置中的L251 Misc中的 disable Warning Number:框中输入57,将此警告屏蔽掉。

image.png

image.png

编译完成后,第一次下载代码需要手动按住P32再按一下POWER进入下载模式,下载一次代码。此后就可以使用ISP软件中的下载按键直接下载程序而不用再手动按P32和POWER键了。使用软件使硬件自动复位,大大减小了开发工作时的动手工作量,减小了不断动按键调试程序的体力劳动。

image.png

不停电下载功能确实很好用。以前我使用ARDUINO编程ESP8266或ESP32的时候要么是开发板要加上硬件下载电路(CH304+三极管)要么就要用CH340+手动按住GPIO0按钮再下载。不过这是有代价的,加载了这个USB库后编译的HEX文件体积加大到了21K多,而不加载这个库时的同样功能的HEX文件只有1K大小。所以也只能是8051U32K64这种大存储空间的单片机才能用这个库。

image.png

image.png

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2024-12-31 01:12:18 | 显示全部楼层

第五集 C语言基础 本集主要内容

image.png

首先打开在stc32_stc8_usb.h文件中使能 PRINT_USB这个语句功能。

image.png

在自己写的程序中在 if(bUsbOutReady) 语句中增加一句 printf("STC YYDS\r\n"); 即当串口收到数据时均返回一句话 STC YYDS 并换行。

image.png

printf函数的使用功能和方法

image.png

printf函数格式字符和转义字符的含义,要熟记,关于串口发送数据的程序经常要用到。

image.png

32位单片机中可以使用64位的变量,这个知识点以前没学过。

image.png

常用运算符

image.png

数值格式定义和设置输出格式

image.png

关系运算符

image.png

逻辑运算符和赋值运算符,是 重要的基础知识点

image.png

要注意变量的定义类型和长度,否则计算时会溢出错误,初学都常犯这个错。比如两个8位整数相乘,结果仍然是一个8位整数,但这并不是我们想要的结果。

image.png

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2024-12-31 10:47:14 | 显示全部楼层

第六集 I/O输入输出

高低电平的概念

image.png

I/O口的四种工作模式选择

image.png

作用在I/O口上高低电平的电压范围是有限制的

image.png

开发板上的按键电路,这有个知识点是IO口复位后的初始值是1,高电平状态。

image.png

在代码中增加一部分实现按下P32时P20灯亮,放开灯灭的效果

image.png

按键抖动的消除

image.png

增加读取P32按键状态,并增加防抖效果的程序,实现按一松开下灯灭,再按一下松开灯亮。红框中为本集新增语句

image.png

课后任务1:按一下P32灯亮,再按一下P33灯灭

image.png

课后任务2:按下亮1个灯,再按下亮2灯,直到全亮

image.png

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2024-12-31 11:51:40 | 显示全部楼层

第七集 定时器中断

image.png

在ISP软件中复制出定时器初始化函数,注意别忘了“使能定时器中断”打勾

image.png

粘贴到main()前部,并在main函数中以 Timer0_Init(); 的形式调用,并在中断函数中写入改变标志位

状态和P20灯状态语句

image.png

主函数中增加一个count变量和修改一下按键计数语句。

image.png

实现3秒亮灭一次和打印按键次数效果

image.png

定时器原理

image.png

定时时间的计算公式

image.png

函数的定义,声明,调用

image.png

任务2:灯亮三秒后关闭

image.png

任务3:救护车灯控制器

image.png

image.png

课后小练:功德箱程序

#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h"
#include "intrins.h"
#define u8 unsigned char //定义8位无符号整数
#define u16 unsigned int //定义16位无符号整数
char *USER_DEVICEDESC=NULL; //自动下载
char *USER_PRODUCTDESC=NULL;
char *USER_STCISPCMD="@STCISP#";
u8 state; //切换功德
u16 count,gonde1; //计秒
u8 run_state=0; //单双功德
void Delay20ms(void) //@24.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
}
void Timer0_Init(void) //1秒@24.000MHz
{
TM0PS = 0x1E; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xFC; //设置定时初始值
TH0 = 0x03; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1; //使能定时器0中断
}
void Timer1_Init(void) //2秒@24.000MHz
{
TM0PS = 0x3D; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xFC; //设置定时初始值
TH0 = 0x03; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void Timer0_Isr(void) interrupt 1
{
P20=1;
}
void main(void) //主函数
{
WTST=0; //设置程序指令延时
EAXFR=1; //扩展寄存器XFR访问使能
CKCON=0; //提高访问XRAM速度
P0M0=0X00;P0M1=0X00; //IO口4种模式设置
P2M0=0X00;P2M1=0X00;
P3M0=0X00;P3M1=0X00;
P4M0=0X00;P4M1=0X00;
P5M0=0X00;P5M1=0X00;
usb_init();
IE2 |=0X80; //使能USB中断
Timer0_Init(); //定时器初始化
EA=1; //IE|=0x80;
while(DeviceState!=DEVSTATE_CONFIGURED); //等待USB完成配置
while(1)
{
if(bUsbOutReady)
{
usb_OUT_done();
}
if(P32==0) //判断P32是否按下
{
Delay20ms(); //延时消抖
if(P32==0) //再次检测P32状态,如果仍按下
{
state=!state; //功德状态改变
while(P32==0); //执行到按键松开,才跳出循环
if(state==1)
{
printf("双倍功德时间\r\n"); //打印双倍
}else{
printf("单倍功德时间\r\n"); //打印单倍
}
}
}
if(P33==0) //判断P33是否按下
{
Delay20ms(); //延时消抖
if(P33==0) //再次检测P33状态,如果仍按下
{
while(P33==0); //执行到按键松开,才跳出循环
if(state==1)
{
gonde1=gonde1+2;
Timer1_Init();
P20=0;
}else{
gonde1++;
Timer0_Init();
P20=0;
}
if(state==1)
{
printf("功德+2 当前功德%d\r\n",gonde1); //打印双倍
}else{
printf("功德+1 当前功德%d\r\n",gonde1); //打印单倍
}
}
}
}
}

image.png

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2025-1-1 22:03:20 | 显示全部楼层

第八集 定时器周期性调度任务

image.png

本集的难点在这个结构体数组的周期性任务调度。首先要学习定义结构体的过程,再将这个结构体实例化,其次编制一个任务标记处理回调函数(负责对各项周期性任务进行计时)和一个任务执行处理回调函数(负责执行各周期性任务程序),最后分别将这两个函数放在定时器中断函数中和主循环程序中就可以将各个不同周期的函数组织起来按顺序执行。

image.png

本例中新的知识点较多,特别是最后的结构体程序计数和调用的过程不太容易理解,我也是反复看了这段视频几遍初步理解后,再把其中的代码全部手打一遍,最后逐个将编译出错的语句进行调试后才慢慢理解了程序的编制意图。学会了这个结构体周期任务调度程序才能让单片机发挥出它的最大效能。

image.png

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2025-1-1 23:26:41 | 显示全部楼层

第九集 数码管 数码管外形

image.png

数码管类型

image.png

实验箱上的8位数码管是用三根数据线通过两个74HC595移位寄存器芯片控制8位LED数码管的

image.png

移位寄存器传输数据原理

image.png

image.png

添加74HC595传输数据语句,同时修改周期任务调度语句,将其它LED灯控制语句注释掉

image.png

数码管动态显示的原理的要求

image.png

将程序集中在main.c中

//运行频率24Mhz,使用ai8051u实验箱,通过两个74hc595向8位数码管显示简易时钟
#include "AI8051U.h"
#include "usb.h" //USB调试及复位所需头文件
#include "intrins.h"
#define MAIN_Fosc 24000000L //定义主时钟
#define u8 unsigned char //8位无符号数
#define u16 unsigned int //16位无符号整数
#define Dat P34 //向595传输数据的引脚编号
#define RCK P35 //595锁存数据引脚编号
#define SCK P32 //向595数据移位时钟信号引脚编号
//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令
bit state0,state1,state2,state3; //状态0~3
u8 Second,Minute,Hour,Bit_Count=0; //秒,分,时,数码管位码控制
u8 Seg_Table[]= //数码管段码表
{
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*/
};
u8 Bit_Table[]= //数码管位码表(需取反)
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
//-----------数码管显示程序--------------------
void Init_595(void) //595初始化,引脚全0
{
Dat=0;SCK=0;RCK=0;
}
void SendByte(u8 Date) //发送1字节数据到595并移位
{
u8 i;
for(i=0;i<8;i++)
{
Date<<=1; //数值左移1位
Dat=CY; //将左移1位的数据(在CY中)放入595数据引脚
SCK=1; //移位时钟线拉高
SCK=0;
}
}
void Seg(u8 Segmented,u8 Bit_Code)
{
SendByte(Segmented); //发送段码数据
SendByte(Bit_Code); //发送位码数据
RCK=1; //锁存器锁存
RCK=0;
}
void Dis_Seg(void)
{
switch(Bit_Count)
{
case 0:
Seg(Seg_Table[Hour/10],~Bit_Table[0]);break; //输出0位数码管数字
case 1:
Seg(Seg_Table[Hour%10],~Bit_Table[1]);break; //输出1位数码管数字
case 2:
Seg(Seg_Table[16],~Bit_Table[2]);break;
case 3:
Seg(Seg_Table[Minute/10],~Bit_Table[3]);break;
case 4:
Seg(Seg_Table[Minute%10],~Bit_Table[4]);break;
case 5:
Seg(Seg_Table[16],~Bit_Table[5]);break;
case 6:
Seg(Seg_Table[Second/10],~Bit_Table[6]);break;
case 7:
Seg(Seg_Table[Second%10],~Bit_Table[7]);break;
}
Bit_Count++;Bit_Count%=8; //位码自加1,且小于8
}
//---------各个定时执行程序--------------------
void Led0(void) //4个led闪烁控制
{
state0=!state0;
P00=state0;
}
void Led1(void)
{
state1=!state1;
P01=state1;
}
void Led2(void)
{
state2=!state2;
P02=state2;
}
void Led3(void)
{
state3=!state3;
P03=state3;
}
void Time(void) //计时程序
{
Second++; //秒
if(Second>59)
{
Second=0;Minute++;
}
if(Minute>59) //分
{
Minute=0;Hour++;
}
if(Hour>23) //时
{
Hour=0;
}
}
//-----------------------周期任务调度-------------------------------
typedef struct //定义周期任务控制结构体
{
u8 Run; //运行程序标志
u16 Count; //计数值
u16 Reload; //重载计数值
void(*Task_Fuction)(void); //需要运行的程序
}Task_Control;
Task_Control Task_Com[]= //周期任务结构体实例化
{ //运行标志/计数值/重载值/运行程序名
{0, 1000, 1000,Led0}, //4个Led定时亮灭
{0, 2000, 2000,Led1},
{0, 4000, 4000,Led2},
{0, 8000, 8000,Led3},
{0, 10000,10000,Time}, //时钟计时
{0, 10, 10,Dis_Seg}, //数码管显示
};
u8 Task_Num=sizeof(Task_Com)/sizeof(Task_Com[0]); //计算要循环几次任务

void Task_Mark_Handler_Callback(void) //任务标记处理回调函数(在中断中)
{
u8 i;
for(i=0;i<Task_Num;i++) //循环各个实例任务
{
if(Task_Com[i].Count) //如计数值非0
{
Task_Com[i].Count--; //计数值自减1
if(Task_Com[i].Count==0) //如计数值为0
{
Task_Com[i].Count=Task_Com[i].Reload; //将重载值赋值给计数值
Task_Com[i].Run=1; //运行程序标志置1
}
}
}
}
void Task_Pro_Handler_Callback(void) //任务处理回调函数
{
u8 i;
for(i=0;i<Task_Num;i++) //逐个循环结构体内各个任务
{
if(Task_Com[i].Run) //如果运行标志为1
{
Task_Com[i].Run=0; //运行标志置0
Task_Com[i].Task_Fuction(); //运行相应位置的程序
}
}
}
//--------------------定时器--------------------------------------
void Timer0_Isr(void) interrupt 1 //定时器0中断
{
Task_Mark_Handler_Callback(); //中断计数标记要执行的结构体程序
}

void Timer0_Init(void) //100微秒@24.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xA0; //设置定时初始值
TH0 = 0xF6; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}

/******************** 主函数 **************************/
void main(void)
{
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度

RSTFLAG |= 0x04; //软复位标志写1清零,设置硬件复位后需要检测P3.2的状态选择运行区域,硬件复位后进入系统ISP检测到P3.2为低电平,才会进入USB下载模式

P0M1 = 0x00; P0M0 = 0xff; //设置为推挽输出
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口

usb_init(); //usb初始化
EUSB = 1; //IE2相关的中断位操作使能后,需要重新设置EUSB
Timer0_Init(); //定时器0初始化
Init_595(); //74HC595初始化
EA = 1; //总中断开关打开
P40 = 0; //P0 LED Power On

while(1)
{
Task_Pro_Handler_Callback(); //当有标识时运行相应的程序
}

}

程序运行效果

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:7
  • 最近打卡:2025-01-17 23:13:44
已绑定手机

1

主题

10

回帖

71

积分

注册会员

积分
71
发表于 2025-1-6 23:07:47 | 显示全部楼层

第十集 虚拟键盘LED和数码管

在ISP软件中进行设置调度接口到CDC。

image.png

image.png

image.png

使用虚拟LED前要更新USB驱动库,更换库文件stc_usb_cdc_32.LIB 2024/11/29的,替换到原来项目内的库文件

image.png

把程序中的stc32_stc8_usb.h更换成AI_usb.h 这样新的库文件中才有虚拟LED的专用函数。

image.png

image.png

程序中要使用printf()函数的话,要把第6行前面的注释//去掉。

image.png

在任务中增加上虚拟LED的命令,实现P2流水灯和P10闪烁的效果。

image.png

虚拟数码管

image.png

新增虚拟数码管显示的代码并加入TASK.C中运行

image.png

这里要注意一下,视频教程中KEY_Count=KEY_Count1*10000+KEY_Count2;没有把KEY_Count1和KEY_Count2这两个变量前面加上(u32)强制转换成32位数,这会使得计算后的结果仍然为16位数,在数值超过65536后将产生溢出,因此该语句应改成KEY_Count=(u32)KEY_Count1*10000+(u32)KEY_Count2;

image.png

虚拟键盘部分

image.png

一个新语句,在程序中调用外部程序的变量,使用extern语句声明该变量名称。后面不能跟=号,否则重复赋值

image.png

image.png

image.png

课后作业

微信截图_20250107002119.png

程序过程中调试了很久,主要的错误原因是自己对于数组的定义,调用,数值范围,数组大小的使用很不熟悉,一直编译出错。字符串处理复制strcpy()和比较命令strcmp()也是从网上找到例程套用的。

main()程序修改的部分

image.png

image.png

task.c修改的部分

image.png

io.c修改的部分

image.png

程序执行效果,这里”open“字符用“-16888-”代替。

回复 支持 反对

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-5-6 21:53 , Processed in 0.131160 second(s), 110 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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