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

帮产生一个 STC32G12K128的演示程序, 利用 PWM测量外部2路输入信号的周期和占空比,

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-8 21:06:11 | 显示全部楼层 |阅读模式
帮产生一个 STC32G12K128的演示程序,
利用 PWM测量外部2路输入信号的周期和占空比,
ADC 周期转换后,串口送出

截图202607082106042651.jpg

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-8 21:06:46 | 显示全部楼层

好的,基于手册中获取的 PWM 输入捕获原理、ADC 配置和串口配置信息,我来编写完整的演示程序。


STC32G12K128 演示程序 — PWM输入捕获2路信号 + ADC周期转换 + 串口输出

程序功能说明

  1. PWM输入捕获:使用 PWMA 的通道1和通道2测量第1路信号(P1.0/PWM1P)的周期和占空比;使用 PWMB 的通道1和通道2测量第2路信号(P2.0/PWM5)的周期和占空比
  2. ADC周期转换:定时器0定时触发,周期性地对 ADC 通道0(P1.0)进行转换
  3. 串口输出:通过串口1(P3.0 RxD, P3.1 TxD)将测量结果发送到上位机

硬件连接

功能 引脚 说明
第1路信号输入 P1.0 (PWM1P) PWMA 通道1/2 捕获
第2路信号输入 P2.0 (PWM5) PWMB 通道1/2 捕获
ADC输入 P1.1 (ADC1) 模拟电压采样
串口1 TxD P3.1 数据发送
串口1 RxD P3.0 数据接收(本程序未用)

完整代码

/*
 * ============================================================
 *  文件名: main.c
 *  芯片  : STC32G12K128
 *  时钟  : 内部IRC 40MHz
 *  功能  : 
 *     1. PWMA 输入捕获第1路信号(P1.0/PWM1P),测量周期和占空比
 *     2. PWMB 输入捕获第2路信号(P2.0/PWM5),测量周期和占空比
 *     3. ADC 周期转换通道1(P1.1),定时器0定时100ms启动
 *     4. 串口1(115200bps, 8N1)输出测量结果
 *  编译  : Keil C251
 *  注意  : 使用 STC32G12K128 头文件,需在 Target 中设置 Code Rom Size 为 Large
 * ============================================================
 */

#include "stc32g.h"

/* 宏定义 */
#define FOSC            40000000UL      // 系统时钟 40MHz
#define PWM_FREQ        1000000UL       // PWM计数器时钟 1MHz (40MHz/40)
#define T0_1MS          (FOSC / 1000)   // 定时器0 1ms 计数值(1T模式)

/* 全局变量 */
volatile unsigned char  g_t0_100ms_flag = 0;    // 100ms 定时标志
volatile unsigned int   g_adc_result = 0;       // ADC 转换结果

/* PWMA 捕获结果(第1路信号,P1.0/PWM1P) */
volatile unsigned int   g_pwma_period = 0;      // 周期值
volatile unsigned int   g_pwma_duty   = 0;      // 占空比值
volatile unsigned char  g_pwma_cap_ok = 0;      // 捕获完成标志

/* PWMB 捕获结果(第2路信号,P2.0/PWM5) */
volatile unsigned int   g_pwmb_period = 0;      // 周期值
volatile unsigned int   g_pwmb_duty   = 0;      // 占空比值
volatile unsigned char  g_pwmb_cap_ok = 0;      // 捕获完成标志

/* 串口发送缓冲区 */
unsigned char  g_uart_tx_buf[64];
unsigned char  g_uart_tx_len = 0;

/* 函数声明 */
void delay_ms(unsigned int ms);
void UART1_Init(void);
void UART1_SendString(unsigned char *str);
void Timer0_Init(void);
void ADC_Init(void);
unsigned int ADC_Read(unsigned char channel);
void PWMA_Cap_Init(void);
void PWMB_Cap_Init(void);

/* ======================== 延时函数 ======================== */
void delay_ms(unsigned int ms)
{
    unsigned int i, j;
    for (i = 0; i < ms; i++)
    {
        for (j = 0; j < 4000; j++);  // 约 1ms @ 40MHz
    }
}

