找回密码
 立即注册
楼主: yzhkpli

stc32g 9.4实验箱-冲哥教学视频学习笔记兼对比8051U实验箱学习

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-3-30 15:01:22 | 显示全部楼层
使用 stc-isp软件生成的延时500ms的代码段

  1. void Delay500ms(void)        //@24.000MHz
  2. {
  3.         unsigned long edata i;
  4.         _nop_();
  5.         _nop_();
  6.         i = 2999998UL;
  7.         while (i) i--;
  8. }
复制代码


其中unsigned long edata i;去掉edata可否?
Qwen3-Coder
14:59:08
可以去掉 edata。 在这个特定的延时函数中,去掉 edata 对功能没有影响,反而是一种更标准和推荐的写法。
为什么可以去掉 edata?
1-edata 的含义:edata 是 Keil C51 编译器的一个扩展关键字,用于指定变量存储在 外部数据存储区 (External Data Area) 的低128字节(地址 0x0000 - 0x00FF)。这个区域可以通过 MOVX @R0 或 MOVX @R1 指令进行寻址,速度比访问高地址的 xdata 区域快。
2-idata vs data:在8051架构中,变量默认存储在 内部数据存储区 (Internal Data Area, idata),也就是片内的 RAM(通常是地址 0x00 - 0xFF)。访问 idata 是最快的。
3-变量的生命周期和作用域:在 Delay500ms 函数中,变量 i 是一个局部变量。在函数被调用时,它会在栈上分配空间(通常在 idata 区)。当函数结束时,它的生命周期也随之结束。edata 关键字在这里并没有实际意义,因为编译器会自动将局部变量分配到最适合的位置(通常是 idata)。
4-编译器行为:现代C51编译器(如 Keil C51)对于这种简单的局部变量,通常会自动将其优化到 idata 区或直接放入寄存器中,以获得最佳性能。强制指定 edata 反而可能阻止这种优化。




在循环中设置延时
  1. <div>while(1){
  2. <span style="white-space:pre">        </span>P40=0;//这句可以放在循环之前
  3. <span style="white-space:pre">        </span>P60=0;P67=0;Delay500ms();
  4. <span style="white-space:pre">        </span>P60=1;P67=1;Delay500ms();
  5. }</div>
复制代码




回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-3-30 16:34:49 | 显示全部楼层
2. 函数的使用
在模块化编程里,函数使用分为如下三步:
函数定义
        返回值 函数名称(入口参数)
        {
                函数要执行的功能
        }
        @返回值:没有返回值就是void
        @函数名称:避开关键词,不重复,非特殊字符随便取
        @入口参数:类型 + 名称,多个参数用 “,” 分开,空就写void
函数声明
        返回值 函数名称(入口参数);
函数调用
        函数名称(入口参数);

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-3-30 16:38:17 | 显示全部楼层
3. 新建文件,使用模块化编程
新建xxx.c和xxx.h文件,代表一个功能块。
xxx.h 格式:

        #ifndef __XXX_H
        #define __XXX_H

        // 调用头文件
        // 函数声明...

        #endif
xxx.c 格式:

        #include "xxx.h"

        // 函数定义

注意:添加文件一定要记得引用路径和添加到工程里。

qwen给出的例子
1. 创建 delay.h 文件
        这个头文件 (delay.h) 定义了延时模块对外的接口(即函数声明),方便其他 .c 文件调用。

// delay.h
#ifndef __DELAY_H
#define __DELAY_H

// ============= 调用头文件 =============
// 本模块不需要额外的头文件,因为它只定义了一个函数
// 如果其他模块需要使用STC32G的寄存器,才需要包含"stc32g.h"
// #include "stc32g.h" // 此处不需要


// ============= 宏定义 =============
// 定义主时钟频率,方便在不同频率下调整延时参数
// 注意:如果在其他文件中也需要这个宏,可以将其移到一个公共的config.h文件中
#define MAIN_Fosc    24000000UL


// ============= 函数声明 =============
// 声明延时函数,供其他模块调用
void Delay500ms(void);

// 如果以后增加其他延时函数,也在这里声明
// void DelayMs(unsigned int ms); // 例如:毫秒级延时
// void DelayUs(unsigned int us); // 例如:微秒级延时

#endif // __DELAY_H


2. 创建 delay.c 文件
这个源文件 (delay.c) 包含了 delay.h 中声明的函数的具体实现(即函数定义)。

// delay.c
// ============= 包含头文件 =============
#include "delay.h"      // 必须首先包含对应的头文件
#include "stc32g.h"     // 因为函数体内使用了_nop_()指令,需要包含STC32G的头文件


