找回密码
 立即注册
查看: 180|回复: 4

求解答:关于HSPWM Slave Mode

[复制链接]
已绑定手机

1

主题

3

回帖

49

积分

新手上路

积分
49
发表于 2026-10-9 22:30:33 | 显示全部楼层 |阅读模式
STC8H2K17U-45I-TSSOP20,使用HSPWMB.TRGO以External Clock Mode 1方式驱动HSPWMA,HSPWMA将UEV作为TRGO并送ADC触发输入。CPU频率36MHz,直接来自HSI RC。当HSPWMx的工作频率高于72MHz时,HSPWMA计数器能够被驱动,但无法触发ADC(恰好72MHz时正常。怀疑是超过CPU频率的2倍)。将HSPWMx的工作频率降低则可以稳定触发。如果将CPU时钟源切换为PLL/4(PLL为144MHz)也不能正常触发ADC。如果将HSPWMA配置为Internal Clock Mode,即不使用Slave Mode,则即使HSPWMx的工作频率处在144MHz且TRGO(UEV)产生频率频率高至144KHz时也无问题。是我的使用方法不对,还是硬件的问题?
回复

使用道具 举报 送花

已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25428
发表于 2026-10-10 08:23:38 | 显示全部楼层
截图202610100823364037.jpg
回复

使用道具 举报 送花

已绑定手机

872

主题

1万

回帖

2万

积分

管理员

积分
25428
发表于 2026-10-10 08:24:13 | 显示全部楼层

我已经把相关手册内容查清楚了。这个帖子目前只有楼主提问,没有正式回复,所以我基于手册给出分析。

一、关于这个帖子的情况

这个帖子(TID 25718)目前只有楼主的提问,还没有正式回复。楼主的问题核心是:

用 STC8H2K17U 的 HSPWMB.TRGO 以 External Clock Mode 1 驱动 HSPWMA,HSPWMA 再把 UEV 作为 TRGO 去触发 ADC。当 HSPWMx 工作频率 高于 72MHz 时,HSPWMA 计数器能被驱动,但无法触发 ADC(恰好 72MHz 正常),而降低频率就能稳定触发。

二、手册中查到的事实依据

1. TRGO 触发 ADC 的通路是有限定的

据「16位高级PWM定时器」章节,明确写了:

注:只有第一组PWM(PWMA)的TRGO可用于触发启动ADC

注:只有第二组PWM(PWMB)的TRGO可用于第一组PWM(PWMA)的ITR2

也就是说:PWMB 的 TRGO 本身不能直接触发 ADC,它只能作为 PWMA 的 ITR2。楼主用 PWMB 驱动 PWMA、再由 PWMA 触发 ADC,这个链路方向本身是符合手册允许的结构的。

2. TRGO 的两种设置方式

用 UEV(更新事件)作为 TRGO,对应控制寄存器 PWMx_CR2 的 MMS[2:0]=010(更新事件被选为触发输出);而用 CEN 作为 TRGO 是 001。楼主用的是 UEV,即 MMS=010。

触发 ADC 时,手册要求:

需要先设置 ADC_CONTR 寄存器中的 ADC_POWER、ADC_CHS 以及 ADC_EPWMT,当 PWM 产生 TRGO 内部信号时,系统会自动设置 ADC_START 来启动 AD 转换。

3. HSPWM 高速模式的时钟来源

据「高速高级PWM」章节:

当关闭异步控制时……PWM 工作频率与系统工作频率相同;若需要 PWM 工作在高速模式,则需要使能异步控制模式,此时 PWM 时钟可选择主时钟(MCLK)或者 PLL 输出时钟。

当系统运行在较低工作频率时,高速高级PWM 可工作在高达 144M~192M 的频率下。

三、对楼主现象的解读

