jellyfish 发表于 2026-1-19 22:56:25

Jellyfish从89C51到AI8051学习贴

<h1>Jellyfish从89C51到AI8051学习贴第一天!</h1>
<blockquote>
<p>今天是正式开始STC32G系列单片机学习的第一天!虽然目前手头只有老旧的89C51开发板,但通过冲哥的视频教程,我已经对STC32G系列有了初步认识。这是一次从传统51架构到现代增强型51内核的重要跨越。</p>
</blockquote>
<h2>0x01 学习重点</h2>
<ol>
<li><strong>STC32G系列概述:</strong>
<ul>
<li><strong>基于1T 8051内核,比传统89C51快8-12倍</strong></li>
<li><strong>丰富的外设资源:多达64KB Flash、8KB SRAM、2KB EEPROM</strong></li>
<li><strong>高速ADC、多路PWM、丰富的通信接口(UART、SPI、I2C、USB)</strong></li>
<li><strong>低功耗设计,多种工作模式</strong></li>
</ul>
</li>
<li><strong>与89C51的主要区别</strong>:
<ul>
<li><strong>时钟频率大幅提升(最高可达35MHz)</strong></li>
<li><strong>寄存器配置更加复杂但也更灵活</strong></li>
<li><strong>新增了大量功能外设</strong></li>
<li><strong>开发环境和工具链完全不同</strong></li>
</ul>
</li>
<li><strong>开发环境搭建</strong>:
<ul>
<li><strong>安装STC-ISP烧录软件</strong></li>
<li><strong>配置KEIL C51开发环境</strong></li>
<li><strong>了解STC32G系列的特殊库函数</strong></li>
</ul>
</li>
</ol>
<h2>0x02 部分思考</h2>
<h3>个人感悟:</h3>
<blockquote>
<p>作为一名从89C51起步的单片机爱好者,看到STC32G的强大功能真是既兴奋又有些忐忑。虽然都是51内核,但功能差异巨大。特别是了解到这系列单片机能够实现USB声卡、示波器、FFT频谱分析仪等高级功能,远超我手中89C51的能力范围。目前最大的困难是手上还没有STC32G开发板,只能通过视频学习理论知识,无法进行实践操作。期待第八天能收到开发板!</p>
</blockquote>
<h2>0x03 待学习重点:</h2>
<ol>
<li>89C51的经验有多少可以迁移到STC32G上</li>
<li>如何将传统51单片机项目迁移到STC32G平台上</li>
</ol>

芯Skye 发表于 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不停电下载, 演示视频链接
https://v.stcai.com/sv/1c5eec2-197fcd9b766/1c5eec2-197fcd9b766.mp4
https://v.stcai.com/sv/1fce8086-197cf2b9dd4/1fce8086-197cf2b9dd4.mp4

jellyfish 发表于 2026-1-21 01:26:44

<h1>Jellyfish从89C51到AI8051学习贴第二天!</h1>
<blockquote>
<p>根据<a href="https://www.stcaimcu.com/space-username-%E8%8A%AFSkye.html">芯Skye</a>版主老师的指引,今天学习了<strong>AiCube-ISP-V6.96O 版</strong>图形化自动配置生成程序工具以及不停电下载的教程。</p>
</blockquote>
<h2>0x01 学习重点</h2>
<ol>
<li>新版本的ISP软件有AiCube项目创建助手功能,可以直接选择特定的单片机型号建立AiCube项目,然后通过图形化的方式配置IO口、中断等东西初始化类型,选择好之后可以非常方便的一键生成一个带所有依赖头文件、可以零错误零警告编译的初始工程!</li>
<li>在<strong>AiCube-ISP-V6.96O</strong>软件里面带有了很多样例程序,可以根据样例程序中的官方样例进行学习!</li>
<li><strong>AiCube-ISP-V6.96O</strong>可以实现不停电直接下载!通过烧录的时候选择<strong>收到用户命令后复位到ISP监控程序</strong>中的**下次使用HID进行ISP下载,即可实现keil中编译即烧录运行。</li>
</ol>
<h2>0x02 部分思考</h2>
<h3>个人感悟:变化太大了!</h3>
<ol>
<li>不管是AiCube-ISP的图形化一键生成初始keil工程,还是USB不停电下载,两者都极大提高了开发效率。以后再也不需要复制工程了!</li>
<li>AI8051自带了<strong>硬件 USB 控制器</strong>,结束了需要通过usb转串口后才能烧录、与上位机通信的时代。这个很方便开发,而且也多了更多的玩法,很期待后续能用这个硬件USB控制器做出更多有意思的功能。</li>
</ol>
<h2>0x03 待学习重点:</h2>
<ol>
<li>89C51的经验有多少可以迁移到STC32G上(已有完成)
<blockquote>
<p>目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。</p>
</blockquote>
</li>
<li>如何将传统51单片机项目迁移到STC32G平台上(已有完成)
<blockquote>
<p>通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!</p>
</blockquote>
</li>
<li>硬件USB控制器可以玩出什么花样?</li>
</ol>

