This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C123GXL PWM模块在没有使用锁相环的情况下修改外部时钟频率,下进板子后的PWM频率仍未改变



单片机 TM4C123GXL,芯片GH6PM

CCS 版本6.0.1

之前程序时钟设置为: SysCtlClockSet(SYSCTL_SYSDIV_64|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN |SYSCTL_XTAL_16MHZ);

在将时钟改为18MHz时,即SysCtlClockSet(SYSCTL_SYSDIV_64|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN |SYSCTL_XTAL_18MHZ);

使用示波器观察对应GPIO口,PWM波频率没有改变,这是为什么呢?

附程序如下:

/*利用PWMO,PWM1产生200HZ和400Hz 50%占空比的方波*/
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/rom.h"
#include "driverlib/gpio.h"
#include "driverlib/pwm.h"
#include "driverlib/fpu.h"
#include "driverlib/pin_map.h"

int main (void)
{
//使能FPU
FPUEnable();
FPULazyStackingEnable();
//设置系统时钟为16MHz
SysCtlClockSet(SYSCTL_SYSDIV_64|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN |SYSCTL_XTAL_16MHZ);
//使能PWM0模块,使能PWM0和PWM1输出所在GPIO
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
GPIOPinConfigure(GPIO_PB6_M0PWM0);
GPIOPinConfigure(GPIO_PB7_M0PWM1);
GPIOPinConfigure(GPIO_PB4_M0PWM2);
GPIOPinConfigure(GPIO_PB5_M0PWM3);

//驱动电流8MA,推挽输出
GPIOPadConfigSet(GPIO_PORTB_BASE,GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7,
GPIO_STRENGTH_8MA,GPIO_PIN_TYPE_STD);

// PWM时钟配置
SysCtlPWMClockSet(SYSCTL_PWMDIV_1);
// SysCtlPWMClockSet(SYSCTL_PWMDIV_32);
//配置PWM发生器0:加减计数,不同步
PWMGenConfigure(PWM0_BASE,PWM_GEN_0,PWM_GEN_MODE_UP_DOWN| PWM_GEN_MODE_NO_SYNC);
PWMGenConfigure(PWM0_BASE,PWM_GEN_1,PWM_GEN_MODE_UP_DOWN| PWM_GEN_MODE_NO_SYNC);
//设置PWM发生器0的频率,时钟频率/PWM分频数/n,16M/64/1250=200HZ
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, 1250);
//设置PWM发生器1的频率,时钟频率/PWM分频数/n,16M/16/625=400HZ
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, 625);
//设置PWM0/PWM1输出的脉冲宽度
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0,1250/2);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1,1250/2);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2,625/2);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_3,625/2);

//使能PWM0和PWM1的输出
PWMOutputState(PWM0_BASE, (PWM_OUT_0_BIT |PWM_OUT_1_BIT| PWM_OUT_2_BIT| PWM_OUT_3_BIT), true);
//使能PWM发生器
PWMGenEnable(PWM0_BASE, PWM_GEN_0);
PWMGenEnable(PWM0_BASE, PWM_GEN_1);