// ============= 函数定义 =============
/**
* @brief  延时函数,基于24MHz时钟,延时约500毫秒
* @param  void
* @retval void
*/
void Delay500ms(void)
{
    // 定义一个无符号长整型变量i作为循环计数器
    // 去掉 'edata' 关键字,使用默认存储类型,更标准、更高效
    unsigned long i;

    // 执行两条空指令,用于微调延时时间
    _nop_();
    _nop_();

    // 设置循环次数,该数值是根据目标延时和时钟频率计算得出的
    i = 2999998UL;

    // 通过一个空的while循环消耗CPU时钟周期,从而实现延时
    while (i--)
    {
        ; // 空语句
    }
}

// 如果在delay.h中声明了其他函数,它们的定义也写在这里
// void DelayMs(unsigned int ms) { ... }
// void DelayUs(unsigned int us) { ... }

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-3-30 17:03:47 | 显示全部楼层
对项目进行了修改,把delay.h和delay.c放入了COMM文件夹中。
main.c改成了
  1. #include "./COMM/stc.h"
  2. #include "./COMM/delay.h"
  3. #define MAIN_Fosc    24000000UL
  4. void sys_init();
  5. //void Delay500ms(void);
  6. void main(void)
  7. {
  8.         sys_init();
  9.         P40=0;
  10.         while(1){
  11.                 //codes
  12.                 P60=0;P62=0;P61=1;P63=1;
  13.                 Delay500ms();
  14.                 P60=1;P62=1;P61=0;P63=0;
  15.                 Delay500ms();               
  16.         }
  17. }
  18. void sys_init()
  19. {
  20.     WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
  21.     EAXFR = 1; //扩展寄存器(XFR)访问使能
  22.     CKCON = 0; //提高访问XRAM速度
  23.             P_SW2 |= 0x80;                //B7位写1,使能访问XFR
  24.     P0M1 = 0x00;   P0M0 = 0x00;
  25.     P1M1 = 0x00;   P1M0 = 0x00;
  26.     P2M1 = 0x00;   P2M0 = 0x00;
  27.     P3M1 = 0x00;   P3M0 = 0x00;
  28.     P4M1 = 0x00;   P4M0 = 0x00;
  29.     P5M1 = 0x00;   P5M0 = 0x00;
  30.     P6M1 = 0x00;   P6M0 = 0x00;
  31.     P7M1 = 0x00;   P7M0 = 0x00;
  32. }       
  33. //void Delay500ms(void)        //@24.000MHz
  34. //{
  35. //        u32 i;
  36. //        _nop_();
  37. //        _nop_();
  38. //        i = 2999998UL;
  39. //        while (i) i--;
  40. //}
复制代码



COMM/delay.h内容
  1. // delay.h
  2. #ifndef __DELAY_H
  3. #define __DELAY_H
  4. // ============= 调用头文件 =============
  5. // 本模块不需要额外的头文件,因为它只定义了一个函数
  6. // 如果其他模块需要使用STC32G的寄存器,才需要包含"stc32g.h"
  7. // #include "stc32g.h" // 此处不需要
  8. // ============= 宏定义 =============
  9. // 定义主时钟频率,方便在不同频率下调整延时参数
  10. // 注意:如果在其他文件中也需要这个宏,可以将其移到一个公共的config.h文件中
  11. #define MAIN_Fosc    24000000UL
  12. // ============= 函数声明 =============
  13. // 声明延时函数,供其他模块调用
  14. void Delay500ms(void);
  15. // 如果以后增加其他延时函数,也在这里声明
  16. // void DelayMs(unsigned int ms); // 例如:毫秒级延时
  17. // void DelayUs(unsigned int us); // 例如:微秒级延时
  18. #endif // __DELAY_H
复制代码


COMM.delay.c内容
  1. // delay.c
  2. // ============= 包含头文件 =============
  3. #include "delay.h"      // 必须首先包含对应的头文件
  4. #include "stc32g.h"     // 因为函数体内使用了_nop_()指令,需要包含STC32G的头文件
  5. // ============= 函数定义 =============
  6. /**
  7. * @brief  延时函数,基于24MHz时钟,延时约500毫秒
  8. * @param  void
  9. * @retval void
  10. */
  11. void Delay500ms(void)
  12. {
  13.     // 定义一个无符号长整型变量i作为循环计数器
  14.     // 去掉 'edata' 关键字,使用默认存储类型,更标准、更高效
  15.     unsigned long i;
  16.     // 执行两条空指令,用于微调延时时间
  17.     _nop_();
  18.     _nop_();
  19.     // 设置循环次数,该数值是根据目标延时和时钟频率计算得出的
  20.     i = 2999998UL;
  21.     // 通过一个空的while循环消耗CPU时钟周期,从而实现延时
  22.     while (i--)
  23.     {
  24.         ; // 空语句
  25.     }
  26. }
  27. // 如果在delay.h中声明了其他函数,它们的定义也写在这里
  28. // void DelayMs(unsigned int ms) { ... }
  29. // void DelayUs(unsigned int us) { ... }
复制代码


记得把delay.c也add进入项目,编译通过



以上已经在26-03-31晚上烧写通过。

06_led_flash_01.zip

85.03 KB, 下载次数: 2

