【GD32】用GD32A503的MFCOM输出PWM信号

前言

        最近在学习使用兆易的车规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信号,在比较值上是有限制的。 目前实验到这里为止,后续会补齐移位器和触发操作。最近还有别的事情要忙,只能等闲下来后再研究了。

        本文为本人学习的记录,可能会比较粗糙和不专业。希望各位大神可以指出问题!

C# 中使用 Interop.ACTMULTILib 主要是为了访问 ActiveX 控制器,ACTMultilib 是 Microsoft 提供的一个用于处理多语言文字 (MFCOM) 对象的库,特别是在处理 Office 应用(如 Word、Excel)中的文本时非常有用。以下是使用它的一般步骤: 1. **引用库**: 首先,在你的 C# 项目中,右键点击 "References"(引用),然后添加对 "Interop.ACTMultilib.dll" 的引用。这个文件通常位于 "C:\Program Files (x86)\Microsoft Office\Office16\VBA\Reference Sources" 或者与你安装的 Office 版本相对应的路径下。 2. **创建实例**: 使用 `System.Runtime.InteropServices.Marshal` 和 `Activator.CreateInstance` 创建一个 ACTLib 对象,例如: ```csharp using System.Runtime.InteropServices; object activexObject = Activator.CreateInstance(Type.GetTypeFromProgID("ACTLib.ACTMultiLib")); ``` 3. **交互调用方法**: 一旦有了 ActiveX 对象,就可以像对待任何其他 COM 对象一样调用其方法了。例如,如果你想要打开一个Word文档,可以这样做: ```csharp dynamic wordApp = activexObject; object wordDoc = wordApp.Documents.Open(@"C:\example.docx"); ``` 4. **处理返回值**: 注意,ActiveX 方法的返回值通常是动态类型的,因此你需要根据实际情况处理它们。 5. **处理异常**: 使用 `try-catch` 块处理可能出现的 COM 异常,因为 Interop 操作可能会抛出 `COMException`。 ```csharp try { // 调用ACTMultiLib方法 } catch (COMException ex) { Console.WriteLine("Error: " + ex.Message); } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值