jellyfish 发表于 2026-1-21 12:20:01

芯Skye 发表于 2026-1-20 08:35
要 做到 USB不停电下载;
要 尝试 AiCube 图形化自动配置生成程序工具;
推荐优先看的:

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

jellyfish 发表于 2026-1-22 01:18:23

<h1>Jellyfish从89C51到AI8051学习贴第三天!</h1>
<blockquote>
<p>今天学习了《8051U深度入门到32位51大型实战教学视频》第二集 硬件及工具介绍。</p>
</blockquote>
<h2>0x01 学习重点</h2>
<ol>
<li>介绍了<strong>Ai8051U-实验箱</strong>的外围器件,意外发现AI8051内部集成实时时钟控制电路,可以外接电源实现低功耗RTC,且需要外接32.768k的无源晶振。</li>
<li>安装keil C251程序:
<ul>
<li>官网下载安装包,然后正常安装;</li>
<li>在<strong>AiCube-ISP-V6.96O</strong>中选择对应的芯片型号,选择添加型号和头文件到keil中;</li>
<li>下载<strong>Keil 中断拓展插件</strong>将keil的中断号拓展到254。</li>
</ul>
</li>
</ol>
<h2>0x02 学习心得</h2>
<ul>
<li><strong>Ai8051U-实验箱</strong>很多外围器件,基本覆盖了这颗强劲的AI8051U内含的功能。</li>
<li>发现了AI8H2K12U可以实现USB转双串口,再次感慨硬件USB的强大之处。</li>
<li>keil安装MDK和C251和C51共存的安装顺序:C251包(c251v560)--&gt;C51包(C51V961)--&gt;MDK包(MDK535)。</li>
</ul>
<h2>0x03 待学习重点</h2>
<ol>
<li>89C51的经验有多少可以迁移到STC32G上(已完成)
<blockquote>
<p>目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。</p>
</blockquote>
</li>
<li>如何将传统51单片机项目迁移到STC32G平台上(已完成)
<blockquote>
<p>通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!</p>
</blockquote>
</li>
<li>硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
<ul>
<li>USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上<strong>MAX232</strong>打上RJ45就可以用来配置交换机了。</li>
</ul>
</li>
</ol>

jellyfish 发表于 2026-1-23 02:54:57

