找回密码
 立即注册
查看: 679|回复: 19

Jellyfish从89C51到AI8051学习贴

[复制链接]
  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-19 22:56:25 | 显示全部楼层 |阅读模式

Jellyfish从89C51到AI8051学习贴第一天!

今天是正式开始STC32G系列单片机学习的第一天!虽然目前手头只有老旧的89C51开发板,但通过冲哥的视频教程,我已经对STC32G系列有了初步认识。这是一次从传统51架构到现代增强型51内核的重要跨越。

0x01 学习重点

  1. STC32G系列概述:
    • 基于1T 8051内核,比传统89C51快8-12倍
    • 丰富的外设资源:多达64KB Flash、8KB SRAM、2KB EEPROM
    • 高速ADC、多路PWM、丰富的通信接口(UART、SPI、I2C、USB)
    • 低功耗设计,多种工作模式
  2. 与89C51的主要区别
    • 时钟频率大幅提升(最高可达35MHz)
    • 寄存器配置更加复杂但也更灵活
    • 新增了大量功能外设
    • 开发环境和工具链完全不同
  3. 开发环境搭建
    • 安装STC-ISP烧录软件
    • 配置KEIL C51开发环境
    • 了解STC32G系列的特殊库函数

0x02 部分思考

个人感悟:

作为一名从89C51起步的单片机爱好者,看到STC32G的强大功能真是既兴奋又有些忐忑。虽然都是51内核,但功能差异巨大。特别是了解到这系列单片机能够实现USB声卡、示波器、FFT频谱分析仪等高级功能,远超我手中89C51的能力范围。目前最大的困难是手上还没有STC32G开发板,只能通过视频学习理论知识,无法进行实践操作。期待第八天能收到开发板!

0x03 待学习重点:

  1. 89C51的经验有多少可以迁移到STC32G上
  2. 如何将传统51单片机项目迁移到STC32G平台上
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:76
  • 最近打卡:2026-03-31 08:31:51

27

主题

94

回帖

956

积分

版主

积分
956
发表于 2026-1-20 08:35:31 | 显示全部楼层
要 做到 USB不停电下载
要 尝试 AiCube 图形化自动配置生成程序工具
推荐优先看的:  
printf_usb("Hello World !\r\n")
USB不停电下载, 演示视频链接:
https://www.stcaimcu.com/thread-19077-1-1.html

下载 最新的 AiCube-ISP-V6.96O 或以上版本软件 !

深圳国芯人工智能有限公司-工具软件

下载 最新的 USB库函数,永远用最新的 USB库函数 !
深圳国芯人工智能有限公司-库函数
下载 最新的 用户手册 !
下载 最新的 上机实践指导书 !

下载 最新的 Ai8051U 用户手册
https://www.stcaimcu.com/data/download/Datasheet/AI8051U.pdf

下载 最新的 Ai8051U 实验指导书,
AiCube 图形化自动配置生成程序工具使用说明
https://www.stcaimcu.com/data/do ... %AF%BC%E4%B9%A6.pdf

推荐优先看的 printf_usb("Hello World !\r\n")及usb不停电下载, 演示视频链接



回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-21 01:26:44 | 显示全部楼层

Jellyfish从89C51到AI8051学习贴第二天!

根据芯Skye版主老师的指引,今天学习了AiCube-ISP-V6.96O 版图形化自动配置生成程序工具以及不停电下载的教程。

0x01 学习重点

  1. 新版本的ISP软件有AiCube项目创建助手功能,可以直接选择特定的单片机型号建立AiCube项目,然后通过图形化的方式配置IO口、中断等东西初始化类型,选择好之后可以非常方便的一键生成一个带所有依赖头文件、可以零错误零警告编译的初始工程!
  2. AiCube-ISP-V6.96O软件里面带有了很多样例程序,可以根据样例程序中的官方样例进行学习!
  3. AiCube-ISP-V6.96O可以实现不停电直接下载!通过烧录的时候选择收到用户命令后复位到ISP监控程序中的**下次使用HID进行ISP下载,即可实现keil中编译即烧录运行。

0x02 部分思考