while(1)
{

}

}

  • SysCtlClockSet(SYSCTL_SYSDIV_64|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN |SYSCTL_XTAL_16MHZ);

    SYSCTL_SYSDIV_64改变这个配置来修改其频率

    SYSCTL_XTAL_16MHZ 这个参数只是你外部接的晶振频率 

    详细你可以参考TI数据手册上具体描述

    //*****************************************************************************
    //
    //! Sets the clocking of the device.
    //!
    //! \param ui32Config is the required configuration of the device clocking.
    //!
    //! This function configures the clocking of the device. The input crystal
    //! frequency, oscillator to be used, use of the PLL, and the system clock
    //! divider are all configured with this function.
    //!
    //! The \e ui32Config parameter is the logical OR of several different values,
    //! many of which are grouped into sets where only one can be chosen.
    //!
    //! The system clock divider is chosen with one of the following values:
    //! \b SYSCTL_SYSDIV_1, \b SYSCTL_SYSDIV_2, \b SYSCTL_SYSDIV_3, ...
    //! \b SYSCTL_SYSDIV_64.
    //!
    //! The use of the PLL is chosen with either \b SYSCTL_USE_PLL or
    //! \b SYSCTL_USE_OSC.
    //!
    //! The external crystal frequency is chosen with one of the following values:
    //! \b SYSCTL_XTAL_4MHZ, \b SYSCTL_XTAL_4_09MHZ, \b SYSCTL_XTAL_4_91MHZ,
    //! \b SYSCTL_XTAL_5MHZ, \b SYSCTL_XTAL_5_12MHZ, \b SYSCTL_XTAL_6MHZ,
    //! \b SYSCTL_XTAL_6_14MHZ, \b SYSCTL_XTAL_7_37MHZ, \b SYSCTL_XTAL_8MHZ,
    //! \b SYSCTL_XTAL_8_19MHZ, \b SYSCTL_XTAL_10MHZ, \b SYSCTL_XTAL_12MHZ,
    //! \b SYSCTL_XTAL_12_2MHZ, \b SYSCTL_XTAL_13_5MHZ, \b SYSCTL_XTAL_14_3MHZ,
    //! \b SYSCTL_XTAL_16MHZ, \b SYSCTL_XTAL_16_3MHZ, \b SYSCTL_XTAL_18MHZ,
    //! \b SYSCTL_XTAL_20MHZ, \b SYSCTL_XTAL_24MHZ, or \b SYSCTL_XTAL_25MHz.
    //! Values below \b SYSCTL_XTAL_5MHZ are not valid when the PLL is in
    //! operation.
    //!
    //! The oscillator source is chosen with one of the following values:
    //! \b SYSCTL_OSC_MAIN, \b SYSCTL_OSC_INT, \b SYSCTL_OSC_INT4,
    //! \b SYSCTL_OSC_INT30, or \b SYSCTL_OSC_EXT32. \b SYSCTL_OSC_EXT32 is only
    //! available on devices with the hibernate module, and then only when the
    //! hibernate module has been enabled.
    //!
    //! The internal and main oscillators are disabled with the
    //! \b SYSCTL_INT_OSC_DIS and \b SYSCTL_MAIN_OSC_DIS flags, respectively.
    //! The external oscillator must be enabled in order to use an external clock
    //! source. Note that attempts to disable the oscillator used to clock the
    //! device is prevented by the hardware.
    //!
    //! To clock the system from an external source (such as an external crystal
    //! oscillator), use \b SYSCTL_USE_OSC \b | \b SYSCTL_OSC_MAIN. To clock the
    //! system from the main oscillator, use \b SYSCTL_USE_OSC \b |
    //! \b SYSCTL_OSC_MAIN. To clock the system from the PLL, use
    //! \b SYSCTL_USE_PLL \b | \b SYSCTL_OSC_MAIN, and select the appropriate
    //! crystal with one of the \b SYSCTL_XTAL_xxx values.
    //!
    //! \note This function should only be called on TM4C123 devices. For
    //! all other devices use the SysCtlClockFreqSet() function.
    //!
    //! \note If selecting the PLL as the system clock source (that is, via
    //! \b SYSCTL_USE_PLL), this function polls the PLL lock interrupt to
    //! determine when the PLL has locked. If an interrupt handler for the
    //! system control interrupt is in place, and it responds to and clears the
    //! PLL lock interrupt, this function delays until its timeout has occurred
    //! instead of completing as soon as PLL lock is achieved.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void
    SysCtlClockSet(uint32_t ui32Config)
    {
    uint32_t ui32Delay, ui32RCC, ui32RCC2;

    //
    // Get the current value of the RCC and RCC2 registers.
    //
    ui32RCC = HWREG(SYSCTL_RCC);
    ui32RCC2 = HWREG(SYSCTL_RCC2);

    //
    // Bypass the PLL and system clock dividers for now.
    //
    ui32RCC |= SYSCTL_RCC_BYPASS;
    ui32RCC &= ~(SYSCTL_RCC_USESYSDIV);
    ui32RCC2 |= SYSCTL_RCC2_BYPASS2;

    //
    // Write the new RCC value.
    //
    HWREG(SYSCTL_RCC) = ui32RCC;
    HWREG(SYSCTL_RCC2) = ui32RCC2;

    //
    // See if the oscillator needs to be enabled.
    //
    if((ui32RCC & SYSCTL_RCC_MOSCDIS) && !(ui32Config & SYSCTL_MAIN_OSC_DIS))
    {
    //
    // Make sure that the required oscillators are enabled. For now, the
    // previously enabled oscillators must be enabled along with the newly
    // requested oscillators.
    //
    ui32RCC &= (~SYSCTL_RCC_MOSCDIS | (ui32Config & SYSCTL_MAIN_OSC_DIS));

    //
    // Clear the MOSC power up raw interrupt status to be sure it is not
    // set when waiting below.
    //
    HWREG(SYSCTL_MISC) = SYSCTL_MISC_MOSCPUPMIS;

    //
    // Write the new RCC value.
    //
    HWREG(SYSCTL_RCC) = ui32RCC;

    //
    // Timeout using the legacy delay value.
    //
    ui32Delay = 524288;

    while((HWREG(SYSCTL_RIS) & SYSCTL_RIS_MOSCPUPRIS) == 0)
    {
    ui32Delay--;

    if(ui32Delay == 0)
    {
    break;
    }
    }

    //
    // If the main oscillator failed to start up then do not switch to
    // it and return.
    //
    if(ui32Delay == 0)
    {
    return;
    }

    }

    //
    // Set the new crystal value and oscillator source. Because the OSCSRC2
    // field in RCC2 overlaps the XTAL field in RCC, the OSCSRC field has a
    // special encoding within ui32Config to avoid the overlap.
    //
    ui32RCC &= ~(SYSCTL_RCC_XTAL_M | SYSCTL_RCC_OSCSRC_M);
    ui32RCC |= ui32Config & (SYSCTL_RCC_XTAL_M | SYSCTL_RCC_OSCSRC_M);
    ui32RCC2 &= ~(SYSCTL_RCC2_USERCC2 | SYSCTL_RCC2_OSCSRC2_M);
    ui32RCC2 |= ui32Config & (SYSCTL_RCC2_USERCC2 | SYSCTL_RCC_OSCSRC_M);
    ui32RCC2 |= (ui32Config & 0x00000008) << 3;

    //
    // Write the new RCC value.
    //
    HWREG(SYSCTL_RCC) = ui32RCC;
    HWREG(SYSCTL_RCC2) = ui32RCC2;

    //
    // Set the PLL configuration.
    //
    ui32RCC &= ~SYSCTL_RCC_PWRDN;
    ui32RCC |= ui32Config & SYSCTL_RCC_PWRDN;
    ui32RCC2 &= ~SYSCTL_RCC2_PWRDN2;
    ui32RCC2 |= ui32Config & SYSCTL_RCC2_PWRDN2;

    //
    // Clear the PLL lock interrupt.
    //
    HWREG(SYSCTL_MISC) = SYSCTL_MISC_PLLLMIS;

    //
    // Write the new RCC value.
    //
    if(ui32RCC2 & SYSCTL_RCC2_USERCC2)
    {
    HWREG(SYSCTL_RCC2) = ui32RCC2;
    HWREG(SYSCTL_RCC) = ui32RCC;
    }
    else
    {
    HWREG(SYSCTL_RCC) = ui32RCC;
    HWREG(SYSCTL_RCC2) = ui32RCC2;
    }

    //
    // Set the requested system divider and disable the appropriate
    // oscillators. This value is not written immediately.
    //
    ui32RCC &= ~(SYSCTL_RCC_SYSDIV_M | SYSCTL_RCC_USESYSDIV |
    SYSCTL_RCC_MOSCDIS);
    ui32RCC |= ui32Config & (SYSCTL_RCC_SYSDIV_M | SYSCTL_RCC_USESYSDIV |
    SYSCTL_RCC_MOSCDIS);
    ui32RCC2 &= ~(SYSCTL_RCC2_SYSDIV2_M);
    ui32RCC2 |= ui32Config & SYSCTL_RCC2_SYSDIV2_M;
    if(ui32Config & SYSCTL_RCC2_DIV400)
    {
    ui32RCC |= SYSCTL_RCC_USESYSDIV;
    ui32RCC2 &= ~(SYSCTL_RCC_USESYSDIV);
    ui32RCC2 |= ui32Config & (SYSCTL_RCC2_DIV400 | SYSCTL_RCC2_SYSDIV2LSB);
    }
    else
    {
    ui32RCC2 &= ~(SYSCTL_RCC2_DIV400);
    }

    //
    // See if the PLL output is being used to clock the system.
    //
    if(!(ui32Config & SYSCTL_RCC_BYPASS))
    {
    //
    // Wait until the PLL has locked.
    //
    for(ui32Delay = 32768; ui32Delay > 0; ui32Delay--)
    {
    if((HWREG(SYSCTL_PLLSTAT) & SYSCTL_PLLSTAT_LOCK))
    {
    break;
    }
    }

    //
    // Enable use of the PLL.
    //
    ui32RCC &= ~(SYSCTL_RCC_BYPASS);
    ui32RCC2 &= ~(SYSCTL_RCC2_BYPASS2);
    }

    //
    // Write the final RCC value.
    //
    HWREG(SYSCTL_RCC) = ui32RCC;
    HWREG(SYSCTL_RCC2) = ui32RCC2;

    //
    // Delay for a little bit so that the system divider takes effect.
    //
    SysCtlDelay(16);
    }

  • Maka:

    您好,

    您的意思指的是在不改变分频数的情况下,修改外接晶振频率是无法改变时钟频率的吗?请问是否就意味着输出时钟频率要么是16MHz要么是锁相环的200MHz?

    谢谢!

  • 问题是你的晶振频率在物理上改变没有

  • HG:

    您好,

    我只是在程序上去选择外接的晶振(可以这样说吗?),用SysCtlClockSet选择不同的外接。您说晶振频率在物理上的改变是像更换晶振这类的吗?

    谢谢

  • 不好意思,我的理解错了,我以为你使用PLL, 如果没用PLL的话, 改变外接晶振应该是可以改变System Clock的。 

    如下图所示,问题应该是在分频设置为64,你将这个改为1试试。

  • Maka:

    您好,

    我确实没有使用PLL,但是我把分频设置(时钟和PWM)都置为1,时钟设置为12MHz时,PB6和PB4分别仍是8KHz和24KHz(时钟改变之前16MHz时计算得到的频率)。换用其它板子也是这种情况。

    谢谢!

    附程序如下:

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/rom.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pwm.h"
    #include "driverlib/fpu.h"
    #include "driverlib/pin_map.h"

    int main (void)
    {
    //使能FPU
    FPUEnable();
    FPULazyStackingEnable();
    //设置系统时钟为12MHz
    SysCtlClockSet(SYSCTL_SYSDIV_1|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN |SYSCTL_XTAL_12MHZ);

    SysCtlDelay(3000);

    //使能PWM0模块,使能PWM0和PWM1输出所在GPIO
    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
    GPIOPinConfigure(GPIO_PB6_M0PWM0);
    GPIOPinConfigure(GPIO_PB7_M0PWM1);
    GPIOPinConfigure(GPIO_PB4_M0PWM2);
    GPIOPinConfigure(GPIO_PB5_M0PWM3);

    //驱动电流8MA,推挽输出
    GPIOPadConfigSet(GPIO_PORTB_BASE,GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7,
    GPIO_STRENGTH_8MA,GPIO_PIN_TYPE_STD);

    // PWM时钟配置
    SysCtlPWMClockSet(SYSCTL_PWMDIV_1);
    //配置PWM发生器0:加减计数,不同步
    PWMGenConfigure(PWM0_BASE,PWM_GEN_0,PWM_GEN_MODE_UP_DOWN| PWM_GEN_MODE_NO_SYNC);
    PWMGenConfigure(PWM0_BASE,PWM_GEN_1,PWM_GEN_MODE_UP_DOWN| PWM_GEN_MODE_NO_SYNC);
    //设置PWM发生器0的频率,时钟频率/PWM分频数/n,12M/1/2000=6KHZ
    PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, 2000);
    //设置PWM发生器1的频率,时钟频率/PWM分频数/n,12M/1/2000*3=18KHZ
    PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, 2000/3);
    //设置PWM0/PWM1输出的脉冲宽度
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, 2000/2);
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, 2000/2);
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2,2000/3/2);
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_3,2000/3/2);

    //使能PWM0和PWM1的输出
    PWMOutputState(PWM0_BASE, (PWM_OUT_0_BIT |PWM_OUT_1_BIT| PWM_OUT_2_BIT| PWM_OUT_3_BIT), true);
    //使能PWM发生器
    PWMGenEnable(PWM0_BASE, PWM_GEN_0);
    PWMGenEnable(PWM0_BASE, PWM_GEN_1);

    while(1)
    {

    }

    }

  • SysCtlClockSet(SYSCTL_SYSDIV_1|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN |SYSCTL_XTAL_12MHZ)

    是不是你理解错了,SYSCTL_XTAL_12MHZ 这个是指外部时钟源配置,并不是系统时钟。

    如果你选择SYSCTL_XTAL_12MHZ,外部晶振必须接12MHz晶振。

    如果你选变时钟,你可以选择PPL倍频后再分频。如果不用PPL,你就需要改变外部晶振,并配置一致。


  • Maka:

    您好!

    我理解错了,谢谢您。