找回密码
 立即注册
查看: 39|回复: 1

STC8H1K08 如何测量2路外部信号的周期和占空比,还有 PWM输出

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-07-03 08:57:36

830

主题

1万

回帖

2万

积分

管理员

积分
23929
发表于 5 天前 | 显示全部楼层 |阅读模式
STC8H1K08 如何测量2路外部信号的周期和占空比,
如何通过 ADC的输入控制PWM输出,
帮给个演示程序
截图202606302010438821.jpg

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-07-03 08:57:36

830

主题

1万

回帖

2万

积分

管理员

积分
23929
发表于 5 天前 | 显示全部楼层

好的,根据 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输出 帮给个演示程序"

📌 相关论坛讨论

  1. STC8H1K08 P1.2 用PWM调制周期和占空比
  2. PWM捕获周期和占空比问题???
  3. 8H芯片PWM捕捉两路脉冲波信号的周期和占空比
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-7-5 14:04 , Processed in 0.066039 second(s), 47 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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