神农鼎 发表于 2023-3-14 16:24:47

如何测量72MHz以下的外部信号,STC32的144MHz的高速PWM

如何测量72MHz以下的外部信号,
STC32的144MHz的高速PWM
STC32G12K128-35MHz-LQFP64/48/32,TSSOP20
STC32G8K64-42MHz-LQFP48/32
STC8H1K08T-33I-TSSOP20/QFN20
的高速 144MHz ~ 196MHz 的PWM, 可以捕获外部脉冲信号,可以工作在 144MHz 以上
理论上 STC32G系列可以测量 144MHz/2以下频率的信号
已验证了:
1, 内部 HIRC-12MHz
2, HIRC-12MHz / 10 = 1.2MHz低频 给 STC32G12K128的CPU使用
3, HIRC-12MHz通过 PLL产生144MHz做PWM的时钟源, PWMB组产生 28.8MHz信号源
4, PWMA组侧量 28.8MHz的信号源正确 !
5, 等72MHz信号发生器来测试72MHz











神农鼎 发表于 2023-3-14 16:39:21



STC32G12K128-35I-TSSOP20 管脚图


STC32G12K128-35I-LQFP32管脚图


STC32G12K128-35I-LQFP48管脚图

STC32G12K128-35I-LQFP64管脚图


zhp 发表于 2023-3-14 17:41:56

STC32G系列单片机的高速PWM可以和CPU工作在不同频率
STC32G12K128系列CPU最高工作频率可达35MHz
STC32G8K64系列CPU最高工作频率可达42MHz
上面两个系列的高级PWM均可工作在144MHz
系统时钟树配置如下:

范例说明:
1、时钟说明:由内部IRC产生12MHz的时钟提供给PLL当作PLL输入时钟,PLL锁频到144MHz输出作为高速PWM的时钟源,
   同时144MHz的PLL输出时钟通过CLKDIV进行4或者5分频,产生36MHz或者28.8MHz的时钟提供给CPU当作系统工作频率
2、PWM高速输出说明:PWMA的CC1通道配置为输出模式,并从P2.0口输出频率为28.8MHz,占空比为40%的PWM型号
3、PWM高速捕获说明:PWMB的CC5和CC6通道配置为捕获输入模式,CC5口P2.0口捕获信号的周期值,CC6从P2.0口捕获信号的占空比
4、测试说明:最后通过查询方式得到周期值和占空比并从串口送到PC显示
#include "stc32g.h"
#include "stdio.h"
#include "intrins.h"

#define FOSC            12000000UL
#define PLLCLK          144000000UL

#define MCLKDIV         4               //144M/4=36M
//#define MCLKDIV       5               //144M/5=28.8M

#define MCLK            PLLCLK
#define SYSCLK          MCLK/MCLKDIV

#define BAUD            (65536 - SYSCLK/4/115200)

#define MCLK_HIRC       0
#define MCLK_IRC32K   1
#define MCLK_IRC48M   2
#define MCLK_XOSC       3
#define MCLK_X32K       4
#define MCLK_PLL      5
#define MCLK_PLLD2      6
#define MCLK_SEL      MCLK_PLL

#define HSCK_MCLK       0
#define HSCK_PLL      1
#define HSCK_SEL      HSCK_PLL

#define PLL_96M         0
#define PLL_144M      1
#define PLL_SEL         PLL_144M

#define CKMS            0x80
#define HSIOCK          0x40
#define MCK2SEL_MSK   0x0c
#define MCK2SEL_SEL1    0x00
#define MCK2SEL_PLL   0x04
#define MCK2SEL_PLLD2   0x08
#define MCK2SEL_IRC48   0x0c
#define MCKSEL_MSK      0x03
#define MCKSEL_HIRC   0x00
#define MCKSEL_XOSC   0x01
#define MCKSEL_X32K   0x02
#define MCKSEL_IRC32K   0x03

