我就是为了用最新的STC32G144K246,先用A8051U来熟悉一下STC的开发流程
{:4_250:}{:4_250:} 【STC单片机入门实战】Day 2:解放双手!实现USB不停电下载(第4集学习笔记)记录从STM32转战STC单片机第2天,解决最烦人的手动下载问题
📅 整体学习路线回顾根据课程目录,我的完整学习路径如下:
第一阶段:基础入门(第2-5集)
[*]✅ Day 1:硬件介绍 + 点亮LED(第2-3集)
[*]🔄 Day 2:USB不停电下载(第4集) ← 今天学习
[*]Day 3:C语言基础(第5集)
第二阶段:外设入门(第6-9集)
[*]Day 4:I/O输入输出(第6集)
[*]Day 5:定时器中断(第7集)
[*]Day 6:定时器调度任务(第8集)
[*]Day 7:数码管显示(第9集)
第三阶段:进阶应用(第10-15集)
[*]虚拟键盘、矩阵按键、复位系统、外部中断、IO中断、定时器计数器等
第四阶段:高级功能(第16-23集)
[*]DS18B20、串口通信、ADC、Flash、比较器、PCA等
今日学习重点:USB不停电下载昨天点亮LED虽然成功了,但每次下载程序都要手动按P3.2按钮的操作,引起了我的注意。这确实是个效率瓶颈!今天学习的第4集内容就是解决这个问题——实现USB不停电下载,彻底解放双手。
从STM32到STC的下载体验对比思考:在STM32开发中,我习惯了ST-Link的一键下载调试。STC这种需要手动断电的下载方式,在初期调试频繁时会严重影响效率。这也是我从STM32转来时最需要适应的点之一。
对比维度STM32STC (传统方式)STC (USB不停电)
下载方式SWD/JTAG调试器串口+手动断电按钮直接USB连接
硬件需求ST-Link/V2等调试器USB转串口工具仅USB线
操作步骤编译→一键下载编译→手动断电→按按钮→下载编译→自动下载
开发效率⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
学习成本中等低,但操作繁琐中等
核心原理解析传统下载的问题所在STC单片机默认是通过系统ISP监控程序实现下载的,这个监控程序在芯片上电时会先运行,检测是否有下载命令。但用户程序运行后,监控程序就被“覆盖”了,要重新进入下载模式就必须:
[*]断电复位
[*]按住P3.2强制进入ISP模式
[*]重新上电
USB不停电下载的聪明解法STC的方案是在用户程序中嵌入一个“后门”:
[*]USB虚拟串口(CDC):程序运行后,USB接口模拟成一个串口设备
[*]特殊握手协议:当STC-ISP软件发送特定命令字符串@STCISP#时
[*]软件复位:程序识别到命令后,自动软复位并跳转到ISP监控程序
[*]无缝切换:实现从用户程序到下载模式的平滑过渡
📋 实现步骤详解第一步:获取必要的库文件从STC官网(深圳国芯人工智能有限公司-库函数)下载USB库文件,关键文件包括:
[*]stc_usb_cdc_32.LIB→ USB通信库(二进制,保护源码)
[*]stc32_stc8_usb.h→ 头文件(接口声明)
注意:STC提供8位和32位两种库,AI8051U是32位内核,要选择对应的32位库文件。
第二步:工程配置与移植1. 库文件添加
// 工程结构变化:
Demo.Uvproj
├── main.c // 用户主程序
├── AI8051U.H // 芯片头文件
└── stc_usb_cdc_32.LIB// ← 新增的USB库
2. 关键代码分析
/*------------------------------------------------------------------
* 文件名称:STC32G_USB_CDC_Demo.c
* 功能描述:STC32G系列单片机USB CDC虚拟串口通信示例程序
* 演示如何通过USB接口实现单片机与电脑间的数据通信
* 硬件平台:STC系列开发板(屠龙刀、擎天柱等)
* 开发环境:Keil C51
* 作者:根据STC32G视频教程整理
* 日期:2026年1月6日
*------------------------------------------------------------------*/
/*--- 头文件包含区域 ---*/
#include "stc32g.h" // STC32G系列单片机专用头文件,包含所有寄存器定义
#include "stc32_stc8_usb.h" // STC官方USB库头文件,提供CDC/HID功能支持
#include "math.h" // 数学函数库,本例中虽未直接使用,但为后续功能扩展预留
#include "stdio.h" // 标准输入输出库,支持printf_usb格式化输出功能
/*--- 主函数:程序入口点 ---*/
void main()
{
/*--- 系统初始化部分 ---*/
/* 关键步骤1:使能扩展寄存器访问权限
* P_SW2是特殊功能寄存器,其最高位(bit7)控制XFR扩展寄存器的访问
* 0x80 = 1000 0000二进制,通过"或等于"操作只设置bit7,不影响其他位
* 这是访问USB相关特殊寄存器的必要步骤(@ref)
*/
P_SW2 |= 0x80;
/*--- GPIO端口模式配置 ---*/
/* 将所有IO口(P0-P7)设置为准双向口模式
* 准双向口是传统51单片机标准模式,兼具输入输出能力
* 每个端口由两个寄存器控制:PxM1和PxM0
* 配置为00:准双向口;01:推挽输出;10:高阻输入;11:开漏输出
* 这里统一配置为准双向口,确保USB通信时端口状态稳定(@ref)
*/
P0M1 = 0x00; P0M0 = 0x00;// 配置P0口
P1M1 = 0x00; P1M0 = 0x00;// 配置P1口
P2M1 = 0x00; P2M0 = 0x00;// 配置P2口
P3M1 = 0x00; P3M0 = 0x00;// 配置P3口(特别注意:P3.0/P3.1与USB D-/D+共用)
P4M1 = 0x00; P4M0 = 0x00;// 配置P4口
P5M1 = 0x00; P5M0 = 0x00;// 配置P5口
P6M1 = 0x00; P6M0 = 0x00;// 配置P6口
P7M1 = 0x00; P7M0 = 0x00;// 配置P7口
/*--- USB模块初始化 ---*/
/* usb_init()函数是STC官方USB库的核心初始化函数
* 该函数会自动配置以下内容:
* 1. 初始化USB时钟源(内部48MHz IRC)
* 2. 配置USB控制寄存器(USBCON, USBCLK等)
* 3. 设置USB端点缓冲区和描述符表
* 4. 将P3.0/P3.1设置为高阻输入模式,避免影响USB D-/D+信号质量
* 此函数调用后,USB硬件模块开始工作,等待电脑枚举识别(@ref)
*/
usb_init();
/*--- 中断系统使能 ---*/
/* EA = 1:开启51单片机全局中断开关
* 类似于STM32中的__enable_irq()功能
* 这是USB中断正常工作的重要前提条件
* 注意:USB中断在stc32_stc8_usb.h中已有默认的中断服务函数
*/
EA = 1;
/*--- 主循环:程序核心逻辑 ---*/
while (1)
{
/* bUsbOutReady是USB库定义的标志变量
* 当电脑通过USB虚拟串口发送数据到单片机时,该标志会自动置1
* 这种查询方式比中断方式更简单可靠,适合初学者使用
*/
if (bUsbOutReady)
{
/* 示例代码:通过printf_usb向电脑发送调试信息
* printf_usb是USB库提供的格式化输出函数,用法与标准printf相同
* OutNumber变量包含本次接收到的数据字节数
* 实际应用中,可以根据接收到的数据内容进行相应处理
*/
// 发送接收到的数据字节数(演示用,实际应用可修改)
// USB_SendData(UsbOutBuffer, OutNumber); // 发送接收数据原样返回(测试用)
/* 使用printf_usb输出格式化调试信息
* 注意:每个printf_usb调用都会作为一个完整的USB数据包发送
* "\n"是换行符,使输出在串口助手中显示更整齐
*/
printf_usb("1. Read Num:%d\n", OutNumber);// 第一次输出
printf_usb("2. Read Num:%d\n", OutNumber);// 第二次输出
printf_usb("3. Read Num:%d\n", OutNumber);// 第三次输出
printf_usb("4. Read Num:%d\n", OutNumber);// 第四次输出
/* 重要:标记当前USB数据包处理完成
* 该函数会清除bUsbOutReady标志,使能接收下一个数据包
* 如果没有调用此函数,USB将无法继续接收新数据
* 这是STC USB库的重要工作机制(@ref)
*/
usb_OUT_done();
}
/* 此处可以添加其他应用代码
* 由于USB通信采用查询方式,需要确保主循环执行时间不会太长
* 如果处理任务较重,建议使用定时器中断或状态机架构
*/
}
}
/*=== 程序使用说明和开发建议 ===*/
/*
* 1. 硬件连接注意事项:
* - 确保USB数据线质量良好,MicroUSB接口连接可靠
* - P3.0/P3.1专用于USB通信,不要作为普通IO使用
* - 电脑端需要安装STC USB驱动(STC-ISP软件自带)
*
* 2. 编译环境配置:
* - 在Keil项目中需要添加STC官方提供的USB库文件(.LIB)
* - 设置正确的头文件包含路径
* - 根据具体芯片型号选择正确的内存模式
第三步:STC-ISP软件配置
必须勾选的三个选项:
[*]✅ 使用默认内部自定义命令 → 对应@STCISP#
[*]✅ 下次使用ID接口进行ISP下载 → 实现后续下载
[*]✅ 每次下载前都先发送自定义命令 → 自动化发送密钥
工作流程:Keil编译生成HEX → STC-ISP检测到文件变化 → 自动发送@STCISP#命令 → 单片机收到后软复位进入下载模式 → 自动开始编程
有趣的知识点笔记1. 查询模式 vs 中断模式教程推荐使用查询模式,原因很实际:
[*]查询模式:在主循环中定期检查USB状态
[*]中断模式:USB事件触发中断立即响应
[*]选择原因:查询模式代码更简单,不易因中断嵌套产生问题,适合初学者
2. 或等于(|=)操作的重要性P_SW2 |= 0x80;// 正确:只改第7位,不影响其他位
P_SW2 = 0x80; // 危险:清零了所有其他位!重要:寄存器操作中,|=是“置位”操作,=是“赋值”操作。STC很多寄存器有保留位或默认配置,错误使用=可能导致系统异常。
总结与期待今天虽然只是理论学习,但USB不停电下载这个功能让我对STC单片机的设计理念有了新认识:
[*]用户思维:从实际开发痛点出发设计功能
[*]渐进式:先解决“有没有”,再考虑“好不好”
[*]生态思维:软硬件工具链的协同设计
最期待的时刻:收到“擎天柱最小系统板”后,我要验证的第一件事就是这个USB不停电下载。从手动到自动的体验提升,是开发效率的关键一步!
作为有STM32背景的学习者,我越来越理解:不同的芯片有不同的哲学。STM32追求强大和通用,STC追求实用和经济。在资源受限的环境中寻找优雅的解决方案,这是一种不同的技术乐趣。
【STC单片机入门实战】Day 3:夯实C语言基础,为32位8051编程铺路(第5集学习笔记)一、核心知识点回顾1. USB-CDC串口printf函数实现
[*]功能:将printf函数重定向到USB-CDC串口,用于调试输出
[*]实现方式:通过宏定义将printf重定向到gap_printf_HID函数
[*]使用方法:在USB库中打开printf的HID宏定义(去掉反斜杠注释)
2. 数的进制转换
[*]二进制:由0和1组成,计算机内部存储格式
[*]十进制:日常使用的计数方式
[*]十六进制:编程中常用,以0x开头表示
[*]转换方法:使用程序员计算器或按位权展开计算
3. 变量的基本类型
[*]8位无符号整数:unsigned char,范围0-255
[*]8位有符号整数:signed char,范围-128~127
[*]16位无符号整数:unsigned int,范围0-65535
[*]16位有符号整数:signed int,范围-32768~32767
4. C语言常用运算符
[*]算术运算符:+、-、*、/、%(取余)、++、--
[*]关系运算符:>、<、>=、<=、==、!=
[*]逻辑运算符:&&(与)、||(或)、!(非)
[*]位运算符:&(与)、|(或)、^(异或)、~(取反)、<<(左移)、>>(右移)
[*]赋值运算符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=
二、工程代码分析1. 工程文件结构05.C语言基础/
├── ai8051u.h // 8051U芯片头文件
├── demo.uvproj // Keil工程文件
├── main.c // 主程序文件
├── stc32_stc8_usb.h // STC USB库头文件
├── stc_usb_cdc_32.LIB// USB CDC库文件
├── sha256.h // SHA-256头文件
└── sha256.c // SHA-256实现文件2. 主程序代码分析2.1 宏定义部分#define u8unsigned char // 8位无符号整数
#define u16 unsigned int // 16位无符号整数
[*]作用:简化变量定义,提高代码可读性
[*]使用:后续定义变量时可直接使用u8代替unsigned char
2.2 全局变量定义u8 X = 200;
u8 Y = 10;
[*]类型:u8(8位无符号整数)
[*]范围:0-255,X=200和Y=10均在此范围内
2.3 主函数结构void main(void)
{
// 初始化代码
// USB初始化
// 主循环
}2.4 USB初始化与配置usb_init(); // USB CDC 接口初始化
IE2 |= 0x80; // 使能USB中断
EA = 1; // 全局中断使能
while (DeviceState != DEVSTATE_CONFIGURED); // 等待USB配置完成
[*]usb_init():初始化USB CDC功能
[*]IE2 |= 0x80:启用USB中断
[*]EA = 1:打开全局中断开关
[*]等待USB配置完成:确保USB设备正常工作后再进入主循环
2.5 主循环与USB数据处理while(1)
{
if (bUsbOutReady) // 检测到接收到数据
{
if( X && Y ) // 如果X和Y都为真
{
printf("条件为真\r\n");
}
usb_OUT_done(); // 清除接收标志
}
}
[*]bUsbOutReady:USB接收就绪标志
[*]printf():重定向到USB-CDC的打印函数
[*]usb_OUT_done():通知USB模块数据已处理完成
三、关键函数解析1. printf函数
[*]原型:#define printf gap_printf_HID
[*]功能:将格式化字符串输出到USB-CDC串口
[*]使用示例:printf("X / Y = %u \r\n",(u16)(X/Y));
printf("X %% Y = %u \r\n",(u16)(X%Y));
[*]格式化说明符:
[*]%d:十进制有符号整数
[*]%u:十进制无符号整数
[*]%s:字符串
[*]%%:输出百分号
2. usb_init()
[*]功能:初始化USB CDC功能
[*]调用时机:主函数开始,系统初始化阶段
[*]返回值:无
3. usb_OUT_done()
[*]功能:通知USB模块数据已处理完成
[*]调用时机:处理完USB接收数据后
[*]返回值:无
四、编程技巧总结1. 宏定义简化类型声明
[*]技巧:使用#define为常用数据类型创建别名
[*]好处:提高代码可读性,方便统一修改类型
[*]示例:#define u8unsigned char
#define u16 unsigned int
2. 变量类型选择
[*]原则:根据变量实际取值范围选择合适类型
[*]注意事项:避免溢出,例如8位变量最大只能存储255
[*]示例:// 错误示例:200*10=2000,超过u8范围
u8 X=200,Y=10,Z;
Z=X*Y; // 结果错误,溢出
// 正确示例:使用u16避免溢出
u16 Z;
Z=(u16)X*Y; // 结果正确
3. printf函数使用
[*]换行符:使用\r\n确保跨平台兼容性
[*]百分号输出:使用%%输出单个百分号
[*]类型转换:输出前进行类型转换,避免格式不匹配
4. 条件判断
[*]逻辑:0为假,非0为真
[*]示例:if( X && Y ) // X和Y都非0时为真
{
// 执行代码
}
5. 运算符优先级
[*]算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符
[*]注意事项:不确定优先级时使用括号
五、学习心得1. C语言基础的重要性
[*]本节课内容是STC 32位8051编程的基础
[*]掌握C语言核心概念是后续学习单片机外设、通信协议等内容的前提
[*]良好的C语言基础能提高代码质量和调试效率
2. 实践出真知
[*]通过USB-CDC的printf功能,可以方便地进行代码调试
[*]实际编写代码并观察运行结果,有助于加深对知识点的理解
[*]遇到问题时,结合理论知识和实际现象分析,提高解决问题的能力
3. 编程规范的养成
[*]宏定义、变量命名、代码缩进等良好习惯,有助于提高代码可读性和可维护性
[*]注释的合理使用,便于后续代码修改和他人阅读
4. 重点关注内容
[*]变量类型与溢出问题:在单片机编程中尤为重要
[*]printf函数的灵活运用:调试的重要工具
[*]运算符的正确使用:特别是位运算和逻辑运算
六、课后练习
[*]尝试修改main.c中的变量X和Y的值,观察printf输出结果
[*]实现不同进制数的转换与输出
[*]练习使用不同的运算符,观察运算结果
[*]编写代码实现简单的数学运算,如加减乘除
[*]尝试使用位运算实现某些功能,如位清零、位置1等
七、SHA256算法实现7.1 为什么要学习SHA256?之前的课程中,我们学习了C语言的基础知识,包括变量类型、运算符、函数等。现在,我将这些知识应用到实际的算法实现中——SHA256哈希算法。哈希函数在单片机领域有很多应用:
[*]数据完整性验证:确保数据在传输或存储过程中没有被篡改
[*]密码存储:安全地存储用户密码(虽然实际应用中还需要加盐)
[*]数字签名:验证数据的来源和完整性
[*]唯一标识符:为数据生成唯一的标识符
学习SHA256算法,不仅可以巩固之前的C语言基础,还能深入理解密码学的核心概念。7.2 SHA256算法原理SHA-256(Secure Hash Algorithm 256-bit)是美国国家安全局(NSA)设计的一种密码散列函数,属于SHA-2家族,符合FIPS 180-4标准。它将任意长度的输入数据转换为256位(32字节)的固定长度输出,称为消息摘要。核心步骤(结合之前的C语言知识):
[*]数据填充:
[*]首先添加一个1位(对应二进制的10000000,即十六进制0x80)
[*]然后添加若干个0位,使数据长度模512等于448位
[*]最后添加64位的原始数据长度(使用大端格式,之前学过的字节序概念)
[*]初始化哈希值:
[*]使用8个32位的初始哈希值(H0-H7)
[*]这些值来自于前8个素数(2, 3, 5, 7, 11, 13, 17, 19)的平方根的小数部分的前32位
[*]对应C语言中的uint32_t state数组
[*]处理512位数据块:
[*]将每个512位数据块转换为16个32位字(W-W)—— 使用位运算和类型转换
[*]通过扩展函数生成额外的48个32位字(W-W)—— 使用循环和位运算
[*]执行64轮哈希计算,每轮使用不同的常量K和消息字W—— 大量使用位运算和循环
[*]更新哈希值状态—— 数组操作
[*]生成最终哈希值:
[*]将8个32位哈希值连接起来,形成256位的最终哈希值
[*]转换为大端格式,方便存储和传输
7.3 与之前课程知识点的联系
之前课程知识点在SHA256中的应用
变量类型使用uint8_t、uint32_t、uint64_t等固定宽度类型,确保跨平台兼容性
位运算大量使用循环右移、异或、与、或等位运算,是SHA256的核心
函数设计将复杂算法拆分为init、update、final等简单函数,符合模块化设计原则
数组操作使用数组存储哈希状态、消息调度数组等
循环结构使用for循环处理数据块、执行64轮哈希计算
指针操作使用指针处理输入数据,提高效率
条件判断处理缓冲区状态、填充规则等
十六进制转换将二进制哈希值转换为可读的十六进制字符串
8.4 实现思路8.4.1 结构体设计typedef struct {
uint32_t state; // 当前哈希值状态 (H0-H7)
uint64_t bit_count; // 输入数据的总位数 (模 2^64)
uint8_t buffer; // 数据缓冲区,用于存储不足512位的数据块
} sha256_context;
[*]state:存储当前的8个哈希值
[*]bit_count:记录输入数据的总位数,确保正确处理大文件
[*]buffer:临时存储不足512位的数据块,当积累到512位时进行处理
7.4.2 核心函数设计(我的实现思路)
[*]sha256_init():初始化SHA-256上下文,设置初始哈希值
[*]学习笔记:使用宏定义的初始哈希值,确保每次初始化都一样
[*]sha256_update():处理输入数据,将数据填充到缓冲区
[*]学习笔记:这个函数可以多次调用,适合处理大文件
[*]之前学过缓冲区的概念,这里就是缓冲区的实际应用
[*]sha256_compress():核心压缩函数,处理512位数据块
[*]学习笔记:这是SHA-256最复杂的部分,包含64轮计算
[*]大量使用之前学过的位运算
[*]sha256_final():完成哈希计算,生成最终哈希值
[*]学习笔记:处理剩余数据,添加填充位和长度信息
[*]sha256():一次性计算哈希值的便捷函数
[*]学习笔记:内部调用init、update、final,简化使用
[*]sha256_to_hex():将哈希值转换为十六进制字符串
[*]学习笔记:使用之前学过的进制转换知识
7.5 关键代码解析(我的学习心得)7.5.1 初始哈希值#define SHA256_INITIAL_STATE {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
}
[*]学习笔记:这些值是SHA-256标准规定的,来自于前8个素数的平方根
[*]之前学过宏定义,这里用宏来定义初始哈希值,方便使用
7.5.2 K常量static const uint32_t K = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
// ... 共64个常量
};
[*]学习笔记:这些常量来自于前64个素数的立方根
[*]使用static const修饰,确保它们在编译时就被确定,提高运行效率
7.5.3 压缩函数核心for (i = 0; i < 64; i++) {
uint32_t S1 = right_rotate(e, 6) ^ right_rotate(e, 11) ^ right_rotate(e, 25);
uint32_t ch = (e & f) ^ ((~e) & g);
uint32_t temp1 = h + S1 + ch + K + W;
uint32_t S0 = right_rotate(a, 2) ^ right_rotate(a, 13) ^ right_rotate(a, 22);
uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
uint32_t temp2 = S0 + maj;
/* 更新工作变量 */
h = g;
g = f;
f = e;
e = d + temp1;
d = c;
c = b;
b = a;
a = temp1 + temp2;
}
[*]学习笔记:这是SHA-256最核心的部分,每轮计算都使用位运算
[*]之前学过的位运算(右移、异或、与、或)在这里得到了充分应用
[*]每轮更新8个工作变量,形成一个迭代过程
7.6 遇到的问题及解决方案(我的调试经历)
[*]问题:在sha256_update()函数中,变量j未声明解决方案:在使用前添加size_t j;声明学习心得:编译时要仔细检查警告信息,变量一定要先声明再使用
[*]问题:缓冲区处理逻辑复杂,容易出错解决方案:分步骤处理:
[*]首先检查缓冲区是否有足够空间
[*]然后填充缓冲区并处理完整块
[*]最后处理剩余的完整块学习心得:复杂逻辑要拆分成简单步骤,提高代码可读性
[*]问题:字节序处理(大端/小端)解决方案:明确使用大端格式处理多字节数据学习心得:跨平台编程时要注意字节序问题,明确使用哪种格式
7.7 测试用例设计(算法验证)使用FIPS 180-4标准中定义的测试向量验证实现的正确性:
[*]测试向量1:空字符串
[*]输入:""
[*]预期输出:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
[*]测试向量2:"abc"
[*]输入:"abc"
[*]预期输出:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
[*]测试向量3:"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
[*]输入:"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
[*]预期输出:248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1
7.8 代码集成与使用(如何在项目中使用)7.8.1 头文件包含#include "sha256.h"7.8.2 使用示例uint8_t hash;
char hex_hash;
// 计算SHA-256哈希值
sha256((uint8_t*)"abc", 3, hash);
// 转换为十六进制字符串
sha256_to_hex(hash, hex_hash);
// 输出结果
printf("SHA-256: %s\r\n", hex_hash);
从开箱到点亮:Ai8051U擎天柱核心板USB下载程序全记录大家好!最近我收到了期待已久的Ai8051U擎天柱核心板,心情超级激动!这款板子基于STC的32位8051单片机,功能强大,特别适合学习和项目开发。开箱后,我迫不及待地插上USB线,想试试板子自带的流水灯程序。但没想到,第一步就遇到了小挫折:插上USB后,STC-ISP软件居然没有检测到串口!板子上的LED灯倒是亮着,说明程序在跑,但怎么才能下载自己的程序呢?于是,我静下心来,翻看了随板附带的《Ai8051U系列技术手册》,重点学习了第2.2章节“安装AiCube-ISP下载/编程/烧录工具,含强大的辅助开发工具”。今天,我就把这次学习经历整理成博客,分享给大家,希望能帮到同样刚入手的朋友们{:4_213:}{:4_213:}{:4_213:}{:4_213:}{:4_213:}
一、开箱初体验:默认流水灯程序与问题发现收到Ai8051U擎天柱核心板时,包装简洁,板子设计精致,正面印有“擎天柱”标识。我按照说明,直接用USB-TypeC线连接电脑和板子。
1001
板子通电后,一组LED灯开始循环闪烁,这就是默认的流水灯程序,效果很酷!但当我打开STC-ISP软件(版本v6.96N),准备下载自己的程序时,问题来了:软件界面上的“扫描串口”列表空空如也,没有显示任何COM口或USB设备。板子明明通电了,为什么软件检测不到呢?{:zhemo:}
我一开始以为是驱动问题,但手册提醒我:Ai8051U支持硬件USB下载,不需要安装额外驱动,只要USB连接的鼠标能工作,USB-HID驱动就是好的。那问题出在哪儿?继续读手册,我发现了关键点:USB下载需要正确的上电顺序和硬件操作,不能简单插拔USB代替电源开关。
二、手册解读:学习USB下载的正确姿势手册第2.2章节详细介绍了AiCube-ISP软件的安装和USB下载流程。我总结了几点核心内容:
[*]AiCube-ISP软件:这是STC官方提供的下载/编程工具,集成了各种辅助开发功能,如串口助手、延时计算器等。软件是绿色版,解压就能用,超级方便。
[*]上电工作过程:单片机复位时,默认从系统程序区启动,判断是否要下载用户程序。如果USB的D+和D-信号接触不良,单片机可能直接跳转到用户程序区运行流水灯,导致软件无法检测到下载模式。
[*]USB下载流程图:手册强调,下载时必须用电源开关控制上电,而不是依赖USB插拔,以确保GND、D+、D-、VCC的接触顺序正确。
三、实战操作:一步步解决无串口检测问题按照手册,我重新操作了一遍,终于成功!下面是详细步骤,我在关键环节留了图片位置,方便大家对照。
步骤1:安装AiCube-ISP软件
我先从STC官网(https://www.stcai.com/gjrj)下载了最新版AiCube-ISP压缩包,解压到D盘,创建了桌面快捷方式。软件界面简洁,功能一目了然。
步骤2:硬件连接与上电顺序
手册指出,USB下载有三种方法,我用了最可靠的方法一:P3.2按键结合停电上电:
[*]用USB-TypeA线连接电脑和板子(板子是TypeC口,我用转接头连接)。
[*]按住板子上的P3.2按键(即P3.2接地)。
[*]按下电源按钮(Power_SW)停电,再松开按钮上电——这就是“冷启动”。
[*]这时,电脑端的AiCube-ISP软件自动识别出了“(HID1) USB-Writer”,表示可以下载了!
这次正确操作后,软件立马检测到了设备。
步骤3:下载默认流水灯程序验证
为了测试,我打开了03.点亮第一个LED程序的HEX文件,点击“下载/编程”,几秒后提示成功。板子上的P20和P21引脚的LED重新点亮,证明下载功能正常!
四、总结与心得这次经历让我深刻体会到:硬件操作的小细节决定成败。Ai8051U的USB下载功能很强大,但必须严格按手册步骤来。总结几个要点:
[*]软件安装简单,AiCube-ISP工具包很实用。
[*]下载时一定要用电源开关控制上电,避免USB插拔顺序问题。
[*]如果软件不识别,先检查P3.2按键是否按下,再按下松开POWER按键冷启动。
现在,我可以愉快地折腾我的Ai8051U了!下一步我准备尝试用USB-CDC虚拟串口通信,相信有手册指导,会顺利很多。如果你也遇到了类似问题,希望这篇博客能帮到你。欢迎在评论区交流心得!
好了,这就是我的学习笔记。如果你有Ai8051U板子,不妨跟着试试,祝你玩得开心!{:hecai:}
LilMonsterOvO 发表于 2026-1-7 01:30
【STC单片机入门实战】Day 3:夯实C语言基础,为32位8051编程铺路(第5集学习笔记)一、核心知识点回顾1. U ...
这个程序有问题,过些天更新SHA256的程序 【STC单片机入门实战】I/O输入输出学习笔记(第6集学习笔记)一、GPIO基本概念1. GPIO定义
[*]GPIO(General Purpose Input Output):通用输入输出端口
[*]通俗理解:单片机的引脚,可以输入或输出高低电平
2. 高低电平定义
[*]高电平:接近电源正极电压(VCC),逻辑1
[*]低电平:接近电源负极电压(GND),逻辑0
[*]注意事项:IO口电压有极限范围,3.3V供电时,IO口电压应在0-3.6V之间
3. IO口四种模式
[*]准双向口:既能输入也能输出,灌电流可达20mA,拉电流仅为微安级
[*]推挽输出:输出电流大,可达20mA
[*]高阻输入:用于高阻抗输入场合
[*]开漏模式:需要外部上拉电阻
4. 拉电流与灌电流
[*]拉电流:电流从IO口流出,准双向口模式下仅几百微安
[*]灌电流:电流流入IO口,准双向口模式下可达20mA
5. 输入电平阈值(3.3V供电)
[*]打开施密特触发器时:
[*]低电平最大值:0.99V
[*]高电平最小值:1.09V
[*]施密特触发器默认使能(上电复位后)
二、按键输入检测1. 按键原理
[*]机械按键:按下时导通,松开时断开
[*]实验箱按键连接:P32引脚通过电阻上拉,按下后接地
[*]未按下:引脚为高电平(1)
[*]按下:引脚为低电平(0)
2. 按键抖动问题
[*]现象:机械按键按下和松开时,电平会出现抖动
[*]抖动时间:一般在20ms以内
[*]影响:导致程序误判按键状态
[*]解决方案:延时消抖(本次课程)、定时器消抖(后续课程)
三、代码实现1. 工程结构
[*]主要文件:main.c、ai8051u.h
[*]开发环境:基于8051U/AI8051U单片机
2. 核心代码分析2.1 系统初始化WTST = 0; // 将程序指令执行速度设置为最快
EAXFR = 1;// 允许访问扩展寄存器(XFR)
CKCON = 0;// 提升XRAM访问速度
// 设置所有IO口为推挽输出模式
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;2.2 延时消抖函数void Delay20ms(void)// @24.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
}2.3 任务1:按下P32灯亮,松开灯灭if( P32 == 0 )// 判断P32按键是否按下
{
P20 = 0; // 灯亮
}
else
{
P20 = 1; // 灯灭
}2.4 任务2:按下P32灯灭,松开灯亮if( P32 == 1 )// 判断P32按键是否松开
{
P20 = 0; // 灯亮
}
else
{
P20 = 1; // 灯灭
}2.5 任务3:按一下灯亮,再按一下灯灭(带消抖)if( P32 == 0 )// 检测到按键按下
{
Delay20ms();// 延时20ms消抖
if( P32 == 0 )// 再次确认按键按下
{
state = !state;// 状态取反
P20 = state; // 输出状态到LED
printf("state:%d\r\n",(int)state);// 串口打印状态
while( P32 == 0 );// 等待按键松开
}
}四、关键知识点总结1. IO口配置
[*]通过PnM1和PnM0寄存器配置IO口模式
[*]00:准双向口模式
[*]01:推挽输出模式
[*]10:高阻输入模式
[*]11:开漏模式
2. 按键检测流程
[*]读取IO口电平
[*]延时消抖(20ms)
[*]再次读取确认
[*]执行相应操作
[*]等待按键松开
3. 消抖方法
[*]硬件消抖:RC滤波电路
[*]软件消抖:延时消抖、定时器消抖
[*]本次课程使用:20ms延时消抖
4. while循环的使用
[*]while(1):无限循环,用于主程序
[*]while(条件):等待条件满足,如等待按键松开
五、课后练习题
[*]按一下P32按钮灯亮,再按一下P33按钮灯灭
[*]按一下P32,左边四个灯亮,右边四个灯灭;再按一下,左边四个灭,右边四个亮
[*]按一下亮一颗灯,再按一下亮两颗灯,直到全亮
六、代码优化方向
[*]使用定时器消抖:替代延时函数,提高CPU利用率
[*]动态延时:根据需要调整延时时间
[*]按键扫描:实现多按键检测
[*]中断方式:使用外部中断处理按键,减少CPU占用
七、学习心得
[*]GPIO是单片机与外部世界交互的重要接口
[*]按键抖动是机械按键的固有特性,必须进行消抖处理
[*]准双向口是最常用的IO口模式,适合大多数应用场景
[*]软件消抖简单易实现,但会占用CPU资源
[*]学习单片机编程需要结合硬件原理和软件实现
通过本次课程的学习,我掌握了GPIO的基本概念、按键输入检测的原理和实现方法,以及如何处理按键抖动问题。这些知识是单片机编程的基础,将在后续的学习中得到广泛应用。八、学习练习:流水灯实现8.1 练习目的
[*]巩固GPIO输入输出的基本概念
[*]掌握按键检测和消抖技术
[*]实现复杂的LED控制逻辑
[*]培养模块化编程思维
8.2 硬件需求
[*]AI8051U单片机开发板
[*]P32按键一个
[*]P2口8个LED灯
8.3 功能描述
[*]按键控制:按下P32按键,流水灯开始流动;松开P32按键,流水灯停止流动并保持当前状态
[*]流水模式:从P20到P27依次点亮LED,到达P27后反向流动,从P27到P20依次点亮,循环往复
[*]消抖处理:添加20ms延时消抖,确保按键检测准确
[*]状态保持:松开按键后,LED保持当前点亮状态
8.4 实现思路
[*]按键检测:使用P32按键作为输入,检测按键状态
[*]消抖处理:添加20ms延时,避免按键抖动影响
[*]状态控制:使用全局变量控制流水灯的开始和停止
[*]流水逻辑:使用位运算控制LED点亮,通过变量记录当前位置和流动方向
[*]模块化设计:将流水灯逻辑封装为独立函数,提高代码可读性和可维护性
8.5 核心代码实现8.5.1 全局变量定义u8 led_flow_flag = 0; // 流水灯控制标志:0-停止,1-流动
u8 led_current = 0; // 当前点亮的LED位置(0-7对应P20-P27)
u8 flow_direction = 0; // 流水方向:0-正向,1-反向8.5.2 延时函数void Delay100ms(void) // 控制流水灯速度
{
unsigned long edata i;
_nop_();
_nop_();
i = 599998UL;
while (i) i--;
}8.5.3 流水灯流动函数void led_flow(void)
{
// 根据当前位置点亮对应的LED
P2 = ~(1 << led_current);
// 控制流水方向
if (flow_direction == 0)// 正向流动:P20 -> P27
{
led_current++;
if (led_current >= 8)// 到达最右端,改变方向
{
led_current = 6;
flow_direction = 1;
}
}
else// 反向流动:P27 -> P20
{
led_current--;
if (led_current >= 8)// 到达最左端,改变方向(利用无符号数溢出)
{
led_current = 1;
flow_direction = 0;
}
}
Delay100ms();// 控制流水速度
}8.5.4 主控制逻辑while(1){ // 按键检测:P32按下开始流水,松开停止流水 if( P32 == 0 ) { Delay20ms();// 延时20ms消抖 if( P32 == 0 ) { led_flow_flag = 1;// 设置流水灯开始标志 } } else { led_flow_flag = 0;// 按键松开,停止流水灯 } // 流水灯控制逻辑 if (led_flow_flag) { led_flow();// 调用流水灯流动函数 } // 否则保持当前状态}8.6 视频展示
1007
【STC单片机入门实战】I/O输入输出学习笔记(第6集学习笔记)一、GPIO基本概念1. GPIO定义
[*]GPIO(General Purpose Input Output):通用输入输出端口
[*]通俗理解:单片机的引脚,可以输入或输出高低电平
2. 高低电平定义
[*]高电平:接近电源正极电压(VCC),逻辑1
[*]低电平:接近电源负极电压(GND),逻辑0
[*]注意事项:IO口电压有极限范围,3.3V供电时,IO口电压应在0-3.6V之间
3. IO口四种模式
[*]准双向口:既能输入也能输出,灌电流可达20mA,拉电流仅为微安级
[*]推挽输出:输出电流大,可达20mA
[*]高阻输入:用于高阻抗输入场合
[*]开漏模式:需要外部上拉电阻
4. 拉电流与灌电流
[*]拉电流:电流从IO口流出,准双向口模式下仅几百微安
[*]灌电流:电流流入IO口,准双向口模式下可达20mA
5. 输入电平阈值(3.3V供电)
[*]打开施密特触发器时:
[*]低电平最大值:0.99V
[*]高电平最小值:1.09V
[*]施密特触发器默认使能(上电复位后)
二、按键输入检测1. 按键原理
[*]机械按键:按下时导通,松开时断开
[*]实验箱按键连接:P32引脚通过电阻上拉,按下后接地
[*]未按下:引脚为高电平(1)
[*]按下:引脚为低电平(0)
2. 按键抖动问题
[*]现象:机械按键按下和松开时,电平会出现抖动
[*]抖动时间:一般在20ms以内
[*]影响:导致程序误判按键状态
[*]解决方案:延时消抖(本次课程)、定时器消抖(后续课程)
三、代码实现1. 工程结构
[*]主要文件:main.c、ai8051u.h
[*]开发环境:基于8051U/AI8051U单片机
2. 核心代码分析2.1 系统初始化WTST = 0; // 将程序指令执行速度设置为最快
EAXFR = 1;// 允许访问扩展寄存器(XFR)
CKCON = 0;// 提升XRAM访问速度
// 设置所有IO口为推挽输出模式
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;2.2 延时消抖函数void Delay20ms(void)// @24.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
}2.3 任务1:按下P32灯亮,松开灯灭if( P32 == 0 )// 判断P32按键是否按下
{
P20 = 0; // 灯亮
}
else
{
P20 = 1; // 灯灭
}2.4 任务2:按下P32灯灭,松开灯亮if( P32 == 1 )// 判断P32按键是否松开
{
P20 = 0; // 灯亮
}
else
{
P20 = 1; // 灯灭
}2.5 任务3:按一下灯亮,再按一下灯灭(带消抖)if( P32 == 0 )// 检测到按键按下
{
Delay20ms();// 延时20ms消抖
if( P32 == 0 )// 再次确认按键按下
{
state = !state;// 状态取反
P20 = state; // 输出状态到LED
printf("state:%d\r\n",(int)state);// 串口打印状态
while( P32 == 0 );// 等待按键松开
}
}四、关键知识点总结1. IO口配置
[*]通过PnM1和PnM0寄存器配置IO口模式
[*]00:准双向口模式
[*]01:推挽输出模式
[*]10:高阻输入模式
[*]11:开漏模式
2. 按键检测流程
[*]读取IO口电平
[*]延时消抖(20ms)
[*]再次读取确认
[*]执行相应操作
[*]等待按键松开
3. 消抖方法
[*]硬件消抖:RC滤波电路
[*]软件消抖:延时消抖、定时器消抖
[*]本次课程使用:20ms延时消抖
4. while循环的使用
[*]while(1):无限循环,用于主程序
[*]while(条件):等待条件满足,如等待按键松开
五、课后练习题
[*]按一下P32按钮灯亮,再按一下P33按钮灯灭
[*]按一下P32,左边四个灯亮,右边四个灯灭;再按一下,左边四个灭,右边四个亮
[*]按一下亮一颗灯,再按一下亮两颗灯,直到全亮
六、代码优化方向
[*]使用定时器消抖:替代延时函数,提高CPU利用率
[*]动态延时:根据需要调整延时时间
[*]按键扫描:实现多按键检测
[*]中断方式:使用外部中断处理按键,减少CPU占用
七、学习心得
[*]GPIO是单片机与外部世界交互的重要接口
[*]按键抖动是机械按键的固有特性,必须进行消抖处理
[*]准双向口是最常用的IO口模式,适合大多数应用场景
[*]软件消抖简单易实现,但会占用CPU资源
[*]学习单片机编程需要结合硬件原理和软件实现
通过本次课程的学习,我掌握了GPIO的基本概念、按键输入检测的原理和实现方法,以及如何处理按键抖动问题。这些知识是单片机编程的基础,将在后续的学习中得到广泛应用。八、学习练习:流水灯实现8.1 练习目的
[*]巩固GPIO输入输出的基本概念
[*]掌握按键检测和消抖技术
[*]实现复杂的LED控制逻辑
[*]培养模块化编程思维
8.2 硬件需求
[*]AI8051U单片机开发板
[*]P32按键一个
[*]P2口8个LED灯
8.3 功能描述
[*]按键控制:按下P32按键,流水灯开始流动;松开P32按键,流水灯停止流动并保持当前状态
[*]流水模式:从P20到P27依次点亮LED,到达P27后反向流动,从P27到P20依次点亮,循环往复
[*]消抖处理:添加20ms延时消抖,确保按键检测准确
[*]状态保持:松开按键后,LED保持当前点亮状态
8.4 实现思路
[*]按键检测:使用P32按键作为输入,检测按键状态
[*]消抖处理:添加20ms延时,避免按键抖动影响
[*]状态控制:使用全局变量控制流水灯的开始和停止
[*]流水逻辑:使用位运算控制LED点亮,通过变量记录当前位置和流动方向
[*]模块化设计:将流水灯逻辑封装为独立函数,提高代码可读性和可维护性
8.5 核心代码实现8.5.1 全局变量定义u8 led_flow_flag = 0; // 流水灯控制标志:0-停止,1-流动
u8 led_current = 0; // 当前点亮的LED位置(0-7对应P20-P27)
u8 flow_direction = 0; // 流水方向:0-正向,1-反向8.5.2 延时函数void Delay100ms(void) // 控制流水灯速度
{
unsigned long edata i;
_nop_();
_nop_();
i = 599998UL;
while (i) i--;
}8.5.3 流水灯流动函数void led_flow(void)
{
// 根据当前位置点亮对应的LED
P2 = ~(1 << led_current);
// 控制流水方向
if (flow_direction == 0)// 正向流动:P20 -> P27
{
led_current++;
if (led_current >= 8)// 到达最右端,改变方向
{
led_current = 6;
flow_direction = 1;
}
}
else// 反向流动:P27 -> P20
{
led_current--;
if (led_current >= 8)// 到达最左端,改变方向(利用无符号数溢出)
{
led_current = 1;
flow_direction = 0;
}
}
Delay100ms();// 控制流水速度
}8.5.4 主控制逻辑while(1){ // 按键检测:P32按下开始流水,松开停止流水 if( P32 == 0 ) { Delay20ms();// 延时20ms消抖 if( P32 == 0 ) { led_flow_flag = 1;// 设置流水灯开始标志 } } else { led_flow_flag = 0;// 按键松开,停止流水灯 } // 流水灯控制逻辑 if (led_flow_flag) { led_flow();// 调用流水灯流动函数 } // 否则保持当前状态}8.6 按键控制流水灯视频
1009
记录每一次学习
【STC单片机入门实战】定时器学习笔记(第7集学习笔记)一、课程概述本次课程主要讲解了8051U单片机中定时器的使用,包括定时器的基本原理、配置方法以及实际应用案例。通过三个任务示例,展示了定时器中断在解决CPU占用问题中的重要作用。二、定时器的作用与原理2.1 为什么需要定时器?传统的延时函数(如Delay20ms)会阻塞CPU执行,导致在延时期间无法响应其他任务(如按键检测)。定时器中断可以解决这个问题,它允许CPU在执行主任务的同时,通过中断机制处理定时任务。2.2 定时器工作原理
[*]定时器本质:定时器是一个计数器,从设定值开始计数,当计数到溢出值(65536)时产生中断。
[*]时钟源:定时器使用系统时钟(本工程为24MHz),可以通过分频器调整计数速度。
[*]自动重载:16位自动重载模式下,定时器溢出后会自动重新加载初始值,实现周期性中断。
[*]中断机制:当定时器溢出时,会触发中断,CPU暂停当前任务,执行中断服务程序,执行完毕后返回继续执行原任务。
2.3 定时器配置参数
参数作用示例值
TM0PS定时器预分频系数0x5B(91分频)
AUXR选择12T/1T模式0x7F(12T模式)
TMOD定时器模式0x00(16位自动重载)
TH0/TL0定时器初始值0x013F(3秒定时)
TR0定时器运行控制1(启动)/0(停止)
ET0定时器中断使能1(使能)/0(禁止)
2.4 定时时间计算公式定时时间 = (65536 - 初始值) * 12 * (TM0PS + 1) / 系统时钟频率例如,3秒定时计算:
[*]系统时钟:24MHz
[*]初始值:0x013F = 319
[*]TM0PS:0x5B = 91
[*]定时时间 = (65536 - 319) * 12 * (91 + 1) / 24000000 ≈ 3秒
三、代码实现分析3.1 工程文件结构c:\Software\STC\测试工程\07.定时器\
├── ai8051u.h // 芯片头文件
├── stc32_stc8_usb.h // USB头文件
├── stc_usb_cdc_32.LIB // USB库文件
├── main.c // 主程序文件3.2 核心代码分析3.2.1 定时器初始化函数void Timer0_Init(void) // 500毫秒@24.000MHz
{
TM0PS = 0x0F; // 设置定时器时钟预分频
AUXR &= 0x7F; // 定时器时钟12T模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0xDC; // 设置定时器初始值低8位
TH0 = 0x0B; // 设置定时器初始值高8位
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1; // 使能定时器0中断
}3.2.2 定时器中断服务程序void Timer0_Isr(void) interrupt 1// 每500毫秒执行一次
{
state = !state; // 状态取反
P20 = state; // 控制LED1
P21 = !state; // 控制LED2(与LED1相反)
}3.2.3 主函数结构void main(void)
{
// 系统初始化
WTST = 0;
EAXFR = 1;
CKCON = 0;
// IO口模式设置
P0M1 = 0x00; P0M0 = 0x00;
// ... 其他IO口设置
// USB初始化
usb_init();
IE2 |= 0x80; // 使能USB中断
EA = 1; // 开启总中断
while(DeviceState != DEVSTATE_CONFIGURED);// 等待USB配置完成
while(1) // 主循环
{
// USB数据处理
if (bUsbOutReady)
{
usb_OUT_done();
}
// 按键检测与任务处理
// ...
}
}四、任务示例任务1:LED3秒闪烁,按键计数
[*]功能:LED每3秒闪烁一次,按下按键时串口打印按键次数
[*]实现要点:
[*]3秒定时器中断控制LED状态
[*]主循环中检测按键,使用Delay20ms消抖
[*]串口打印时注意特殊字符处理(添加\xfd解决乱码)
任务2:按键点亮LED,3秒后自动熄灭
[*]功能:按下按键点亮LED,3秒后自动熄灭
[*]实现要点:
[*]按键按下时点亮LED并启动3秒定时器
[*]定时器中断中熄灭LED并关闭定时器
任务3:救护车灯控制
[*]功能:按下按键红蓝灯交替闪烁,再次按下停止闪烁
[*]实现要点:
[*]使用Run_State变量控制运行状态
[*]500毫秒定时器中断控制红蓝灯交替
[*]停止时关闭定时器并熄灭所有灯
五、函数定义、声明和调用5.1 函数定义函数定义包含返回值类型、函数名、参数列表和函数体:void Delay20ms(void) // @24.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
}5.2 函数声明函数声明告诉编译器函数的存在,位于调用之前:void Timer0_Init(void); // 定时器初始化函数声明5.3 函数调用函数调用时需要提供参数(如果有的话):Timer0_Init(); // 调用定时器初始化函数
Delay20ms(); // 调用延时函数5.4 函数位置注意事项
[*]如果函数定义在调用之后,必须先声明
[*]如果函数定义在调用之前,可以省略声明
六、注意事项
[*]特殊字符处理:在串口打印中,某些汉字(如"次"、"过"等)需要在后面添加\xfd,否则会显示乱码。
[*]中断优先级:定时器中断优先级高于主循环,确保定时任务及时执行。
[*]IO口模式:使用定时器控制LED时,需要正确设置IO口为推挽输出模式。
[*]按键消抖:机械按键需要添加消抖处理(如Delay20ms),避免误触发。
七、课后练习实现7.1 电子功德箱实现功能需求:
[*]按下按钮P32,切换功德模式(单倍/双倍)
[*]按下按钮P33,根据当前模式增加功德值(单倍+1,双倍+2)
[*]通过串口打印功德增加信息与当前功德值
[*]LED指示灯显示当前模式(P20亮表示单倍,P21亮表示双倍)
实现代码:// 全局变量定义u8 state = 0; // 初始状态u8 Run_State = 0; // 运行状态(用于模式切换)u16 merit = 0; // 功德值void main(void){ // 系统初始化 WTST = 0; EAXFR = 1; CKCON = 0; // IO口模式设置 P0M1 = 0x00; P0M0 = 0x00; P1M1 = 0x00; P1M0 = 0x00; P2M1 = 0x00; P2M0 = 0x00;// LED端口设置为推挽输出 P3M1 = 0x00; P3M0 = 0x00;// 按键端口设置为推挽输出 // ... 其他IO口设置 // USB初始化 usb_init(); IE2 |= 0x80; // 使能USB中断 EA = 1; // 开启总中断 // 初始状态为单倍功德模式 Run_State = 0; P20 = 0; // P20表示单倍模式 P21 = 1; // P21熄灭 printf("Current mode: Single merit\r\n"); while (DeviceState != DEVSTATE_CONFIGURED);// 等待USB配置完成 while(1) { // USB数据处理 if (bUsbOutReady) { usb_OUT_done(); } // 任务1:P32按键切换模式 单倍/双倍 if( P32 == 0 ) // 判断P32按键是否按下 { Delay20ms(); // 延时20ms消抖 if( P32 == 0 ) { Run_State = !Run_State; // 模式取反 if(Run_State == 1) { printf("Current mode: Double merit\r\n"); P21 = 0; // P21表示双倍模式 P20 = 1; // P20熄灭 } else { printf("Current mode: Single merit\r\n"); P20 = 0; // P20表示单倍模式 P21 = 1; // P21熄灭 } while( P32 == 0 ); // 等待P32释放 } } // 任务2:P33按键增加功德值 if( P33 == 0 ) // 判断P33按键是否按下 { Delay20ms(); // 延时20ms消抖 if( P33 == 0 ) { if(P20 == 0) // P20表示单倍模式 { merit += 1; printf("Merit+1\r\n"); } else if(P21 == 0) // P21表示双倍模式 { merit += 2; printf("Merit+2\r\n"); } printf("Current merit\xfd:%d\r\n", (int)merit); while( P33 == 0 ); // 等待P33释放 } } }}实现要点:
[*]模式切换:使用Run_State变量控制当前模式,通过P32按键切换
[*]LED指示:P20亮表示单倍模式,P21亮表示双倍模式
[*]按键消抖:使用Delay20ms()函数消除按键抖动
[*]功德计算:根据当前模式计算功德值增加量
[*]串口通信:使用printf函数输出功德信息
串口输出结果:
遇到的问题及解决方案:
[*]问题:串口输出中文显示乱码原因:8051U单片机编译器对某些中文字符支持不好,会出现乱码解决方案:将所有中文字符串替换为英文,确保串口输出正常显示// 中文(乱码)// printf("当前为单倍功德模式\r\n");// 英文(正常显示)printf("Current mode: Single merit\r\n");
[*]问题:按键检测不准确原因:机械按键存在抖动现象解决方案:添加Delay20ms()函数进行消抖处理
[*]问题:LED指示灯不亮原因:IO口模式设置不正确解决方案:将LED端口设置为推挽输出模式(P2M1 = 0x00; P2M0 = 0x00;)
通过这个项目,我们学习了如何综合运用单片机的GPIO、串口通信、按键检测等功能,实现一个完整的电子功德箱系统。同时,我们也学会了如何解决实际开发中遇到的问题,如串口乱码、按键抖动等。
页:
1
[2]