TM4C123G PWM输出频率设定、频率下限、输出误差相关问题的记录

在用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:博主也理解的不是很深入,如果有更好的解决方法欢迎在评论区补充。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值