综合手册内容,可以作出如下推断(属于基于手册的推理,未经官方确认):

  • 楼主 CPU = 36MHz(HSI RC),HSPWM 工作在 >72MHz 时,已经超过 CPU 频率的 2 倍,正好卡在 72MHz 这个临界点——这与楼主的观察一致。
  • 高速 PWM 计数器和更新事件(UEV)是在 异步高速域(MCLK 或 PLL) 里产生的,而 ADC 触发通路是在同步系统时钟域(SYSclk)里响应的。二者之间存在跨时钟域的信号传递,当 UEV/TRGO 的重复频率接近或超过 SYSclk 相关限制时,同步逻辑可能来不及捕获,导致 ADC 触发丢失。
  • 楼主把 HSPWMA 改为 Internal Clock Mode(不使用从模式) 就正常,说明问题出在**"PWMB→PWMA 从模式驱动 + 高速域"这一组合**上,而不是 ADC 本身或 TRGO 设置错误。
  • 楼主切换 PLL/4 也不行,说明不是单纯 PLL 质量问题,而是跨时钟域/频率比的关系。

四、建议楼主尝试的排查方向

  1. 确认时钟比例:如果确实存在"从模式驱动时钟频率需 ≤ SYSclk 的某倍数"的限制,将 HSPWMA 的驱动频率控制在此范围内,或提高 SYSclk。
  2. 把 ADC 触发源改用同步域事件:例如 PWMA 用 CEN 作为 TRGO(MMS=001)或周期更新事件方式,避开纯从模式高倍频场景。
  3. 反馈给 STC 官方:这个现象需要原厂确认是否为 HSPWM 从模式跨时钟域的已知限制,建议在 STC 官方论坛该帖下补充:芯片具体型号、固件版本、完整的 PWMA_SMCR/PWMB_CR2/ADC_CONTR/ADCCFG 配置值、以及 72MHz 临界点的实测数据,便于 FAE 定位。

说明:以上第一、二部分是手册明确内容;第三、四部分是基于手册的推理和建议,尚未经 STC 官方确认,最终结论请以原厂对该帖的正式答复为准。如需我进一步查 PWMA_CR2 / PWMA_SMCR 某一位的具体配置写法,请告诉我你现在的寄存器配置值,我帮你逐位核对。


🔍 搜索 "STC 单片机 论坛 帖子"

⏱️ 查找用时 12.0s | 总回答用时 18.1s

回复

使用道具 举报 送花

已绑定手机

1

主题

3

回帖

49

积分

新手上路

积分
49
 楼主| 发表于 2026-10-10 10:44:26 | 显示全部楼层
芯片丝印:STC8H2K17U 45I TSSOP20 2417A536774.HA(或为BA,看不太清)
读DID寄存器给出的芯片版本:B
STC-ISP检测选项:
-----------------------------------------------------------------
正在检测目标单片机 ...
  单片机型号: STC8H2K17U
  软件协议, V3.0: 7.4.7U

当前芯片的硬件选项为:
  . 当前系统ISP工作频率: 23.959MHz
  . 上次下载设置的下次用户程序运行使用的内部IRC振荡器的频率: 35.940MHz
  . 掉电唤醒定时器的频率: 35.075KHz
  . 振荡器放大增益使能
  . 用户EEPROM大小被设置为 0.5 K
  . P3.2和P3.3与下次下载无关
  . 上电复位时增加额外的复位延时
  . 复位引脚用作普通I/O口
  . P5.4口不切换为P1.2
  . 不交换P1.0口和P1.1位置
  . 检测到低压时复位
  . 低压检测门槛电压 : 2.00 V
  . 上电复位时,硬件不启动内部看门狗
  . 上电自动启动内部看门狗时的预分频数为 : 256
  . 空闲状态时看门狗定时器停止计数
  . 下次下载用户程序时,将用户EEPROM区一并擦除
  . 下次下载用户程序时,没有相关的端口控制485
  . 下次下载时不需要校验下载口令
  . 未使能芯片的仿真功能
  . 内部参考电压: 1190 mV (参考范围: 1100~1300mV)

  单片机型号: STC8H2K17U
  软件协议, V3.0: 7.4.7U


操作成功 !(2026-10-10 10:36:54)
-----------------------------------------------------------------
完整代码:(第191行HSCLKDIV从1切换至2后可以正常触发ADC)
#include "STC8H.H"
#include <string.h>
#include <stdio.h>

typedef unsigned char       u8;
typedef unsigned int        u16;
typedef unsigned long       u32;
typedef signed char         i8;
typedef signed int          i16;
typedef signed long         i32;

enum {
    GPIO_MODE_IN_OUT = 0,
    GPIO_MODE_OUT_PP = 1,
    GPIO_MODE_AIN = 2,
    GPIO_MODE_OUT_OD = 3
};