个人感悟:变化太大了!

  1. 不管是AiCube-ISP的图形化一键生成初始keil工程,还是USB不停电下载,两者都极大提高了开发效率。以后再也不需要复制工程了!
  2. AI8051自带了硬件 USB 控制器,结束了需要通过usb转串口后才能烧录、与上位机通信的时代。这个很方便开发,而且也多了更多的玩法,很期待后续能用这个硬件USB控制器做出更多有意思的功能。

0x03 待学习重点:

  1. 89C51的经验有多少可以迁移到STC32G上(已有完成)

    目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。

  2. 如何将传统51单片机项目迁移到STC32G平台上(已有完成)

    通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!

  3. 硬件USB控制器可以玩出什么花样?
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-21 12:20:01 | 显示全部楼层
芯S*** 发表于 2026-1-20 08:35
要 做到 USB不停电下载;
要 尝试 AiCube 图形化自动配置生成程序工具;
推荐优先看的:  

感谢版主老师给的指引推荐🥳
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-22 01:18:23 | 显示全部楼层

Jellyfish从89C51到AI8051学习贴第三天!

今天学习了《8051U深度入门到32位51大型实战教学视频》第二集 硬件及工具介绍。

0x01 学习重点

  1. 介绍了Ai8051U-实验箱的外围器件,意外发现AI8051内部集成实时时钟控制电路,可以外接电源实现低功耗RTC,且需要外接32.768k的无源晶振。
  2. 安装keil C251程序:
    • 官网下载安装包,然后正常安装;
    • AiCube-ISP-V6.96O中选择对应的芯片型号,选择添加型号和头文件到keil中;
    • 下载Keil 中断拓展插件将keil的中断号拓展到254。

0x02 学习心得

  • Ai8051U-实验箱很多外围器件,基本覆盖了这颗强劲的AI8051U内含的功能。
  • 发现了AI8H2K12U可以实现USB转双串口,再次感慨硬件USB的强大之处。
  • keil安装MDK和C251和C51共存的安装顺序:C251包(c251v560)-->C51包(C51V961)-->MDK包(MDK535)。

0x03 待学习重点

  1. 89C51的经验有多少可以迁移到STC32G上(已完成)

    目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。

  2. 如何将传统51单片机项目迁移到STC32G平台上(已完成)

    通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!

  3. 硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
    • USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上MAX232打上RJ45就可以用来配置交换机了。
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-23 02:54:57 | 显示全部楼层

Jellyfish从89C51到AI8051学习贴第四天!

今天继续学习《8051U深度入门到32位51大型实战教学视频》第三集 点亮第一颗LED灯

直接上图,已实现STC32G12K128的USB不停电下载+USB库函数printf_usb("Hello World !\r\n")

image.png

吐槽一下某宝卖的核心板居然默认把P3.0和P3.1接到了ch340上,而且还没有给P3.2做按钮,可恶!

手动把两个0Ω电阻换到了usb引脚上,还顺便把排针焊上去了方便短接P3.2.

IMG_20260123_011050.jpg


0x01 学习重点

STC 单片机 I/O 口配置指南
每个 I/O 口均由 PnM0PnM1 两个寄存器成对配置。以 P0.x 为例,由 P0M0[x]P0M1[x] 的位组合决定其工作模式。

1. I/O 模式配置表

PnM1 PnM0 I/O 工作模式 特性说明
0 0 准双向口 传统 8051 模式。弱上拉,灌电流可达 20mA,拉电流约 150~270μA。
0 1 推挽输出 强上拉输出。电流可达 20mA,必须外接限流电阻以保护硬件。
1 0 高阻输入 电流无法流入/流出,常用于 ADC 检测或传感器输入。
1 1 开漏模式 内部上拉断开。需外接上拉电阻才能输出高电平或读取外部状态。

开漏模式(Open-Drain)进阶逻辑:

  • 输出 1 时,等同于 高阻输入
  • 配合 内部/外部上拉电阻 后,功能等同于 准双向口

