前言
最近在学习使用兆易的车规MCU——GD32A503,看到手册里面有个外设叫多功能通信接口(MFCOM),查看官方提供的Demo,只有关于MFCOM作为I2C、I2S、SPI、USART此类典型通讯接口的Demo,而手册中所见,是可以作为PWM输出,遂产生自己写一个输出PWM功能的想法,且写下文章,记录学习的进度和成果。
A503用户手册
官方Demo目录
官方资料
既然官方没有现成的Demo,那按照以往的经验,应该先看看官方的用户手册,有的芯片用户手册会有详细的配置教程。所以第一步我们先看官方的用户手册。
从框图上看跟我们想输出PWM有关的也就定时器、触发和输出引脚,而且定时器可以直接与输出引脚连接,输出PWM应该不会是一个比较难的事情。
从手册上看,PWM模式配置后,通过触发信号,即可输出PWM信号。
配置简单MFCOM输出PWM
通过参考MFCOM_USART的USART_polling写出以下配置代码
void mfcom_init(void)
{
//选用输出引脚为PA11 MFCOM_D4
rcu_periph_clock_enable(RCU_GPIOA);//配置引脚时钟
rcu_periph_clock_enable(RCU_MFCOM);//配置MFCOM时钟
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);//PA11 D4
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_af_set(GPIOA, GPIO_AF_6, GPIO_PIN_11);//PA11复用功能4为MFCOM_D4
mfcom_timer_parameter_struct mfcom_timer;
mfcom_timer.trigger_select = MFCOM_TIMER_TRGSEL_EXTERNAL0;//触发事件,因为后续使用ALWAYS使能,所以该配置先忽略
mfcom_timer.trigger_polarity = MFCOM_TIMER_TRGPOL_ACTIVE_HIGH;//触发极性,配置为高触发,但与trigger_select原因一致,先忽略
mfcom_timer.pin_config = MFCOM_TIMER_PINCFG_OUTPUT;//引脚配置,设置为输出
mfcom_timer.pin_select = MFCOM_TIMER_PINSEL_PIN4;//引脚选择
mfcom_timer.pin_polarity = MFCOM_TIMER_PINPOL_ACTIVE_HIGH;//引脚极性,设置为高电平有效
mfcom_timer.mode = MFCOM_TIMER_PWMMODE;//定时器模式,设置为PWM模式
mfcom_timer.output = MFCOM_TIMER_OUT_HIGH_EN;//定时器输出
mfcom_timer.decrement = MFCOM_TIMER_DEC_CLK_SHIFT_OUT;//定时器递减
mfcom_timer.reset = MFCOM_TIMER_RESET_NEVER;//定时器复位,配置为定时器从不复位
mfcom_timer.disable = MFCOM_TIMER_DISMODE_NEVER;//禁能定时器
mfcom_timer.enable = MFCOM_TIMER_ENMODE_ALWAYS;//定时器使能,设定为一直使能
mfcom_timer.stopbit = MFCOM_TIMER_STOPBIT_DISABLE;//定时器停止位
mfcom_timer.startbit = MFCOM_TIMER_STARTBIT_DISABLE;//定时器启动位
mfcom_timer.compare = 0xffff;//定时器比较值
mfcom_timer_init(MFCOM_TIMER_0, &mfcom_timer);
}
如上,将MFCOM配置为简单的PWM输出模式,没有用到特定的触发事件和移位器,想能正常输出PWM后,再逐步添加其他东西。
如上图,当配置为PWM模式后,比较值16位寄存器分为高低8位,用于配置高低电平的周期,而我的代码中compare为0xFFFF,实际出现的PWM信号为占空比50%,频率为
195KHz。
而当我把比较值设置为0x0000后,出现如下波形,占空比50%,频率50MHz。
MFCOM比较值寄存器与频率关系
首先在上文配置MFCOM输出PWM中,根据TMCVALUE寄存器的说明,我们可以得知16位寄存器中,分为高低两位设定高低电平的出现周期,但显然该寄存器配置的周期是缺少一个单位的。例如,当我将寄存器设定为0xFFFF后,高低电平出现周期应该都为0xFF+1,即256,但这个256的单位是什么,手册里面是没有的,所以在后续配置准确的输出频率时,是一个难题。
那么根据现有线索我们需要做一些小小的推理和验证。
目前的线索有:1.在主频为100MHz时,寄存器配置为0x0000,输出PWM频率为50MHz。
2.在主频为100MHz时,寄存器配置为0xFFFF,输出PWM频率为195KHz。
首先,前文中我们将定时器设定为不需触发、ALWAYS启动的模式,所以这个频率应该与触发无关。在平时使用定时器的时候,一般我们都是将总线时钟分频,然后再设定计数值去实现想要的频率输出。那么会不会,MFCOM定时器也是一种总线分频类型的定时器呢。
通过手册,可见MFCOM是挂载在AHB1总线上的,当我的主频设定为100MHz时,AHB1总线也为100MHz,如果说TMCVALUE实际为AHB1总线的时钟分频系数,那么当寄存器值为0x0000时,高低8位分别为1+1=2,频率则为100/2=50MHz;当寄存器值为0xFFFF时,高低8位分别为256+256=512,100/512=195.3KHz,是符合现象的。进一步推论,TIMCVALUE在8位PWM模式下,输出频率为AHB1/(TIMCVALUE[7:0]+TIMCVALUE[15:8]+2),占空比为TIMCVALUE[7:0]/TIMCVALUE[15:8]
那么就需要下面几个实验验证这种猜想:
1.主频100MHz下,设置寄存器值为0x3131,理想输出频率为1MHz,占空比为50%的PWM波。
2.主频100MHz下,设置寄存器值为0x184A,理想输出频率为1MHz,占空比为75%的PWM波。
3.主频48MHz下,设置寄存器值为0x0000,理想输出频率为24MHz,占空比50%的PWM波。
4.主频48MHz下,设置寄存器值为0xFFFF,理想输出频率为93.7KHz,占空比为50%的PWM波。
实验一
实验二
实验三
实验四
结论
由上可见,四个实验都按照理想值输出了相应的PWM波,故可印证:
1.TMCVALUE寄存器实际为AHB1总线的时钟分频系数。
2.MFCOM PWM模式下,PWM频率为AHB1/(TIMCVALUE[7:0]+TIMCVALUE[15:8]+2)。
3.MFCOM PWM模式下,PWM占空比为TIMCVALUE[7:0]/TIMCVALUE[15:8]
触发方式启停PWM
回顾上文MFCOM框图和手册。MFCOM的触发方式可以分为两类,一类是外部触发用到了TRIGSEL触发选择控制器,一类是内部触发,可通过移位器和其他的MFCOM_TIMER进行触发,下面来讲一下不同触发方式的配置方法和实验结果。
TRIGSEL触发PWM
该种触发方式用到了TRIGSEL触发选择控制器,而关于TRIGSEL部分的内容这里不做细究,如果能坚持写博客的话,后续再考虑写一篇。
本次实验,运用PA1作为外部输入触发的引脚,PA11依旧为PWM信号输出的引脚,当PA1为高电平时,PA11输出PWM信号;当PA1为下降沿时,PA11停止输出。话不多说,直接来看代码。
引脚配置
void gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_SYSCFG);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);//配置PA11->D4
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_af_set(GPIOA, GPIO_AF_6, GPIO_PIN_11); //IO功能复用
/*配置触发引脚*/
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_1);
}
MFCOM配置
void mfcom_init(void)
{
rcu_periph_clock_enable(RCU_MFCOM);
mfcom_timer_parameter_struct mfcom_timer;
mfcom_timer.trigger_select = MFCOM_TIMER_TRGSEL_EXTERNAL0; //配置为外部输出0
mfcom_timer.trigger_polarity = MFCOM_TIMER_TRGPOL_ACTIVE_HIGH; //触发信号高电平有效
mfcom_timer.pin_config = MFCOM_TIMER_PINCFG_OUTPUT; //D4配置为输出
mfcom_timer.pin_select = MFCOM_TIMER_PINSEL_PIN4; //MFCOM_D4
mfcom_timer.pin_polarity = MFCOM_TIMER_PINPOL_ACTIVE_HIGH; //引脚高电平有效
mfcom_timer.mode = MFCOM_TIMER_PWMMODE; //PWM模式
mfcom_timer.output = MFCOM_TIMER_OUT_LOW_EN_RESET;
mfcom_timer.decrement = MFCOM_TIMER_DEC_CLK_SHIFT_OUT;
mfcom_timer.reset = MFCOM_TIMER_OUT_LOW_EN;
mfcom_timer.disable = MFCOM_TIMER_DISMODE_TRIGFALLING; //下降沿禁能TIMER
mfcom_timer.enable = MFCOM_TIMER_ENMODE_TRIGHIGH; //高电平使能TIMER
mfcom_timer.stopbit = MFCOM_TIMER_STOPBIT_DISABLE;
mfcom_timer.startbit = MFCOM_TIMER_STARTBIT_DISABLE;
mfcom_timer.compare = 0x184A; //输出1MHz,50%占空比PWM
mfcom_timer_init(MFCOM_TIMER_0, &mfcom_timer);
}
Main函数&TRIGSEL绑定配置
int main(void)
{
gpio_config();
mfcom_init();
mfcom_enable(); //使能MFCOM,要在使能RCU_MFCOM后
/* enable TRIGSEL clock */
rcu_periph_clock_enable(RCU_TRIGSEL);
/* select TRIGSEL_IN0 to trigger TIMER1 */
trigsel_init(TRIGSEL_OUTPUT_MFCOM_TRG_TIMER0, TRIGSEL_INPUT_TRIGSEL_IN0);
/* lock trigger register */
trigsel_register_lock_set(TRIGSEL_OUTPUT_MFCOM_TRG_TIMER0); //寄存器上锁
while(1);
}
根据上面的配置,有下面的现象。而上面配置中的触发方式,触发引脚,都可以按照自己不同的需要去配置,这里就不赘述了。
下图中黄线为PA11,绿线为PA1,可见实际情况跟我们要配置的情况是一致的。
PA1为上升沿时,PWM输出
PA1为下降沿时,PWM停止输出
MFCOM引脚触发PWM
PWM波振铃效应
开始前先说句题外话,看上面的图可以发现,每次PWM上升沿都会出现一个明显尖刺,为振铃效应,为减少此效应,可适当减小IO驱动能力。
本次设置PC10->MFCOM_D3,作为触发源,与上面的TRIGSEL触发类似,只少了相关的设置,运用PC10作为输入触发的引脚,PA11依旧为PWM信号输出的引脚,当PC10为高电平时,PA11输出PWM信号;当PC10为下降沿时,PA11停止输出,直接看代码即可。
引脚配置
void gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_SYSCFG);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);//配置PA11->D4
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_11);//IO速度设置为2MHZ,降低驱动能力,减小振铃
gpio_af_set(GPIOA, GPIO_AF_6, GPIO_PIN_11); //IO功能复用
/*配置触发引脚*/
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);//配置PC10->D3
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_10);//IO速度设置为2MHZ,降低驱动能力,减小振铃
gpio_af_set(GPIOC, GPIO_AF_6, GPIO_PIN_10);
}
MFCOM配置
void mfcom_init(void)
{
rcu_periph_clock_enable(RCU_MFCOM);
mfcom_timer_parameter_struct mfcom_timer;
mfcom_shifter_parameter_struct mfcom_shifter;
mfcom_timer.trigger_select = MFCOM_TIMER_TRGSEL_PIN3;//设置D3为触发源
mfcom_timer.trigger_polarity = MFCOM_TIMER_TRGPOL_ACTIVE_HIGH;
mfcom_timer.pin_config = MFCOM_TIMER_PINCFG_OUTPUT;
mfcom_timer.pin_select = MFCOM_TIMER_PINSEL_PIN4;
mfcom_timer.pin_polarity = MFCOM_TIMER_PINPOL_ACTIVE_HIGH;
mfcom_timer.mode = MFCOM_TIMER_PWMMODE;
mfcom_timer.output = MFCOM_TIMER_OUT_LOW_EN_RESET;
mfcom_timer.decrement = MFCOM_TIMER_DEC_CLK_SHIFT_OUT;
mfcom_timer.reset = MFCOM_TIMER_OUT_LOW_EN;
mfcom_timer.disable = MFCOM_TIMER_DISMODE_TRIGFALLING;
mfcom_timer.enable = MFCOM_TIMER_ENMODE_TRIGHIGH;
mfcom_timer.stopbit = MFCOM_TIMER_STOPBIT_DISABLE;
mfcom_timer.startbit = MFCOM_TIMER_STARTBIT_DISABLE;
mfcom_timer.compare = 0x184A;
mfcom_timer_init(MFCOM_TIMER_0, &mfcom_timer);
}
Main函数
int main(void)
{
gpio_config();
mfcom_init();
mfcom_enable();
while(1);
}
根据上述配置有如下波形图,其中绿色为PC10,黄色为PB11。
PC10为高电平,PB11输出PWM
PC10为下降沿,PB11停止输出
总结
明显MFCOM输出PWM信号,在比较值上是有限制的。 目前实验到这里为止,后续会补齐移位器和触发操作。最近还有别的事情要忙,只能等闲下来后再研究了。
本文为本人学习的记录,可能会比较粗糙和不专业。希望各位大神可以指出问题!