<h1>Jellyfish从89C51到AI8051学习贴第四天!</h1>
<blockquote>
<p>今天继续学习《8051U深度入门到32位51大型实战教学视频》<strong>第三集 点亮第一颗LED灯</strong>。</p>
</blockquote>
<p>直接上图,已实现STC32G12K128的<strong>USB不停电下载</strong>+<strong>USB库函数printf_usb(&quot;Hello World !\r\n&quot;)</strong>!</p>
<p><img src="https://www.stcaimcu.com/data/attachment/forum/202601/23/010546hbp59cuiwuwqiw37.png" alt="image.png" title="image.png" /></p>
<blockquote>
<p>吐槽一下某宝卖的核心板居然默认把P3.0和P3.1接到了ch340上,而且还没有给P3.2做按钮,可恶!</p>
</blockquote>
<p>手动把两个0Ω电阻换到了usb引脚上,还顺便把排针焊上去了方便短接P3.2.</p>
<p><img src="data/attachment/forum/202601/23/011345j342568es7bxzele.jpg" alt="IMG_20260123_011050.jpg" title="IMG_20260123_011050.jpg" /></p>
<hr />
<h2>0x01 学习重点</h2>
<p><strong>STC 单片机 I/O 口配置指南</strong><br />
每个 I/O 口均由 <strong>PnM0</strong> 和 <strong>PnM1</strong> 两个寄存器成对配置。以 <strong>P0.x</strong> 为例,由 <code>P0M0</code> 与 <code>P0M1</code> 的位组合决定其工作模式。</p>
<p><strong>1. I/O 模式配置表</strong></p>
<table>
<thead>
<tr>
<th>PnM1</th>
<th>PnM0</th>
<th>I/O 工作模式</th>
<th>特性说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>0</strong></td>
<td><strong>0</strong></td>
<td><strong>准双向口</strong></td>
<td><strong>传统 8051 模式</strong>。弱上拉,灌电流可达 20mA,拉电流约 150~270μA。</td>
</tr>
<tr>
<td><strong>0</strong></td>
<td><strong>1</strong></td>
<td><strong>推挽输出</strong></td>
<td><strong>强上拉输出</strong>。电流可达 20mA,<strong>必须外接限流电阻</strong>以保护硬件。</td>
</tr>
<tr>
<td><strong>1</strong></td>
<td><strong>0</strong></td>
<td><strong>高阻输入</strong></td>
<td>电流无法流入/流出,常用于 ADC 检测或传感器输入。</td>
</tr>
<tr>
<td><strong>1</strong></td>
<td><strong>1</strong></td>
<td><strong>开漏模式</strong></td>
<td>内部上拉断开。<strong>需外接上拉电阻</strong>才能输出高电平或读取外部状态。</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>开漏模式(Open-Drain)进阶逻辑:</strong></p>
<ul>
<li><strong>输出</strong> <code>1</code> <strong>时,等同于</strong> <strong>高阻输入</strong>。</li>
<li><strong>配合</strong> <strong>内部/外部上拉电阻</strong> <strong>后,功能等同于</strong> <strong>准双向口</strong>。</li>
</ul>
</blockquote>
<hr />
<p><strong>2. 电流负载与安全规范</strong></p>
<ul>
<li><strong>单口驱动能力</strong>:在准双向、推挽或开漏模式下,单个 I/O 的灌电流(电平流入)和拉电流(推挽模式下输出)能力均可达<strong>20mA</strong>。</li>
<li><strong>硬件保护建议</strong>:外部务必接入限流电阻(常见阻值:560Ω、1KΩ 或 4.7KΩ)。</li>
<li><strong>整机功耗限制</strong>:芯片整体 VCC 流入或 GND 流出电流建议<strong>不超过 90mA</strong>。在设计多路 LED 驱动或大功率电路时,应确保所有 I/O 总电流在此安全范围内。</li>
</ul>
<hr />
<p><strong>点亮LED程序代码</strong></p>
<pre><code class="language-c">#include &quot;STC32.h&quot; //头文件

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:无下拉电阻!!
    }
}
</code></pre>
<h2>0x02 学习心得</h2>
<p><strong>成功的通过STC32G12K128芯片实现的LED的点亮</strong></p>
<ol>
<li>新芯片的IO口模式通过寄存器进行配置</li>
<li>通过M0=1,M1=0将IO口配置成推挽输出模式实现更大电流的输出。</li>
</ol>
<p><strong>通过硬件USB控制器实现了<em>USB不停电下载+USB库函数printf</em></strong></p>
<ol>
<li>一开始觉得实现起来很简单,但是后来点灯的时候发现烧录了别的代码后,USB就识别不出来不能USB下载代码了,需要转到串口进行下载。</li>
<li>结合分析和查询数据手册,发现是需要在<strong>用户代码中嵌入USB-CDC代码</strong>后才能实现一直USB下载。</li>
</ol>
<blockquote>
<p>当单片机需要和电脑之间进行数据交换时,首选一般都是串口通讯。STC32G 系列单片机内置 USB 控制器和收发器,当用户代码中嵌入 USB-CDC 代码后,使用 USB 线将单片机与电脑直接相连接,在电 脑端就可识别出【USB-CDC 虚拟串口】,【USB-CDC 虚拟串口】就是【串口】。</p>
</blockquote>
<h2>0x03 待学习重点</h2>
<ol>
<li>
<p>89C51的经验有多少可以迁移到STC32G上(已完成)</p>
<blockquote>
<p>目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。</p>
</blockquote>
</li>
<li>
<p>如何将传统51单片机项目迁移到STC32G平台上(已完成)</p>
<blockquote>
<p>通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!</p>
</blockquote>
</li>
<li>
<p>硬件USB控制器可以玩出什么花样?(持续学习 持续探索)</p>
<ul>
<li>USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上<strong>MAX232</strong>打上RJ45就可以用来配置交换机了。</li>
</ul>
</li>
<li>
<p>在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直<strong>USB不停电下载</strong>。</p>
</li>
</ol>

jellyfish 发表于 2026-1-25 15:54:58

<h1>Jellyfish从89C51到AI8051学习贴第五天!</h1>
<blockquote>
<p>今天继续学习《8051U深度入门到32位51大型实战教学视频》<strong>第四集 USB不停电下载</strong>。</p>
</blockquote>
<h2>0x01 学习重点</h2>
<ol>
<li>
<p>在<a href="https://stcai.com/khs">官网</a>下载函数库<strong>USB库文件</strong>,学会了通过添加代码移植到本地工程。</p>
<pre><code class="language-c">#include &quot;stc32_stc8_usb.h&quot; //引用usb程序库
//根据新版本说明,新USB库函数,用户程序中不需要再对USER_DEVICEDESC,USER_PRODUCTDESC,USER_STCISPCMD进行定义,与冲哥课程存在小差异。