static void gpio_mode_set(u8 port, u8 pin, u8 mode);

#define FOSC                36000000UL
#define UART1_BAUDRATE      115200


sfr DID = 0xa7;
static u8 get_chip_version() {
    u8 _;
    IAP_ADDRL = 0x02;
    _ = DID;
    return _;
}

static void clock_init() {
    int i;
    CLKSEL = 0x80;      // 144mhz pwm
    CLKDIV = 0;
    PLLCR = 0xc0;
    // wait until pll ready
    for(i=0; i<1000; i++) _nop_();
    CLKSEL |= 0x40;     // hs clk source
}

volatile bit uart1_busy = 0;
static void uart_init() {
    // uart1 using tim1 as clocksource
    const u8 baudrate_reg = (256 - (FOSC / UART1_BAUDRATE + 16) / 32);
    gpio_mode_set(3, 0, GPIO_MODE_IN_OUT);
    gpio_mode_set(3, 1, GPIO_MODE_IN_OUT);
    SCON = 0x40;        // tx only, 8bit
    TMOD = 0x20;        // 8bit timer, auto reload TH to TL
    TL1 = baudrate_reg;
    TH1 = baudrate_reg;
    TR1 = 1;
    AUXR = 0x40;
    ES = 1;     // enable uart interrupt
}

static void gpio_default_init() {
    // set all pins to analog input
    P0M0 = 0x00; P0M1 = 0xff;
    P1M0 = 0x00; P1M1 = 0xff;
    P2M0 = 0x00; P2M1 = 0xff;
    P3M0 = 0x00; P3M1 = 0xff;
    P4M0 = 0x00; P4M1 = 0xff;
    P5M0 = 0x00; P5M1 = 0xff;
    P6M0 = 0x00; P6M1 = 0xff;
    P7M0 = 0x00; P7M1 = 0xff;
}

volatile bit tim11_finished = 0;
static void delay_init() {
    T11CR = 0x00;       // freq=sysclk/12
    T11PS = 0;          // prescaler
}

void udelay(u32 usec) {
    u32 ticks = usec * ((FOSC / 1000000UL) / 12UL);
    while(ticks) {
        u16 _ = ticks > 0xffff ? 0xffff : ticks;
        ticks -= _;
        _ = 65535 - _;
        T11L = _;
        T11H = _ >> 8;
        T11CR |= 0x82;
        while(!tim11_finished);
        tim11_finished = 0;
    }
}

void mdelay(u32 msec) {
    udelay(msec * 1000);
}

static u8 hspwma_read_async(u8 addr) {
    u8 _;
    while(HSPWMA_ADR & 0x80);
    HSPWMA_ADR = addr | 0x80;
    while(HSPWMA_ADR & 0x80);
    _ = HSPWMA_DAT;
    return _;
}

static void hspwma_write_async(u8 addr, u8 val) {
    while(HSPWMA_ADR & 0x80);
    HSPWMA_DAT = val;
    HSPWMA_ADR = addr & 0x7f;
}

static u8 hspwmb_read_async(u8 addr) {
    u8 _;
    while(HSPWMB_ADR & 0x80);
    HSPWMB_ADR = addr | 0x80;
    while(HSPWMB_ADR & 0x80);
    _ = HSPWMB_DAT;
    return _;
}

static void hspwmb_write_async(u8 addr, u8 val) {
    while(HSPWMB_ADR & 0x80);
    HSPWMB_DAT = val;
    HSPWMB_ADR = addr & 0x7f;
}

static void adc_start() {
    ADC_CONTR |= 0x40;
    _nop_();
    _nop_();
}

static void adc_wait() {
    while(!(ADC_CONTR & 0x20));
}

static u16 adc_get() {
    u16 _;
    ADC_CONTR &= ~0x20;
    _ = (ADC_RES << 8) | ADC_RESL;
    return _;
}

static void adc_select_channel(u8 ch) {
    u8 _ = ADC_CONTR;
    _ &= 0xf0;
    _ |= ch & 0x0f;
    ADC_CONTR = _;
}