建立了简单目录结构的led闪烁程序

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-3-31 22:31:41 | 显示全部楼层
今天折腾一大顿终于让stc32g 9.4实验箱通过一箭双雕烧写程序了。(实验箱和一箭双雕都没坏,是电脑的程序有了问题,装了mpython alpha版本,卸载后恢复正常了)

printf程序有了问题。
  1. #include "stc32g.h"
  2. #include "STC32_STC8_USB.h"
  3. void sys_init();
  4. void main()
  5. {
  6.                 //USER_STCISPCMD = "@STCISP#";
  7.     sys_init();
  8.                 usb_init();//该函数声明在stc32_stc8_usb.h中,定义在stc_usb_hid_32g.LIB中
  9.                 EA=1;
  10.        
  11. // 注册回调函数(这行代码“调用”了 set_usb_IN_callback)
  12. //   set_usb_IN_callback(my_usb_in_callback);
  13. //// 例如,通过函数调用来设置ISP命令
  14. //    set_usb_ispcmd("@MY_CUSTOM_ISP_COMMAND#");
  15.     while (1)
  16.     {
  17. //                        if(DeviceState != DEVSTATE_CONFIGURED)
  18. //                                continue;
  19.                         if(DeviceState == DEVSTATE_CONFIGURED){
  20.                                         if (bUsbOutReady)
  21.                                         {
  22.                                                 usb_OUT_done();   //接收应答(固定格式)
  23.                                                 //常规用法:
  24.                                                 printf("Hello World!");
  25.                                                 printf("室内温度:%.2f\r\n", 11.2);
  26.                                         }
  27.                                         P40=0;
  28.                                         P60=0;
  29.                                         P62=0;
  30.                         }else{
  31.             int i;
  32.             for(i=0; i<50000; i++); // 空循环,消耗时间
  33.             
  34.             P40 = ~P40; // 反转P40状态,实现闪烁
  35.             P60 = 1; // 熄灭其他LED
  36.             P62 = 1; // 熄灭其他LED                       
  37.                         }
  38.     }
  39. }
  40. void sys_init()
  41. {
  42.     WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
  43.     EAXFR = 1; //扩展寄存器(XFR)访问使能
  44.     CKCON = 0; //提高访问XRAM速度
  45.     P0M1 = 0x30;   P0M0 = 0x30;   //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
  46.     P1M1 = 0x32;   P1M0 = 0x32;   //设置P1.1、P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V), P1.1在PWM当DAC电路通过电阻串联到P2.3
  47.     P2M1 = 0x3c;   P2M0 = 0x3c;   //设置P2.2~P2.5为漏极开路(实验箱加了上拉电阻到3.3V),设置开漏模式需要断开PWM当DAC电路中的R2电阻
  48.     P3M1 = 0x50;   P3M0 = 0x50;   //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
  49.     P4M1 = 0x3c;   P4M0 = 0x3c;   //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
  50.     P5M1 = 0x0c;   P5M0 = 0x0c;   //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
  51.     P6M1 = 0xff;   P6M0 = 0xff;   //设置为漏极开路(实验箱加了上拉电阻到3.3V)
  52.     P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
  53.           //设置USB使用的时钟源
  54.     IRC48MCR = 0x80;    //使能内部48M高速IRC
  55.     while (!(IRC48MCR & 0x01));  //等待时钟稳定
  56. }
复制代码

结果连led都不闪!!真是莫名其妙

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-5-10 21:24:03 | 显示全部楼层
更新一下,这个程序是可以在aicuble-isp软件中的cdc/hid-串口助手中显示printf内容,可用keil c51学习c编程
硬件平台:STC官方 STC8H V9.6版 试验箱
下载/通信板:一箭双雕双串口板(USB转串口)
PC端连接:USB接口,识别为 COM5 和 COM6(COM5用于下载程序)
开发环境:Keil C51

