在用TM4C123G的PWM模块输出PWM时,按照网上大部分教程的方法,进行设置,遇到了一些问题。
先贴上初始化的代码
void PWM_Init(void)
{
SysCtlPWMClockSet(SYSCTL_PWMDIV_16); // 设定PWM时钟分频
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0); // 使能时钟
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_PWM0)); //等待完成
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB));
// SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
// while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOC));
// SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
// while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE));
GPIOPinConfigure(GPIO_PB6_M0PWM0); //注册引脚复用
GPIOPinConfigure(GPIO_PB7_M0PWM1);
// GPIOPinConfigure(GPIO_PB4_M0PWM2);
// GPIOPinConfigure(GPIO_PB5_M0PWM3);
// GPIOPinConfigure(GPIO_PE4_M0PWM4);
// GPIOPinConfigure(GPIO_PE5_M0PWM5);
// GPIOPinConfigure(GPIO_PC4_M0PWM6);
// GPIOPinConfigure(GPIO_PC5_M0PWM7);
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_6);//M0PWM0 //设置引脚为PWM引脚
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_7);//M0PWM1
// GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_4);//M0PWM2
// GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_5);//M0PWM3
// GPIOPinTypePWM(GPIO_PORTE_BASE, GPIO_PIN_4);//M0PWM4
// GPIOPinTypePWM(GPIO_PORTE_BASE, GPIO_PIN_5);//M0PWM5
// GPIOPinTypePWM(GPIO_PORTC_BASE, GPIO_PIN_4);//M0PWM6
// GPIOPinTypePWM(GPIO_PORTC_BASE, GPIO_PIN_5);//M0PWM7
PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC); //注册PWM发生器及相关设置
// PWMGenConfigure(PWM0_BASE, PWM_GEN_1, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
// PWMGenConfigure(PWM0_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
// PWMGenConfigure(PWM0_BASE, PWM_GEN_3, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, SysCtlClockGet() / 16 / 1000 - 1); // 设置频率为1000Hz
// PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, SysCtlClockGet() / 16 / 1000 - 1);
// PWMGenPeriodSet(PWM0_BASE, PWM_GEN_2, SysCtlClockGet() / 16 / 1000 - 1);
// PWMGenPeriodSet(PWM0_BASE, PWM_GEN_3, SysCtlClockGet() / 16 / 1000 - 1);
PWMGenEnable(PWM0_BASE, PWM_GEN_0); //使能PWM0发生器0
// PWMGenEnable(PWM0_BASE, PWM_GEN_1);
// PWMGenEnable(PWM0_BASE, PWM_GEN_2);
// PWMGenEnable(PWM0_BASE, PWM_GEN_3);
PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT, true); //使能输出
PWMPulseWidthSet(PWM0_BASE,PWM_OUT_1,PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0)*0.5 - 1);//PB7 //设置占空比
PWMPulseWidthSet(PWM0_BASE,PWM_OUT_0,PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0)*0.5 - 1);//PB6
}
关于PWM初始化就不详细介绍了,代码里都有注释,网上教程也很多,这里主要讲一下当时写的时候主要遇到几个问题。
- 第一个问题:
PWMGenPeriodSet
这个函数的装载值计算问题
这里直接填写SysCtlClockGet() / 分频值 / 目标频率 - 1就可以。SysCtlClockGet
会获取你目前的系统时钟频率,这里的分频值就是上面SysCtlPWMClockSet
函数里设置的分频值,我这里设置的是16分频
- 第二个问题:分频值选择对输出频率下限的影响
这个问题当时困扰了我好久,最初我设置的是1分频SysCtlPWMClockSet(SYSCTL_PWMDIV_1)
,想要输出50Hz的PWM波,结果总是输出错误,研究了一番后发现犯了一个很容易忽略的问题。
按照我最初的设置系统时钟是SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_XTAL_16MHZ | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN);
系统时钟分频器系数4、选择外部晶振频率、使用PLL锁相环作为系统时钟源,这样最终系统频率是PLL的200MHz4分频为50MHz,也就是SysCtlClockGet
得到的时钟频率为50MHz。
这样按照计算公式50M / 1 / 50 - 1= 999999,而对应的寄存器是16位(应该是,没有特意去看过),999999这个数值明显超出了范围,这就是之前一直输出错误的原因。
这就需要将输入PWM模块的时钟频率降低,才能使寄存器不溢出,有两种方法实现:
第一种是降低系统时钟频率,修改SysCtlClockSet
函数
第二种是提高PWM时钟分频,修改SysCtlPWMClockSet
函数
我选择修改PWM时钟分频值,修改为16分频SysCtlPWMClockSet(SYSCTL_PWMDIV_16
,这样50M / 16 / 50 - 1 = 62499没有超出范围
- 第三个问题:分频值对输出频率精度的影响
解决了输出50Hz的问题后,又试着提高输出频率,发现频率到几千后实际输出频率和设定频率偏差较大(偏差一两百Hz),有趣的是将分频值降低后,偏差又变小了。
猜测可能是当频率高了之后对应的装载值变小了,精度变低,当把分频系数减小后,装载值变大,精度变高(纯猜测)
所以要同时解决频率下限和频率上限问题还挺麻烦的。
ps:博主也理解的不是很深入,如果有更好的解决方法欢迎在评论区补充。