/* ======================== 串口1初始化 ======================== */
void UART1_Init(void)
{
    /* 使用定时器2作为波特率发生器,115200bps @ 40MHz */
    /* 波特率 = SYSclk / 4 / (65536 - RL)  =>  RL = 65536 - SYSclk/4/115200 */
    /* 40MHz / 4 / 115200 ≈ 86.8, 取整 87 */
    /* 实际波特率 = 40000000 / 4 / 87 = 114942 ≈ 115200 */
  
    SCON = 0x50;            // 模式1: 8位UART, 允许接收
    AUXR |= 0x01;           // S1BRT = 1, 选择定时器2作为波特率发生器
    AUXR |= 0x04;           // T2x12 = 1, 定时器2 1T模式
    T2L = (65536 - (unsigned int)(FOSC / 4 / 115200)) & 0xFF;
    T2H = (65536 - (unsigned int)(FOSC / 4 / 115200)) >> 8;
    AUXR |= 0x10;           // T2R = 1, 启动定时器2
  
    TI = 0;                 // 清除发送中断标志
}

/* ======================== 串口1发送字符串 ======================== */
void UART1_SendString(unsigned char *str)
{
    while (*str)
    {
        SBUF = *str++;
        while (TI == 0);    // 等待发送完成
        TI = 0;
    }
}

/* ======================== 定时器0初始化 ======================== */
/* 定时100ms,用于周期启动ADC */
void Timer0_Init(void)
{
    /* 定时器0 16位自动重装载模式,1T模式 */
    /* 计数值 = FOSC / 1000 * 100 = FOSC / 10 */
    /* 40MHz / 10 = 4000000, 需用16位定时器级联或降低分频 */
    /* 改用 12T 模式: 计数值 = FOSC / 12 / 10 = 333333, 超出16位范围 */
    /* 采用 1T 模式 + 软件计数: 定时1ms, 软件计100次 */
  
    AUXR |= 0x80;           // T0x12 = 1, 定时器0 1T模式
    TMOD &= 0xF0;           // 设置定时器0为模式0 (16位自动重装载)
    TMOD |= 0x00;           // 模式0
  
    /* 1ms 计数值 = FOSC / 1000 = 40000 */
    TL0 = (65536 - T0_1MS) & 0xFF;
    TH0 = (65536 - T0_1MS) >> 8;
  
    ET0 = 1;                // 使能定时器0中断
    TR0 = 1;                // 启动定时器0
}

/* ======================== ADC初始化 ======================== */
void ADC_Init(void)
{
    /* 打开 ADC 电源 */
    ADC_CONTR = 0x80;       // ADC_POWER = 1, 其余位为0
  
    delay_ms(1);            // 等待 ADC 电源稳定(约1ms)
  
    /* 配置 ADC 时序 */
    /* 设置 ADC 时钟 = SYSclk / 2 / (SPEED+1) */
    /* SPEED = 14 => ADC时钟 = 40MHz / 2 / 15 ≈ 1.33MHz */
    ADCCFG = 0x20;          // RESFMT=1 (右对齐), SPEED=0000 (SYSclk/2/1)
    ADCCFG = 0x2E;          // RESFMT=1, SPEED=1110 (SYSclk/2/15)
  
    /* 设置采样时间 */
    P_SW2 |= 0x80;          // 使能访问 XFR
    ADCTIM = 0x2A;          // CSSETUP=0, CSHOLD=01(2), SMPDUTY=01010(11)
    P_SW2 &= 0x7F;          // 关闭 XFR 访问
}

/* ======================== ADC读取 ======================== */
unsigned int ADC_Read(unsigned char channel)
{
    unsigned int result;
  
    /* 设置通道并启动转换 */
    ADC_CONTR = (ADC_CONTR & 0xF0) | channel;  // 设置通道
    ADC_CONTR |= 0x40;                          // ADC_START = 1, 启动转换
  
    /* 等待转换完成 */
    while (!(ADC_CONTR & 0x20));                // 等待 ADC_FLAG = 1
    ADC_CONTR &= ~0x20;                         // 清除 ADC_FLAG
  
    /* 读取结果(右对齐) */
    result = ((unsigned int)ADC_RES << 8) | ADC_RESL;
  
    return result;
}