//在主函数中放下如下语句即可实现不停电下载
//通过控制P_SW2的第八位(即B7)为1使能访问 XFR。
P_SW2 |= 0x80;
//USB CDC 接口配置
usb_init();
EA = 1;
</code></pre>
</li>
<li>
<p>配置寄存器,可以通过 <code>|=</code>或等于的方式,如 <code>P_SW2 |= 0x80;</code>实现在不影响其他位将第八位置为1。</p>
</li>
<li>
<p>内存模式的区别:</p>
<table>
<thead>
<tr>
<th>模式</th>
<th>在 STC32G 里的表现</th>
<th>官方评价</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Tiny / Small</strong></td>
<td>只能用 128 字节 RAM。</td>
<td><strong>不建议使用</strong>。</td>
</tr>
<tr>
<td><strong>XSmall</strong></td>
<td><strong>变量默认放 4K edata</strong>。</td>
<td><strong>强烈推荐</strong>(速度最快,空间够用)。</td>
</tr>
<tr>
<td><strong>Large</strong></td>
<td>变量默认放 xdata。</td>
<td><strong>不推荐</strong>(浪费 CPU 性能,变慢了)。</td>
</tr>
<tr>
<td><strong>Huge</strong></td>
<td>变量可以跨段。</td>
<td><strong>除非有超级大数组</strong>。</td>
</tr>
</tbody>
</table>
</li>
</ol>
<blockquote>
<p>不建议使用“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 个时钟,访问速度慢。</p>
</blockquote>
<h2>0x02 学习心得</h2>
<ol>
<li>通过USB库文件实现了USB下载,彻底摆脱串口的依赖。</li>
<li>搞清楚了为什么自己一开始写的东西不能在电脑上识别出来USB-CBC。</li>
<li>学会了 <code>|=</code>或等于的<strong>按位或赋值运算符</strong>,可以只操作寄存器的某一位。</li>
</ol>
<h2>0x03 待学习重点</h2>
<ol>
<li>89C51的经验有多少可以迁移到STC32G上(已完成)
<blockquote>
<p>目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。</p>
</blockquote>
</li>
<li>如何将传统51单片机项目迁移到STC32G平台上(已完成)
<blockquote>
<p>通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!</p>
</blockquote>
</li>
<li>硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
<ul>
<li>USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上<strong>MAX232</strong>打上RJ45就可以用来配置交换机了。</li>
</ul>
</li>
<li>在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直<strong>USB不停电下载</strong>。(已完成)</li>
</ol>

jellyfish 发表于 2026-1-25 16:33:04

<h1>Jellyfish从89C51到AI8051学习贴第六天!</h1>
<blockquote>
<p>今天继续学习《8051U深度入门到32位51大型实战教学视频》<strong>第五集 C语言基础</strong>。</p>
</blockquote>
<h2>0x01 学习重点</h2>
<ol>
<li>
<p>C语言printf函数实现</p>
<pre><code class="language-c">#define printf printf_hid
//%s占位符,后面可以用字符串填充
printf(&quot;STC深圳国芯人工智能有限公司 %s\n&quot;,&quot;yyds&quot;);
</code></pre>
<table>
<thead>
<tr>
<th>占位符</th>
<th>数据类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><code>%d</code></strong></td>
<td><code>int</code></td>
<td>有符号十进制整数</td>
</tr>
<tr>
<td><strong><code>%u</code></strong></td>
<td><code>unsigned int</code></td>
<td>无符号十进制整数</td>
</tr>
<tr>
<td><strong><code>%ld</code></strong></td>
<td><code>long</code></td>
<td>长整型(单片机中常用,避免溢出)</td>
</tr>
<tr>
<td><strong><code>%f</code></strong></td>
<td><code>float</code> / <code>double</code></td>
<td>浮点数(默认 6 位小数)</td>
</tr>
<tr>
<td><strong><code>%c</code></strong></td>
<td><code>char</code></td>
<td>单个字符</td>
</tr>
<tr>
<td><strong><code>%s</code></strong></td>
<td><code>char *</code></td>
<td>字符串(以 <code>\0</code> 结尾)</td>
</tr>
<tr>
<td><strong><code>%p</code></strong></td>
<td><code>void *</code></td>
<td>指针地址(十六进制显示)</td>
</tr>
<tr>
<td><strong><code>%i</code></strong></td>
<td>有符号整数</td>
<td>与 <code>%d</code> 类似,但在 <code>scanf</code> 时能自动识别八/十六进制。</td>
</tr>
<tr>
<td><strong><code>%E</code></strong></td>
<td>大写科学计数法</td>
<td>输出:<code>1.23E+03</code>(字母 E 大写)。</td>
</tr>
<tr>
<td><strong><code>%G</code></strong></td>
<td>大写智能选择</td>
<td>指数部分显示大写 E。</td>
</tr>
<tr>
<td><strong><code>%#x</code></strong></td>
<td>自动带 0x</td>
<td>输出 <code>0xaf</code> 而不是 <code>af</code>。</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>美化输出,可以通过在 % 后面加数字来控制输出宽度和精度:</p>
<blockquote>
<p>%5d:右对齐,宽度为 5(不足补空格)。<br />
%-5d:左对齐,宽度为 5。<br />
%05d:最常用,宽度为 5,不足的位补 0(如 00001)。<br />
%.2f:保留两位小数。<br />
%#x:自动带上 0x 前缀(输出如 0x1a)。</p>
</blockquote>
</li>
<li>
<p>ascii码表<br />
<img src="data/attachment/forum/202601/25/161533jz8u7udkfcotwu5o.png" alt="ascii-1-1.png" title="ascii-1-1.png" /><br />
<img src="data/attachment/forum/202601/25/161606ukpd3j2za2l2hqd5.png" alt="ascii-2-1.png" title="ascii-2-1.png" /></p>
</li>
<li>
<p>想要使用64位的double类型需要声明 <code>#pragma float64</code></p>
</li>
<li>
<p>复合赋值位运算笔记</p>
</li>
</ol>
<table>
<thead>
<tr>
<th>运算符</th>
<th>名称</th>
<th>等价表达式</th>
<th>单片机开发中的核心含义</th>
<th>典型实战场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><code>&amp;=</code></strong></td>
<td><strong>与等于</strong></td>
<td><code>A = A &amp; B</code></td>
<td><strong>按位清零/屏蔽</strong> 仅关闭特定的开关,其余保持不变。</td>
<td><code>IE &amp;= ~0x80;</code><em>(关闭总中断)</em></td>
</tr>
<tr>
<td><strong><code>|=</code></strong></td>
<td><strong>或等于</strong></td>
<td><code>A = A | B</code></td>
<td><strong>按位设1</strong> 仅开启特定的开关,不影响其他位。</td>
<td><code>P2M0 |= 0x03;</code><em>(设置 P2.0/0.1 模式)</em></td>
</tr>
<tr>
<td><strong><code>^=</code></strong></td>
<td><strong>异或等于</strong></td>
<td><code>A = A ^ B</code></td>
<td><strong>按位取反</strong> 让指定位在 0 和 1 之间反复横跳。</td>
<td><code>P1 ^= 0x01;</code><em>(翻转 P1.0 引脚电平)</em></td>
</tr>
<tr>
<td><strong><code>&lt;&lt;=</code></strong></td>
<td><strong>左移等于</strong></td>
<td><code>A = A &lt;&lt; B</code></td>
<td><strong>位权提升</strong> 将数据整体向左推,通常用于构建协议。</td>
<td><code>val &lt;&lt;= 4;</code><em>(将低 4 位数据移到高 4 位)</em></td>
</tr>
<tr>
<td><strong><code>&gt;&gt;=</code></strong></td>
<td><strong>右移等于</strong></td>
<td><code>A = A &gt;&gt; B</code></td>
<td><strong>位权降低</strong> 将数据整体向右推,用于解析协议。</td>
<td><code>data &gt;&gt;= 8;</code><em>(丢弃低 8 位,保留高位数据)</em></td>
</tr>
</tbody>
</table>
<h2>0x02 学习心得</h2>
<ol>
<li>本节课学会了C语言一些常用的运算符。</li>
<li>与电脑通过printf函数根ISP程序进行通信。</li>
</ol>
<h2>0x03 待学习重点</h2>
<ol>
<li>89C51的经验有多少可以迁移到STC32G上(已完成)
<blockquote>
<p>目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。</p>
</blockquote>
</li>
<li>如何将传统51单片机项目迁移到STC32G平台上(已完成)
<blockquote>
<p>通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!</p>
</blockquote>
</li>
<li>硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
<ul>
<li>USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上<strong>MAX232</strong>打上RJ45就可以用来配置交换机了。</li>
</ul>
</li>
<li>在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直<strong>USB不停电下载</strong>。(已完成)</li>
</ol>

