找回密码
 立即注册
查看: 465|回复: 14

STC8H1K08-30I-TSSOP20 快速入门配套教程 | 建议 STC8H2K12U-45I-TSSOP20

[复制链接]
  • 打卡等级:常住居民I
  • 打卡总天数:70
  • 最近打卡:2026-03-29 16:54:48
已绑定手机

2

主题

37

回帖

120

积分

注册会员

积分
120
发表于 2026-1-3 09:01:57 | 显示全部楼层 |阅读模式
大家早上好,我是UP志城。


接下来我将带你快速入门STC8H1K08-30I-TSSOP20的芯片,


车速较快,良心分享,请各位座好扶稳。

芯片引脚图.png

1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
缓慢而坚定,为值得的事情倾注所有。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:370
  • 最近打卡:2026-04-04 12:33:12

844

主题

1万

回帖

2万

积分

管理员

积分
22805
发表于 2026-1-3 09:18:57 | 显示全部楼层
建议楼主,直接用
STC8H2K12U-45I-TSSOP20/SOP16,
STC8H2K32U-45I-TSSOP20/LQFP32,
STC8H8K64U-45I-TSSOP20/LQFP32/LQFP48,PDIP40 来讲
好处 :

===【USB直接下载】,【USB不停电下载】
===【USB直接仿真】,
===【USB直接通信】


用下面这个 【STCAI-万能实验板-V2.x】,带大家一起做实验学习
用【STCAI-万能实验板】做实验,DIY 拿奖励,500元/人,前20名 ! - 做实验拿奖励@STCAI万能板,500元 国芯人工智能技术交流网站 - AI32位8051交流社区

截图202601030918056256.jpg
STCAI-万能实验板-V2.3,支持 封装形式/接口:
LQFP48 / 32,TF卡 插座,FPC接口
TSSOP28/24/20/16/14;
SOP28/24/20;
WSOP16/8;
SOP16/8;
SOT23-6/5/4/3, DFN8;
DIP40/28/20/16/8,

贴片 电阻 / 电容 也可直接焊在插件的2个焊盘之间,
FPC焊接/插座支持间距:

0.5mm、0.62mm、0.65mm、0.7mm、0.8mm
截图202601030918192134.jpg


建议 第一节课是 :


截图202601030923424050.jpg

截图202601030924151869.jpg

截图202601030924517075.jpg

截图202601030925257513.jpg

STC 的 MCU 从 0 开始入门:
先搞定 【USB不停电下载】/【USB直接通信】,再搞其他的


要 做到 USB不停电下载
要 尝试 AiCube 图形化自动配置生成程序工具
推荐优先看的:  
printf_usb("Hello World !\r\n")
USB不停电下载, 演示视频链接:
https://www.stcaimcu.com/thread-19077-1-1.html

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

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

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

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


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

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


上面是 小李 演示:STC8H8K64U, printf_usb("Hello World !\r\n")及usb不停电下载@AiCube之图形化程序自动生成


回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:70
  • 最近打卡:2026-03-29 16:54:48
已绑定手机

2

主题

37

回帖

120

积分

注册会员

积分
120
发表于 2026-1-3 09:06:01 | 显示全部楼层
第二节:带领大家实现串口通信控制舵机。

  1. /************************************************************
  2. * 文件: main.c
  3. * 作者: 志城
  4. * 日期: 2025-12-26
  5. * 版本: 最终成功版本 2512260442
  6. * 功能: 基于STC8H1K08A单片机的舵机控制系统,通过串口触发实现控制。
  7. * 硬件: STC8H1K08A 20PIN单片机,主时钟22118400LMHz
  8. * 引脚: 具体配置见下方IO口模式设置及串口初始化部分
  9. ************************************************************/
  10. /* 头文件包含 */
  11. #include "config.h"
  12. #include "STC8G_H_Delay.h"
  13. #include "STC8G_H_Servo.h"
  14. #include "STC8G_H_UART1_UART2.h"
  15. /* 宏定义 */
  16. #define MAIN_Fosc       22118400L          /* 主时钟频率 */
  17. #define Timer0_Reload   (65536UL - (MAIN_Fosc / 1000)) /* 定时器0重载值,用于1ms定时 */
  18. /**
  19. * @brief 定时器0中断服务函数
  20. * @details 提供1ms定时基准,用于系统计时和舵机平滑运动控制。
  21. *          中断中更新舵机控制信号。
  22. */
  23. void timer0(void) interrupt 1
  24. {
  25.     static u16 ms_counter = 0; /* 毫秒计数器 */
  26.     /* 重载定时器初值 */
  27.     TH0 = (u8)(Timer0_Reload / 256);
  28.     TL0 = (u8)(Timer0_Reload % 256);
  29.     /* 1秒计时逻辑 */
  30.     ms_counter++;
  31.     if (ms_counter >= 1000)
  32.     {
  33.         ms_counter = 0;
  34.         B_1ms = 1; /* 置位1秒标志 */
  35.     }
  36.     /* 调用舵机更新函数,必须在中断中定期调用以维持PWM信号 */
  37.     Servo_Update();
  38. }
  39. /**
  40. * @brief 主函数
  41. * @details 程序入口点。初始化系统硬件(定时器、舵机、IO口、串口),
  42. *          然后进入主循环,循环处理来自两个串口的控制命令。
  43. */
  44. void main(void)
  45. {
  46.     /* 1. 扩展寄存器访问使能 */
  47.     P_SW2 |= 0x80;
  48.     /* 2. 定时器0初始化 - 用于产生1ms中断 */
  49.     AUXR = 0x80;                    /* 定时器0为1T模式 */
  50.     TH0 = (u8)(Timer0_Reload / 256);
  51.     TL0 = (u8)(Timer0_Reload % 256);
  52.     ET0 = 1;                        /* 使能定时器0中断 */
  53.     TR0 = 1;                        /* 启动定时器0 */
  54.     /* 3. 舵机控制模块初始化 */
  55.     Servo_Init();
  56.     /* 4. IO口模式配置 (准双向/推挽/高阻/开漏) */
  57.     /* 根据STC8G系列数据手册配置,此处配置可能与具体硬件连接相关 */
  58.     P0M1 = 0x30;   P0M0 = 0x30;
  59.     P1M1 = 0x30;   P1M0 = 0x30;
  60.     P2M1 = 0x3c;   P2M0 = 0x3c;
  61.     P3M1 = 0x50;   P3M0 = 0x50;
  62.     P4M1 = 0x3c;   P4M0 = 0x3c;
  63.     P5M1 = 0x0c;   P5M0 = 0x0c;
  64.     P6M1 = 0xff;   P6M0 = 0xff;
  65.     P7M1 = 0x00;   P7M0 = 0x00;
  66.     /* 5. 串口初始化 */
  67.     UART1_config(2); /* 初始化UART1,参数2可能与波特率设置相关 */
  68.     UART2_config(2); /* 初始化UART2 */
  69.     /* 6. 特定引脚模式配置(可能用于串口或舵机信号输出) */
  70.     P1M0 = 0x18;
  71.     P1M1 = 0x00;
  72.     P2M0 &= ~0x01;
  73.     P2M1 &= ~0x01;
  74.     /* 7. 开启全局中断 */
  75.     EA = 1;
  76.     /* 8. 主循环 */
  77.     while (1)
  78.     {
  79.         /* 处理来自UART1和UART2的命令 */
  80.         UART_ProcessCommand(1); /* 处理串口1命令 */
  81.         UART_ProcessCommand(2); /* 处理串口2命令 */
  82.         /* 原摆动测试代码已注释,通过串口命令控制舵机 */
  83.         /*
  84.         SetServoAngle(0);
  85.         Delay2000ms();
  86.         SetServoAngle(180);
  87.         Delay2000ms();
  88.         */
  89.     }
  90. }