/* ======================== PWMA 输入捕获初始化 ======================== */
/* 测量第1路信号(P1.0/PWM1P)的周期和占空比 */
/* 使用通道1捕获上升沿(周期),通道2捕获下降沿(占空比) */
void PWMA_Cap_Init(void)
{
    P_SW2 |= 0x80;          // 使能访问 XFR
  
    /* 配置 PWM1P 引脚为输入模式 */
    P1M0 &= ~0x01;          // P1.0 设置为高阻输入
    P1M1 |= 0x01;
  
    /* 关闭 PWMA */
    PWMA_CR1 = 0x00;
  
    /* 设置 PWMA 时钟分频:40MHz / 40 = 1MHz */
    PWMA_PSCRH = 0x00;
    PWMA_PSCRL = 39;        // 分频值 = 39, 实际分频 = 39+1 = 40
  
    /* 设置自动重装载值(最大计数值),16位最大值 */
    PWMA_ARRH = 0xFF;
    PWMA_ARRL = 0xFF;
  
    /* 通道1配置:捕获模式,映射到 TI1,上升沿捕获 */
    PWMA_CCMR1 = 0x01;      // CC1S=01, 通道1作为输入, 映射到TI1
  
    /* 通道2配置:捕获模式,映射到 TI1FP2,下降沿捕获 */
    PWMA_CCMR2 = 0x02;      // CC2S=10, 通道2作为输入, 映射到TI1FP2
  
    /* 配置捕获极性 */
    PWMA_CCER1 = 0x00;      // 先清零
    PWMA_CCER1 |= 0x01;     // CC1E=1, CC1P=0, 通道1上升沿捕获
    PWMA_CCER1 |= 0x10;     // CC2E=1, CC2P=1, 通道2下降沿捕获
  
    /* 配置触发源为 TI1FP1,复位触发模式 */
    PWMA_SMCR = 0x00;       // 先清零
    PWMA_SMCR |= 0x54;      // SMS=100 (复位触发模式), TS=101 (选择TI1FP1)
  
    /* 使能捕获/比较中断 */
    PWMA_IER = 0x00;
    PWMA_IER |= 0x02;       // CC1IE = 1, 使能通道1捕获中断
    PWMA_IER |= 0x04;       // CC2IE = 1, 使能通道2捕获中断
  
    /* 使能 PWMA 计数器 */
    PWMA_CR1 |= 0x01;       // CEN = 1, 使能计数器
  
    P_SW2 &= 0x7F;          // 关闭 XFR 访问
}

/* ======================== PWMB 输入捕获初始化 ======================== */
/* 测量第2路信号(P2.0/PWM5)的周期和占空比 */
/* 使用通道1(PWM5)捕获上升沿(周期),通道2(PWM6)捕获下降沿(占空比) */
void PWMB_Cap_Init(void)
{
    P_SW2 |= 0x80;          // 使能访问 XFR
  
    /* 配置 PWM5 引脚(P2.0)为输入模式 */
    P2M0 &= ~0x01;          // P2.0 设置为高阻输入
    P2M1 |= 0x01;
  
    /* 配置 PWM6 引脚(P2.1)为输入模式(用于捕获下降沿) */
    P2M0 &= ~0x02;          // P2.1 设置为高阻输入
    P2M1 |= 0x02;
  
    /* 关闭 PWMB */
    PWMB_CR1 = 0x00;
  
    /* 设置 PWMB 时钟分频:40MHz / 40 = 1MHz */
    PWMB_PSCRH = 0x00;
    PWMB_PSCRL = 39;        // 分频值 = 39, 实际分频 = 39+1 = 40
  
    /* 设置自动重装载值 */
    PWMB_ARRH = 0xFF;
    PWMB_ARRL = 0xFF;
  
    /* 通道1(PWM5)配置:捕获模式,映射到 TI1,上升沿捕获 */
    PWMB_CCMR1 = 0x01;      // CC1S=01, 通道1作为输入, 映射到TI1
  
    /* 通道2(PWM6)配置:捕获模式,映射到 TI1FP2,下降沿捕获 */
    PWMB_CCMR2 = 0x02;      // CC2S=10, 通道2作为输入, 映射到TI1FP2
  
    /* 配置捕获极性 */
    PWMB_CCER1 = 0x00;      // 先清零
    PWMB_CCER1 |= 0x01;     // CC1E=1, CC1P=0, 通道1上升沿捕获
    PWMB_CCER1 |= 0x10;     // CC2E=1, CC2P=1, 通道2下降沿捕获
  
    /* 配置触发源为 TI1FP1,复位触发模式 */
    PWMB_SMCR = 0x00;       // 先清零
    PWMB_SMCR |= 0x54;      // SMS=100 (复位触发模式), TS=101 (选择TI1FP1)
  
    /* 使能捕获/比较中断 */
    PWMB_IER = 0x00;
    PWMB_IER |= 0x02;       // CC1IE = 1, 使能通道1捕获中断
    PWMB_IER |= 0x04;       // CC2IE = 1, 使能通道2捕获中断
  
    /* 使能 PWMB 计数器 */
    PWMB_CR1 |= 0x01;       // CEN = 1, 使能计数器
  
    P_SW2 &= 0x7F;          // 关闭 XFR 访问
}