jellyfish 发表于 2026-1-26 02:05:07

<h1>Jellyfish从89C51到AI8051学习贴第七天!</h1>
<blockquote>
<p>今天继续学习《8051U深度入门到32位51大型实战教学视频》<strong>第六集 IO输入输出</strong>。</p>
</blockquote>
<h2>0x01 学习重点</h2>
<ol>
<li>
<p>GPIO口的输入输出电压是有阈值的,一般3.3V工作电压下低是1.07以下,高电平是1.18V以上。<br />
<strong>直流特性(VSS=0V,VDD=3.3V,测试温度=25℃)</strong><br />
<img src="data/attachment/forum/202601/26/005434omrf7uzf7uzuqua7.png" alt="image.png" title="image.png" /><br />
<strong>直流特性(VSS=0V,VDD=5.0V,测试温度=25℃)</strong><br />
<img src="data/attachment/forum/202601/26/010144flrldrr4ppto4ynp.png" alt="image.png" title="image.png" /></p>
</li>
<li>
<p>按键会存在抖动,需要做前沿后沿的判断。</p>
</li>
<li>
<p>通过ISP软件计算延时程序。</p>
</li>
<li>
<p>将WTST赋值为0提高访问速度。</p>
<pre><code class="language-c">WTST = 0x00; //设置程序代码等待参数,赋值为 0 可将 CPU 执行程序的速度设置为最快。
CKCON = 0; //设置访问片内的 xdata 速度,赋值为 0 表示用最快速度访问,不增加额外的等待时间。
</code></pre>
</li>
<li>
<p>C语言的变量类型</p>
<table>
<thead>
<tr>
<th>类型关键字</th>
<th>占用位 (Bit)</th>
<th>字节 (Byte)</th>
<th>取值范围</th>
<th>常用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><code>bit</code></strong></td>
<td>1</td>
<td>-</td>
<td>0 或 1</td>
<td><strong>位标志</strong>,仅限部分 RAM 区 (C51/C251 特有)</td>
</tr>
<tr>
<td><strong><code>char</code></strong></td>
<td>8</td>
<td>1</td>
<td>-128~ 127</td>
<td>存储 ASCII 字符</td>
</tr>
<tr>
<td><strong><code>unsigned char</code></strong></td>
<td>8</td>
<td>1</td>
<td>0~ 255</td>
<td><strong>最常用</strong>,用于 8 位寄存器、颜色、小计数器</td>
</tr>
<tr>
<td><strong><code>int</code></strong></td>
<td>16</td>
<td>2</td>
<td>-32,768~ 32,767</td>
<td>通用整数运算</td>
</tr>
<tr>
<td><strong><code>unsigned int</code></strong></td>
<td>16</td>
<td>2</td>
<td>0~ 65,535</td>
<td>16 位定时器初值、传感器 16 位原始数据</td>
</tr>
<tr>
<td><strong><code>long</code></strong></td>
<td>32</td>
<td>4</td>
<td>-21亿~ 21亿</td>
<td>时间戳、大数值累加</td>
</tr>
<tr>
<td><strong><code>unsigned long</code></strong></td>
<td>32</td>
<td>4</td>
<td>0~ 42亿</td>
<td>频率计算、大范围计数</td>
</tr>
<tr>
<td><strong><code>float</code></strong></td>
<td>32</td>
<td>4</td>
<td>小数点后<strong>37 个零</strong></td>
<td>传感器数据(如温度 25.5℃)、简单物理计算</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>特有类型</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>说明</th>
<th>提示</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><code>sbit</code></strong></td>
<td>定义特殊功能寄存器的某一位</td>
<td><code>sbit LED = P1^0;</code> (方便直接操作引脚)</td>
</tr>
<tr>
<td><strong><code>sfr</code></strong></td>
<td>定义 8 位特殊功能寄存器</td>
<td><code>sfr P0 = 0x80;</code></td>
</tr>
<tr>
<td><strong><code>sfr16</code></strong></td>
<td>定义 16 位特殊功能寄存器</td>
<td><code>sfr16 T0 = 0x8A;</code></td>
</tr>
<tr>
<td><strong><code>far</code></strong></td>
<td><strong>24 位寻址指针</strong></td>
<td><strong>STC32G 必用</strong>,用于访问 64KB 以外的代码或数据</td>
</tr>
</tbody>
</table>
</li>
</ol>
<h2>0x02 学习心得</h2>
<ol>
<li>
<p>学习了解GPIO的电器特性。</p>
</li>
<li>
<p>掌握按键的抖动处理。</p>
</li>
<li>
<p>课后小作业(按下按键实现开关小灯):</p>
<pre><code class="language-c">#include &quot;STC32.h&quot;
#include &quot;stc32_stc8_usb.h&quot;

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 &amp;= ~0x10; P1M1 &amp;= ~0x10;      //我把按键接到了P1.4

    P_SW2 |= 0x80;
    usb_init();                        //USB CDC 接口配置
    EA = 1;

    while (1)
    {
      if (bUsbOutReady)
      {
            USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
            printf(&quot;\nSTC深圳国芯人工智能有限公司 %s\n&quot;,&quot;yyds&quot;);
            usb_OUT_done();
      }

      if(P14==0){
            Delay100ms();               //消抖
            if(P14==0){
                LED=!LED;
                P00=LED;
                printf(&quot;LED状态更换成了%bd\n&quot;,LED);
                while(P14==0){};
            }

      }
    }
}
</code></pre>
</li>
</ol>
<p><img src="data/attachment/forum/202601/26/014135xsmesiffmacfec9z.png" alt="image.png" title="image.png" /></p>
<ol start="4">
<li>
<p>课后小作业(按下按键依次增加LED点亮的数量)</p>
<pre><code class="language-c">#include &quot;STC32.h&quot;
#include &quot;stc32_stc8_usb.h&quot;

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 &amp;= ~0x10; P1M1 &amp;= ~0x10;      //我把按键接到了P1.4

    P_SW2 |= 0x80;
    usb_init();                                     //USB CDC 接口配置
    EA = 1;
    P0=0;
    while (1)
    {
      if (bUsbOutReady)
      {
            USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
            printf(&quot;\nSTC深圳国芯人工智能有限公司 %s\n&quot;,&quot;yyds&quot;);
            usb_OUT_done();
      }

      if(P14==0){
            Delay100ms();             //消抖
            if(P14==0){
                if(LED==8){
                  LED=0;
                }
                LED++;
                P0=(1&lt;&lt;LED)-1;      //核心代码,通过&lt;&lt;位移实现2的n次方。
                printf(&quot;LED状态更换,现在有%d个LED点亮,P0的值为%d&quot;,LED,(1&lt;&lt;LED)-1);
                while(P14==0){};
            }

      }
    }
}
</code></pre>
<p><img src="data/attachment/forum/202601/26/015746enrfu7oxzh3g336v.png" alt="image.png" title="image.png" /></p>
</li>
</ol>
<h2>0x03 待学习重点</h2>
<ol>
<li>89C51的经验有多少可以迁移到STC32G上(已完成)
<blockquote>
<p>目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。</p>
</blockquote>
</li>
<li>如何将传统51单片机项目迁移到STC32G平台上(已完成)
<blockquote>
<p>通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!</p>
</blockquote>
</li>
<li>硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
<ul>
<li>USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上<strong>MAX232</strong>打上RJ45就可以用来配置交换机了。</li>
</ul>
</li>
<li>在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直<strong>USB不停电下载</strong>。(已完成)</li>
<li>通过定时器实现替代延时函数。</li>
<li>实现动态延时函数。</li>
</ol>