#define ENCKM         0x80
#define PCKI_MSK      0x60
#define PCKI_D1         0x00
#define PCKI_D2         0x20
#define PCKI_D4         0x40
#define PCKI_D8         0x60

void delay()
{
    int i;
   
    for (i=0; i<100; i++);
}

char ReadPWMA(char addr)
{
    char dat;
   
    while (HSPWMA_ADR & 0x80);            //等待前一个异步读写完成
    HSPWMA_ADR = addr | 0x80;               //设置间接访问地址,只需要设置原XFR地址的低7位
                                          //HSPWMA_ADDR寄存器的最高位写1,表示读数据
    while (HSPWMA_ADR & 0x80);            //等待当前异步读取完成
    dat = HSPWMA_DAT;                     //读取异步数据
   
    return dat;
}

void WritePWMA(char addr, char dat)
{
    while (HSPWMA_ADR & 0x80);            //等待前一个异步读写完成
    HSPWMA_DAT = dat;                     //准备需要写入的数据
    HSPWMA_ADR = addr & 0x7f;               //设置间接访问地址,只需要设置原XFR地址的低7位
                                          //HSPWMA_ADDR寄存器的最高位写0,表示写数据
}

char ReadPWMB(char addr)
{
    char dat;
   
    while (HSPWMB_ADR & 0x80);            //等待前一个异步读写完成
    HSPWMB_ADR = addr | 0x80;               //设置间接访问地址,只需要设置原XFR地址的低7位
                                          //HSPWMB_ADDR寄存器的最高位写1,表示读数据
    while (HSPWMB_ADR & 0x80);            //等待当前异步读取完成
    dat = HSPWMB_DAT;                     //读取异步数据
   
    return dat;
}

void WritePWMB(char addr, char dat)
{
    while (HSPWMB_ADR & 0x80);            //等待前一个异步读写完成
    HSPWMB_DAT = dat;                     //准备需要写入的数据
    HSPWMB_ADR = addr & 0x7f;               //设置间接访问地址,只需要设置原XFR地址的低7位
                                          //HSPWMB_ADDR寄存器的最高位写0,表示写数据
}