复制代码
缓慢而坚定,为值得的事情倾注所有。
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:70
  • 最近打卡:2026-03-29 16:54:48
已绑定手机

2

主题

37

回帖

120

积分

注册会员

积分
120
发表于 2026-1-3 09:06:46 | 显示全部楼层
  1. /******************************************************************************
  2. * @file    STC8G_H_Servo.c
  3. * @brief   STC8G系列单片机舵机控制模块
  4. * @details 本文件实现了基于PWM的舵机角度控制,支持0-180度范围,具有平滑运动功能。
  5. *          适用于SG90等标准舵机,使用定时器中断实现非阻塞式角度更新。
  6. * @author  [志城]
  7. * @date    2025-12-26
  8. * @version 1.0
  9. *****************************************************************************/
  10. #include    "config.h"          // 包含项目配置文件,定义系统时钟、定时器重载值等宏[1](@ref)
  11. #include "STC8G_H_Servo.h"      // 包含舵机控制模块的头文件,声明外部函数和宏
  12. // 全局变量定义
  13. bit B_1ms = 0;                  // 1ms定时标志位,用于系统时基
  14. u16 servo_angle = 90;          // 当前舵机角度,初始化为中间位置(90度)
  15. u16 servo_target = 0;          // 目标角度,由SetServoAngle()函数设置
  16. u16 servo_step = 0;            // 步进值,预留用于更复杂的运动规划
  17. u16 servo_speed = 10;          // 舵机转动速度,值越大转动越慢(单位:定时周期)
  18. u16 PWM4_Duty = INITIAL_DUTY_90_DEGREES;  // PWM占空比初始值,对应90度位置
  19. // PWM通道配置宏定义
  20. #define PWM4_4      0xC0        // PWM4输出映射到P3.4引脚的位掩码
  21. #define ENO4P       0x40        // PWM4正输出使能位
  22. #define ENO4N       0x80        // PWM4互补输出使能位(本应用中未使用互补输出)
  23. /**
  24. * @brief 舵机初始化函数
  25. * @details 初始化PWM模块,配置定时器和GPIO,设置舵机初始位置为90度。
  26. *          本函数配置了PWM周期为20ms(标准舵机控制周期),并初始化所有相关寄存器。
  27. * @note 调用此函数前需确保config.h中PWM_PERIOD等宏已正确定义。
  28. * @param 无
  29. * @return 无
  30. */
  31. void Servo_Init(void)
  32. {
  33.     // 配置GPIO为推挽输出模式
  34.     P3M1 = 0x00;                // 清除P3口输入模式配置
  35.     P3M0 = 0xFF;                // 设置P3口所有引脚为推挽输出模式
  36.    
  37.     // 配置其他GPIO口为准双向模式(默认状态)
  38.     P0M1 = 0x00;                // P0口配置
  39.     P0M0 = 0x00;
  40.     P1M1 = 0x00;                // P1口配置
  41.     P1M0 = 0x00;
  42.     P2M1 = 0x00;                // P2口配置
  43.     P2M0 = 0x00;
  44.     P4M1 = 0x00;                // P4口配置
  45.     P4M0 = 0x00;
  46.     P5M1 = 0x00;                // P5口配置
  47.     P5M0 = 0x00;
  48.    
  49.     // PWM模块配置:先关闭所有通道输出使能
  50.     PWMA_CCER1 = 0x00;          // 关闭通道1-3输出
  51.     PWMA_CCER2 = 0x00;          // 关闭通道4-6输出
  52.    
  53.     // 配置PWM通道4工作模式
  54.     PWMA_CCMR4 = 0x60;          // PWM模式1,预装载使能[1](@ref)
  55.     PWMA_CCER2 = 0x50;          // 通道4输出使能,高电平有效
  56.    
  57.     PWMA_CCMR4 |= 0x08;         // 快速使能(CCxE和CCxNE位立即生效)
  58.     PWMA_PSCRH = 0x00;          // 预分频器高字节清零
  59.     PWMA_PSCRL = 63;            // 预分频系数64(实际分频值=63+1)
  60.    
  61.     // 设置PWM周期为20ms(标准舵机控制周期)
  62.     PWMA_ARRH = (u8)(PWM_PERIOD >> 8);  // 自动重装载值高字节
  63.     PWMA_ARRL = (u8)PWM_PERIOD;         // 自动重装载值低字节
  64.    
  65.     // 使能PWM4输出到指定引脚
  66.     PWMA_ENO = 0x00;            // 清除所有输出使能
  67.     PWMA_ENO |= ENO4P | ENO4N;  // 使能PWM4正输出和互补输出
  68.    
  69.     PWMA_PS = 0x00;             // 清除引脚映射配置
  70.     PWMA_PS |= PWM4_4;          // 映射PWM4到P3.4引脚
  71.    
  72.     PWMA_BKR = 0x80;            // 主输出使能(MOE=1)
  73.     PWMA_CR1 |= 0x01;           // 使能计数器(CEN=1),开始产生PWM
  74.    
  75.     // 设置初始角度为90度(中间位置)
  76.     servo_angle = 90;           // 更新当前角度变量
  77.     servo_target = 90;          // 设置目标角度为90度
  78.     PWM4_Duty = INITIAL_DUTY_90_DEGREES;  // 使用预定义的90度占空比值
  79.     UpdatePwm();                // 更新PWM输出
  80. }
  81. /**
  82. * @brief 设置舵机目标角度
  83. * @param angle 目标角度值,范围0-180度
  84. * @details 将目标角度限制在有效范围内,舵机会通过Servo_Update()函数平滑移动到该角度。
  85. *          本函数仅设置目标值,实际运动由定时器中断控制,实现非阻塞式运动。
  86. * @note 角度值超出范围时会被自动限制在边界值。
  87. */
  88. void SetServoAngle(u16 angle)
  89. {
  90.     // 角度范围限制:确保输入角度在舵机有效工作范围内
  91.     if(angle < SERVO_MIN_ANGLE)          // 低于最小角度时取最小值
  92.         angle = SERVO_MIN_ANGLE;
  93.     if(angle > SERVO_MAX_ANGLE)          // 高于最大角度时取最大值
  94.         angle = SERVO_MAX_ANGLE;
  95.    
  96.     servo_target = angle;                // 更新目标角度变量
  97. }
  98. /**
  99. * @brief 角度到占空比转换函数
  100. * @param angle 舵机角度,范围0-180度
  101. * @return 对应的PWM占空比值,用于设置PWM比较寄存器
  102. * @details 根据SG90舵机规格,将角度线性映射到脉冲宽度。
  103. *          标准舵机控制信号:0度对应0.5ms脉冲,180度对应2.5ms脉冲,周期20ms。
  104. *          转换公式:脉宽 = 500 + angle * (2500-500)/180
  105. *                  占空比 = (脉宽 * PWM_PERIOD) / 20000
  106. * @note 计算过程中使用32位整数防止溢出,最终结果限制在PWM_PERIOD范围内。
  107. */
  108. u16 AngleToDuty(u16 angle)
  109. {
  110.     u32 pulse_width;    // 脉冲宽度(微秒)
  111.     u32 duty_value;     // 计算出的占空比值
  112.    
  113.     // 线性映射:角度 -> 脉冲宽度(us)
  114.     // 角度0°: 500us, 角度180°: 2500us[4](@ref)
  115.     pulse_width = SERVO_MIN_US + ((u32)angle * (SERVO_MAX_US - SERVO_MIN_US) / SERVO_MAX_ANGLE);
  116.    
  117.     // 转换为PWM占空比值
  118.     // 占空比 = (脉冲宽度 * PWM周期值) / 20000us(20ms周期)
  119.     duty_value = (pulse_width * PWM_PERIOD) / 20000UL;
  120.    
  121.     // 边界检查:确保占空比不超过PWM周期范围
  122.     if(duty_value > PWM_PERIOD)         // 超过最大值时取最大值
  123.         duty_value = PWM_PERIOD;
  124.     if(duty_value < 0)                  // 低于最小值时取0(实际不会发生)
  125.         duty_value = 0;
  126.    
  127.     return (u16)duty_value;              // 返回16位占空比值
  128. }
  129. /**
  130. * @brief 更新PWM占空比
  131. * @details 将计算好的占空比值写入PWM比较寄存器CCR4,立即改变PWM输出脉宽。
  132. *          本函数不进行任何计算,仅负责寄存器写入操作。
  133. * @param 无
  134. * @return 无
  135. */
  136. void UpdatePwm(void)
  137. {
  138.     // 将16位占空比值写入通道4比较寄存器(分高低字节写入)
  139.     PWMA_CCR4H = (u8)(PWM4_Duty >> 8);  // 写入高字节
  140.     PWMA_CCR4L = (u8)PWM4_Duty;         // 写入低字节
  141. }
  142. /**
  143. * @brief 舵机更新函数
  144. * @details 应在定时器中断服务程序中周期性调用(建议1ms调用一次)。
  145. *          实现舵机平滑运动:每次调用检查是否到达速度控制时间,
  146. *          然后逐步调整当前角度向目标角度靠近,最后更新PWM输出。
  147. * @note 本函数采用非阻塞设计,通过全局变量控制运动,不影响主程序运行。
  148. */
  149. void Servo_Update(void)
  150. {
  151.     static u16 time_counter = 0;        // 时间计数器,用于速度控制
  152.    
  153.     time_counter++;                     // 每次调用计数器加1
  154.    
  155.     // 速度控制:每servo_speed次调用执行一次角度更新
  156.     if(time_counter >= servo_speed)
  157.     {
  158.         time_counter = 0;               // 重置计数器
  159.         
  160.         // 平滑移动到目标角度:逐步调整当前角度
  161.         if(servo_angle < servo_target)  // 当前角度小于目标角度
  162.         {
  163.             servo_angle++;              // 角度增加1度
  164.             if(servo_angle > SERVO_MAX_ANGLE)  // 边界检查
  165.                 servo_angle = SERVO_MAX_ANGLE;
  166.         }
  167.         else if(servo_angle > servo_target)  // 当前角度大于目标角度
  168.         {
  169.             servo_angle--;              // 角度减少1度
  170.             if(servo_angle < SERVO_MIN_ANGLE)  // 边界检查
  171.                 servo_angle = SERVO_MIN_ANGLE;
  172.         }
  173.         // 如果servo_angle == servo_target,则不执行任何操作
  174.         
  175.         // 根据新角度计算并更新PWM占空比
  176.         PWM4_Duty = AngleToDuty(servo_angle);  // 角度转占空比
  177.         UpdatePwm();                    // 更新PWM输出
  178.     }
  179. }
  180. /******************************************************************************
  181. * 文件结束
  182. * 修改记录:
  183. * 1. 2025-12-26 初始版本创建
  184. *    - 实现基本舵机控制功能
  185. *    - 添加平滑运动算法
  186. *    - 完善注释文档
  187. *****************************************************************************/