/* ======================== 主函数 ======================== */
void main(void)
{
    unsigned int adc_val;
    unsigned long freq1, duty1, freq2, duty2;
    unsigned char str_buf[80];
  
    /* 关闭所有中断 */
    EA = 0;
  
    /* 设置系统时钟为内部 40MHz */
    /* STC32G 上电默认使用内部高速IRC,无需额外配置 */
  
    /* 初始化外设 */
    UART1_Init();
    delay_ms(10);
  
    UART1_SendString("STC32G12K128 PWM Capture + ADC Demo\r\n");
    UART1_SendString("System Clock: 40MHz\r\n");
  
    /* 初始化定时器0 */
    Timer0_Init();
  
    /* 初始化 ADC */
    ADC_Init();
  
    /* 初始化 PWMA 输入捕获(第1路信号,P1.0/PWM1P) */
    PWMA_Cap_Init();
  
    /* 初始化 PWMB 输入捕获(第2路信号,P2.0/PWM5) */
    PWMB_Cap_Init();
  
    /* 使能总中断 */
    EA = 1;
  
    /* 主循环 */
    while (1)
    {
        /* 每100ms处理一次 */
        if (g_t0_100ms_flag)
        {
            g_t0_100ms_flag = 0;
          
            /* ---- ADC 转换 ---- */
            adc_val = ADC_Read(1);          // 读取 ADC 通道1 (P1.1)
            g_adc_result = adc_val;
          
            /* ---- 计算第1路信号频率和占空比 ---- */
            if (g_pwma_cap_ok)
            {
                /* 频率 = PWM时钟 / 周期值 */
                /* PWM时钟 = 1MHz */
                if (g_pwma_period > 0)
                {
                    freq1 = 1000000UL / (unsigned long)g_pwma_period;
                }
                else
                {
                    freq1 = 0;
                }
              
                /* 占空比 = 占空比值 / 周期值 * 100% */
                if (g_pwma_period > 0)
                {
                    duty1 = (unsigned long)g_pwma_duty * 10000UL / (unsigned long)g_pwma_period;
                }
                else
                {
                    duty1 = 0;
                }
              
                g_pwma_cap_ok = 0;
            }
            else
            {
                freq1 = 0;
                duty1 = 0;
            }
          
            /* ---- 计算第2路信号频率和占空比 ---- */
            if (g_pwmb_cap_ok)
            {
                if (g_pwmb_period > 0)
                {
                    freq2 = 1000000UL / (unsigned long)g_pwmb_period;
                }
                else
                {
                    freq2 = 0;
                }
              
                if (g_pwmb_period > 0)
                {
                    duty2 = (unsigned long)g_pwmb_duty * 10000UL / (unsigned long)g_pwmb_period;
                }
                else
                {
                    duty2 = 0;
                }
              
                g_pwmb_cap_ok = 0;
            }
            else
            {
                freq2 = 0;
                duty2 = 0;
            }
          
            /* ---- 串口输出 ---- */
            /* 格式: CH1: freq=1000Hz duty=50.00%  CH2: freq=2000Hz duty=33.33%  ADC=2048 */
            sprintf(str_buf, 
                "CH1: freq=%luHz duty=%lu.%02lu%%  CH2: freq=%luHz duty=%lu.%02lu%%  ADC=%u\r\n",
                freq1, duty1 / 100, duty1 % 100,
                freq2, duty2 / 100, duty2 % 100,
                adc_val);
          
            UART1_SendString(str_buf);
        }
    }
}

/* ======================== 定时器0中断服务函数 ======================== */
/* 每1ms中断一次,计100次产生100ms标志 */
void Timer0_ISR(void) interrupt 1
{
    static unsigned int t0_cnt = 0;
  
    t0_cnt++;
    if (t0_cnt >= 100)      // 100ms
    {
        t0_cnt = 0;
        g_t0_100ms_flag = 1;
    }
}