void main()
{
    WTST = 0x00;
    P_SW2 = 0x80;
   
    P0M0 = 0x00; P0M1 = 0x00;
    P1M0 = 0x00; P1M1 = 0x00;
    P2M0 = 0x00; P2M1 = 0x00;
    P3M0 = 0x00; P3M1 = 0x00;
    P5M0 = 0x10; P5M1 = 0x00;
   
    SCON = 0x52;
    AUXR = 0x40;
    TMOD = 0x00;
    TL1 = BAUD;
    TH1 = BAUD >> 8;
    TR1 = 1;
   
    //选择PLL输出时钟
#if (PLL_SEL == PLL_96M)
    CLKSEL &= ~CKMS;                        //选择PLL的96M作为PLL的输出时钟
#elif (PLL_SEL == PLL_144M)
    CLKSEL |= CKMS;                         //选择PLL的144M作为PLL的输出时钟
#else
    CLKSEL &= ~CKMS;                        //默认选择PLL的96M作为PLL的输出时钟
#endif
   
    //选择PLL输入时钟分频,保证输入时钟为12M
    USBCLK &= ~PCKI_MSK;
#if (FOSC == 12000000UL)
    USBCLK |= PCKI_D1;                      //PLL输入时钟1分频
#elif (FOSC == 24000000UL)
    USBCLK |= PCKI_D2;                      //PLL输入时钟2分频
#elif (FOSC == 48000000UL)
    USBCLK |= PCKI_D4;                      //PLL输入时钟4分频
#elif (FOSC == 96000000UL)
    USBCLK |= PCKI_D8;                      //PLL输入时钟8分频
#else
    USBCLK |= PCKI_D1;                      //默认PLL输入时钟1分频
#endif

    //启动PLL
    USBCLK |= ENCKM;                        //使能PLL倍频
   
    delay();                              //等待PLL锁频
   
    //选择主时钟源
    CLKDIV = MCLKDIV;                     //主时钟选择高速频率前,必须先设置分频系数,否则程序会当掉
   
    CLKSEL &= ~MCKSEL_MSK;
    CLKSEL &= ~MCK2SEL_MSK;
#if (MCLK_SEL == MCLK_HIRC)
    CLKSEL |= MCKSEL_HIRC;                  //选择内部高速IRC作为主时钟
    CLKSEL |= MCK2SEL_SEL1;               //选择MCKSEL选择的时钟作为主时钟
#elif (MCLK_SEL == MCLK_IRC32K)
    CLKSEL |= MCKSEL_X32K;                  //选择外部32K作为主时钟
    CLKSEL |= MCK2SEL_SEL1;               //选择MCKSEL选择的时钟作为主时钟
#elif (MCLK_SEL == MCLK_IRC48M)
    CLKSEL |= MCKSEL_HIRC;                  //选择内部高速IRC作为主时钟
    CLKSEL |= MCK2SEL_IRC48;                //选择内部48M的IRC作为主时钟
#elif (MCLK_SEL == MCLK_XOSC)
    CLKSEL |= MCKSEL_XOSC;                  //选择外部高速晶振作为主时钟
    CLKSEL |= MCK2SEL_SEL1;               //选择MCKSEL选择的时钟作为主时钟
#elif (MCLK_SEL == MCLK_X32K)
    CLKSEL |= MCKSEL_IRC32K;                //选择内部低速IRC作为主时钟
    CLKSEL |= MCK2SEL_SEL1;               //选择MCKSEL选择的时钟作为主时钟
#elif (MCLK_SEL == MCLK_PLL)
    CLKSEL |= MCKSEL_HIRC;                  //选择内部高速IRC作为主时钟
    CLKSEL |= MCK2SEL_PLL;                  //选择PLL输出时钟作为主时钟
#elif (MCLK_SEL == MCLK_PLLD2)
    CLKSEL |= MCKSEL_HIRC;                  //选择内部高速IRC作为主时钟
    CLKSEL |= MCK2SEL_PLLD2;                //选择PLL输出时钟2分频后的时钟作为主时钟
#else
    CLKSEL |= MCKSEL_HIRC;                  //默认选择内部高速IRC作为主时钟
    CLKSEL |= MCK2SEL_SEL1;               //默认选择MCKSEL选择的时钟作为主时钟
#endif

    MCLKOCR = 0x04;                         //系统时钟4分频后输出到P5.4口

    //选择HSPWM/HSSPI时钟
#if (HSCK_SEL == HSCK_MCLK)
    CLKSEL &= ~HSIOCK;                      //HSPWM/HSSPI选择主时钟为时钟源
#elif (HSCK_SEL == HSCK_PLL)
    CLKSEL |= HSIOCK;                     //HSPWM/HSSPI选择PLL输出时钟为时钟源
#else
    CLKSEL &= ~HSIOCK;                      //默认HSPWM/HSSPI选择主时钟为时钟源
#endif

    HSCLKDIV = 0;                           //HSPWM/HSSPI时钟源不分频
   
    HSPWMA_CFG = 0x03;                      //使能PWMA相关寄存器异步访问功能
    HSPWMB_CFG = 0x03;                      //使能PWMB相关寄存器异步访问功能
   
    PWMA_PS = 0x01;                         //PWMA_CC1高速PWM输出到P2.0口
                                          //PWMB_CC5从P2.0口进行捕获

    //通过异步方式设置PWMA/PWMB的相关寄存器
    WritePWMA((char)&PWMA_CCER1, 0x00);
    WritePWMA((char)&PWMA_CCMR1, 0x00);   //CC1为输出模式
    WritePWMA((char)&PWMA_CCMR1, 0x60);   //OC1REF输出PWM1(CNT<CCR时输出有效电平1)
    WritePWMA((char)&PWMA_CCER1, 0x05);   //使能CC1/CC1N上的输出功能
    WritePWMA((char)&PWMA_ENO, 0x03);       //使能PWM信号输出到端口
    WritePWMA((char)&PWMA_BKR, 0x80);       //使能主输出
    WritePWMA((char)&PWMA_CCR1H, 0x00);   //设置输出PWM的占空比
    WritePWMA((char)&PWMA_CCR1L, 0x02);
    WritePWMA((char)&PWMA_ARRH, 0x00);      //设置输出PWM的周期
    WritePWMA((char)&PWMA_ARRL, 0x04);
    WritePWMA((char)&PWMA_CR1, 0x01);       //开始PWM计数

    WritePWMB((char)&PWMB_CCER1, 0x00);
    WritePWMB((char)&PWMB_CCMR1, 0x01);   //CC5为输入模式,且映射到TI5FP5上
    WritePWMB((char)&PWMB_CCMR2, 0x02);   //CC6为输入模式,且映射到TI6FP5上
    WritePWMB((char)&PWMB_CCER1, 0x31);   //使能CC5上的捕获功能(上升沿捕获)
    WritePWMB((char)&PWMB_SMCR, 0x54);      //上升沿复位模式
    WritePWMB((char)&PWMB_CR1, 0x01);       //开始PWM计数
   
    while (1)
    {
      if (ReadPWMB((char)&PWMB_SR1) & 0x02)   //等待捕获完成
      {
            WritePWMB((char)&PWMB_SR1, 0x00);   //清除完成标志
            
            //读取捕获到的周期值
            printf("%02x", (unsigned int)ReadPWMB((char)&PWMB_CCR5H) & 0xff);
            printf("%02x", (unsigned int)ReadPWMB((char)&PWMB_CCR5L) & 0xff);
            printf(" ");
            
            //读取捕获到的占空比值
            printf("%02x", (unsigned int)ReadPWMB((char)&PWMB_CCR6H) & 0xff);
            printf("%02x", (unsigned int)ReadPWMB((char)&PWMB_CCR6L) & 0xff);
            printf("\n");
      }
    }
}

