金水32051编译器下的AI8051U单片机入门:从点亮LED到“你好,世界,我来了!”
前言在嵌入式系统学习的漫漫长路上点亮第一颗LED与向世界说出“Hello, World!”是每一位开发者最具仪式感的第一步。然而在8051单片机的世界里这一小步往往伴随着复杂的寄存器配置、神秘的汇编指令和海量的芯片手册。许多初学者满怀热情打开一个工程却被那些陌生的头文件、特殊功能寄存器和内嵌汇编吓得望而却步。今天我们要为你展示的不仅仅是一个“点亮LED”的例程而是一套完整、规范、值得反复研习的单片机项目框架。这套框架基于金水32051编译器运行在AI8051U这颗性能强劲的8位单片机之上采用了经典的8BIT模式兼容8051指令集。它包含了精确的软件延时、标准串口初始化、CPU性能调优以及清晰的后台主循环结构。可以说吃透了这个程序你对8051的理解将不再是“照葫芦画瓢”而是真正踏入工程师的门槛。本文将以这个“官方范例”为主线用超过八千字的篇幅手把手带你理解其中每一个细节。我们将从单片机本身讲起逐步深入到时钟配置、汇编延时、串口通信、I/O端口控制最后在闪亮的跑马灯和课后练习中巩固你的知识。请准备好你的开发板、编译器和一颗好奇心让我们开始这段奇妙的8051之旅。第一部分 初识AI8051U与金水32051编译器1.1 什么是AI8051UAI8051U是宏晶科技STC推出的一款高性能1T 8051单片机。与传统的12T 8051例如经典的AT89C51相比它的指令执行速度有了质的飞跃——在同等工作频率下大部分指令仅需1个机器周期而非12个。这也就意味着即使运行在相同的晶振频率下AI8051U的处理能力也相当于传统8051的12倍。然而AI8051U远不止“提速”这么简单。它内置了丰富的外设多个定时器/计数器、串行口、高速ADC、PWM、SPI、I2C以及大容量的片内Flash和XRAM。更强大的是它支持两种指令集模式8BIT模式本文所用完全兼容传统8051指令集使用经典的C51编译工具链即可开发。所有原有的8051代码几乎可以无缝迁移但又能享受芯片本身的高速和丰富资源。32BIT模式部分型号支持切换到32位总线模式能更高效地访问片内大容量存储器通常需要特定编译器的支持。在本项目中我们选择8BIT模式并使用金水32051编译器这非常适合初学者快速上手同时又能深度理解底层硬件。1.2 金水32051编译器简介金水32051编译器是一款专门为新一代8051单片机特别是STC的1T系列优化的C语言编译器。它完整支持C51标准并做了大量改进比如优化代码生成、支持更灵活的指针运算、内嵌汇编接口更加自然。在我们的项目框架中你会看到这样的代码#pragma asm // 汇编代码 #pragma endasm这就是金水编译器以及某些扩展C51编译器支持的内嵌汇编语法。它允许我们在C函数内部直接书写8051汇编指令享用C的结构化优势同时又保留了对硬件的精准控制。后面我们将详细剖析那个精妙的软件延时函数它正是得益于这种C与汇编的完美融合。1.3 项目框架的意义很多初学者一上来就只盯着main()函数里的几行点灯代码而忽略了大段大段的初始化设置。实际上一个严谨的嵌入式项目框架包含硬件相关的头文件寄存器地址定义标准输入输出库方便调试系统时钟与波特率定义底层硬件初始化CPU、端口、串口用户应用程序LOGO、主循环任务这套框架的意义在于“规范”。当你把AI8051U所有I/O口都配置成准双向口当系统频率明确定义为33.1776MHz当波特率计算公示清晰呈现时整个程序的行为就是可预期的、可维护的。无论是小到一个LED闪烁还是将来扩展成一个复杂的智能家居控制器这个框架都能成为你的基石。第二部分 头文件通往硬件世界的钥匙让我们翻开源程序最前面几行#include ..\C351_XSFR_AI8051U_8BIT.h #include ..\C351_STDIO.h #include ..\C351_PRTSCN.h三个头文件各司其职。2.1 单片机XFR定义头文件C351_XSFR_AI8051U_8BIT.h是整座大厦的地基。它定义了AI8051U所有特殊功能寄存器SFR和扩展寄存器XFR的地址。在8051中诸如P0、P1、SCON、TH1等寄存器都映射在固定的地址上例如P0地址为0x80SCON为0x98。这些定义通常类似于sfr P0 0x80; sfr SCON 0x98;而AI8051U拥有大量位于扩展地址空间的寄存器如P_SW2、AUXR、P0M1、WTST等它们通常需要通过特定的使能位才能访问。因此头文件中不仅有这些寄存器的地址声明还有一些位操作的宏定义让我们能用易读的符号去操作寄存器。没有这个头文件我们面对的就是一堆冷冰冰的十六进制数字极易出错。务必确保工程中正确包含了与你单片机型号匹配的这个文件。2.2 标准输入输出库与串口打印C351_STDIO.h和C351_PRTSCN.h提供了标准C库函数的支持尤其是我们马上要用到的printf函数。对于没有操作系统的单片机而言printf的输出重定向到哪里呢答案是串口。金水32051编译器的STDIO库已经做了底层适配当你调用printf字符就会通过串口1发送出去。这意味着我们只要在PC端打开串口调试助手就能实时接收到单片机打印的信息这是软件开发中最为基础的调试手段。需要注意的是在使用printf之前必须初始化串口并设置好波特率否则单片机并不知道该以何种速率发送数据。后面我们将看到串口初始化函数JSx51_Uart_Init()是如何完成的。2.3 宏定义的魅力#define构建系统常量在头文件之后程序定义了几个宏#define Fosc_KHZ_A51 33177 // 主频 33.1776MHz #define MAIN_Fosc 33177600UL #define Baudrate 115200L #define TM (65536 -(MAIN_Fosc/Baudrate/4))这些宏是整个程序运行参数的集中体现体现了“一处修改处处生效”的良好风格。我们需要深刻理解它们。Fosc_KHZ_A51系统主频以kHz为单位。为什么有个这么奇怪的值因为AI8051U内部可以产生多种频率33.1776MHz是一个常用频率它恰好是11.0592MHz的3倍而11.0592是传统8051串口通信的“神奇频率”。把它保存为整数用于内嵌汇编延时中的立即数赋值。之所以专门设置一个以kHz为单位的主频是为了兼容Keil的A51A51只支持16位的整数。MAIN_Fosc主频以Hz为单位带UL后缀表示无符号长整型确保在计算时不发生溢出。Baudrate目标波特率115200bps这是现代串口通信最常用的高速率。TM这是一个精密的计算宏用于产生波特率所需定时器重载值。后面在串口初始化部分我们将仔细推导这个公式。宏定义的幂等性、无类型特性可能带来隐患但在这里清晰、简洁地完成了系统参数设定是值得初学者模仿的优秀实践。第三部分 精确软件延时C与汇编的完美协奏如果只能挑选一段代码作为本框架的精华那毫无疑问是JSx51_Delay_MS函数。它用短短二十多行内嵌汇编实现了一个精确的毫秒级软件延时不占用任何定时器硬件资源。3.1 为什么需要软件延时在点亮LED或跑马灯这类应用中我们需要让亮、灭的状态保持一段时间人眼才能分辨。延时有两种方案硬件定时器延时精确但会占用宝贵的定时器资源。软件延时通过执行一堆“无用”指令消耗时间简单不占外设。对于入门学习和非实时任务软件延时足够了。而精确度则是区分“随便写的循环”和“工程级别代码”的分水岭。许多初学者会这样写void delay(unsigned int x) { while(x--) ; }这种C语言延时与系统主频、编译器优化等级高度相关换一个环境时间就变了简直是一场灾难。而我们的框架则给出了教科书般的解决方案。3.2 函数原型与参数传递void JSx51_Delay_MS(unsigned int MS)在8051的C编译器调用约定中当传入一个unsigned int参数时通常通过寄存器组传递。以金水32051编译器为例第一个参数放在R6、R7中R7低字节R6高字节。因此在汇编体内可以认为MS的计数值已经安静地躺在R6:R7里了。3.3 逐行解析汇编艺术我们将汇编代码一步步拆开请保持耐心当你完全看懂它时你对8051汇编指令的掌握将上一个大台阶。MOV A, R7 JZ JSx51_Delay_MS_01 INC R6 JSx51_Delay_MS_01:这一段处理的是毫秒数边界情况。如果R7为0意味着低8位为0那么高8位需要减1吗实际上这是为后面的DJNZ双重循环构建准确的16位计数值。DJNZ指令是先减1再判零如果R7原本为0递减后变为0xFF并不为0就会循环。这么一转换确保了(R6,R7)这个16位组合能正确地控制循环次数。MOV R2, #HIGH (Fosc_KHZ_A51 / 10) ; 取高8位 MOV R3, #LOW (Fosc_KHZ_A51 / 10) ; 取低8位这里出现了核心常数Fosc_KHZ_A51/10 33177 / 10 3317。这是什么意思呢我们想产生1毫秒的延时可以做一个空循环这个空循环的次数就等于Fosc_KHZ_A51除以循环一次需要的指令周期数对于1T单片机这个周期数等于10这样就先构建了一个1毫秒的基本延时。R2:R3保存的数值3317就是这个1ms延时的循环基数。JSx51_Delay_MS_02: MOV A, R2 MOV R4, A MOV A, R3 MOV R5, A ; ----------- 内层16位循环 ----------- JSx51_Delay_MS_03: MOV A, R5 DEC R5 JNZ JSx51_Delay_MS_04 DEC R4 JSx51_Delay_MS_04: DEC A ORL A, R4 JNZ JSx51_Delay_MS_03 ; ------------------------------------ DJNZ R7, JSx51_Delay_MS_02 DJNZ R6, JSx51_Delay_MS_02内层循环的本质是一个16位减法计数器从(R4,R5)递减到0。每递减一步需要执行若干条指令。当(R4,R5)减到0时内层循环结束然后外层DJNZ使(R6,R7)减1并判断是否继续。这样总循环次数 R6:R7次 × 内层循环所需的时间而内层循环所需时间正是10T。在AI8051U的8BIT模式下大部分指令为单周期MOV A,R5、DEC R5为1周期JNZ跳转时3周期不跳转2周期。我们只要精确计算出一轮内层循环消耗的机器周期数10T乘以3317就能得到1ms的精确时间。再将这个基数交给外层进行毫秒级计数。3.4 为什么不用C写延时C语言中for循环延时完全依赖编译器的代码生成策略。开启优化后可能被直接删除不开优化又会插入很多冗余代码。一个精确的软件延时必须牢牢控制每一个机器周期汇编是唯一的选择。金水32051编译器支持#pragma asm让我们无需单独创建汇编文件在C函数内部即可“全速”手动操控是嵌入式开发者的利器。第四部分 串口通信搭起与PC对话的桥梁JSx51_Uart_Init函数虽然不到十行却蕴含了通用异步收发传输器UART配置的全部关键要素。4.1 串口模式选择SCON (SCON 0x3f) | 0x40;SCON是串行口控制寄存器。这一行将SM0和SM1位设置为01即模式18位UART波特率可变由定时器1提供。同时保留SM2、REN、TB8、RB8等其他位的原有状态通过 0x3f清除高2位后再置位0x40。这种“读-修改-写”操作是单片机编程的典范避免误改无关位。4.2 定时器1作为波特率发生器8051的串口模式1波特率来源于定时器1的溢出率。AI8051U支持将定时器1配置为1T模式每个时钟周期计数一次而非传统的12分频以提高波特率精度。AUXR | (16); // T1x121定时器1时钟1T模式 AUXR ~S1BRT; // 清除S1BRT位选择定时器1作为串口1波特率发生器AUXR是辅助寄存器。位6为T1x12写1后定时器1的时钟就等于系统主频不再进行12分频。位3为S1BRT写0表示用定时器1写1则使用独立波特率发生器AI8051U新增。这里清0选择了经典的定时器1方案。4.3 波特率重载值计算TL1 TM; TH1 TM8; TR1 1;TM我们前面已经定义过#define TM (65536 - (MAIN_Fosc / Baudrate / 4))为什么这个公式成立在定时器1的1T模式、串口模式1、SMOD0默认不倍频的情况下波特率与定时器溢出率的关系为BaudrateSYSCLK/(4x(65536−TH1:TL1))将Baudrate115200SYSCLK33177600代入65536−RL33177600/(4×115200)72RL65536−7265464654640xFFB8宏TM计算的就是这个值。这正是主频33.1776MHz能够精准产生115200波特率的原因——72是个整数没有误差倘若使用12MHz晶振无论如何也得不到115200的零误差帧错误率会非常高。这个例子完美诠释了为什么通信系统中要选用“神奇频率”。最后TR1 1启动定时器1串口立刻开始工作。此后在main中的printf就能够顺利将“金水32051编译器”发送到PC端了。第五部分 系统初始化为奔跑做好所有准备在进入应用逻辑前main()函数开头的一大段寄存器操作称为“系统初始化”。它们的作用就像交响乐开始前的调音缺一不可。5.1 中断总开关EA 0;EA是中断总允许位先将其清零确保在初始化过程中不被任何中断打断。配置完全部硬件、进入主循环前如果有需要可以重新打开EA并开启所需的中断源。用RTOS操作系统的话来说单片机开机的硬件初始化过程是一个不容许被打断的过程是一个临界区关闭总中断是一个最高级别的临界区保护措施。虽然单片机冷启动时EA是处于关闭状态但是由于程序BUG导致的系统重启时比如中断堆栈溢出从地址0x0000执行中断却有可能是开着的所以单片机程序的man()函数开头关闭总中断是笔者以沉痛代价换来的经验。5.2 CPU状态字和扩展寄存器访问PSW 0; P_SW2 | 0x80; // EAXFR1PSW是程序状态字保存了进位、半进位、寄存器组选择等标志。初始化为0意味着使用第0组寄存器组无任何算术标志置位。P_SW2的位7是EAXFR当它为1时允许访问XFR扩展特殊功能寄存器也就是地址超过0x97的寄存器空间。AI8051U的很多高级寄存器包括WTST、CKCON、端口模式控制等都在扩展RAM区域必须先使能EAXFR才能配置。5.3 速度优化寄存器WTST 0; // 程序指令延时等待 CKCON 0; // 提高访问XRAM速度WTST是程序存储器等待时间控制寄存器。在较高频率下CPU可能需要插入等待周期以保证从Flash中稳定取指。赋值为0表示不插入等待这是AI8051U在33.1776MHz下支持的极速模式。CKCON的某些位控制着片内XRAM的访问时序赋0可使访问XRAM的速度最快。这两项设置决定了单片机能否火力全开发挥1T内核的真正实力。5.4 I/O端口模式的统一配置P0M1 0x00; P0M0 0x00; P1M1 0x00; P1M0 0x00; // ... 一直到 P7M1, P7M0AI8051U的每一个I/O口都可以独立配置为四种模式准双向口传统8051模式、推挽输出、仅输入高阻、开漏输出。这些模式由两个寄存器PxM1和PxM0控制00准双向口最强驱动力适中可承受外部强行拉低适合直接驱动LED01推挽输出强上拉和强下拉10高阻输入11开漏输出将所有端口均设为00准双向口意味着完全兼容传统8051的I/O特性可以直接点亮LED、读取按键而无需额外处理。这也是一种“最安全”的默认状态大大降低初学者因端口模式配置错误造成的硬件损伤风险。第六部分 程序LOGO与延时测试——第一次说“Hello”系统初始化完成后通常我们需要一个人机交互的信号证明单片机已经活过来了。这就是“LOGO”的由来。for(i0; i3; i) { P00x00; P10x00; P20x00; JSx51_Delay_MS(50); P00xFF; P10xFF; P20xFF; JSx51_Delay_MS(500); } printf(\r\n\r\n金水32051编译器\r\n);开始时端口P0、P1、P2全部输出低电平0x00如果这些端口接了LED低电平驱动发光则会全亮。延时50ms后全部输出高电平0xFF灯灭并延时500ms。如此反复3次后产生三下快速的“闪烁”效果类似于开机自检灯。随后printf输出字符串通过串口调试助手可以看到“金水32051编译器”几个字自带两个换行保证清晰。这便是嵌入式的“Hello, World!”同时测试了串口发送功能。注释中保留了一段被注释掉的for(;;)循环// for(;;) { // 软件延时测试 // ... // }这是用户调试时可能用到的通过不断全亮、全灭来观察软件延时的计时是否稳定用示波器或肉眼大致验证软件延时的准确性。一旦确认无误便可注释掉进入正式任务。第七部分 后台任务主循环与跑马灯艺术LOGO展示完毕后程序进入一个无限循环Main_Loop。这也是嵌入式软件的经典形态——“初始化 超级循环”。Main_Loop: printf(\r\n跑马灯开始); // 跑马灯实现 ... goto Main_Loop;7.1 跑马灯最经典的入门算法跑马灯也叫流水灯是指一排LED依次点亮形成光点流动的效果。在我们的代码中P0、P1、P2三个端口同时驱动三组LEDm 0x1; for(i0; i8; i) { P0 ~m; P1 ~m; P2 ~m; JSx51_Delay_MS(100); m m * 2; }逻辑非常清晰。变量m的初始值为0x010000 0001然后循环8次每次将m按位取反后送给端口。因为LED低电平点亮所以~m为0xFE1111 1110时最低位LED亮延时100毫秒让人眼观察到状态m左移一位乘以2依次变为0x02、0x04 … 0x80点亮的LED位置依次从低位移动到高位。这样每个端口上就出现了依次点亮的效果。三个端口同步动作可以保证不论初学者使用什么样的开发板只要这3个端口上连有LED就可以看到效果如果硬件上接了LED阵列会非常壮观。7.2 goto的使用争议与合理很多教材会告诉你“避免使用goto”但在极简单的超级循环中goto Main_Loop清晰声明了跳转意向且不会造成深层的嵌套和混乱。也可以用while(1)代替完全等同。选择goto在这里更多是风格偏好本质是形成一个永不退出的循环持续执行跑马灯。初学者可以试着把它换成while(1){...}编译后看看汇编语言是否一致答案是在汇编语言中两者是一模一样。从本质上来说所有的CPU指令集只有与C语言“goto”对应的“JMP”跳转指令并且裸机程序的中断过程和RTOS的任务切换的本质也是“goto”对应的“JMP”跳转过程因此学好单片机编程必须学好“goto”语句。7.3 主循环中的打印与实时性你可能注意到循环中还有一句printf。这意味着每进行一次跑马灯循环都会在串口中打印一次“跑马灯开始”。这种操作在开发调试期非常常用但要注意printf在115200波特率下发送一串汉字需要较长时间大约几毫秒会轻微影响流水灯的延时精度。在实际产品中我们通常会将调试输出关闭或减少以确保控制时序稳定。这里展示的正好是调试与功能并存的典型形态。总结从最初的茫然看着数个#include到现在能够头头是道地解析每个寄存器的配置你已经完成了一次深刻的8051单片机学习旅程。我们回顾一下本文的核心收获AI8051U是一款高速1T 8051单片机在8BIT模式下完全兼容传统指令集同时拥有可调的多级速度优化寄存器金水32051编译器提供了优秀的内嵌汇编接口、完善的STDIO库让开发效率大幅提升头文件是硬件的抽象它把枯燥的地址变成有意义的符号是工程可维护性的基石精确软件延时函数演示了内嵌汇编的强大通过精心设计的指令序列实现了与硬件定时器媲美的精度这是算法与底层功底的结合串口初始化过程清晰地示范了如何通过定时器1和AUXR寄存器产生零误差的115200波特率频率选择背后的数学逻辑令人着迷系统初始化顺序关中断→配置CPU→端口模式→外设启动体现了嵌入式开发的严谨性LOGO与跑马灯则让冰冷的硬件发出了光芒同时也在主循环结构中留下了灵活的扩展接口。或许你会觉得33.1776MHz、EAXFR、1T模式这些名词还有些生涩但只要亲手编译、下载看着LED规律地闪烁听着串口助手“叮咚”弹出调试信息理论便能瞬间转化为会心一笑。这正是嵌入式开发最大的魅力。课后练习理论需要实践的浇灌请在开发板上完成以下任务巩固所学知识。练习一修改延时时间观察流水灯速度变化找到JSx51_Delay_MS(100)将100改为50、200、500编译下载。观察跑马灯流动速度的变化并能简单解释为什么延时常数直接影响视觉效果。练习二改变流水灯花样修改for循环内的逻辑实现从高位向低位流水即反向跑马灯。提示可以将m初始值设为0x80然后每次m m / 2或使用移位运算符。尝试实现一个来回往复的“霹雳灯”效果。练习三理解汇编延时计算一个内层循环的精确时间查阅AI8051U数据手册或编译器文档确认在WTST0,CKCON0时各指令的执行周期数1T。手工计算JSx51_Delay_MS_03循环每执行一次消耗的机器周期数并推导3317这个常数是如何对应到1ms的。写出计算公式和步骤。练习四切换到独立波特率发生器AI8051U还有一个独立的波特率发生器BRT它在某些场合比定时器1更灵活。修改JSx51_Uart_Init使用BRT产生115200波特率可查阅芯片手册的公式。要求串口打印功能依然正常并验证效果。练习五新增按键控制跑马灯方向电路上增加一个按键如P3.2在Main_Loop中检测按键状态。当按键按下时跑马灯反向流动未按下时正向流动。需要考虑按键消抖可再次调用延时函数。这考察你对端口输入、条件判断与主循环结合的能力。练习六代码模块化将JSx51_Delay_MS、JSx51_Uart_Init以及端口初始化等分别移入独立的.c文件和.h文件形成一个最小化的驱动库。在主程序中通过extern声明或include头文件来调用。完成后体会模块化工程与单文件巨大差异。完成以上练习后你不仅掌握了如何在AI8051U上写出规范的C程序还具备了独立查阅芯片手册、推导时序公式和构建自己代码库的工程素养。记住从点亮第一个LED开始你的每一个debug、每一次寄存器配置都在为更大的嵌入式梦想铺路。愿你在此刻的跑马灯光芒中照亮通往复杂系统的坦途。附件C语言程序全文/* ---------------------------------------------金水32051编译器项目框架 主程序单片机 AI8051U-8BIT系统运行主频 33.1776MHz作者 杨为民 2026-05QQ 86547995--------------------------------------------- */// 单片机 类型模式 定义 // ---- 单片机XSFR定义 ----------------#include ..\C351_XSFR_AI8051U_8BIT.h// ---- 标准IO库 ------------------------#include ..\C351_STDIO.h#include ..\C351_PRTSCN.h// **** 系统运行设置 *******************// 系统运行参数 #define Fosc_KHZ_A51 33177 // 主频 33.1776MHz#define MAIN_Fosc 33177600UL#define Baudrate 115200L#define TM (65536 -(MAIN_Fosc/Baudrate/4))// 软件延时 毫秒 void JSx51_Delay_MS(unsigned int MS){#pragma asm// ---- R6R7 参数 ----------------MOV A, R7;JZ JSx51_Delay_MS_01;INC R6;JSx51_Delay_MS_01:MOV R2, # HIGH (Fosc_KHZ_A51 / 10); // STC8051U_8BitMOV R3, # LOW (Fosc_KHZ_A51 / 10); // STC8051U_8BitJSx51_Delay_MS_02:MOV A, R2;MOV R4, A ;MOV A, R3;MOV R5, A ;JSx51_Delay_MS_03:MOV A, R5 ;DEC R5 ;JNZ JSx51_Delay_MS_04 ;DEC R4 ;JSx51_Delay_MS_04:DEC A ;ORL A, R4 ;JNZ JSx51_Delay_MS_03 ;DJNZ R7, JSx51_Delay_MS_02 ;DJNZ R6, JSx51_Delay_MS_02 ;#pragma endasm}// 串口1 设置 void JSx51_Uart_Init(void){SCON (SCON 0x3f) | 0x40;AUXR | (16); //定时器时钟1T模式AUXR ~S1BRT; //串口1选择定时器1为波特率发生器TL1 TM;TH1 TM8;TR1 1; //定时器1开始计时}// **** C351 主函数 单片机入口 ****************void main(void){int i, m;// 单片机 CPU 初始化 // ---- 明显关闭中断 开始设置 -----------EA0;// ---- CPU 设置 -----------PSW0;P_SW2 | 0x80; // EAXFR1 扩展寄存器(XFR)访问使能WTST 0; //设置程序指令延时参数赋值为0可将CPU执行指令的速度设置为最快CKCON 0; //提高访问XRAM速度// 单片机系统 硬件设置 P0M1 0x00; P0M0 0x00; //设置为准双向口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; //设置为准双向口JSx51_Uart_Init();// LOGO // for(;;) { // 软件延时测试for(i0;i3;i) { // LOGOP00x00;P10x00;P20x00;JSx51_Delay_MS(50);P00xFF;P10xFF;P20xFF;JSx51_Delay_MS(500);}printf(\r\n\r\n金水32051编译器\r\n);// }// 任务主循环 Main_Loop:printf(\r\n跑马灯开始);// ---- 跑马灯 -----------------------m0x1;for(i0;i8;i) {P0~m;P1~m;P2~m;JSx51_Delay_MS(100);mm*2;}// ---- 主循环 结束 -----------------goto Main_Loop;}