jellyfish 发表于 2026-1-27 01:24:01

<h1>Jellyfish从89C51到AI8051学习贴第九天!</h1>
<blockquote>
<p>今天继续学习《8051U深度入门到32位51大型实战教学视频》<strong>第八集 定时器周期性任务调度</strong>。</p>
</blockquote>
<h2>0x01 学习重点</h2>
<ol>
<li>如何通过1个定时器进行多个任务序列调度。</li>
<li>C语言数组使用。</li>
<li>C语言for循环的使用。</li>
<li>新版本的按键抖动判断逻辑。</li>
<li>C语言的.c和.h文件的建立与使用。</li>
<li>C语言的结构体使用。</li>
</ol>
<h2>0x02 学习心得</h2>
<ol>
<li>
<p><strong>C 语言 for 循环的使用</strong></p>
<pre><code class="language-c">//基础语法
for (初始化; 条件判断; 更新) {
    // 循环体
}
//死循环
for(;;) { // 等同于 while(1)
    // 运行任务
}
//软件阻塞延时(不精确,受时钟频率影响
void Delay(uint32_t count) {
    for(; count &gt; 0; count--); // 简单的递减延时
}
</code></pre>
</li>
<li>
<p><strong>C 语言数组使用</strong></p>
<pre><code class="language-c">//静态初始化
uint8_t buffer = {0xAA, 0xBB, 0xCC, 0xDD};
//查表法优化(节省 CPU 计算量):
//例如控制 LED 的呼吸灯亮度曲线,预先计算好放入数组:
const uint8_t PWM_Table[] = {0, 10, 50, 150, 255}; // 存放在 Flash 中,节省 RAM
//数组越界检查:单片机没有操作系统保护内存,数组越界会导致死机或改写其他变量的值。
for(uint8_t i = 0; i &lt; sizeof(buffer); i++) { // 使用 sizeof 动态计算长度
    Send_UART(buffer);
}
</code></pre>
</li>
<li>
<p><strong>C 语言的结构体(Struct)</strong></p>
<pre><code class="language-c">//基本定义与封装:
//结构体可以将不同类型的数据(如任务开关、周期、函数指针)打包在一起。
typedef struct {
    uint8_tID;      // 任务 ID
    uint16_t Interval;// 执行间隔
    void (*TaskPtr)(void); // 函数指针,指向具体要执行的代码
} Task_TypeDef;
//结构体指针与硬件寄存器映射
GPIO_A-&gt;ODR = 0x01; // GPIO_A 是一个结构体指针,指向特定的内存地址
//位域(Bit-fields)优化内存(嵌入式黑科技)
struct Status {
    uint8_t isRunning : 1; // 只占 1 个 bit
    uint8_t errorCode : 7; // 占 7 个 bit
}; // 整个结构体仅占 1 字节
</code></pre>
</li>
<li>
<p>通过定时器中断函数对数组的多个成员累加,在主函数判断实现多任务序列调度。</p>
<pre><code class="language-c">unsigned int led_count={0,0,0};
int length = sizeof(led_count) / sizeof(led_count);
unsigned char LED_state = {0,0,0};
void Timer0_Isr(void) interrupt 1
{
    //中断函数实现
    int i;
    for (i=0;i&lt;length;i++){
      led_count++;
    }
    if(led_count&gt;=3){
      led_count=0;
      LED_state=!LED_state;
      P00=LED_state;
      printf(&quot;现在LED00的状态变更为:%d\n&quot;,(int)LED_state);
    }
    if(led_count&gt;=6){
      led_count=0;
      LED_state=!LED_state;
      P01=LED_state;
      printf(&quot;现在LED01的状态变更为:%d\n&quot;,(int)LED_state);
    }
    if(led_count&gt;=9){
      led_count=0;
      LED_state=!LED_state;
      P02=LED_state;
      printf(&quot;现在LED02的状态变更为:%d\n&quot;,(int)LED_state);
    }

}