复制代码
缓慢而坚定,为值得的事情倾注所有。
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:70
  • 最近打卡:2026-03-29 16:54:48
已绑定手机

2

主题

37

回帖

120

积分

注册会员

积分
120
发表于 2026-1-3 09:07:18 | 显示全部楼层
  1. #ifndef __STC8G_H_SERVO_H
  2. #define __STC8G_H_SERVO_H
  3. #include "config.h"
  4. // 舵机参数配置
  5. #define SERVO_MIN_US        500     // 0度对应的脉冲宽度(us)
  6. #define SERVO_MAX_US        2500    // 180度对应的脉冲宽度(us)
  7. #define SERVO_MIN_ANGLE     0       // 最小角度(度)
  8. #define SERVO_MAX_ANGLE     180     // 最大角度(度)
  9. #define PWM_PERIOD          7499    // PWM周期值
  10. // 角度到占空比转换相关
  11. #define INITIAL_DUTY_90_DEGREES   ((u16)((1500UL * PWM_PERIOD) / 20000UL))
  12. // 全局变量声明
  13. extern bit B_1ms;
  14. extern u16 servo_angle;
  15. extern u16 servo_target;
  16. extern u16 servo_step;
  17. extern u16 servo_speed;
  18. extern u16 PWM4_Duty;
  19. // 函数声明
  20. void Servo_Init(void);
  21. void SetServoAngle(u16 angle);
  22. u16 AngleToDuty(u16 angle);
  23. void UpdatePwm(void);
  24. void Servo_Update(void);
  25. #endif // _SERVO_H