/* ======================== PWMA 捕获中断服务函数 ======================== */
/* 第1路信号(P1.0/PWM1P)捕获完成 */
void PWMA_Cap_ISR(void) interrupt 26
{
    unsigned char sr;
  
    P_SW2 |= 0x80;          // 使能访问 XFR
  
    sr = PWMA_SR1;          // 读取中断状态寄存器
  
    if (sr & 0x02)          // CC1IF (通道1捕获中断)
    {
        /* 通道1捕获到上升沿,CCR1 中保存的是周期值 */
        g_pwma_period = ((unsigned int)PWMA_CCR1H << 8) | PWMA_CCR1L;
        PWMA_SR1 &= ~0x02;  // 清除 CC1IF 标志
    }
  
    if (sr & 0x04)          // CC2IF (通道2捕获中断)
    {
        /* 通道2捕获到下降沿,CCR2 中保存的是占空比值 */
        g_pwma_duty = ((unsigned int)PWMA_CCR2H << 8) | PWMA_CCR2L;
        PWMA_SR1 &= ~0x04;  // 清除 CC2IF 标志
        g_pwma_cap_ok = 1;  // 标记捕获完成
    }
  
    P_SW2 &= 0x7F;          // 关闭 XFR 访问
}

/* ======================== PWMB 捕获中断服务函数 ======================== */
/* 第2路信号(P2.0/PWM5)捕获完成 */
void PWMB_Cap_ISR(void) interrupt 27
{
    unsigned char sr;
  
    P_SW2 |= 0x80;          // 使能访问 XFR
  
    sr = PWMB_SR1;          // 读取中断状态寄存器
  
    if (sr & 0x02)          // CC1IF (通道1捕获中断)
    {
        /* 通道1捕获到上升沿,CCR1 中保存的是周期值 */
        g_pwmb_period = ((unsigned int)PWMB_CCR1H << 8) | PWMB_CCR1L;
        PWMB_SR1 &= ~0x02;  // 清除 CC1IF 标志
    }
  
    if (sr & 0x04)          // CC2IF (通道2捕获中断)
    {
        /* 通道2捕获到下降沿,CCR2 中保存的是占空比值 */
        g_pwmb_duty = ((unsigned int)PWMB_CCR2H << 8) | PWMB_CCR2L;
        PWMB_SR1 &= ~0x04;  // 清除 CC2IF 标志
        g_pwmb_cap_ok = 1;  // 标记捕获完成
    }
  
    P_SW2 &= 0x7F;          // 关闭 XFR 访问
}

程序说明

1. PWM 输入捕获原理

根据手册第26章"16位高级PWM定时器"的说明,STC32G 的高级 PWM 支持PWM输入捕获模式,可同时测量信号的周期和占空比:

  • 将外部信号同时接入同一组 PWM 的两路通道
  • 通道1配置为上升沿捕获(捕获周期值到 CCR1)
  • 通道2配置为下降沿捕获(捕获占空比值到 CCR2)
  • 触发模式控制器配置为复位触发模式(SMS=100),每次上升沿复位计数器

2. 信号测量范围

  • PWM 计数器时钟 = 40MHz / 40 = 1MHz
  • 最大可测周期 = 65535 / 1MHz ≈ 65.5ms(对应约 15.26Hz)
  • 最小可测周期 = 2 / 1MHz = 2μs(对应 500kHz)
  • 占空比分辨率 = 1 / 65535 ≈ 0.0015%

3. ADC 转换

  • 定时器0每 100ms 触发一次 ADC 转换
  • ADC 时钟 ≈ 1.33MHz,12位精度,右对齐格式
  • 转换结果通过串口输出

4. 串口输出格式

CH1: freq=1000Hz duty=50.00%  CH2: freq=2000Hz duty=33.33%  ADC=2048

使用说明

  1. 将第1路被测信号连接到 P1.0(PWM1P)
  2. 将第2路被测信号连接到 P2.0(PWM5)
  3. 将模拟电压连接到 P1.1(ADC1)
  4. 串口1(P3.0/P3.1)连接 USB转串口模块,波特率 115200
  5. 用 Keil C251 编译下载到 STC32G12K128

注意:被测信号的频率范围建议在 15Hz ~ 500kHz 之间,且信号电平需与单片机 I/O 电平兼容(0~Vcc)。如果信号幅度不匹配,需加电平转换或分压电路。


🔍 搜索 "帮产生一个 STC32G12K128的演示程序 利用 PWM测量外部2路输入信号的周期和占空比 ADC 周期转换后"

📌 相关论坛讨论

  1. 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】
  2. STC【AI 助手】帮 STC8H8K64U自动写一个完整的演示程序,要包含下面这些:
  3. STC8H1K08 如何测量2路外部信号的周期和占空比,还有 PWM输出

⏱️ 查找用时 37.0s | 总回答用时 78.1s

回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-7-22 10:32 , Processed in 0.069598 second(s), 47 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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