好的,根据 STC8H 数据手册,STC8H1K08 拥有 16位高级PWM定时器(PWMA + PWMB) 和 9通道10位ADC,完全能够实现您的需求。下面我基于手册中的范例为您整理两个演示程序。
一、STC8H1K08 的可用资源(TSSOP20/SOP20封装)
| 功能 |
可用引脚 |
| PWMA 输入捕获 |
PWM1P(P1.0)、PWM2P(P1.2)、PWM3P(P1.4)、PWM4P(P1.6) |
| PWMB 输入捕获 |
PWM5(P1.7, 需切脚)、PWM7(P3.3)、PWM8(P3.4) |
| ADC 输入 |
ADC0(P1.0)、ADC1(P1.1)、ADC8(P3.0)、ADC9(P3.1)、ADC10ADC14(P3.2P3.6) |
| PWM 输出 |
PWM1P/N(P1.0/P1.1)、PWM2P/N(P1.2/P1.3)、PWM3P/N(P1.4/P1.5) 等 |
二、演示程序 1:测量2路外部信号的周期和占空比
测量原理(参考手册 25.10.12 节):利用高级PWM内部的两通道捕获模块 CCx 和 CCx+1 同时捕获同一引脚——CCx 捕获上升沿,CCx+1 捕获下降沿,并设置上升沿为复位触发信号。CCx 的捕获值即为周期,CCx+1 的捕获值即为占空比(高电平宽度)。
- 第1路:PWMA 的 CC1+CC2 捕获 PWM1P(P1.0),复位触发
- 第2路:PWMB 的 CC5+CC6 捕获 PWM5(P1.7),复位触发(需通过 PWMB_PS 将 PWM5 切到 P1.7)
// 测试工作频率:11.0592MHz
// 第1路信号输入:P1.0(PWM1P)
// 第2路信号输入:P1.7(PWM5, 经PWMB_PS切脚)
// 测量结果通过串口1打印输出(P3.0/RxD, P3.1/TxD, 波特率115200)
#include "stc8h.h"
#include "intrins.h"
#include "stdio.h"
#define FOSC 11059200UL
#define BRT (65536 - FOSC / 115200 / 4)
unsigned int g_Period1, g_Duty1; // 第1路周期和占空比(高电平宽度)
unsigned int g_Period2, g_Duty2; // 第2路周期和占空比(高电平宽度)
bit bCap1_Ready, bCap2_Ready;
void UART1_Init(void)
{
SCON = 0x52; // 模式1, 8位UART, 允许接收
T2L = BRT;
T2H = BRT >> 8;
AUXR |= 0x15; // T2做波特率发生器, 1T模式
}
void main()
{
P_SW2 |= 0x80; // 使能访问XFR
// I/O口全部设为双向口
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
UART1_Init();
printf("STC8H1K08 2路信号周期和占空比测量\r\n");
// ========== 第1路:PWMA CC1+CC2 捕获 PWM1P(P1.0) ==========
PWMA_CCER1 = 0x00; // 先关闭通道
PWMA_CCMR1 = 0x01; // CC1输入模式,映射到TI1FP1
PWMA_CCMR2 = 0x02; // CC2输入模式,映射到TI1FP2
PWMA_CCER1 = 0x11; // 使能CC1和CC2捕获
// CC1上升沿(默认) | CC2下降沿(bit5置1)
PWMA_CCER1 |= 0x20; // CC2捕获下降沿
PWMA_SMCR = 0x54; // TS=TI1FP1, SMS=复位触发模式
PWMA_IER = 0x06; // 使能CC1和CC2中断
PWMA_CR1 = 0x01; // 使能计数器
// ========== 第2路:PWMB CC5+CC6 捕获 PWM5(P1.7) ==========
PWMB_PS = 0x40; // C5PS=01, PWM5映射到P1.7
PWMB_CCER1 = 0x00;
PWMB_CCMR1 = 0x01; // CC5输入模式,映射到TI5FP5
PWMB_CCMR2 = 0x02; // CC6输入模式,映射到TI5FP6
PWMB_CCER1 = 0x11; // 使能CC5和CC6捕获
PWMB_CCER1 |= 0x20; // CC6捕获下降沿
PWMB_SMCR = 0x54; // TS=TI5FP5, SMS=复位触发模式
PWMB_IER = 0x06; // 使能CC5和CC6中断
PWMB_CR1 = 0x01; // 使能计数器
EA = 1; // 开启总中断
while(1)
{
if(bCap1_Ready)
{
bCap1_Ready = 0;
// 周期 = g_Period1 / FOSC (单位:秒)
// 占空比 = (float)g_Duty1 / g_Period1 * 100 (%)
printf("CH1: Period=%u ticks, Duty(High)=%u ticks\r\n",
g_Period1, g_Duty1);
}
if(bCap2_Ready)
{
bCap2_Ready = 0;
printf("CH2: Period=%u ticks, Duty(High)=%u ticks\r\n",
g_Period2, g_Duty2);
}
}
}
// PWMA中断服务函数 (中断号26)
void PWMA_ISR() interrupt 26
{
if(PWMA_SR1 & 0x02) // CC1捕获中断 → 周期
{
PWMA_SR1 &= ~0x02;
g_Period1 = PWMA_CCR1;
bCap1_Ready = 1;
}
if(PWMA_SR1 & 0x04) // CC2捕获中断 → 占空比(高电平宽度)
{
PWMA_SR1 &= ~0x04;
g_Duty1 = PWMA_CCR2;
}
}
// PWMB中断服务函数 (中断号27)
void PWMB_ISR() interrupt 27
{
if(PWMB_SR1 & 0x02) // CC5捕获中断 → 周期
{
PWMB_SR1 &= ~0x02;
g_Period2 = PWMB_CCR5;
bCap2_Ready = 1;
}
if(PWMB_SR1 & 0x04) // CC6捕获中断 → 占空比(高电平宽度)
{
PWMB_SR1 &= ~0x04;
g_Duty2 = PWMB_CCR6;
}
}
📌 说明:
- 由于设置了复位触发模式(
SMS=0x54),CCx 的捕获值就是周期,CCx+1 的值就是高电平宽度(占空比),无需做减法。
- 测量值单位是 PWM时钟的 tick 数。若预分频器为1(不分频),则每个 tick = 1/FOSC 秒。例如 FOSC=11.0592MHz,测得 Period=11059 对应 1ms。
- 如需测量更低频率的信号,可增大 PWMA_PSCR 预分频器。
三、演示程序 2:ADC 输入控制 PWM 输出
实现思路:ADC 采样模拟电压 → 将 10 位 ADC 结果映射到 PWM 的比较值 → 更新占空比。
- ADC输入:P3.0/ADC8(电位器分压输入)
- PWM输出:P1.2/PWM2P(CC2输出模式,频率 = 11.0592MHz / 1000 ≈ 11kHz)
// 测试工作频率:11.0592MHz
// ADC输入:P3.0(ADC8),电位器分压
// PWM输出:P1.2(PWM2P),周期=1000 ticks,占空比由ADC值控制 0~100%
#include "stc8h.h"
#include "intrins.h"
#define PWM_PERIOD 1000 // PWM周期
void main()
{
P_SW2 |= 0x80; // 使能访问XFR
// I/O口模式配置
P1M0 = 0x00; P1M1 = 0x00; // P1.2(PWM输出)由PWM外设控制
P3M0 = 0x00; P3M1 = 0x01; // P3.0(ADC8)设为高阻输入
// ===== 1. 配置 PWM2P(P1.2) 输出 =====
PWMA_CCER1 = 0x00; // 写CCMR前先关闭通道
PWMA_CCMR2 = 0x60; // CC2设为PWM输出模式
PWMA_CCER1 |= 0x10; // 使能CC2通道(CC2E=1)
PWMA_CCR2 = 0; // 初始占空比=0
PWMA_ARR = PWM_PERIOD - 1; // 周期值(0~999)
PWMA_ENO = 0x02; // 使能PWM2P输出(bit1)
PWMA_BKR = 0x80; // 使能主输出(MOE=1)
PWMA_CR1 = 0x01; // 开始计数
// ===== 2. 配置 ADC(通道8 = P3.0) =====
ADCTIM = 0x3f; // ADC内部时序
ADCCFG = 0x0f; // ADC时钟 = sys/2/16, 结果左对齐
ADC_CONTR = 0x80; // 使能ADC模块
while(1)
{
// 启动ADC转换:通道8(P3.0)
ADC_CONTR = 0x88; // 0x80 | 0x08(通道8) | 0x40(启动)
_nop_();
_nop_();
// 等待ADC转换完成
while(!(ADC_CONTR & 0x20));
ADC_CONTR &= ~0x20; // 清除完成标志
// 读取10位ADC结果(左对齐模式)
// ADCCFG[5]=0(默认)时:ADC_RES=高8位,ADC_RESL[7:6]=低2位
unsigned int adc_val;
adc_val = (unsigned int)ADC_RES << 2;
adc_val |= (ADC_RESL >> 6) & 0x03;
// 将ADC值(0~1023)映射到PWM占空比(0~PWM_PERIOD)
PWMA_CCR2 = (unsigned int)((unsigned long)adc_val * PWM_PERIOD / 1024);
// 适当延时,避免PWM抖动太快
// 也可以去掉延时,用定时器定时更新
}
}
📌 说明:
- ADC 为 10位,左对齐模式下:
ADC结果 = (ADC_RES << 2) | (ADC_RESL >> 6)。
- 旋转电位器使 ADC 输入电压在 0~Vref 之间变化,PWM 占空比会从 0% 线性变化到 100%。
- PWM 频率 = 主时钟 / (PWM_PERIOD × 预分频)。本例未设预分频(
PWMA_PSCR=0),频率 ≈ 11.0592MHz / 1000 ≈ 11kHz。
- 如果需要更平滑的变化,可以在主循环中加一个数字滤波(多次采样取平均)。
以上两个程序均基于 STC8H 数据手册中的标准范例整合而来,已考虑 STC8H1K08(TSSOP20)的引脚资源限制。如果您需要调整具体的引脚或参数,请告知!
🔍 搜索 "STC8H1K08 如何测量2路外部信号的周期和占空比 如何通过 ADC的输入控制PWM输出 帮给个演示程序"
📌 相关论坛讨论
- STC8H1K08 P1.2 用PWM调制周期和占空比
- PWM捕获周期和占空比问题???
- 8H芯片PWM捕捉两路脉冲波信号的周期和占空比