复制代码

缓慢而坚定,为值得的事情倾注所有。
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:70
  • 最近打卡:2026-03-29 16:54:48
已绑定手机

2

主题

37

回帖

120

积分

注册会员

积分
120
发表于 2026-1-3 09:08:12 | 显示全部楼层
  1. /**
  2. * @file    STC8G_H_UART1_UART2.c
  3. * @brief   STC8系列单片机UART1和UART2驱动文件
  4. * @details 本文件实现了UART1和UART2的初始化、发送、接收功能,采用环形缓冲区管理数据
  5. * @note    使用前需在config.h中定义MAIN_Fosc(系统时钟频率)和Baudrate1/Baudrate2(波特率)
  6. * @author  [志城]
  7. * @date    2025-12-24
  8. * @version 1.0
  9. */
  10. #include "config.h"
  11. #include "STC8G_H_UART1_UART2.h"
  12. #include "STC8G_H_Servo.h"
  13. /* 全局变量定义 */
  14. UART_Control_t xdata Uart1_Ctrl;  /* UART1控制结构体,用于管理发送/接收缓冲区及状态 */
  15. UART_Control_t xdata Uart2_Ctrl;  /* UART2控制结构体,用于管理发送/接收缓冲区及状态 */
  16. /**
  17. * @brief   配置定时器2作为UART的波特率发生器
  18. * @param   dat: 定时器重装载值,用于计算波特率
  19. * @retval  无
  20. * @note    该函数设置AUXR寄存器,配置定时器2为1T模式,并设置重装载值[1](@ref)
  21. */
  22. void SetTimer2Baudraye(u16 dat)
  23. {
  24.     AUXR &= ~(1<<4);      /* 清除T2_C/T位,选择定时器模式 */
  25.     AUXR &= ~(1<<3);      /* 清除T2x12位,选择12T模式(兼容传统51) */
  26.     AUXR |=  (1<<2);      /* 设置T2R位,启动定时器2运行 */
  27.     T2H = dat / 256;      /* 设置定时器2高8位重装载值 */
  28.     T2L = dat % 256;      /* 设置定时器2低8位重装载值 */
  29.     IE2  &= ~(1<<2);      /* 禁止定时器2中断 */
  30.     AUXR |=  (1<<4);      /* 设置T2_C/T位,选择计数器模式(用于波特率发生器) */
  31. }
  32. /**
  33. * @brief   配置UART1串口
  34. * @param   brt: 波特率发生器选择,1=使用定时器1,2=使用定时器2
  35. * @retval  无
  36. * @note    该函数根据brt参数选择不同的波特率发生器,并初始化UART1的环形缓冲区[1](@ref)[2](@ref)
  37. */
  38. void UART1_config(u8 brt)
  39. {
  40.     if(brt == 2)  /* 使用定时器2作为波特率发生器 */
  41.     {
  42.         AUXR |= 0x01;  /* 设置UART_M0x6位,选择定时器2为波特率发生器 */
  43.         SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / Baudrate1);  /* 计算并设置定时器2重装载值 */
  44.     }
  45.     else  /* 使用定时器1作为波特率发生器 */
  46.     {
  47.         TR1 = 0;        /* 停止定时器1 */
  48.         AUXR &= ~0x01;  /* 清除UART_M0x6位,选择定时器1为波特率发生器 */
  49.         AUXR |=  (1<<6);/* 设置T1x12位,定时器1为1T模式 */
  50.         TMOD &= ~(1<<6);/* 清除GATE位,定时器1不受外部引脚控制 */
  51.         TMOD &= ~0x30;  /* 清除M1、M0位,设置定时器1为模式0(16位自动重装载) */
  52.         TH1 = (u8)((65536UL - (MAIN_Fosc / 4) / Baudrate1) / 256);  /* 计算定时器1高8位重装载值 */
  53.         TL1 = (u8)((65536UL - (MAIN_Fosc / 4) / Baudrate1) % 256);  /* 计算定时器1低8位重装载值 */
  54.         ET1 = 0;        /* 禁止定时器1中断 */
  55.         INTCLKO &= ~0x02;  /* 清除T1CLKO位,禁止定时器1时钟输出 */
  56.         TR1  = 1;       /* 启动定时器1 */
  57.     }
  58.     SCON = (SCON & 0x3f) | 0x40;  /* 设置UART1为模式1(8位UART,可变波特率) */
  59.     ES  = 1;      /* 使能UART1中断 */
  60.     REN = 1;      /* 使能UART1接收 */
  61.     P_SW1 &= 0x3f;  /* 清除UART1引脚选择位,默认使用P3.0/RxD和P3.1/TxD */
  62.     /* 初始化UART1环形缓冲区控制变量 */
  63.     Uart1_Ctrl.rx_head = 0;    /* 接收缓冲区头指针 */
  64.     Uart1_Ctrl.rx_tail = 0;    /* 接收缓冲区尾指针 */
  65.     Uart1_Ctrl.tx_head = 0;    /* 发送缓冲区头指针 */
  66.     Uart1_Ctrl.tx_tail = 0;    /* 发送缓冲区尾指针 */
  67.     Uart1_Ctrl.tx_count = 0;   /* 发送缓冲区中待发送字节数 */
  68.     Uart1_Ctrl.tx_busy = 0;    /* 发送忙标志,0=空闲,1=正在发送 */
  69.     Uart1_Ctrl.rx_ready = 0;   /* 接收就绪标志,0=无数据,1=有数据 */
  70. }
  71. /**
  72. * @brief   配置UART2串口
  73. * @param   brt: 波特率发生器选择,目前仅支持2=使用定时器2
  74. * @retval  无
  75. * @note    该函数配置UART2使用定时器2作为波特率发生器,并初始化UART2的环形缓冲区[1](@ref)[2](@ref)
  76. */
  77. void UART2_config(u8 brt)
  78. {
  79.     if(brt == 2)  /* 使用定时器2作为波特率发生器 */
  80.     {
  81.         SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / Baudrate2);  /* 计算并设置定时器2重装载值 */
  82.         S2CON &= ~(1<<7);  /* 清除S2SM0位,设置UART2为模式0(8位UART,可变波特率) */
  83.         IE2   |= 1;        /* 使能UART2中断 */
  84.         S2CON |= (1<<4);   /* 设置S2REN位,使能UART2接收 */
  85.         P_SW2 &= ~0x01;    /* 清除S2_S位,选择UART2引脚为默认位置 */
  86.         P_SW2 |= 0;        /* 确保S2_S为0,使用P1.0/RxD2和P1.1/TxD2 */
  87.         /* 初始化UART2环形缓冲区控制变量 */
  88.         Uart2_Ctrl.rx_head = 0;    /* 接收缓冲区头指针 */
  89.         Uart2_Ctrl.rx_tail = 0;    /* 接收缓冲区尾指针 */
  90.         Uart2_Ctrl.tx_head = 0;    /* 发送缓冲区头指针 */
  91.         Uart2_Ctrl.tx_tail = 0;    /* 发送缓冲区尾指针 */
  92.         Uart2_Ctrl.tx_count = 0;   /* 发送缓冲区中待发送字节数 */
  93.         Uart2_Ctrl.tx_busy = 0;    /* 发送忙标志,0=空闲,1=正在发送 */
  94.         Uart2_Ctrl.rx_ready = 0;   /* 接收就绪标志,0=无数据,1=有数据 */
  95.     }
  96. }
  97. /**
  98. * @brief   从UART1接收缓冲区读取一个字节
  99. * @param   无
  100. * @retval  读取到的数据字节,如果缓冲区为空则返回0
  101. * @note    该函数从环形接收缓冲区读取数据,采用非阻塞方式[3](@ref)
  102. */
  103. u8 UART1_GetByte(void)
  104. {
  105.     u8 dat;
  106.     if(Uart1_Ctrl.rx_head != Uart1_Ctrl.rx_tail)  /* 检查接收缓冲区是否有数据 */
  107.     {
  108.         dat = Uart1_Ctrl.rx_buffer[Uart1_Ctrl.rx_tail];  /* 从缓冲区尾部读取数据 */
  109.         if(++Uart1_Ctrl.rx_tail >= UART_BUF_LENGTH) Uart1_Ctrl.rx_tail = 0;  /* 尾指针循环 */
  110.         if(Uart1_Ctrl.rx_head == Uart1_Ctrl.rx_tail) Uart1_Ctrl.rx_ready = 0;  /* 缓冲区为空时清除就绪标志 */
  111.         return dat;
  112.     }
  113.     return 0;  /* 缓冲区为空时返回0 */
  114. }
  115. /**
  116. * @brief   从UART2接收缓冲区读取一个字节
  117. * @param   无
  118. * @retval  读取到的数据字节,如果缓冲区为空则返回0
  119. * @note    该函数从环形接收缓冲区读取数据,采用非阻塞方式[3](@ref)
  120. */
  121. u8 UART2_GetByte(void)
  122. {
  123.     u8 dat;
  124.     if(Uart2_Ctrl.rx_head != Uart2_Ctrl.rx_tail)  /* 检查接收缓冲区是否有数据 */
  125.     {
  126.         dat = Uart2_Ctrl.rx_buffer[Uart2_Ctrl.rx_tail];  /* 从缓冲区尾部读取数据 */
  127.         if(++Uart2_Ctrl.rx_tail >= UART_BUF_LENGTH) Uart2_Ctrl.rx_tail = 0;  /* 尾指针循环 */
  128.         if(Uart2_Ctrl.rx_head == Uart2_Ctrl.rx_tail) Uart2_Ctrl.rx_ready = 0;  /* 缓冲区为空时清除就绪标志 */
  129.         return dat;
  130.     }
  131.     return 0;  /* 缓冲区为空时返回0 */
  132. }
  133. /**
  134. * @brief   向UART1发送一个字节数据
  135. * @param   dat: 要发送的数据字节
  136. * @retval  无
  137. * @note    该函数将数据放入发送缓冲区,如果发送器空闲则立即启动发送[4](@ref)
  138. */
  139. void UART1_SendByte(u8 dat)
  140. {
  141.     ES = 0;  /* 禁止UART1中断,保护缓冲区操作 */
  142.     if(Uart1_Ctrl.tx_count < UART_BUF_LENGTH)  /* 检查发送缓冲区是否有空间 */
  143.     {
  144.         Uart1_Ctrl.tx_buffer[Uart1_Ctrl.tx_head] = dat;  /* 将数据放入缓冲区头部 */
  145.         if(++Uart1_Ctrl.tx_head >= UART_BUF_LENGTH) Uart1_Ctrl.tx_head = 0;  /* 头指针循环 */
  146.         Uart1_Ctrl.tx_count++;  /* 增加待发送字节计数 */
  147.         if(!Uart1_Ctrl.tx_busy)  /* 如果发送器空闲 */
  148.         {
  149.             Uart1_Ctrl.tx_busy = 1;  /* 设置发送忙标志 */
  150.             SBUF = Uart1_Ctrl.tx_buffer[Uart1_Ctrl.tx_tail];  /* 从缓冲区尾部取出数据发送 */
  151.             if(++Uart1_Ctrl.tx_tail >= UART_BUF_LENGTH) Uart1_Ctrl.tx_tail = 0;  /* 尾指针循环 */
  152.             Uart1_Ctrl.tx_count--;  /* 减少待发送字节计数 */
  153.         }
  154.     }
  155.     ES = 1;  /* 重新使能UART1中断 */
  156. }
  157. /**
  158. * @brief   向UART2发送一个字节数据
  159. * @param   dat: 要发送的数据字节
  160. * @retval  无
  161. * @note    该函数将数据放入发送缓冲区,如果发送器空闲则立即启动发送[4](@ref)
  162. */
  163. void UART2_SendByte(u8 dat)
  164. {
  165.     IE2 &= ~0x01;  /* 禁止UART2中断,保护缓冲区操作 */
  166.     if(Uart2_Ctrl.tx_count < UART_BUF_LENGTH)  /* 检查发送缓冲区是否有空间 */
  167.     {
  168.         Uart2_Ctrl.tx_buffer[Uart2_Ctrl.tx_head] = dat;  /* 将数据放入缓冲区头部 */
  169.         if(++Uart2_Ctrl.tx_head >= UART_BUF_LENGTH) Uart2_Ctrl.tx_head = 0;  /* 头指针循环 */
  170.         Uart2_Ctrl.tx_count++;  /* 增加待发送字节计数 */
  171.         if(!Uart2_Ctrl.tx_busy)  /* 如果发送器空闲 */
  172.         {
  173.             Uart2_Ctrl.tx_busy = 1;  /* 设置发送忙标志 */
  174.             S2BUF = Uart2_Ctrl.tx_buffer[Uart2_Ctrl.tx_tail];  /* 从缓冲区尾部取出数据发送 */
  175.             if(++Uart2_Ctrl.tx_tail >= UART_BUF_LENGTH) Uart2_Ctrl.tx_tail = 0;  /* 尾指针循环 */
  176.             Uart2_Ctrl.tx_count--;  /* 减少待发送字节计数 */
  177.         }
  178.     }
  179.     IE2 |= 0x01;  /* 重新使能UART2中断 */
  180. }
  181. /**
  182. * @brief   UART1中断服务函数
  183. * @param   无
  184. * @retval  无
  185. * @note    中断号4对应UART1中断,处理接收和发送中断[1](@ref)
  186. */
  187. void UART1_int (void) interrupt 4
  188. {
  189.     if(RI)  /* 接收中断标志 */
  190.     {
  191.         RI = 0;  /* 清除接收中断标志 */
  192.         Uart1_Ctrl.rx_buffer[Uart1_Ctrl.rx_head] = SBUF;  /* 读取接收到的数据存入缓冲区 */
  193.         if(++Uart1_Ctrl.rx_head >= UART_BUF_LENGTH) Uart1_Ctrl.rx_head = 0;  /* 头指针循环 */
  194.         Uart1_Ctrl.rx_ready = 1;  /* 设置接收就绪标志 */
  195.     }
  196.     if(TI)  /* 发送中断标志 */
  197.     {
  198.         TI = 0;  /* 清除发送中断标志 */
  199.         if(Uart1_Ctrl.tx_count > 0)  /* 如果发送缓冲区还有数据 */
  200.         {
  201.             SBUF = Uart1_Ctrl.tx_buffer[Uart1_Ctrl.tx_tail];  /* 发送下一个字节 */
  202.             if(++Uart1_Ctrl.tx_tail >= UART_BUF_LENGTH) Uart1_Ctrl.tx_tail = 0;  /* 尾指针循环 */
  203.             Uart1_Ctrl.tx_count--;  /* 减少待发送字节计数 */
  204.         }
  205.         else
  206.         {
  207.             Uart1_Ctrl.tx_busy = 0;  /* 发送完成,清除发送忙标志 */
  208.         }
  209.     }
  210. }
  211. /**
  212. * @brief   UART2中断服务函数
  213. * @param   无
  214. * @retval  无
  215. * @note    中断号8对应UART2中断,处理接收和发送中断[1](@ref)
  216. */
  217. void UART2_int (void) interrupt 8
  218. {
  219.     if((S2CON & 1) != 0)  /* 检查UART2接收中断标志S2RI */
  220.     {
  221.         S2CON &= ~1;  /* 清除接收中断标志 */
  222.         Uart2_Ctrl.rx_buffer[Uart2_Ctrl.rx_head] = S2BUF;  /* 读取接收到的数据存入缓冲区 */
  223.         if(++Uart2_Ctrl.rx_head >= UART_BUF_LENGTH) Uart2_Ctrl.rx_head = 0;  /* 头指针循环 */
  224.         Uart2_Ctrl.rx_ready = 1;  /* 设置接收就绪标志 */
  225.     }
  226.     if((S2CON & 2) != 0)  /* 检查UART2发送中断标志S2TI */
  227.     {
  228.         S2CON &= ~2;  /* 清除发送中断标志 */
  229.         if(Uart2_Ctrl.tx_count > 0)  /* 如果发送缓冲区还有数据 */
  230.         {
  231.             S2BUF = Uart2_Ctrl.tx_buffer[Uart2_Ctrl.tx_tail];  /* 发送下一个字节 */
  232.             if(++Uart2_Ctrl.tx_tail >= UART_BUF_LENGTH) Uart2_Ctrl.tx_tail = 0;  /* 尾指针循环 */
  233.             Uart2_Ctrl.tx_count--;  /* 减少待发送字节计数 */
  234.         }
  235.         else
  236.         {
  237.             Uart2_Ctrl.tx_busy = 0;  /* 发送完成,清除发送忙标志 */
  238.         }
  239.     }
  240. }
  241. /**
  242. * @brief   处理指定UART接收到的命令并回传响应
  243. * @param   uart_num: 指定要处理的UART编号(1代表UART1,2代表UART2)
  244. * @retval  无
  245. * @note    此函数会检查对应UART的接收就绪标志,读取一个字节数据,
  246. *          并根据预定义的命令映射表发送相应的响应字节。
  247. *          如果接收到的数据不是预定义命令,则发送0xFF作为错误响应。
  248. *          该函数还根据命令调用舵机控制函数[6](@ref)[8](@ref)
  249. */
  250. void UART_ProcessCommand(u8 uart_num)
  251. {
  252.     u8 received_data;
  253.     u8 response_data;
  254.     u8 is_data_ready = 0; /* 使用u8替代bool,0表示假,非0表示真 */
  255.     /* 1. 根据UART编号选择对应的控制结构体和硬件接口 */
  256.     switch(uart_num)
  257.     {
  258.         case 1:
  259.             is_data_ready = Uart1_Ctrl.rx_ready; /* 检查UART1接收就绪标志 */
  260.             break;
  261.         case 2:
  262.             is_data_ready = Uart2_Ctrl.rx_ready; /* 检查UART2接收就绪标志 */
  263.             break;
  264.         default:
  265.             return; /* 无效的UART编号,直接返回 */
  266.     }
  267.     /* 2. 判断是否有数据就绪 */
  268.     if(is_data_ready != 0)
  269.     {
  270.         /* 3. 从对应的UART读取一个字节数据 */
  271.         if(uart_num == 1)
  272.         {
  273.             received_data = UART1_GetByte(); /* 从UART1接收缓冲区读取数据 */
  274.         }
  275.         else if(uart_num == 2)
  276.         {
  277.             received_data = UART2_GetByte(); /* 从UART2接收缓冲区读取数据 */
  278.         }
  279.         /* 4. 命令解析与响应映射 */
  280.         switch(received_data)
  281.         {
  282.             case 0x01:
  283.                 response_data = 0x01;
  284.                 SetServoAngle(0);   /* 设置舵机到0度位置 */
  285.                 break;
  286.             case 0x02:
  287.                 response_data = 0x02;
  288.                 SetServoAngle(45);  /* 设置舵机到45度位置 */
  289.                 break;
  290.             case 0x03:
  291.                 response_data = 0x03;
  292.                 SetServoAngle(90);  /* 设置舵机到90度位置 */
  293.                 break;
  294.             case 0x04:
  295.                 response_data = 0x04;
  296.                 SetServoAngle(135); /* 设置舵机到135度位置 */
  297.                 break;
  298.             case 0x05:
  299.                 response_data = 0x05;
  300.                 SetServoAngle(180); /* 设置舵机到180度位置 */
  301.                 break;
  302.             default:
  303.                 response_data = 0xFF; /* 未定义命令的默认响应 */
  304.                 break;
  305.         }
  306.         /* 5. 通过对应的UART发送响应字节 */
  307.         if(uart_num == 1)
  308.         {
  309.             UART1_SendByte(response_data); /* 通过UART1发送响应 */
  310.         }
  311.         else if(uart_num == 2)
  312.         {
  313.             UART2_SendByte(response_data); /* 通过UART2发送响应 */
  314.         }
  315.     }
  316. }