void Timer0_Init(void)                //100毫秒@24.000MHz
{
        TM0PS = 0x03;                        //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
        AUXR &amp;= 0x7F;                        //定时器时钟12T模式
        TMOD &amp;= 0xF0;                        //设置定时器模式
        TL0 = 0xB0;                                //设置定时初始值
        TH0 = 0x3C;                                //设置定时初始值
        TF0 = 0;                                //清除TF0标志
        TR0 = 1;                                //定时器0开始计时
        ET0 = 1;                                //使能定时器0中断
}
</code></pre>
</li>
<li>
<p>通过按键间隔时间判断是否按下,累加判断成功次数,实现按键抖动判断不影响其他任务执行。</p>
<p><strong>此时LED灯正常闪烁!</strong></p>
<p><img src="data/attachment/forum/202601/28/014043cso222pw6swtyo7w.png" alt="image.png" title="image.png" /></p>
<pre><code class="language-c">#include &quot;STC32.h&quot;
#include &quot;stc32_stc8_usb.h&quot;

unsigned int time_count={0,0,0,0};
int length = sizeof(time_count) / sizeof(time_count);
unsigned char LED_state = {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&lt;length;i++){
      time_count++;
    }
    if(time_count&gt;=30){
      time_count=0;
      LED_state=!LED_state;
      P00=LED_state;
      //printf(&quot;现在LED00的状态变更为:%d\n&quot;,(int)LED_state);
    }
    if(time_count&gt;=60){
      time_count=0;
      LED_state=!LED_state;
      P01=LED_state;
      //printf(&quot;现在LED01的状态变更为:%d\n&quot;,(int)LED_state);
    }
    if(time_count&gt;=90){
      time_count=0;
      LED_state=!LED_state;
      P02=LED_state;
      //printf(&quot;现在LED02的状态变更为:%d\n&quot;,(int)LED_state);
    }
    if(time_count&gt;=1){
      time_count=0;
      if(P14==0){
            if(key_pressed_flag == 0) { // 如果之前还没处理过这个按键
                key_count++;
                if(key_count&gt;=10){
                  key_pressed_flag = 1; // 只立个 Fla

                }
            }
      }else{
            key_count=0;
            key_pressed_flag = 0; // 允许下一次按下处理
      }
    }

}

