Good morning, everyone, I am a firmware developer and am currently using the GD32F303R8T6 microcontroller to control a brushless motor. To control such a motor, I plan to use the timer peripheral TIMER_0 (advanced timer) to generate 3 PWM signals with related negated signals (in total, 6 signals). These signals are sent inside an ST driver that will command the motor. The problem I am experiencing is configuring the TIMER_0 timer to have the PWM signals generated.
Specifications on PWM:
- Freq SYS_CLK: 72MHz
- PWM freq: 8kHz
- PWM Center Aligned
With the code shown, the goal is to generate the 6 PWM signals (3 normal and 3 complementary), then the control part will be implemented later.
In the following I'll write down the code that I tried to use to set the TIMER_0:
// cleaning control registers
CLEAR_REG(TIMER_CTL0(TIMER0));
CLEAR_REG(TIMER_CTL1(TIMER0));
// [ CLT0: 0x00000075 ]
WRITE_REG(TIMER_CTL0(TIMER0), TIMER_CTL0_UPS |
TIMER_CTL0_CAM_0 |
TIMER_CTL0_CAM_1 |
0);
// [ CLT1: 0x00000070 ]
WRITE_REG(TIMER_CTL1(TIMER0), TIMER_CTL1_MMC |
0);
// [ DMAINTEN: 0x00000001 ]
WRITE_REG(TIMER_DMAINTEN(TIMER0), TIMER_DMAINTEN_UPIE);
// [ CHCTL0: 0x00006868 ]
WRITE_REG(TIMER_CHCTL0(TIMER0), TIMER_CHCTL0_CH0COMSEN |
TIMER_CHCTL0_CH0COMCTL_PWM0 |
TIMER_CHCTL0_CH1COMSEN |
TIMER_CHCTL0_CH1COMCTL_PWM0 |
0);
// [ CHCTL1: 0x00006868 ]
WRITE_REG(TIMER_CHCTL1(TIMER0), TIMER_CHCTL1_CH2COMSEN |
TIMER_CHCTL1_CH2COMCTL_PWM0 |
TIMER_CHCTL1_CH3COMSEN |
TIMER_CHCTL1_CH3COMCTL_PWM0 |
0);
// enable of the 3 + 3 channels
// [ CHCTL2: 0x00001555 ]
WRITE_REG(TIMER_CHCTL2(TIMER0), TIMER_CHCTL2_CH0EN |
TIMER_CHCTL2_CH0NEN |
TIMER_CHCTL2_CH1EN |
TIMER_CHCTL2_CH1NEN |
TIMER_CHCTL2_CH2EN |
TIMER_CHCTL2_CH2NEN |
TIMER_CHCTL2_CH3EN |
0);
WRITE_REG(TIMER_PSC(TIMER0), 0);
// "autoreload register"
WRITE_REG(TIMER_CAR(TIMER0), 4500);
// set of Compare Value Registers
WRITE_REG(TIMER_CH0CV(TIMER0), 2250);
WRITE_REG(TIMER_CH1CV(TIMER0), 2250);
WRITE_REG(TIMER_CH2CV(TIMER0), 2250);
WRITE_REG(TIMER_CH3CV(TIMER0), 2250);
// [ CCHP: 0x00004088 ]
WRITE_REG(TIMER_CCHP(TIMER0), TIMER_CCHP_OAEN |
//TIMER_CCHP_POEN |
//TIMER_CCHP_BRKEN |
//TIMER_CCHP_ROS |
TIMER_CCHP_DTCFG_88 |
0);
// enable counter (start PWM generation)
MODIFY_REG(TIMER_CTL0(TIMER0), TIMER_CTL0_CEN, TIMER_CTL0_CEN);
In the datasheet of this microcontroller, it is indicated that the PINs dedicated to TIMER_0 for PWM are as follows:
- PA8 [ CH0 ]
- PA9 [ CH1 ]
- PA10 [ CH2 ]
- PB13 [ CH0N ]
- PB14 [ CH1N ]
- PB15 [ CH2N ]
The following is the setting of the registers related to the above GPIOs.
// enable clock to GPIO A and B
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
// enable AF clock
rcu_periph_clock_enable(RCU_AF);
WRITE_REG(GPIO_CTL1(GPIOA), GPIO_CTL1_MD8_1 |
GPIO_CTL1_CTL8_2 |
GPIO_CTL1_MD9_1 |
GPIO_CTL1_CTL9_2 |
GPIO_CTL1_MD10_1 |
GPIO_CTL1_CTL10_2 |
GPIO_CTL1_MD13_1 |
GPIO_CTL1_CTL13_2 |
GPIO_CTL1_MD14_1 |
GPIO_CTL1_CTL14_2 |
GPIO_CTL1_MD15_1 |
GPIO_CTL1_CTL15_2 |
0);
This is the part of the schematic showing how the microcontroller pins are connected to the power bridge: Scheme Connetion Micro to Bridge
The red arrows represent the 6 pins dedicated to the 6 PWM signals, the red dots represent the points in which I connect my oscilloscope.
With the configuration of TIMER0 and GPIO that I show u before, if I watch the oscilloscope I see active just CH0 and CH2 and no other signals (also all the complementary channels are completely dead). As debugger I'm using a JLINK. This is the Reference Manual and the datasheet documents that I used to configure the micro:
After trying to set the logs manually, I tried using the functions already available to set the logs. Below is the code for only the TIMER0 part of the timer, the GPIO configuration remains the same as before.
/* -----------------------------------------------------------------------
TIMER0 configuration:
generate 3 complementary PWM signal.
TIMER0CLK is fixed to systemcoreclock, the TIMER0 prescaler is equal to 119
so the TIMER0 counter clock used is 1MHz.
insert a dead time equal to 200/systemcoreclock =1.67us
configure the break feature, active at low level, and using the automatic
output enable feature.
use the locking parameters level 0.
----------------------------------------------------------------------- */
timer_oc_parameter_struct timer_ocintpara;
timer_parameter_struct timer_initpara;
timer_break_parameter_struct timer_breakpara;
//rcu_periph_clock_enable(RCU_TIMER0);
timer_deinit(TIMER0);
/* TIMER0 configuration */
timer_initpara.prescaler = 0;
timer_initpara.alignedmode = TIMER_COUNTER_CENTER_BOTH;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 4499;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER0,&timer_initpara);
/* CH0/CH0N,CH1/CH1N and CH2/CH2N configuration in timing mode */
timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
timer_ocintpara.outputnstate = TIMER_CCXN_ENABLE;
timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_HIGH;
timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_HIGH;
timer_channel_output_config(TIMER0,TIMER_CH_0,&timer_ocintpara);
timer_channel_output_config(TIMER0,TIMER_CH_1,&timer_ocintpara);
timer_channel_output_config(TIMER0,TIMER_CH_2,&timer_ocintpara);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_0,2249);
timer_channel_output_mode_config(TIMER0,TIMER_CH_0,TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER0,TIMER_CH_0,TIMER_OC_SHADOW_ENABLE);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_1,2249);
timer_channel_output_mode_config(TIMER0,TIMER_CH_1,TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER0,TIMER_CH_1,TIMER_OC_SHADOW_ENABLE);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_2,2249);
timer_channel_output_mode_config(TIMER0,TIMER_CH_2,TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER0,TIMER_CH_2,TIMER_OC_SHADOW_ENABLE);
/* automatic output enable, break, dead time and lock configuration*/
timer_breakpara.runoffstate = TIMER_ROS_STATE_ENABLE;
timer_breakpara.ideloffstate = TIMER_IOS_STATE_ENABLE ;
timer_breakpara.deadtime = 164;
timer_breakpara.breakpolarity = TIMER_BREAK_POLARITY_LOW;
timer_breakpara.outputautostate = TIMER_OUTAUTO_ENABLE;
timer_breakpara.protectmode = TIMER_CCHP_PROT_OFF;
timer_breakpara.breakstate = TIMER_BREAK_DISABLE;
timer_break_config(TIMER0,&timer_breakpara);
/* TIMER0 primary output function enable */
timer_primary_output_config(TIMER0,ENABLE);
/* TIMER0 channel control update interrupt enable */
timer_interrupt_enable(TIMER0,TIMER_INT_CMT);
/* TIMER0 break interrupt disable */
timer_interrupt_disable(TIMER0,TIMER_INT_BRK);
/* TIMER0 counter enable */
timer_enable(TIMER0);
Even with this new configuration, the result does not change from before: only the CH0 channel and CH2 generate the required PWM, while the other channels (even the complementary ones) remain inactive.
NOTE: The configuration you see above, I found on a Gitlab repository dedicated to GigaDevice. Below I attach the link if you find it useful.
LINK for GitLab Repo for GigaDevice Config Peripherals
Can you help me understand what I am doing wrong in the configuration of TIMER0 to generate all PWM signals ? Thank you in advance and I hope I have explained my problem as best as I can
Aristide