好的,基于手册中已获取的信息,我为您编写一个完整的 STC8H8K64U 演示程序。由于手册中关于 PWM 输入捕获的详细寄存器位定义(CCMR1、CCER1、SMCR 等)在多次查询中未能展开详细内容,我将基于 STC8H 系列 PWM 高级定时器的标准架构(兼容 STM32 风格高级定时器)给出完整代码,并标注关键寄存器配置。
以下是完整的演示程序:
/******************************************************************************
* 文件名: main.c
* 芯片: STC8H8K64U
* 功能:
* 1. 串口1接收命令,启动ADC转换,返回ADC值
* 2. 串口1接收命令,改变PWM输出频率和占空比
* 3. 使用PWM输入捕获测量2路外部信号的周期和占空比
*
* 串口命令格式(字符串,以回车换行结束):
* ADC<ch>\r\n — 启动ADC通道ch(0~14)转换,返回结果
* PWM<freq>,<duty>\r\n — 设置PWM频率(Hz)和占空比(0~1000=0.0%~100.0%)
* CAP\r\n — 返回两路输入捕获的周期(us)和占空比(%)
*
* 硬件连接:
* P3.0(RxD) / P3.1(TxD) — 串口1 (115200bps)
* P1.0 — PWM输出通道 (PWMA_CH1)
* P1.1 — 输入捕获通道1 (PWMA_CH2)
* P1.2 — 输入捕获通道2 (PWMA_CH3)
* ADC通道0~14对应P1.0~P1.7, P0.0~P0.6等,根据实际接线选择
******************************************************************************/
#include "STC8H.h"
#include <stdio.h>
#include <string.h>
/**************************** 用户配置宏 *************************************/
#define MAIN_Fosc 24000000UL // 系统主频 24MHz
#define BAUD 115200 // 串口波特率
#define PWM_FREQ_DEFAULT 1000 // 默认PWM输出频率 1kHz
#define PWM_DUTY_DEFAULT 500 // 默认占空比 50.0% (0~1000)
#define CAPTURE_CLOCK 24000000UL // 捕获定时器时钟 = 系统时钟(不分频)
/**************************** 全局变量 ***************************************/
volatile uint8_t UART1_RxFlag = 0; // 串口收到一帧标志
volatile uint8_t UART1_RxBuf[64]; // 串口接收缓冲区
volatile uint8_t UART1_RxCnt = 0; // 接收计数
// ADC 结果
volatile uint16_t ADC_Result = 0;
volatile uint8_t ADC_FinishFlag = 0;
// 输入捕获结果
volatile uint16_t Cap1_Period = 0; // 捕获通道1 周期 (定时器计数值)
volatile uint16_t Cap1_Duty = 0; // 捕获通道1 高电平宽度
volatile uint16_t Cap2_Period = 0; // 捕获通道2 周期
volatile uint16_t Cap2_Duty = 0; // 捕获通道2 高电平宽度
volatile uint8_t Cap1_Ready = 0; // 捕获1数据就绪
volatile uint8_t Cap2_Ready = 0; // 捕获2数据就绪
/**************************** 函数声明 ***************************************/
void System_Init(void);
void UART1_Init(void);
void ADC_Init(void);
void PWM_Output_Init(void);
void PWM_Capture_Init(void);
void SendByte(uint8_t dat);
void SendString(uint8_t *str);
void SendHex16(uint16_t val);
void SendDec16(uint16_t val);
void ProcessCommand(uint8_t *cmd);
void Delay_ms(uint16_t ms);
/**************************** 主函数 *****************************************/
void main(void)
{
System_Init();
UART1_Init();
ADC_Init();
PWM_Output_Init();
PWM_Capture_Init();
EA = 1; // 开总中断
SendString("STC8H8K64U Demo Ready.\r\n");
SendString("Commands: ADC<ch>, PWM<freq>,<duty>, CAP\r\n");
while(1)
{
if(UART1_RxFlag)
{
UART1_RxFlag = 0;
ProcessCommand((uint8_t *)UART1_RxBuf);
UART1_RxCnt = 0;
memset((uint8_t *)UART1_RxBuf, 0, 64);
}
}
}
/**************************** 系统初始化 *************************************/
void System_Init(void)
{
// 选择内部IRC 24MHz
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
// 设置内部IRC频率为24MHz
IRC24M_CR = 0x80; // 使能24MHz内部IRC
while(!(IRC24M_CR & 0x01)); // 等待稳定
CLKSEL = 0x00; // 选择内部IRC为主时钟
}
/**************************** 串口1初始化 ************************************/
void UART1_Init(void)
{
// 定时器1作为波特率发生器,8位自动重装,模式0
// 波特率 = 定时器1溢出率 / 4
// 定时器1溢出率 = MAIN_Fosc / 12 / (256 - TH1)
// TH1 = 256 - MAIN_Fosc / 12 / 4 / BAUD
// 115200bps @ 24MHz: TH1 = 256 - 24000000/12/4/115200 = 256 - 4.34 ≈ 252 (0xFC)
AUXR |= 0x40; // 定时器1使用1T模式 (AUXR.6 = T1x12)
TMOD &= 0x0F; // 定时器1模式0 (16位自动重装)
TMOD |= 0x20; // 实际设置为模式2 (8位自动重装)
// 1T模式下: 波特率 = MAIN_Fosc / 4 / (256 - TH1)
// TH1 = 256 - MAIN_Fosc / 4 / BAUD
// 115200: TH1 = 256 - 24000000/4/115200 = 256 - 52.08 = 204 (0xCC)
TH1 = 256 - MAIN_Fosc / 4 / BAUD;
TL1 = TH1;
TR1 = 1; // 启动定时器1
SCON = 0x50; // 模式1 (8位UART), REN=1 允许接收
ES = 1; // 使能串口1中断
}
/**************************** ADC初始化 **************************************/
void ADC_Init(void)
{
// ADC_CONTR: 0xBC 地址
// 位7: ADC_POWER ADC电源
// 位6: ADC_START 启动转换
// 位5: ADC_FLAG 转换完成标志
// 位4: 保留
// 位3-0: ADC_CHS 通道选择
// ADCCFG: 0xDE 地址
// 位7: 保留
// 位6-4: 时钟分频 (000=2分频, 001=4分频, ...)
// 位3: 保留
// 位0: RESFMT (0=左对齐, 1=右对齐)
P1M0 = 0x00; P1M1 = 0x00; // ADC通道对应IO设为高阻输入(实际由ADC功能自动配置)
ADCCFG = 0x20; // 时钟分频: 010 = 8分频, ADC时钟=24MHz/2/8=1.5MHz
ADCCFG |= 0x01; // RESFMT=1 右对齐 (结果在ADC_RESL和ADC_RES中)
ADC_CONTR = 0x80; // 开启ADC电源
Delay_ms(1); // 等待ADC电源稳定
}
/**************************** PWM输出初始化 **********************************/
void PWM_Output_Init(void)
{
// 使用PWMA (高级PWM定时器A)
// 输出通道: PWMA_CH1 (P1.0)
// 边沿对齐模式, 向上计数
// 1. 使能PWM外设时钟
PWMA_CR1 = 0x00; // 先关闭计数器
// 2. 配置P1.0为PWMA_CH1输出
P1M0 |= 0x01; // P1.0 推挽输出
P1M1 &= ~0x01;
// 3. 设置预分频器 (PSCR)
// PWM频率 = MAIN_Fosc / (PSCR+1) / (ARR+1)
// 默认1kHz: 设PSCR=0, ARR=24000-1=23999
PWMA_PSCRH = 0x00;
PWMA_PSCRL = 0x00; // 预分频 = 0, 不分频
PWMA_ARRH = (uint8_t)((MAIN_Fosc / PWM_FREQ_DEFAULT - 1) >> 8);
PWMA_ARRL = (uint8_t)(MAIN_Fosc / PWM_FREQ_DEFAULT - 1);
// 4. 设置占空比 (CCR1)
uint16_t arr = MAIN_Fosc / PWM_FREQ_DEFAULT;
uint16_t ccr = (uint16_t)((uint32_t)arr * PWM_DUTY_DEFAULT / 1000);
PWMA_CCR1H = (uint8_t)(ccr >> 8);
PWMA_CCR1L = (uint8_t)(ccr);
// 5. 配置CCMR1为输出模式 (OC1)
// CCMR1: 位6:4 OC1M = 110 (PWM模式1), 位3 OC1PE=0 (预装载禁止)
PWMA_CCMR1 = 0x60; // PWM模式1, 向上计数时OC1低电平有效
// 6. 使能CC1输出, 极性高电平有效
// CCER1: 位0 CC1E=1 使能, 位1 CC1P=0 高电平有效
PWMA_CCER1 = 0x01;
// 7. 主输出使能
PWMA_BKR = 0x80; // MOE=1 主输出使能
// 8. 使能自动重装载预装载, 启动计数器
PWMA_CR1 = 0x01; // CEN=1 使能计数器
}
/**************************** PWM输入捕获初始化 ******************************/
void PWM_Capture_Init(void)
{
// 使用PWMA的CH2(P1.1)和CH3(P1.2)作为输入捕获
// 测量外部信号的周期和占空比
//
// 原理: 配置两个通道为输入捕获模式
// CH2: 上升沿捕获 -> 记录高电平开始
// 下降沿捕获(从模式: 复位+触发) -> 记录高电平结束 = 占空比宽度
// 实际简化方案: 使用CH2捕获上升沿和下降沿分别测量
// 配置P1.1, P1.2为高阻输入
P1M0 &= ~0x06;
P1M1 |= 0x06;
// 配置PWMA_CH2 (P1.1) 输入捕获
// CCMR1的位15:8对应CC2配置
// 但寄存器是8位, 需要操作PWMA_CCMR1的高字节?
// STC8H的PWMA_CCMR1是8位寄存器(地址FED0H), 控制CC1
// PWMA_CCMR2是8位寄存器(地址FED1H), 控制CC2
// 需要查具体寄存器地址映射
// 由于手册中PWM输入捕获的详细寄存器映射信息有限,
// 以下给出基于标准高级定时器架构的配置框架,
// 实际使用时请参考STC8H手册"25 16位高级PWM定时器"章节的寄存器定义
// === 以下为配置框架,具体寄存器地址需对照手册确认 ===
// 1. 配置CC2为输入, 映射到TI2, 无预分频, 无滤波
// PWMA_CCMR2 = 0x01; // CC2S=01 (CC2通道配置为输入, IC2映射到TI2)
// 2. 配置CC3为输入, 映射到TI3
// PWMA_CCMR3 = 0x01; // CC3S=01
// 3. 配置CC2捕获极性: 上升沿 (用于测量周期)
// PWMA_CCER1 &= ~0x10; // CC2P=0 上升沿捕获
// 4. 配置CC3捕获极性: 下降沿 (用于测量占空比)
// PWMA_CCER2 |= 0x10; // CC3P=1 下降沿捕获
// 5. 使能CC2, CC3捕获
// PWMA_CCER1 |= 0x10; // CC2E=1
// PWMA_CCER2 |= 0x10; // CC3E=1
// 6. 配置从模式: 复位模式, 触发源为TI2
// PWMA_SMCR = 0x54; // SMS=100 (复位模式), TS=101 (TI2)
// 7. 使能捕获中断
// PWMA_IER |= 0x0C; // CC2IE=1, CC3IE=1
// 8. 使能计数器
// PWMA_CR1 |= 0x01; // CEN=1
// 注意: 以上寄存器名称和地址请以STC8H手册第25章为准
// 实际项目中请查阅手册确认 PWMA_CCMR2, PWMA_CCMR3, PWMA_SMCR 等寄存器的地址和位定义
}
/**************************** 串口1中断服务 **********************************/
void UART1_ISR(void) interrupt 4
{
uint8_t dat;
if(RI)
{
RI = 0;
dat = SBUF;
if(dat == '\n' || dat == '\r')
{
if(UART1_RxCnt > 0)
{
UART1_RxBuf[UART1_RxCnt] = '\0';
UART1_RxFlag = 1;
}
}
else
{
if(UART1_RxCnt < 63)
{
UART1_RxBuf[UART1_RxCnt++] = dat;
}
}
}
if(TI)
{
TI = 0;
}
}
/**************************** ADC中断服务 ************************************/
void ADC_ISR(void) interrupt 13
{
if(ADC_CONTR & 0x20) // ADC_FLAG
{
ADC_CONTR &= ~0x20; // 清除标志
// 右对齐: ADC_RES[3:0] + ADC_RESL[7:0] = 12位结果
ADC_Result = ((uint16_t)ADC_RES << 8) | ADC_RESL;
ADC_FinishFlag = 1;
}
}
/**************************** PWM捕获中断服务 ********************************/
void PWMA_ISR(void) interrupt 22
{
uint8_t sr1;
// 读取状态寄存器 (需根据手册确认具体寄存器)
// sr1 = PWMA_SR1;
// if(sr1 & 0x04) // CC2IF
// {
// PWMA_SR1 &= ~0x04;
// Cap1_Period = ((uint16_t)PWMA_CCR2H << 8) | PWMA_CCR2L;
// Cap1_Ready = 1;
// }
// if(sr1 & 0x08) // CC3IF
// {
// PWMA_SR1 &= ~0x08;
// Cap2_Period = ((uint16_t)PWMA_CCR3H << 8) | PWMA_CCR3L;
// Cap2_Ready = 1;
// }
}
/**************************** 命令处理 ***************************************/
void ProcessCommand(uint8_t *cmd)
{
// 命令格式1: ADC<ch> 例如 ADC5
if(cmd[0] == 'A' && cmd[1] == 'D' && cmd[2] == 'C')
{
uint8_t ch = 0;
uint8_t i = 3;
// 解析通道号
while(cmd[i] >= '0' && cmd[i] <= '9')
{
ch = ch * 10 + (cmd[i] - '0');
i++;
}
if(ch > 14)
{
SendString("ADC channel 0~14 only.\r\n");
return;
}
// 启动ADC转换
ADC_CONTR &= 0xF0; // 清除通道选择
ADC_CONTR |= ch; // 设置通道
ADC_FinishFlag = 0;
ADC_CONTR |= 0x40; // 启动转换 (ADC_START=1)
// 等待转换完成 (或使用中断)
uint16_t timeout = 10000;
while(!ADC_FinishFlag && timeout--);
if(ADC_FinishFlag)
{
SendString("ADC_CH");
SendDec16(ch);
SendString(" = ");
SendDec16(ADC_Result);
SendString(" (0x");
SendHex16(ADC_Result);
SendString(")\r\n");
}
else
{
SendString("ADC timeout.\r\n");
}
return;
}
// 命令格式2: PWM<freq>,<duty> 例如 PWM2000,750
if(cmd[0] == 'P' && cmd[1] == 'W' && cmd[2] == 'M')
{
uint32_t freq = 0;
uint16_t duty = 0;
uint8_t i = 3;
// 解析频率
while(cmd[i] >= '0' && cmd[i] <= '9')
{
freq = freq * 10 + (cmd[i] - '0');
i++;
}
if(cmd[i] == ',') i++;
// 解析占空比 (0~1000 对应 0.0%~100.0%)
while(cmd[i] >= '0' && cmd[i] <= '9')
{
duty = duty * 10 + (cmd[i] - '0');
i++;
}
if(freq < 10 || freq > 1000000)
{
SendString("Freq 10~1000000 Hz.\r\n");
return;
}
if(duty > 1000)
{
SendString("Duty 0~1000 (0.0%~100.0%).\r\n");
return;
}
// 更新PWM频率 (ARR)
uint16_t arr = (uint16_t)(MAIN_Fosc / freq - 1);
PWMA_ARRH = (uint8_t)(arr >> 8);
PWMA_ARRL = (uint8_t)(arr);
// 更新占空比 (CCR1)
uint16_t ccr = (uint16_t)((uint32_t)arr * duty / 1000);
PWMA_CCR1H = (uint8_t)(ccr >> 8);
PWMA_CCR1L = (uint8_t)(ccr);
SendString("PWM set: ");
SendDec16((uint16_t)freq);
SendString("Hz, ");
SendDec16(duty);
SendString("/1000 duty.\r\n");
return;
}
// 命令格式3: CAP 读取输入捕获结果
if(cmd[0] == 'C' && cmd[1] == 'A' && cmd[2] == 'P')
{
SendString("--- Input Capture ---\r\n");
// 通道1
if(Cap1_Ready)
{
uint32_t period_us = (uint32_t)Cap1_Period * 1000000UL / CAPTURE_CLOCK;
uint32_t duty_percent = (uint32_t)Cap1_Duty * 10000UL / Cap1_Period;
SendString("CH1: Period=");
SendDec16((uint16_t)period_us);
SendString("us, Duty=");
SendDec16((uint16_t)(duty_percent / 100));
SendString(".");
SendDec16((uint16_t)(duty_percent % 100));
SendString("%\r\n");
}
else
{
SendString("CH1: No signal detected.\r\n");
}
// 通道2
if(Cap2_Ready)
{
uint32_t period_us = (uint32_t)Cap2_Period * 1000000UL / CAPTURE_CLOCK;
uint32_t duty_percent = (uint32_t)Cap2_Duty * 10000UL / Cap2_Period;
SendString("CH2: Period=");
SendDec16((uint16_t)period_us);
SendString("us, Duty=");
SendDec16((uint16_t)(duty_percent / 100));
SendString(".");
SendDec16((uint16_t)(duty_percent % 100));
SendString("%\r\n");
}
else
{
SendString("CH2: No signal detected.\r\n");
}
return;
}
// 未知命令
SendString("Unknown command.\r\n");
}
/**************************** 串口发送函数 ***********************************/
void SendByte(uint8_t dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
void SendString(uint8_t *str)
{
while(*str)
{
SendByte(*str++);
}
}
void SendHex16(uint16_t val)
{
uint8_t i;
uint8_t hex[4];
hex[0] = (val >> 12) & 0x0F;
hex[1] = (val >> 8) & 0x0F;
hex[2] = (val >> 4) & 0x0F;
hex[3] = val & 0x0F;
for(i = 0; i < 4; i++)
{
if(hex[i] < 10)
SendByte('0' + hex[i]);
else
SendByte('A' + hex[i] - 10);
}
}
void SendDec16(uint16_t val)
{
uint8_t buf[6];
uint8_t i = 0;
if(val == 0)
{
SendByte('0');
return;
}
while(val > 0)
{
buf[i++] = '0' + (val % 10);
val /= 10;
}
while(i > 0)
{
SendByte(buf[--i]);
}
}
/**************************** 简单延时 ***************************************/
void Delay_ms(uint16_t ms)
{
uint16_t i, j;
for(i = 0; i < ms; i++)
{
// 24MHz下约1ms
for(j = 0; j < 3000; j++);
}
}
程序说明
一、串口命令系统
| 命令格式 |
功能 |
示例 |
ADC<ch> |
启动ADC通道ch(0~14)转换,返回12位结果 |
ADC5 → 返回通道5的ADC值 |
PWM<freq>,<duty> |
设置PWM输出频率(Hz)和占空比(0~1000) |
PWM2000,750 → 2kHz, 75.0% |
CAP |
返回两路输入捕获测量的周期(us)和占空比(%) |
CAP → 显示CH1/CH2结果 |
二、功能模块说明
1. ADC转换(参考手册第21章 "ADC模数转换")
- 12位分辨率,右对齐格式
- 通过串口命令指定通道,软件触发启动
- 支持中断方式读取结果
2. PWM输出(参考手册第25章 "16位高级PWM定时器")
- 使用 PWMA_CH1 (P1.0) 输出
- 边沿对齐PWM模式1
- 通过串口命令动态调整频率和占空比
3. PWM输入捕获(参考手册第25章 "16位高级PWM定时器")
- 使用 PWMA_CH2 (P1.1) 和 PWMA_CH3 (P1.2)
- 测量外部信号的周期和占空比
- 注意:代码中给出了输入捕获的配置框架,但
PWMA_CCMR2、PWMA_CCMR3、PWMA_SMCR 等寄存器的具体地址和位定义,请查阅 STC8H 手册第25章的详细寄存器描述进行确认和补全。
三、硬件连接
| 引脚 |
功能 |
| P3.0 / P3.1 |
串口1 (RxD/TxD) 115200bps |
| P1.0 |
PWM输出 (PWMA_CH1) |
| P1.1 |
输入捕获通道1 (PWMA_CH2) |
| P1.2 |
输入捕获通道2 (PWMA_CH3) |
| ADC通道 |
根据实际接线选择 P1.0P1.7, P0.0P0.6 等 |
四、使用说明
- 用 STC-ISP 软件下载程序,选择内部IRC 24MHz
- 打开串口助手,波特率 115200
- 发送
ADC0 测试ADC通道0
- 发送
PWM5000,300 设置PWM为5kHz、占空比30%
- 发送
CAP 读取输入捕获结果
提示:输入捕获部分的寄存器配置(PWMA_CCMR2、PWMA_CCMR3、PWMA_SMCR、PWMA_SR1 等)在手册第25章中有详细定义,请根据实际芯片版本对照手册补全寄存器地址和位域值。
🔍 搜索 "STC8H8K64U 串口 接收 命令 程序"
⏱️ 查找用时 33.5s | 总回答用时 84.7s