复制代码
缓慢而坚定,为值得的事情倾注所有。
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:70
  • 最近打卡:2026-03-29 16:54:48
已绑定手机

2

主题

37

回帖

120

积分

注册会员

积分
120
发表于 2026-1-3 09:08:44 | 显示全部楼层
  1. /**
  2. * @file    STC8G_H_UART1_UART2.h
  3. * @brief   STC8系列单片机UART1和UART2驱动头文件
  4. * @details 定义UART控制结构体、缓冲区大小和函数声明
  5. * @author  [志城]
  6. * @date    2025-12-24
  7. * @version 1.0
  8. */
  9. #ifndef __STC8G_H_UART1_UART2_H__
  10. #define __STC8G_H_UART1_UART2_H__
  11. #include        "config.h"
  12. #define MAIN_Fosc       22118400L
  13. #define Baudrate1       115200UL
  14. #define Baudrate2       115200UL
  15. #define UART_BUF_LENGTH 64
  16. typedef struct {
  17.     u8  rx_buffer[UART_BUF_LENGTH];
  18.     u8  tx_buffer[UART_BUF_LENGTH];
  19.     u8  rx_head;
  20.     u8  rx_tail;
  21.     u8  tx_head;
  22.     u8  tx_tail;
  23.     u8  tx_count;
  24.     u8  tx_busy;
  25.     u8  rx_ready;
  26. } UART_Control_t;
  27. extern UART_Control_t xdata Uart1_Ctrl;
  28. extern UART_Control_t xdata Uart2_Ctrl;
  29. void UART1_config(u8 brt);
  30. void UART2_config(u8 brt);
  31. void SetTimer2Baudraye(u16 dat);
  32. u8 UART1_GetByte(void);
  33. u8 UART2_GetByte(void);
  34. void UART1_SendByte(u8 dat);
  35. void UART2_SendByte(u8 dat);
  36. void UART1_SendString(u8 *str);
  37. void UART2_SendString(u8 *str);
  38. void UART1_int(void);
  39. void UART2_int(void);
  40. void UART_ProcessCommand(u8 uart_num);
  41. #endif