有一个问题,就是printf必须放在while(1)中,如果放在while外边,根本看不到输出的内容。

  1. /******************************************************************************
  2.   * @file    main.c
  3.   * @author  硬件家园
  4.   * @version V1.0
  5.   * @date    2020-11-8
  6.   * @Conpany
  7.   * @project STC8H单片机基础板
  8. *******************************************************************************/
  9. /* Includes ------------------------------------------------------------------*/
  10. #include <STC8H.H>
  11. #include <stdio.h>  //标准IO口头文件。 printf打印函数位于此
  12. /* Private define-------------------------------------------------------------*/
  13. #define     MAIN_Fosc       11059200L   //定义主时钟 11059200L
  14. typedef     unsigned char   u8;
  15. typedef     unsigned int    u16;
  16. typedef     unsigned long   u32;
  17. /* Private variables----------------------------------------------------------*/
  18. bit TX_Busy_Flag = 0; //串口发送忙碌标志位
  19. unsigned char  ucTemp;
  20. signed   char  cTemp;
  21. unsigned int   usTemp;
  22. unsigned long int ulTemp;
  23. float          fTemp;
  24. /* Public variables-----------------------------------------------------------*/
  25. u8 xdata ledIndex;
  26. u8 code ledNum[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
  27. /* Private function prototypes------------------------------------------------*/
  28. void UART1_Init(void);         //串口1初始化
  29. //void GPIO_Init(void);          //GPIO口初始化
  30. void  delay_ms(u16 ms);
  31. /*
  32.         * @name   main
  33.         * @brief  主函数
  34.         * @param  None        
  35.         * @retval None      
  36. */
  37. int main()
  38. {               
  39.         EX0  = 1;             //打开外部中断0
  40.         IT0  = 1;             //下降沿触发
  41.         EX1  = 1;             //打开外部中断1
  42.         IT1  = 1;             //下降沿触发
  43.         
  44.         EA  =  1;                               //打开总中断
  45.         
  46.     P_SW2 |= 0x80;  //扩展寄存器(XFR)访问使能
  47.     P0M1 = 0x30;   P0M0 = 0x30;   //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
  48.     P1M1 = 0x30;   P1M0 = 0x30;   //设置P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V)
  49.     P2M1 = 0x3c;   P2M0 = 0x3c;   //设置P2.2~P2.5为漏极开路(实验箱加了上拉电阻到3.3V)
  50.     P3M1 = 0x50;   P3M0 = 0x50;   //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
  51.     P4M1 = 0x3c;   P4M0 = 0x3c;   //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
  52.     P5M1 = 0x0c;   P5M0 = 0x0c;   //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
  53.     P6M1 = 0x00;   P6M0 = 0xff;   //设置为推挽输出
  54.     P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
  55.     P40 = 0;                //LED Power On
  56.     ledIndex = 0;
  57.         
  58.         //串口1初始化
  59.         UART1_Init();
  60.         
  61.         //GPIO口初始化
  62. //        GPIO_Init();  
  63.         //串口打印信息
  64.         printf("Initialization completed, system startup!\r\n\r\n");
  65.         printf("1234567890");
  66.         
  67.     while(1)
  68.     {
  69. //        P6 = ~ledNum[ledIndex];        //输出低驱动
  70. //        ledIndex++;
  71. //        if(ledIndex > 7)
  72. //        {
  73. //            ledIndex = 0;
  74. //        }
  75.                         P6=0XF0;
  76.         delay_ms(2250);
  77.                         P6=0X0F;
  78.         delay_ms(2250);
  79.                         //串口打印信息
  80.                         ucTemp = 200;
  81.                         printf("The ucTemp = %d\r\n", (unsigned int)ucTemp);
  82.                         cTemp=-100;
  83.                         printf("The cTemp = %d\r\n", (signed int)cTemp);
  84.                         printf("Initialization completed2, system startup!\r\n\r\n");
  85.     }
  86. }
  87. void UART1_Init()//115200bps@11.0592MHz,
  88. {
  89.         SCON  = 0x50;                //8位数据,可变波特率
  90.         AUXR &= 0xBF;                //定时器1时钟为Fosc/12,即12T
  91.         AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
  92.         TMOD &= 0x0F;                //设定定时器1为16位自动重装方式
  93.   TL1 = 0xFE;                  //设定定时初值
  94.         TH1 = 0xFF;                  //设定定时初值
  95.         ET1 = 0;                    //禁止定时器1中断
  96.         TR1 = 1;                    //启动定时器1
  97.         
  98.         ES = 1;
  99.         EA = 1;
  100.         TI = 1;  // ★ 加上这一行!表示发送器初始就绪,允许第一次发送
  101. }
  102. /*
  103.         * @name   SendData
  104.         * @brief  发送字符
  105.         * @param  dat:待发送字符
  106.         * @retval None      
  107. */
  108. void SendData(unsigned char dat)
  109. {
  110.         while(TX_Busy_Flag);  //等待前面的数据发送完
  111.                 TX_Busy_Flag = 1; //置位忙碌标志
  112.         SBUF = dat;           //写数据至UART1寄存器.如果是UART2,寄存器名是 S2BUF
  113. }
  114. //重构字符发送函数
  115. extern char putchar(char c)
  116. {
  117.         SendData((unsigned char)c);
  118.         
  119.         return c;
  120. }
  121. /*
  122.         * @name   UART1_isr
  123.         * @brief  串口1中断服务函数
  124.         * @param  None
  125.         * @retval None      
  126. */
  127. void UART1_isr() interrupt 4  using 3
  128. {
  129.         if(RI)
  130.         {                                       
  131.                 RI = (bit)0; //清除接收中断标志
  132.         }
  133.         
  134.         if(TI)
  135.         {
  136.                 TI = (bit)0;       //清除发送中断标志
  137.                 TX_Busy_Flag = 0;  //清忙碌标志
  138.         }
  139. }
  140. //========================================================================
  141. // 函数: void delay_ms(u8 ms)
  142. // 描述: 延时函数。
  143. // 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
  144. // 返回: none.
  145. // 版本: VER1.0
  146. // 日期: 2021-3-9
  147. // 备注:
  148. //========================================================================
  149. void delay_ms(u16 ms)
  150. {
  151.      u16 i;
  152.      do{
  153.           i = MAIN_Fosc / 10000;
  154.           while(--i);   //10T per loop
  155.      }while(--ms);
  156. }
  157. /********************************************************
  158.   End Of File
  159. ********************************************************/
复制代码


回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-5-11 11:00:37 | 显示全部楼层
上面的程序,GLM ai给的意见

中断隐患:using 3 关键字
void UART1_isr() interrupt 4 using 3 中的 using 3 表示该中断使用第 3 组寄存器。
后果:在 C51 中,使用 using 会额外占用 RAM 空间,且如果中断函数和主程序传递参数/变量时不小心,极易引发难以调试的寄存器冲突Bug。除非对执行速度有极致要求,否则不建议使用。
改进:去掉 using 3,让编译器自动压栈保护。


串口接收漏洞
在中断里 RI = 0; 清除了接收标志,但没有读取 SBUF,这在某些情况下会导致串口状态机异常。如果不需要接收数据,建议直接关闭接收使能。


ps:程序出了一个漏洞,
程序的main函数改成
    while(1)
    {
                printf("Initialization completed2, system startup!\r\n\r\n");
        }
正常烧写都写不进去了。
GLM说
为什么没法烧写新程序了?(核心问题)
这是 STC 单片机最常见的坑:串口被用户程序占用了,导致下载器握手中断。

STC 单片机每次上电时,会先运行内置的 Bootloader(引导程序),短暂检测串口是否有来自电脑的“下载握手信号”(通常是连续的 0x7F)。

如果检测到,就进入 ISP 下载模式。
如果没检测到,就跳转到你的用户程序运行。
现在你的用户程序一运行,就极快、持续地通过串口发送数据。这导致:

单片机 TX 引脚一直在往外发数据,和电脑发来的握手信号在总线上产生冲突(电平打架)。
电脑下载器收到满屏的 “Initialization…”,根本无法从单片机获得正确的握手回应。
所以,下载器一直提示通讯失败,不是芯片坏了,而是单片机太“吵”了,听不到下载器的指令。

🚨 怎么解决?(三种方法,推荐第一种)
方法一:断开 TX 引脚法(最稳妥,100%成功)
这是对付“串口被占用导致无法下载”最无脑有效的物理手段:

找到你的 STC8H 单片机的 TXD (P3.1) 引脚连接到 USB 转串口芯片的那条线(通常是排针或跳线帽)。
拔掉 TXD 的跳线帽(或者断开那根导线),让单片机的 TX 引脚和电脑断开。RX 引脚保持连接。
在电脑上点击“下载”。
给单片机重新上电(拔插 USB 或按板子上的电源开关/复位键)。
此时单片机发疯的 printf 数据无法传到电脑,而电脑发来的握手信号能被单片机的 RX 正常接收。下载器会提示“正在编程…”。
烧写成功后,把 TXD 的跳线帽插回去即可。

方法二:极速冷启动法(拼手速,有概率成功)
如果板子上 TX 引脚没法断开,可以尝试抢在单片机发疯之前完成握手:

在下载软件上点击“下载”。
观察下载软件,当它显示“正在检测单片机…”或“尝试连接…”时,瞬间给单片机断电再上电。
因为单片机上电瞬间有几毫秒的 Bootloader 窗口期,如果下载器刚好在这个瞬间发出握手信号,就能连上。
如果不行,多试几次,手速要快。

方法三:降低波特率法(辅助方法)
        在 STC-ISP 软件中,把“下载波特率”强行调到最低(比如 2400 或 4800)。
        点击下载,然后给单片机冷启动。
        有时候低波特率抗干扰能力强,能穿过你 printf 产生的噪音完成握手。

第一个方法,我找遍了实验箱开发板的跳线也没法断开tx,于是采用方法2,点下载瞬间,马上按一下开发板的on/off,终于可以重新烧写程序了。。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-5-11 11:36:51 | 显示全部楼层
以下是GLM帮助修改的一个用串口助手窗口打印时间的程序,注意,因为串口助手是烧写之后延时1s才打开串口的,所以在main的while(1)之前printf内容看不到,而因为while(1)内各分支执行太快,所以又没法在while(1)中直接printf一句话。

所以GLM帮助下修改了printf程序为:
这样仅仅输出一次printf

  1. /******************************************************************************
  2.   * @file    main.c
  3.   * @author  硬件家园 (优化版)
  4.   * @version V2.0
  5.   * @project STC8H单片机基础板
  6. *******************************************************************************/
  7. /* Includes ------------------------------------------------------------------*/
  8. #include <STC8H.H>
  9. #include <stdio.h>  
  10. /* Private define-------------------------------------------------------------*/
  11. #define     MAIN_Fosc       11059200L   //定义主时钟 11059200L
  12. typedef     unsigned char   u8;
  13. typedef     unsigned int    u16;
  14. typedef     unsigned long   u32;
  15. /* Private variables----------------------------------------------------------*/
  16. bit TX_Busy_Flag = 0; //串口发送忙碌标志位
  17. bit sec_update_flag = 0;
  18. bit led_toggle_flag = 0; // 新增:LED翻转标志位
  19. unsigned char ucSec=0;
  20. //unsigned char ucMin=0;
  21. //unsigned char ucHour=0;
  22. unsigned char ucStartupCount=0;
  23. /* Public variables-----------------------------------------------------------*/
  24. u8 xdata ledIndex;
  25. u8 code ledNum[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
  26. /* Private function prototypes------------------------------------------------*/
  27. void Timer0_Init(void);
  28. void UART1_Init(void);
  29. void update_time(void);
  30. void led_flash_toggle(){
  31.         P6=~P6;
  32. }
  33. /*
  34.         * @name   main
  35.         * @brief  主函数
  36.         * @param  None       
  37.         * @retval None      
  38. */
  39. int main()
  40. {
  41.         EA  =  1;                               //打开总中断
  42.     P_SW2 |= 0x80;             //扩展寄存器(XFR)访问使能
  43.     P0M1 = 0x30;   P0M0 = 0x30;   
  44.     P1M1 = 0x30;   P1M0 = 0x30;  
  45.     P2M1 = 0x3c;   P2M0 = 0x3c;   
  46.     P3M1 = 0x50;   P3M0 = 0x50;  
  47.     P4M1 = 0x3c;   P4M0 = 0x3c;  
  48.     P5M1 = 0x0c;   P5M0 = 0x0c;  
  49.     P6M1 = 0x00;   P6M0 = 0xff;   //设置为推挽输出
  50.     P7M1 = 0x00;   P7M0 = 0x00;   
  51.     P40 = 0;                            //LED Power On
  52.     ledIndex = 0;
  53.        
  54.         Timer0_Init();
  55.         UART1_Init();
  56.        
  57.         //串口打印启动信息(只在开机打印一次)
  58.         printf("Initialization completed, system startup!\r\n");
  59.         P6=0XF0;
  60.     while(1)
  61.     {
  62.         // 任务1:处理LED翻转 (非阻塞方式)
  63.         if(led_toggle_flag == 1)
  64.         {
  65.             led_toggle_flag = 0;
  66.                         led_flash_toggle();
  67.         }
  68.                        
  69.         // 任务2:处理时间更新
  70.         if(sec_update_flag == 1)
  71.         {
  72.             update_time();
  73.             // %02bd 表示:2位宽度,不足补0,bd是C51针对unsigned char的格式符
  74.             //printf("Time is %02bd:%02bd:%02bd\r\n", ucHour, ucMin, ucSec);
  75.                         // --- 核心改进:启动窗口打印逻辑 ---
  76.             if(ucStartupCount < 3) // 前3秒:打印启动信息,给你留3秒时间开串口助手
  77.             {
  78.                 printf("Initialization completed, system startup!\r\n");
  79.                 ucStartupCount++;
  80.             }
  81.             else // 3秒后:进入正常的时间打印
  82.             {
  83.                 //printf("Time is %02bd:%02bd:%02bd\r\n", ucHour, ucMin, ucSec);               
  84.             }                       
  85.         }
  86.     }
  87. }
  88. void Timer0_Init(void)                //20毫秒@11.0592MHz
  89. {
  90.         AUXR &= 0x7F;                        //定时器时钟12T模式
  91.         TMOD &= 0xF0;                        //设置定时器模式
  92.         TL0 = 0x00;                                //设置定时初始值
  93.         TH0 = 0xB8;                                //设置定时初始值
  94.         TF0 = 0;                                //清除TF0标志
  95.         ET0 = 1;                //允许定时器0中断
  96.         TR0 = 1;                                //定时器0开始计时
  97. }
  98. void Timer0_Isr() interrupt 1 //去掉了 using 3,更安全
  99. {                                
  100.    static unsigned char cnt_20ms = 0;
  101.    static unsigned char cnt_500ms = 0; // 新增:用于LED闪烁的计数器
  102.    
  103.    cnt_20ms++;
  104.    cnt_500ms++;
  105.    
  106.    // 1秒计时逻辑
  107.    if(cnt_20ms >= 50)
  108.    {
  109.        sec_update_flag = 1;
  110.        cnt_20ms = 0;
  111.    }
  112.    
  113.    // 500ms LED翻转逻辑 (原来的 2250ms * 2 = 4.5秒太慢了,这里改为1秒闪一次,可自行修改)
  114.    if(cnt_500ms >= 25) // 25 * 20ms = 500ms
  115.    {
  116.        led_toggle_flag = 1;
  117.        cnt_500ms = 0;
  118.    }
  119. }
  120. void UART1_Init()//115200bps@11.0592MHz,
  121. {
  122.         SCON  = 0x50;                //8位数据,可变波特率
  123.         AUXR &= 0xBF;                //定时器1时钟为Fosc/12,即12T
  124.         AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
  125.         TMOD &= 0x0F;                //设定定时器1为16位自动重装方式
  126.     TL1 = 0xFE;                //设定定时初值
  127.         TH1 = 0xFF;                //设定定时初值
  128.         ET1 = 0;                    //禁止定时器1中断
  129.         TR1 = 1;                    //启动定时器1
  130.        
  131.         ES = 1;
  132.         EA = 1;
  133.         TI = 1;  
  134. }
  135. void SendData(unsigned char dat)
  136. {
  137.         while(TX_Busy_Flag);  //等待前面的数据发送完
  138.         TX_Busy_Flag = 1;     //置位忙碌标志
  139.         SBUF = dat;           //写数据至UART1寄存器
  140. }
  141. extern char putchar(char c)
  142. {
  143.         SendData((unsigned char)c);
  144.         return c;
  145. }
  146. void UART1_isr() interrupt 4  // 去掉了 using 3
  147. {
  148.         if(RI)
  149.         {                                       
  150.                 RI = 0; //清除接收中断标志
  151.         //volatile unsigned char dummy = SBUF; //读取SBUF清除潜在状态(若不需接收可忽略)
  152.         //(void)dummy; // 防止编译器警告
  153.         }
  154.        
  155.         if(TI)
  156.         {
  157.                 TI = 0;            //清除发送中断标志
  158.                 TX_Busy_Flag = 0;  //清忙碌标志
  159.         }
  160. }
  161. void update_time(void)
  162. {
  163.         sec_update_flag = 0;
  164.         ucSec++;
  165.         if(ucSec >= 60)
  166.     {
  167.                 ucSec = 0;
  168.         }
  169. }
  170. // 注意:delay_ms 函数已经不再需要,可以删除,如果其他地方用不到的话
复制代码
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-5-12 14:20:29 | 显示全部楼层
学写状态机程序,使用的不是stc的试验箱,因为不会用矩阵键盘。。。。

硬件为普中A2实验板,平常状态下,K1弹起,状态为1,8个led中最左边d0是灭的,K1按下去后,能看到经过一个很短暂的d1灭d0亮后,保持在d0和d1都灭(状态3)

  1. #include <STC89C5xRC.H>
  2. typedef unsigned char uint8_t;
  3. typedef unsigned int uint16_t;
  4. //全局变量 isr_10ms表示10ms中断标志位
  5. bit isr_10ms=0;
  6. //按键状态
  7. uint8_t key_state=0;
  8. sbit key1=P3^1;
  9. void Timer0_Init(void)                //1毫秒@11.0592MHz
  10. {
  11.         AUXR &= 0x7F;                        //定时器时钟12T模式
  12.         TMOD &= 0xF0;                        //设置定时器模式
  13.         TMOD |= 0x01; // 设置定时器0为模式1(16位定时器)
  14.         TL0 = 0x66;                                //设置定时初始值
  15.         TH0 = 0xFC;                                //设置定时初始值
  16.         TF0 = 0;                                //清除TF0标志
  17.         ET0=1;
  18.         EA=1;
  19.         TR0 = 1;                                //定时器0开始计时
  20. }
  21. void timer0_isr() interrupt 1 //timer0为定时1ms
  22. {
  23.         static uint8_t cnt_10ms=0;
  24.         TL0 = 0x66;                                //设置定时初始值
  25.         TH0 = 0xFC;                                //设置定时初始值
  26.         cnt_10ms++;
  27.         if(cnt_10ms>=10){
  28.                 isr_10ms=1;//10ms中断标志位置1
  29.                 cnt_10ms=0;
  30.         }
  31.         P2=key_state;
  32. }
  33. void main(void){
  34.         Timer0_Init();
  35.         EA=1;
  36.         while(1){
  37.                 if(1==isr_10ms){
  38.                         isr_10ms=0;
  39.                         switch(key_state){
  40.                                 case 1:if(0==key1)key_state=2;break;
  41.                                 case 2:if(0==key1)key_state=3;break;
  42.                                 case 3:if(1==key1)key_state=4;break;
  43.                                 case 4:if(1==key1)key_state=1;break;
  44.                                 default:key_state=1;
  45.                         }
  46.                 }
  47.         }
  48. }
复制代码
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-05-29 10:43:45

34

主题

199

回帖

637

积分

高级会员

积分
637
发表于 2026-5-12 18:03:03 | 显示全部楼层
这是一个修改版(基本来自硬件家园的程序)以下程序成功的在普中A2实验板上实现了
按下K3按钮(走线为P32---K3---GND)弹起时候P2接的led亮灭反转。

注释为GLM添加


  1. //==============================================types.h
  2. #ifndef __TYPES_H_
  3. #define __TYPES_H_
  4. typedef unsigned char uint8_t;
  5. typedef unsigned int  uint16_t;
  6. #endif
  7. //==============================================KEY.h
  8. #ifndef __KEY_H_
  9. #define __KEY_H_
  10. #include "types.h"
  11. // 定义按键状态机枚举,描述按键的4个状态
  12. typedef enum
  13. {
  14.         STA1_KEY_Up_Status         = 0x01, // 状态1:按键弹起稳定状态
  15.         STA2_KEY_DownShake_Status  = 0x02, // 状态2:按键按下抖动状态
  16.         STA3_KEY_Down_Status       = 0x03, // 状态3:按键按下稳定状态
  17.         STA4_KEY_UpShake_Status    = 0x04, // 状态4:按键弹起抖动状态
  18. } KEY_STA_Machine_Status_t;
  19.         
  20. // 声明外部变量,供主函数和中断服务函数使用
  21. extern volatile unsigned char KEY_Scan_Timer; // 按键扫描计时器,由定时器中断累加
  22. extern bit KEY1_Click_Flag;          // 按键单击标志位,按下并弹起后置1
  23. // 声明外部函数
  24. extern void KEY1_Detect(void);       // 按键1检测函数
  25. #endif
  26. //==============================================KEY.c
  27. #include <STC89C5xRC.H>
  28. #include "KEY.h"
  29. #define KEY1_Status P32 // 按键1的引脚定义,对应P3.2
  30. // 状态机当前状态,初始化为弹起状态
  31. static KEY_STA_Machine_Status_t KEY_STA_Machine_Status = STA1_KEY_Up_Status;
  32. // 按键扫描时间计数器
  33. uint8_t KEY_Scan_Timer = 0;
  34. // 按键单击标志位,只有按下并弹起才算一次
  35. bit KEY1_Click_Flag = 0;
  36. /*
  37.         * @name   KEY1_Detect
  38.         * @brief  按键1检测函数,基于状态机实现软件消抖
  39.         * @param  None
  40.         * @retval None      
  41. */
  42. void KEY1_Detect(void){
  43.         // 判断扫描间隔是否达到10ms(定时器每1ms加1,加10次即10ms)
  44.         if(KEY_Scan_Timer >= 10){
  45.                
  46.                 switch(KEY_STA_Machine_Status){
  47.                         case STA1_KEY_Up_Status: // 弹起稳定状态
  48.                         {
  49.                                 if(0 == KEY1_Status){ // 如果检测到低电平(按下)
  50.                                         KEY_STA_Machine_Status = STA2_KEY_DownShake_Status; // 进入按下抖动状态
  51.                                 }
  52.                         };break;
  53.                         
  54.                         case STA2_KEY_DownShake_Status: // 按下抖动状态
  55.                         {
  56.                                 if(0 == KEY1_Status){ // 10ms后再次检测,如果仍为低电平,说明是真按下
  57.                                         KEY_STA_Machine_Status = STA3_KEY_Down_Status; // 进入按下稳定状态
  58.                                 }else{ // 如果变成了高电平,说明是抖动导致的误触发
  59.                                         KEY_STA_Machine_Status = STA1_KEY_Up_Status; // 回退到弹起稳定状态
  60.                                 }
  61.                         };break;
  62.                         
  63.                         case STA3_KEY_Down_Status: // 按下稳定状态
  64.                         {
  65.                                 if(1 == KEY1_Status){ // 检测到高电平(松手)
  66.                                         KEY_STA_Machine_Status = STA4_KEY_UpShake_Status; // 进入弹起抖动状态
  67.                                 }else{
  68.                                         // 此时按键处于稳定按下状态,可以在此处添加长按逻辑
  69.                                 }
  70.                         };break;               
  71.                         
  72.                         case STA4_KEY_UpShake_Status: // 弹起抖动状态
  73.                         {
  74.                                 if(0 == KEY1_Status){ // 10ms后再次检测,如果又变成低电平,说明是松手抖动
  75.                                         KEY_STA_Machine_Status = STA3_KEY_Down_Status; // 回退到按下稳定状态
  76.                                 }else{ // 确认是稳定的高电平,说明按键真正松开了
  77.                                         KEY1_Click_Flag = 1; // 置位单击标志位,代表一次完整的按键动作完成
  78.                                         KEY_STA_Machine_Status = STA1_KEY_Up_Status; // 回到弹起稳定状态
  79.                                 }
  80.                         };break;
  81.                         
  82.                         default: KEY_STA_Machine_Status = STA1_KEY_Up_Status; // 异常恢复
  83.                 }
  84.                 KEY_Scan_Timer = 0; // 处理完按键状态后要把这个按键扫描时间清零,等待下一个10ms
  85.         }
  86. }
复制代码
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-5-30 04:45 , Processed in 0.149525 second(s), 80 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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