void Timer0_Init(void)                //10毫秒@24.000MHz
{
        TM0PS = 0x00;                        //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
        AUXR &amp;= 0x7F;                        //定时器时钟12T模式
        TMOD &amp;= 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 &amp;= ~0x30; P1M1 &amp;= ~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(&quot;\nSTC深圳国芯人工智能有限公司 %s\n&quot;,&quot;yyds&quot;);
            usb_OUT_done();
      }
      if(key_pressed_flag==1) {
            printf(&quot;按键逻辑触发已经超过%d此判断按键按下&quot;,(int)key_count);
            key_pressed_flag = 2;
      }   
    }
}
</code></pre>
</li>
<li>
<p>通过的结构体使用,高效使用定时器</p>
<pre><code class="language-c">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);

//========================================================================
// 函数: Task_Handler_Callback
// 描述: 任务标记回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
//========================================================================
//在中断函数中执行这个函数代码,实现数量累加
void Task_Marks_Handler_Callback(void)
{
    u8 i;
    for(i=0; i&lt;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
// 描述: 任务处理回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
//========================================================================
//这个函数在main函数里面持续执行,当判断发现有任务时间到了就开始执行
void Task_Pro_Handler_Callback(void)
{
    u8 i;
    for(i=0; i&lt;Tasks_Max; i++)
    {
      if(Task_Comps.Run) /* If task can be run */
      {
            Task_Comps.Run = 0;      /* Flag clear 0 */
            Task_Comps.TaskHook();   /* Run task */
      }
    }
}
</code></pre>
</li>
</ol>
<h2>0x03 待学习重点</h2>
<ol>
<li>
<p>89C51的经验有多少可以迁移到STC32G上(已完成)</p>
<blockquote>
<p>目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。</p>
</blockquote>
</li>
<li>
<p>如何将传统51单片机项目迁移到STC32G平台上(已完成)</p>
<blockquote>
<p>通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!</p>
</blockquote>
</li>
<li>
<p>硬件USB控制器可以玩出什么花样?(持续学习 持续探索)</p>
<ul>
<li>USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上<strong>MAX232</strong>打上RJ45就可以用来配置交换机了。</li>
</ul>
</li>
<li>
<p>在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直<strong>USB不停电下载</strong>。(已完成)</p>
</li>
<li>
<p>定时器一次只能定时一个此,如果有很多个任务怎么办?(已完成)</p>
<blockquote>
<p>初步猜测可能性:1、在定时器函数内部增加if/else判断某个全局变量的值确定要执行哪一段语句。2、使用多个定时器。</p>
<p>答案:想的差不多,方法如下</p>
<ul>
<li>通过数组(<strong>包括结构体数组</strong>),定时器中断函数对数组的多个成员累加,在主函数判断实现多任务序列调度。</li>
</ul>
</blockquote>
</li>
<li>
<p>如果我的函数执行的很慢,定时器等待的时间很短,会不会出现问题?</p>
</li>
</ol>
页: [1] 2 3
查看完整版本: Jellyfish从89C51到AI8051学习贴