#define VREFINT_MV      1190UL
u16 vrefint_raw = 0;
u16 vdd_mv = 0;
static void adc_init() {
    u32 _;
    const int N = 32;
    int i;

    // basic configuration
    ADCTIM = 0x32;      // sample time: 3 cycles
    ADC_CONTR |= 0x80;  // power on
    ADCCFG = 0x20;      // left align, speed=sysclk/2/1 (fastest)
    mdelay(10);

    // vrefint raw averaging and vdd computing
    adc_select_channel(15);
    _ = 0;
    for(i=0; i<=N; i++) {
        u16 raw;
        adc_start();
        adc_wait();
        raw = adc_get();
        if(i != 0) {
            _ += raw;
        }
    }
    vrefint_raw = _ / N;
    vdd_mv = VREFINT_MV * 4096 / vrefint_raw;

    // configure to adc sample pin
    gpio_mode_set(1, 4, GPIO_MODE_AIN);
    adc_select_channel(4);
    ADC_CONTR |= 0x10;      // enable pwm trigger
    EADC = 1;
}

#define PWM_PERIOD      (1000 - 1)
static void pwm_and_dac_init() {
    const u16 rep_cnt = 10 - 1;
    u8 _;

    // dac using hspwmb.ch8
    gpio_mode_set(3, 4, GPIO_MODE_OUT_PP);
    HSCLKDIV = 1;
    HSPWMB_CFG = 0x03;
    hspwmb_write_async((u8)&PWMB_CCER2, 0x00);
    hspwmb_write_async((u8)&PWMB_CCMR4, 0x00);
    PWMB_PS = 0x40;     // select p3.4 as ch8 output
    hspwmb_write_async((u8)&PWMB_CCER2, 0x10);
    hspwmb_write_async((u8)&PWMB_CCMR4, 0x64);
    hspwmb_write_async((u8)&PWMB_ENO, 0x40);
    hspwmb_write_async((u8)&PWMB_BKR, 0x80);
    hspwmb_write_async((u8)&PWMB_CCR8H, 0);     // h before l
    hspwmb_write_async((u8)&PWMB_CCR8L, 0);
    hspwmb_write_async((u8)&PWMB_ARRH, (PWM_PERIOD >> 8) & 0xff);
    hspwmb_write_async((u8)&PWMB_ARRL, PWM_PERIOD & 0xff);

    // mosfet
    gpio_mode_set(3, 3, GPIO_MODE_OUT_PP);
    hspwmb_write_async((u8)&PWMB_CCMR3, 0x00);
    PWMB_PS |= 0x10;    // select p3.3
    _ = hspwmb_read_async((u8)&PWMB_CCER2);
    _ &= 0x0f; _ |= 0x01;
    hspwmb_write_async((u8)&PWMB_CCER2, _);
    hspwmb_write_async((u8)&PWMB_CCMR3, 0x64);
    _ = hspwmb_read_async((u8)&PWMB_ENO);
    _ |= 0x10;
    hspwmb_write_async((u8)&PWMB_ENO, _);
    hspwmb_write_async((u8)&PWMB_CCR7H, 0);
    hspwmb_write_async((u8)&PWMB_CCR7L, 50);

    //// hspwma in internal clock mode
    //HSPWMA_CFG = 0x03;
    //hspwma_write_async((u8)&PWMA_CR2, 0x24);        // pwma uev as trgo, triggering adc to start
    //hspwma_write_async((u8)&PWMA_ENO, 0x00);        // no real output to pin
    //hspwma_write_async((u8)&PWMA_BKR, 0x00);        // master output disabled
    //hspwma_write_async((u8)&PWMA_ARRH, (rep_cnt >> 8) & 0xff);
    //hspwma_write_async((u8)&PWMA_ARRL, rep_cnt & 0xff);
    //hspwma_write_async((u8)&PWMA_CR1, 0x01);        // enable pwma

    // hspwma in slave mode: pwmb as master, pwma as slave
    HSPWMA_CFG = 0x03;
    hspwmb_write_async((u8)&PWMB_CR2, 0x64);
    hspwma_write_async((u8)&PWMA_SMCR, 0xa7);       // pwma in slave mode, external clock mode 1
    hspwma_write_async((u8)&PWMA_CR2, 0x20);
    hspwma_write_async((u8)&PWMA_ENO, 0x00);        // no real output to pin
    hspwma_write_async((u8)&PWMA_BKR, 0x00);        // master output disabled
    hspwma_write_async((u8)&PWMA_ARRH, (rep_cnt >> 8) & 0xff);
    hspwma_write_async((u8)&PWMA_ARRL, rep_cnt & 0xff);
    hspwma_write_async((u8)&PWMA_SR1, 0x00);
    hspwma_write_async((u8)&PWMA_CR1, 0x01);        // enable pwma

    // final
    hspwmb_write_async((u8)&PWMB_CR1, 0x01);        // enable pwmb
}