2. 电流负载与安全规范

  • 单口驱动能力:在准双向、推挽或开漏模式下,单个 I/O 的灌电流(电平流入)和拉电流(推挽模式下输出)能力均可达20mA
  • 硬件保护建议:外部务必接入限流电阻(常见阻值:560Ω、1KΩ 或 4.7KΩ)。
  • 整机功耗限制:芯片整体 VCC 流入或 GND 流出电流建议不超过 90mA。在设计多路 LED 驱动或大功率电路时,应确保所有 I/O 总电流在此安全范围内。

点亮LED程序代码

#include "STC32.h" //头文件

void main() { //主函数
    P0M0 = 0xff;  //M0=1,M1=0推挽输出模式。
    P0M1 = 0x00; //我的外围LED电路是IO口接到LED的阳极,配置成推挽输出提供更大电流。
    //P0M0 = 0x00; P0M1 = 0x00;  //实测准双向模式无法点亮我的LED,估计是串联了PR20 101(100Ω)的排阻。
    while (1) {
        P00 = 0; //配置P0.0口为强下拉模式,!!STC32G12K128 / STC32G12K64:无下拉电阻!!
        P01 = 1; //配置P0.1口为强上拉模式。
        P02 = 0; //配置P0.2口为强下拉模式,!!STC32G12K128 / STC32G12K64:无下拉电阻!!
        P03 = 0; //配置P0.3口为强下拉模式,!!STC32G12K128 / STC32G12K64:无下拉电阻!!
    }
}

0x02 学习心得

成功的通过STC32G12K128芯片实现的LED的点亮

  1. 新芯片的IO口模式通过寄存器进行配置
  2. 通过M0=1,M1=0将IO口配置成推挽输出模式实现更大电流的输出。

通过硬件USB控制器实现了USB不停电下载+USB库函数printf

  1. 一开始觉得实现起来很简单,但是后来点灯的时候发现烧录了别的代码后,USB就识别不出来不能USB下载代码了,需要转到串口进行下载。
  2. 结合分析和查询数据手册,发现是需要在用户代码中嵌入USB-CDC代码后才能实现一直USB下载。

当单片机需要和电脑之间进行数据交换时,首选一般都是串口通讯。STC32G 系列单片机内置 USB 控制器和收发器,当用户代码中嵌入 USB-CDC 代码后,使用 USB 线将单片机与电脑直接相连接,在电 脑端就可识别出【USB-CDC 虚拟串口】,【USB-CDC 虚拟串口】就是【串口】。

0x03 待学习重点

  1. 89C51的经验有多少可以迁移到STC32G上(已完成)

    目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。

  2. 如何将传统51单片机项目迁移到STC32G平台上(已完成)

    通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!

  3. 硬件USB控制器可以玩出什么花样?(持续学习 持续探索)

    • USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上MAX232打上RJ45就可以用来配置交换机了。
  4. 在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直USB不停电下载

回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-25 15:54:58 | 显示全部楼层

Jellyfish从89C51到AI8051学习贴第五天!

今天继续学习《8051U深度入门到32位51大型实战教学视频》第四集 USB不停电下载

0x01 学习重点

  1. 官网下载函数库USB库文件,学会了通过添加代码移植到本地工程。

    #include "stc32_stc8_usb.h" //引用usb程序库
    //根据新版本说明,新USB库函数,用户程序中不需要再对USER_DEVICEDESC,USER_PRODUCTDESC,USER_STCISPCMD进行定义,与冲哥课程存在小差异。
    
    //在主函数中放下如下语句即可实现不停电下载
    //通过控制P_SW2的第八位(即B7)为1使能访问 XFR。
    P_SW2 |= 0x80; 
    //USB CDC 接口配置
    usb_init();
    EA = 1;
    
  2. 配置寄存器,可以通过 |=或等于的方式,如 P_SW2 |= 0x80;实现在不影响其他位将第八位置为1。

  3. 内存模式的区别:

    模式 在 STC32G 里的表现 官方评价
    Tiny / Small 只能用 128 字节 RAM。 不建议使用
    XSmall 变量默认放 4K edata 强烈推荐(速度最快,空间够用)。
    Large 变量默认放 xdata。 不推荐(浪费 CPU 性能,变慢了)。
    Huge 变量可以跨段。 除非有超级大数组

