神农鼎 发表于 2026-6-30 20:10:47

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

STC8H1K08 如何测量2路外部信号的周期和占空比,
如何通过 ADC的输入控制PWM输出,
帮给个演示程序


神农鼎 发表于 2026-6-30 20:11:19

<p>好的,根据 STC8H 数据手册,STC8H1K08 拥有 <strong>16位高级PWM定时器(PWMA + PWMB)</strong> 和 <strong>9通道10位ADC</strong>,完全能够实现您的需求。下面我基于手册中的范例为您整理两个演示程序。</p>
<hr />
<h2>一、STC8H1K08 的可用资源(TSSOP20/SOP20封装)</h2>
<table>
<thead>
<tr>
<th align="left">功能</th>
<th align="left">可用引脚</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><strong>PWMA 输入捕获</strong></td>
<td align="left">PWM1P(P1.0)、PWM2P(P1.2)、PWM3P(P1.4)、PWM4P(P1.6)</td>
</tr>
<tr>
<td align="left"><strong>PWMB 输入捕获</strong></td>
<td align="left">PWM5(P1.7, 需切脚)、PWM7(P3.3)、PWM8(P3.4)</td>
</tr>
<tr>
<td align="left"><strong>ADC 输入</strong></td>
<td align="left">ADC0(P1.0)、ADC1(P1.1)、ADC8(P3.0)、ADC9(P3.1)、ADC10<del>ADC14(P3.2</del>P3.6)</td>
</tr>
<tr>
<td align="left"><strong>PWM 输出</strong></td>
<td align="left">PWM1P/N(P1.0/P1.1)、PWM2P/N(P1.2/P1.3)、PWM3P/N(P1.4/P1.5) 等</td>
</tr>
</tbody>
</table>
<hr />
<h2>二、演示程序 1:测量2路外部信号的周期和占空比</h2>
<p><strong>测量原理</strong>(参考手册 <strong>25.10.12 节</strong>):利用高级PWM内部的两通道捕获模块 CCx 和 CCx+1 同时捕获同一引脚——CCx 捕获上升沿,CCx+1 捕获下降沿,并设置上升沿为复位触发信号。CCx 的捕获值即为<strong>周期</strong>,CCx+1 的捕获值即为<strong>占空比(高电平宽度)</strong>。</p>
<ul>
<li><strong>第1路</strong>:PWMA 的 <strong>CC1+CC2</strong> 捕获 <strong>PWM1P(P1.0)</strong>,复位触发</li>
<li><strong>第2路</strong>:PWMB 的 <strong>CC5+CC6</strong> 捕获 <strong>PWM5(P1.7)</strong>,复位触发(需通过 PWMB_PS 将 PWM5 切到 P1.7)</li>
</ul>
<pre><code class="language-c">// 测试工作频率:11.0592MHz
// 第1路信号输入:P1.0(PWM1P)
// 第2路信号输入:P1.7(PWM5, 经PWMB_PS切脚)
// 测量结果通过串口1打印输出(P3.0/RxD, P3.1/TxD, 波特率115200)

#include &quot;stc8h.h&quot;
#include &quot;intrins.h&quot;
#include &quot;stdio.h&quot;

#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 &gt;&gt; 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(&quot;STC8H1K08 2路信号周期和占空比测量\r\n&quot;);

    // ========== 第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(&quot;CH1: Period=%u ticks, Duty(High)=%u ticks\r\n&quot;,
                   g_Period1, g_Duty1);
      }
      if(bCap2_Ready)
      {
            bCap2_Ready = 0;
            printf(&quot;CH2: Period=%u ticks, Duty(High)=%u ticks\r\n&quot;,
                   g_Period2, g_Duty2);
      }
    }
}

// PWMA中断服务函数 (中断号26)
void PWMA_ISR() interrupt 26
{
    if(PWMA_SR1 &amp; 0x02)             // CC1捕获中断 → 周期
    {
      PWMA_SR1 &amp;= ~0x02;
      g_Period1 = PWMA_CCR1;
      bCap1_Ready = 1;
    }
    if(PWMA_SR1 &amp; 0x04)             // CC2捕获中断 → 占空比(高电平宽度)
    {
      PWMA_SR1 &amp;= ~0x04;
      g_Duty1 = PWMA_CCR2;
    }
}