复制代码
缓慢而坚定,为值得的事情倾注所有。
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:70
  • 最近打卡:2026-03-29 16:54:48
已绑定手机

2

主题

37

回帖

120

积分

注册会员

积分
120
发表于 2026-1-3 09:11:05 | 显示全部楼层
管理别删我的帖子,我要把前几个楼占住,这样好形成一个系统。会逐步修改添加内容。

之前的被你删除了,我只好先糊代码上来占楼,还请理解。
缓慢而坚定,为值得的事情倾注所有。
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:70
  • 最近打卡:2026-03-29 16:54:48
已绑定手机

2

主题

37

回帖

120

积分

注册会员

积分
120
发表于 2026-1-3 09:12:01 | 显示全部楼层
配套的压缩包在这里,大家别复制代码了

07A1、基于STC8H1K08单片机通过两个串口控制一路舵机已成功;版本2512260453.zip

704.64 KB, 下载次数: 6

缓慢而坚定,为值得的事情倾注所有。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:370
  • 最近打卡:2026-04-04 12:33:12

844

主题

1万

回帖

2万

积分

管理员

积分
22805
发表于 2026-1-3 09:33:39 | 显示全部楼层
【STCAI-万能实验板-V2.x】+ STC8H 快速入门 配套教程
可放我们论坛第一个版块,
也建议发B站:DIY 学 最强 51-MCU

截图202601030935189476.jpg

截图202601030936022376.jpg
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-4-4 16:04 , Processed in 0.122072 second(s), 82 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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