不建议使用“Small”“Tiny”和“XTiny”模式,推荐使用“XSmall”模式,这种模式默认将变量定义在内部 RAM(edata),单时钟存取,访问速度快,且 STC32G12K128 系列芯片有 4K 的 edata 可以使用;使用“Small”模式时,默认将变量定义在内部 RAM(data),data 默认只有 128 字节,当用户对 RAM 需求超过 128字节时,Keil 编译器会报错,data 区数量有限,容易报错,所以不建议使用;不推荐使用“Large”模式,虽然该模式也能正确访问 STC32G 的全部 16M 寻址空间,但“Large”模式默认将变量定义在内部扩展 RAM(xdata)里面,存取需要 2~3 个时钟,访问速度慢。

0x02 学习心得

  1. 通过USB库文件实现了USB下载,彻底摆脱串口的依赖。
  2. 搞清楚了为什么自己一开始写的东西不能在电脑上识别出来USB-CBC。
  3. 学会了 |=或等于的按位或赋值运算符,可以只操作寄存器的某一位。

0x03 待学习重点

  1. 89C51的经验有多少可以迁移到STC32G上(已完成)

    目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。

  2. 如何将传统51单片机项目迁移到STC32G平台上(已完成)

    通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!

  3. 硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
    • USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上MAX232打上RJ45就可以用来配置交换机了。
  4. 在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直USB不停电下载。(已完成)
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-25 16:33:04 | 显示全部楼层

Jellyfish从89C51到AI8051学习贴第六天!

今天继续学习《8051U深度入门到32位51大型实战教学视频》第五集 C语言基础

0x01 学习重点

  1. C语言printf函数实现

    #define printf printf_hid
    //%s占位符,后面可以用字符串填充
    printf("STC深圳国芯人工智能有限公司 %s\n","yyds");
    
    占位符 数据类型 说明
    %d int 有符号十进制整数
    %u unsigned int 无符号十进制整数
    %ld long 长整型(单片机中常用,避免溢出)
    %f float / double 浮点数(默认 6 位小数)
    %c char 单个字符
    %s char * 字符串(以 \0 结尾)
    %p void * 指针地址(十六进制显示)
    %i 有符号整数 %d 类似,但在 scanf 时能自动识别八/十六进制。
    %E 大写科学计数法 输出:1.23E+03(字母 E 大写)。
    %G 大写智能选择 指数部分显示大写 E。
    %#x 自动带 0x 输出 0xaf 而不是 af
  2. 美化输出,可以通过在 % 后面加数字来控制输出宽度和精度:

    %5d:右对齐,宽度为 5(不足补空格)。
    %-5d:左对齐,宽度为 5。
    %05d:最常用,宽度为 5,不足的位补 0(如 00001)。
    %.2f:保留两位小数。
    %#x:自动带上 0x 前缀(输出如 0x1a)。

  3. ascii码表
    ascii-1-1.png
    ascii-2-1.png

  4. 想要使用64位的double类型需要声明 #pragma float64

  5. 复合赋值位运算笔记

运算符 名称 等价表达式 单片机开发中的核心含义 典型实战场景
&= 与等于 A = A & B 按位清零/屏蔽 仅关闭特定的开关,其余保持不变。 IE &= ~0x80;(关闭总中断)
|= 或等于 A = A | B 按位设1 仅开启特定的开关,不影响其他位。 P2M0 |= 0x03;(设置 P2.0/0.1 模式)
^= 异或等于 A = A ^ B 按位取反 让指定位在 0 和 1 之间反复横跳。 P1 ^= 0x01;(翻转 P1.0 引脚电平)
<<= 左移等于 A = A << B 位权提升 将数据整体向左推,通常用于构建协议。 val <<= 4;(将低 4 位数据移到高 4 位)
>>= 右移等于 A = A >> B 位权降低 将数据整体向右推,用于解析协议。 data >>= 8;(丢弃低 8 位,保留高位数据)

0x02 学习心得

  1. 本节课学会了C语言一些常用的运算符。
  2. 与电脑通过printf函数根ISP程序进行通信。

0x03 待学习重点

  1. 89C51的经验有多少可以迁移到STC32G上(已完成)

    目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。

  2. 如何将传统51单片机项目迁移到STC32G平台上(已完成)

    通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!

  3. 硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
    • USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上MAX232打上RJ45就可以用来配置交换机了。
  4. 在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直USB不停电下载。(已完成)
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-26 02:05:07 | 显示全部楼层