// PWMB中断服务函数 (中断号27)
void PWMB_ISR() interrupt 27
{
    if(PWMB_SR1 &amp; 0x02)             // CC5捕获中断 → 周期
    {
      PWMB_SR1 &amp;= ~0x02;
      g_Period2 = PWMB_CCR5;
      bCap2_Ready = 1;
    }
    if(PWMB_SR1 &amp; 0x04)             // CC6捕获中断 → 占空比(高电平宽度)
    {
      PWMB_SR1 &amp;= ~0x04;
      g_Duty2 = PWMB_CCR6;
    }
}
</code></pre>
<blockquote>
<p><strong>📌 说明</strong>:</p>
<ul>
<li>由于设置了复位触发模式(<code>SMS=0x54</code>),<strong>CCx 的捕获值就是周期</strong>,<strong>CCx+1 的值就是高电平宽度(占空比)</strong>,无需做减法。</li>
<li>测量值单位是 <strong>PWM时钟的 tick 数</strong>。若预分频器为1(不分频),则每个 tick = 1/FOSC 秒。例如 FOSC=11.0592MHz,测得 Period=11059 对应 1ms。</li>
<li>如需测量更低频率的信号,可增大 PWMA_PSCR 预分频器。</li>
</ul>
</blockquote>
<hr />
<h2>三、演示程序 2:ADC 输入控制 PWM 输出</h2>
<p><strong>实现思路</strong>:ADC 采样模拟电压 → 将 10 位 ADC 结果映射到 PWM 的比较值 → 更新占空比。</p>
<ul>
<li><strong>ADC输入</strong>:<strong>P3.0/ADC8</strong>(电位器分压输入)</li>
<li><strong>PWM输出</strong>:<strong>P1.2/PWM2P</strong>(CC2输出模式,频率 = 11.0592MHz / 1000 ≈ 11kHz)</li>
</ul>
<pre><code class="language-c">// 测试工作频率:11.0592MHz
// ADC输入:P3.0(ADC8),电位器分压
// PWM输出:P1.2(PWM2P),周期=1000 ticks,占空比由ADC值控制 0~100%

#include &quot;stc8h.h&quot;
#include &quot;intrins.h&quot;