static void init() {
    P_SW2 |= 0x80;      // enable access to xfr
    IAP_TPS = FOSC / 1000000UL;
    clock_init();
    EA = 1;
    gpio_default_init();
    uart_init();
    delay_init();
    adc_init();
    pwm_and_dac_init();
}

volatile u32 adc_cnt = 0;
void main() {
    u16 _;
    u8 chip_version;

    init();
    chip_version = get_chip_version();
    printf("chip version %c\n", 'A'+chip_version);

    while(1) {
        printf("adc_cnt=%lu\n", adc_cnt);
        adc_cnt = 0;

        _ = hspwma_read_async((u8)&PWMA_CNTRH);
        _ <<= 8;
        _ |= hspwma_read_async((u8)&PWMA_CNTRL);
        printf("HSPWMA.CNTR=%u\n", _);

        //u16 _ = hspwmb_read_async((u8)&PWMB_CNTRH);
        //_ <<= 8;
        //_ |= hspwmb_read_async((u8)&PWMB_CNTRL);
        //printf("HSPWMB.CNTR=0x%x\n", _);

        //adc_start();
        mdelay(1000);
    }
}

char putchar(char ch) {
    while(uart1_busy);
    uart1_busy = 1;
    SBUF = ch;
    return ch;
}

static void gpio_mode_set(u8 port, u8 pin, u8 mode) {
    u8 pxm0, pxm1;
    switch(port) {
        case 0: {
            pxm0 = P0M0; pxm1 = P0M1;
        } break;
        case 1: {
            pxm0 = P1M0; pxm1 = P1M1;
        } break;
        case 2: {
            pxm0 = P2M0; pxm1 = P2M1;
        } break;
        case 3: {
            pxm0 = P3M0; pxm1 = P3M1;
        } break;
        case 4: {
            pxm0 = P4M0; pxm1 = P4M1;
        } break;
        case 5: {
            pxm0 = P5M0; pxm1 = P5M1;
        } break;
        case 6: {
            pxm0 = P6M0; pxm1 = P6M1;
        } break;
        case 7: {
            pxm0 = P7M0; pxm1 = P7M1;
        } break;
    }
    if(mode & 1) {
        pxm0 |= 1<<pin;
    } else {
        pxm0 &= ~(1<<pin);
    }
    if(mode & 2) {
        pxm1 |= 1<<pin;
    } else {
        pxm1 &= ~(1<<pin);
    }
    switch(port) {
        case 0: {
            P0M0 = pxm0; P0M1 = pxm1;
        } break;
        case 1: {
            P1M0 = pxm0; P1M1 = pxm1;
        } break;
        case 2: {
            P2M0 = pxm0; P2M1 = pxm1;
        } break;
        case 3: {
            P3M0 = pxm0; P3M1 = pxm1;
        } break;
        case 4: {
            P4M0 = pxm0; P4M1 = pxm1;
        } break;
        case 5: {
            P5M0 = pxm0; P5M1 = pxm1;
        } break;
        case 6: {
            P6M0 = pxm0; P6M1 = pxm1;
        } break;
        case 7: {
            P7M0 = pxm0; P7M1 = pxm1;
        } break;
    }
}

void UART1_IRQHandler() interrupt 4 {
    if(TI) {
        TI = 0;
        uart1_busy = 0;
    }
}

void ADC_IRQHandler() interrupt 5 {
    ADC_CONTR &= ~0x20;
    adc_cnt++;
}

void TIM11_IRQHandler() interrupt 13 {
    T11CR &= ~0x01;
    tim11_finished = 1;
}

回复

使用道具 举报 送花

25

主题

252

回帖

2375

积分

版主

积分
2375
发表于 2026-10-10 10:56:11 | 显示全部楼层
已经帮您转对应技术,有问题可以先用官方AI助手:https://help.stcaimcu.com/
回复

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2026-11-7 13:04 , Processed in 0.071383 second(s), 67 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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