Jellyfish从89C51到AI8051学习贴第七天!

今天继续学习《8051U深度入门到32位51大型实战教学视频》第六集 IO输入输出

0x01 学习重点

  1. GPIO口的输入输出电压是有阈值的,一般3.3V工作电压下低是1.07以下,高电平是1.18V以上。
    直流特性(VSS=0V,VDD=3.3V,测试温度=25℃)
    image.png
    直流特性(VSS=0V,VDD=5.0V,测试温度=25℃)
    image.png

  2. 按键会存在抖动,需要做前沿后沿的判断。

  3. 通过ISP软件计算延时程序。

  4. 将WTST赋值为0提高访问速度。

    WTST = 0x00; //设置程序代码等待参数,赋值为 0 可将 CPU 执行程序的速度设置为最快。
    CKCON = 0; //设置访问片内的 xdata 速度,赋值为 0 表示用最快速度访问,不增加额外的等待时间。
    
  5. C语言的变量类型

    类型关键字 占用位 (Bit) 字节 (Byte) 取值范围 常用场景
    bit 1 - 0 或 1 位标志,仅限部分 RAM 区 (C51/C251 特有)
    char 8 1 -128~ 127 存储 ASCII 字符
    unsigned char 8 1 0~ 255 最常用,用于 8 位寄存器、颜色、小计数器
    int 16 2 -32,768~ 32,767 通用整数运算
    unsigned int 16 2 0~ 65,535 16 位定时器初值、传感器 16 位原始数据
    long 32 4 -21亿~ 21亿 时间戳、大数值累加
    unsigned long 32 4 0~ 42亿 频率计算、大范围计数
    float 32 4 小数点后37 个零 传感器数据(如温度 25.5℃)、简单物理计算
  6. 特有类型

    类型 说明 提示
    sbit 定义特殊功能寄存器的某一位 sbit LED = P1^0; (方便直接操作引脚)
    sfr 定义 8 位特殊功能寄存器 sfr P0 = 0x80;
    sfr16 定义 16 位特殊功能寄存器 sfr16 T0 = 0x8A;
    far 24 位寻址指针 STC32G 必用,用于访问 64KB 以外的代码或数据

0x02 学习心得

  1. 学习了解GPIO的电器特性。

  2. 掌握按键的抖动处理。

  3. 课后小作业(按下按键实现开关小灯):

    #include "STC32.h"
    #include "stc32_stc8_usb.h"
    
    void Delay100ms(void)    //@11.0592MHz
    {
        unsigned long edata i;
    
        _nop_();
        _nop_();
        i = 276478UL;
        while (i) i--;
    }
    
    bit LED=0;
    void main()
    {
        WTST = 0;
        CKCON = 0;
        P0M0 = 0xff;
        P0M1 = 0x00;                       //我的外围LED电路是IO口接到LED的阳极,配置成推挽输出提供更大电流。
        //P0M0 = 0x00; P0M1 = 0x00;        //实测准双向模式无法点亮我的LED,估计是串联了PR20 101(100Ω)的排阻。
        P1M0 &= ~0x10; P1M1 &= ~0x10;      //我把按键接到了P1.4
    
        P_SW2 |= 0x80; 
        usb_init();                        //USB CDC 接口配置
        EA = 1;
    
        while (1)
        {
            if (bUsbOutReady)
            {
                USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
                printf("\nSTC深圳国芯人工智能有限公司 %s\n","yyds");
                usb_OUT_done();
            }
    
            if(P14==0){
                Delay100ms();               //消抖
                if(P14==0){
                    LED=!LED;
                    P00=LED;
                    printf("LED状态更换成了%bd\n",LED);
                    while(P14==0){};
                }
    
            }
        }
    }
    