#define PWM_PERIOD1000            // 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 &amp; 0x20));
      ADC_CONTR &amp;= ~0x20;         // 清除完成标志

      // 读取10位ADC结果(左对齐模式)
      // ADCCFG=0(默认)时:ADC_RES=高8位,ADC_RESL=低2位
      unsigned int adc_val;
      adc_val= (unsigned int)ADC_RES &lt;&lt; 2;
      adc_val |= (ADC_RESL &gt;&gt; 6) &amp; 0x03;

      // 将ADC值(0~1023)映射到PWM占空比(0~PWM_PERIOD)
      PWMA_CCR2 = (unsigned int)((unsigned long)adc_val * PWM_PERIOD / 1024);

      // 适当延时,避免PWM抖动太快
      // 也可以去掉延时,用定时器定时更新
    }
}
</code></pre>
<blockquote>
<p><strong>📌 说明</strong>:</p>
<ul>
<li>ADC 为 <strong>10位</strong>,左对齐模式下:<code>ADC结果 = (ADC_RES &lt;&lt; 2) | (ADC_RESL &gt;&gt; 6)</code>。</li>
<li>旋转电位器使 ADC 输入电压在 0~Vref 之间变化,PWM 占空比会从 0% 线性变化到 100%。</li>
<li>PWM 频率 = 主时钟 / (PWM_PERIOD × 预分频)。本例未设预分频(<code>PWMA_PSCR=0</code>),频率 ≈ 11.0592MHz / 1000 ≈ <strong>11kHz</strong>。</li>
<li>如果需要更平滑的变化,可以在主循环中加一个<strong>数字滤波</strong>(多次采样取平均)。</li>
</ul>
</blockquote>
<hr />
<p>以上两个程序均基于 STC8H 数据手册中的标准范例整合而来,已考虑 STC8H1K08(TSSOP20)的引脚资源限制。如果您需要调整具体的引脚或参数,请告知!</p>
<hr />
<p><a href="https://www.stcaimcu.com/plugin.php?id=twpx_xunsearch&amp;mod=forum&amp;q=STC8H1K08%20%E5%A6%82%E4%BD%95%E6%B5%8B%E9%87%8F2%E8%B7%AF%E5%A4%96%E9%83%A8%E4%BF%A1%E5%8F%B7%E7%9A%84%E5%91%A8%E6%9C%9F%E5%92%8C%E5%8D%A0%E7%A9%BA%E6%AF%94%20%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%20ADC%E7%9A%84%E8%BE%93%E5%85%A5%E6%8E%A7%E5%88%B6PWM%E8%BE%93%E5%87%BA%20%E5%B8%AE%E7%BB%99%E4%B8%AA%E6%BC%94%E7%A4%BA%E7%A8%8B%E5%BA%8F&amp;f=_all&amp;m=yes&amp;syn=yes&amp;s=relevance&amp;fid=0">🔍 搜索 &quot;STC8H1K08 如何测量2路外部信号的周期和占空比 如何通过 ADC的输入控制PWM输出 帮给个演示程序&quot;</a></p>
<p><strong>📌 相关论坛讨论</strong></p>
<ol>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=3188&amp;highlight=STC8H1K08%20%E5%A6%82%E4%BD%95%E6%B5%8B%E9%87%8F2%E8%B7%AF%E5%A4%96%E9%83%A8%E4%BF%A1%E5%8F%B7%E7%9A%84%E5%91%A8%E6%9C%9F%E5%92%8C%E5%8D%A0%E7%A9%BA%E6%AF%94%EF%BC%8C%0A%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%20ADC%E7%9A%84%E8%BE%93%E5%85%A5%E6%8E%A7%E5%88%B6PWM%E8%BE%93%E5%87%BA%EF%BC%8C%0A%E5%B8%AE%E7%BB%99%E4%B8%AA%E6%BC%94%E7%A4%BA%E7%A8%8B%E5%BA%8F&amp;page=1&amp;extra=#pid21445">STC8H1K08 P1.2 用PWM调制周期和占空比</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=21788&amp;highlight=STC8H1K08%20%E5%A6%82%E4%BD%95%E6%B5%8B%E9%87%8F2%E8%B7%AF%E5%A4%96%E9%83%A8%E4%BF%A1%E5%8F%B7%E7%9A%84%E5%91%A8%E6%9C%9F%E5%92%8C%E5%8D%A0%E7%A9%BA%E6%AF%94%EF%BC%8C%0A%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%20ADC%E7%9A%84%E8%BE%93%E5%85%A5%E6%8E%A7%E5%88%B6PWM%E8%BE%93%E5%87%BA%EF%BC%8C%0A%E5%B8%AE%E7%BB%99%E4%B8%AA%E6%BC%94%E7%A4%BA%E7%A8%8B%E5%BA%8F&amp;page=1&amp;extra=#pid200864">PWM捕获周期和占空比问题???</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=13081&amp;highlight=STC8H1K08%20%E5%A6%82%E4%BD%95%E6%B5%8B%E9%87%8F2%E8%B7%AF%E5%A4%96%E9%83%A8%E4%BF%A1%E5%8F%B7%E7%9A%84%E5%91%A8%E6%9C%9F%E5%92%8C%E5%8D%A0%E7%A9%BA%E6%AF%94%EF%BC%8C%0A%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%20ADC%E7%9A%84%E8%BE%93%E5%85%A5%E6%8E%A7%E5%88%B6PWM%E8%BE%93%E5%87%BA%EF%BC%8C%0A%E5%B8%AE%E7%BB%99%E4%B8%AA%E6%BC%94%E7%A4%BA%E7%A8%8B%E5%BA%8F&amp;page=1&amp;extra=#pid132705">8H芯片PWM捕捉两路脉冲波信号的周期和占空比</a></li>
</ol>
页: [1]
查看完整版本: STC8H1K08 如何测量2路外部信号的周期和占空比,还有 PWM输出