神农鼎 发表于 2023-3-14 19:15:52

PWM测量外部信号是输入,跟I/O对外的输出速度无关,输入速度可以很快
I/O的输出也可以做的很快,但就会对外电磁辐射大,
所以I/O对外输出STC实际加了滤波电路,这限制了I/O对外输出的速度,输出大概28MHz 附近
这样I/O的输出对外电磁辐射就很小,完美

丽江春水 发表于 2023-3-18 09:09:20

之前我一直以为I/O口的翻转速度会限制其输入信号的频率,而其翻转频率不能太高。看了本文后感觉I/O口在做输入用时,不需要翻转,就能把输入的信息接受,并进行处理,我去实际测一下看看效果。

w13229095473 发表于 2023-3-20 07:25:42

用这个特点能做个啥玩意?

神农鼎 发表于 2023-3-20 08:53:17

能测量频率比较高的外部脉冲

cdcityboy 发表于 2023-3-23 11:05:47

可以做个逻辑分析仪:lol

Sushijin 发表于 2023-4-2 16:28:49

请教一下,测量72M一下信号频率对信号的上升沿或者下降沿时间有没有要求,比如我的待测频率是50M,虽然周期是20nS,但是脉冲宽度只有4nS,这样的频率信号可以测量吗?

神农鼎 发表于 2023-4-2 16:38:56

占空比不是50%的,速度肯定上不来,
占空比50%, 理想状态,PWM的时钟源是 144MHz, 就可以测量 72MHz 以下
这个 PLL时钟是:
1,PLL锁相环有2级:
   A, 输入时钟频率*8后输出;
   B, 输入时钟频率*12后输出 !

2,输入时钟只能在 【8MHz ~ 16MHz】范围;

3,所以PLL时钟输出到PWM作为时钟源输入的频率可以在 【64MHz ~ 192MHz】
页: [1] 2 3
查看完整版本: 如何测量72MHz以下的外部信号,STC32的144MHz的高速PWM