image.png

  1. 课后小作业(按下按键依次增加LED点亮的数量)

    #include "STC32.h"
    #include "stc32_stc8_usb.h"
    
    void Delay100ms(void)    //@11.0592MHz
    {
        unsigned long edata i;
    
        _nop_();
        _nop_();
        i = 276478UL;
        while (i) i--;
    }
    
    unsigned int LED=0;
    void main()
    {
        WTST = 0;
        CKCON = 0;
        P0M0 = 0xff;
        P0M1 = 0x00;                       //我的外围LED电路是IO口接到LED的阳极,配置成推挽输出提供更大电流。
        //P0M0 = 0x00; P0M1 = 0x00;        //实测准双向模式无法点亮我的LED,估计是串联了PR20 101(100Ω)的排阻。
        P1M0 &= ~0x10; P1M1 &= ~0x10;      //我把按键接到了P1.4
    
        P_SW2 |= 0x80; 
        usb_init();                                     //USB CDC 接口配置
        EA = 1;
        P0=0;
        while (1)
        {
            if (bUsbOutReady)
            {
                USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
                printf("\nSTC深圳国芯人工智能有限公司 %s\n","yyds");
                usb_OUT_done();
            }
    
            if(P14==0){
                Delay100ms();             //消抖
                if(P14==0){
                    if(LED==8){
                        LED=0;
                    }
                    LED++;
                    P0=(1<<LED)-1;        //核心代码,通过<<位移实现2的n次方。
                    printf("LED状态更换,现在有%d个LED点亮,P0的值为%d",LED,(1<<LED)-1);
                    while(P14==0){};
                }
    
            }
        }
    }
    

    image.png

0x03 待学习重点

  1. 89C51的经验有多少可以迁移到STC32G上(已完成)

    目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。

  2. 如何将传统51单片机项目迁移到STC32G平台上(已完成)

    通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!

  3. 硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
    • USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上MAX232打上RJ45就可以用来配置交换机了。
  4. 在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直USB不停电下载。(已完成)
  5. 通过定时器实现替代延时函数。
  6. 实现动态延时函数。
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-03-31 00:08:41

2

主题

16

回帖

260

积分

中级会员

积分
260
发表于 2026-1-27 01:24:01 | 显示全部楼层

Jellyfish从89C51到AI8051学习贴第九天!

今天继续学习《8051U深度入门到32位51大型实战教学视频》第八集 定时器周期性任务调度

0x01 学习重点

  1. 如何通过1个定时器进行多个任务序列调度。
  2. C语言数组使用。
  3. C语言for循环的使用。
  4. 新版本的按键抖动判断逻辑。
  5. C语言的.c和.h文件的建立与使用。
  6. C语言的结构体使用。

0x02 学习心得

  1. C 语言 for 循环的使用

    //基础语法
    for (初始化; 条件判断; 更新) {
        // 循环体
    }
    //死循环
    for(;;) { // 等同于 while(1)
        // 运行任务
    }
    //软件阻塞延时(不精确,受时钟频率影响
    void Delay(uint32_t count) {
        for(; count > 0; count--); // 简单的递减延时
    }
    
  2. C 语言数组使用

    //静态初始化
    uint8_t buffer[4] = {0xAA, 0xBB, 0xCC, 0xDD};
    //查表法优化(节省 CPU 计算量):
    //例如控制 LED 的呼吸灯亮度曲线,预先计算好放入数组:
    const uint8_t PWM_Table[] = {0, 10, 50, 150, 255}; // 存放在 Flash 中,节省 RAM
    //数组越界检查:单片机没有操作系统保护内存,数组越界会导致死机或改写其他变量的值。
    for(uint8_t i = 0; i < sizeof(buffer); i++) { // 使用 sizeof 动态计算长度
        Send_UART(buffer[i]);
    }
    
  3. C 语言的结构体(Struct)

    //基本定义与封装:
    //结构体可以将不同类型的数据(如任务开关、周期、函数指针)打包在一起。
    typedef struct {
        uint8_t  ID;        // 任务 ID
        uint16_t Interval;  // 执行间隔
        void (*TaskPtr)(void); // 函数指针,指向具体要执行的代码
    } Task_TypeDef;
    //结构体指针与硬件寄存器映射
    GPIO_A->ODR = 0x01; // GPIO_A 是一个结构体指针,指向特定的内存地址
    //位域(Bit-fields)优化内存(嵌入式黑科技)
    struct Status {
        uint8_t isRunning : 1; // 只占 1 个 bit
        uint8_t errorCode : 7; // 占 7 个 bit
    }; // 整个结构体仅占 1 字节
    
  4. 通过定时器中断函数对数组的多个成员累加,在主函数判断实现多任务序列调度。

    unsigned int led_count[3]={0,0,0};
    int length = sizeof(led_count) / sizeof(led_count[0]);
    unsigned char LED_state[3] = {0,0,0};
    void Timer0_Isr(void) interrupt 1
    {
        //中断函数实现
        int i;
        for (i=0;i<length;i++){
            led_count[i]++;
        }
        if(led_count[0]>=3){
            led_count[0]=0;
            LED_state[0]=!LED_state[0];
            P00=LED_state[0];
            printf("现在LED00的状态变更为:%d\n",(int)LED_state[0]);
        }
        if(led_count[1]>=6){
            led_count[1]=0;
            LED_state[1]=!LED_state[1];
            P01=LED_state[1];
            printf("现在LED01的状态变更为:%d\n",(int)LED_state[1]);
        }
        if(led_count[2]>=9){
            led_count[2]=0;
            LED_state[2]=!LED_state[2];
            P02=LED_state[2];
            printf("现在LED02的状态变更为:%d\n",(int)LED_state[2]);
        }
    
    }
    
    void Timer0_Init(void)		//100毫秒@24.000MHz
    {
    	TM0PS = 0x03;			//设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
    	AUXR &= 0x7F;			//定时器时钟12T模式
    	TMOD &= 0xF0;			//设置定时器模式
    	TL0 = 0xB0;				//设置定时初始值
    	TH0 = 0x3C;				//设置定时初始值
    	TF0 = 0;				//清除TF0标志
    	TR0 = 1;				//定时器0开始计时
    	ET0 = 1;				//使能定时器0中断
    }
    
  5. 通过按键间隔时间判断是否按下,累加判断成功次数,实现按键抖动判断不影响其他任务执行。

    此时LED灯正常闪烁!

    image.png

    #include "STC32.h"
    #include "stc32_stc8_usb.h"
    
    unsigned int time_count[4]={0,0,0,0};
    int length = sizeof(time_count) / sizeof(time_count[0]);
    unsigned char LED_state[3] = {0,0,0};
    unsigned int key_count=0;
    unsigned int key_pressed_flag = 0; // 定义标志位
    
    void Timer0_Isr(void) interrupt 1
    {
        //中断函数实现
        int i;
        for (i=0;i<length;i++){
            time_count[i]++;
        }
        if(time_count[0]>=30){
            time_count[0]=0;
            LED_state[0]=!LED_state[0];
            P00=LED_state[0];
            //printf("现在LED00的状态变更为:%d\n",(int)LED_state[0]);
        }
        if(time_count[1]>=60){
            time_count[1]=0;
            LED_state[1]=!LED_state[1];
            P01=LED_state[1];
            //printf("现在LED01的状态变更为:%d\n",(int)LED_state[1]);
        }
        if(time_count[2]>=90){
            time_count[2]=0;
            LED_state[2]=!LED_state[2];
            P02=LED_state[2];
            //printf("现在LED02的状态变更为:%d\n",(int)LED_state[2]);
        }
        if(time_count[3]>=1){
            time_count[3]=0;
            if(P14==0){
                if(key_pressed_flag == 0) { // 如果之前还没处理过这个按键
                    key_count++;
                    if(key_count>=10){
                        key_pressed_flag = 1; // 只立个 Fla
    
                    }
                }
            }else{
                key_count=0;
                key_pressed_flag = 0; // 允许下一次按下处理
            }
        }
    
    }
    
    void Timer0_Init(void)		//10毫秒@24.000MHz
    {
    	TM0PS = 0x00;			//设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
    	AUXR &= 0x7F;			//定时器时钟12T模式
    	TMOD &= 0xF0;			//设置定时器模式
    	TL0 = 0xE0;				//设置定时初始值
    	TH0 = 0xB1;				//设置定时初始值
    	TF0 = 0;				//清除TF0标志
    	TR0 = 1;				//定时器0开始计时
    	ET0 = 1;				//使能定时器0中断
    }
    
    void main()
    {
        WTST = 0;
        CKCON = 0;
        P0M0 = 0xff;
        P0M1 = 0x00;                        //我的外围LED电路是IO口接到LED的阳极,配置成推挽输出提供更大电流。
        //P0M0 = 0x00; P0M1 = 0x00;         //实测准双向模式无法点亮我的LED,估计是串联了PR20 101(100Ω)的排阻。
        P1M0 &= ~0x30; P1M1 &= ~0x30;       //我把按键接到了P1.4、P1.5分别代表按键1和按键2。
        P2M0 = 0x00; P2M1 = 0x00; 
        P2=0;                               //这个P2的0、1、2口接了个74hc138控制数码管。
        P_SW2 |= 0x80; 
        usb_init();                                     //USB CDC 接口配置
        Timer0_Init();
        EA = 1;
        P0=0;
        while (1)
        {
            if (bUsbOutReady)
            {
                USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
                printf("\nSTC深圳国芯人工智能有限公司 %s\n","yyds");
                usb_OUT_done();
            }
            if(key_pressed_flag==1) {
                printf("按键逻辑触发已经超过%d此判断按键按下",(int)key_count);
                key_pressed_flag = 2;
            }     
        }
    }
    
  6. 通过的结构体使用,高效使用定时器

    typedef struct 
    {
    	u8 Run;               //任务状态:Run/Stop
    	u16 TIMCount;         //定时计数器
    	u16 TRITime;          //重载计数器
    	void (*TaskHook) (void); //任务函数
    } TASK_COMPONENTS;   
    
    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[0]);
    
    //========================================================================
    // 函数: Task_Handler_Callback
    // 描述: 任务标记回调函数.
    // 参数: None.
    // 返回: None.
    // 版本: V1.0, 2012-10-22
    //========================================================================
    //在中断函数中执行这个函数代码,实现数量累加
    void Task_Marks_Handler_Callback(void)
    {
        u8 i;
        for(i=0; i<Tasks_Max; i++)
        {
            if(Task_Comps[i].TIMCount)      /* If the time is not 0 */
            {
                Task_Comps[i].TIMCount--;   /* Time counter decrement */
                if(Task_Comps[i].TIMCount == 0) /* If time arrives */
                {
                    /*Resume the timer value and try again */
                    Task_Comps[i].TIMCount = Task_Comps[i].TRITime;  
                    Task_Comps[i].Run = 1;      /* The task can be run */
                }
            }
        }
    }
    
    //========================================================================
    // 函数: Task_Pro_Handler_Callback
    // 描述: 任务处理回调函数.
    // 参数: None.
    // 返回: None.
    // 版本: V1.0, 2012-10-22
    //========================================================================
    //这个函数在main函数里面持续执行,当判断发现有任务时间到了就开始执行
    void Task_Pro_Handler_Callback(void)
    {
        u8 i;
        for(i=0; i<Tasks_Max; i++)
        {
            if(Task_Comps[i].Run) /* If task can be run */
            {
                Task_Comps[i].Run = 0;      /* Flag clear 0 */
                Task_Comps[i].TaskHook();   /* Run task */
            }
        }
    }
    

0x03 待学习重点

  1. 89C51的经验有多少可以迁移到STC32G上(已完成)

    目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。

  2. 如何将传统51单片机项目迁移到STC32G平台上(已完成)

    通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!

  3. 硬件USB控制器可以玩出什么花样?(持续学习 持续探索)

    • USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上MAX232打上RJ45就可以用来配置交换机了。
  4. 在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直USB不停电下载。(已完成)

  5. 定时器一次只能定时一个此,如果有很多个任务怎么办?(已完成)

    初步猜测可能性:1、在定时器函数内部增加if/else判断某个全局变量的值确定要执行哪一段语句。2、使用多个定时器。

    答案:想的差不多,方法如下

    • 通过数组(包括结构体数组),定时器中断函数对数组的多个成员累加,在主函数判断实现多任务序列调度。
  6. 如果我的函数执行的很慢,定时器等待的时间很短,会不会出现问题?

回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-3-31 20:02 , Processed in 0.123468 second(s), 94 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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