[SOLVED] MCPWM does not want to start

P32Studio
Posts: 5
Joined: Wed Jul 21, 2021 4:47 pm

[SOLVED] MCPWM does not want to start

Postby P32Studio » Wed Jul 21, 2021 5:12 pm

Hello, here is the deal: I have made a simple C environment where I can run bare-metal code. I wanted to configure mcpwm peripheral to control an H-Bridge for 2 coils stepper motor.
Used Chip is ESP32-D0WDQ6 (revision 1).

The problem is that when I call two_phase_init();, the registers hold their values, but when I read PWM_TIMER0_STATUS_REG, I see zeros, as if the CLK_160M timer was not enabled.
Please assume that this is a single core application, so no synchronization is required, and I have unlocked the watchdog so the core does not reset every x-miliseconds.

Did I forget something, such as some clock init? Or is some configuration in a wrong order? Relevant MEM dump is as follows:
0x3FF5E000 0b 00000000 00000000 00000000 11111010 // PWM_CLK_CFG_REG
0x3FF5E004 0b 00000000 00000000 00000111 11111010 // PWM_TIMER0_CFG0_REG
0x3FF5E008 0b 00000000 00000000 00000000 00010010 // PWM_TIMER0_CFG1_REG (Timer start holds value 2 - start & run on)
0x3FF5E00C 0b 00000000 00000000 00000000 01000100 // PWM_TIMER0_SYNC_REG
0x3FF5E010 0b 00000000 00000000 00000000 00000000 // PWM_TIMER0_STATUS_REG (Value = 0, Direction = Increment(wrong))

This is pretty much what would run in the PRO CPU entry function called from the ROM code.

Code: Select all

#include <motor_control_reg.h>
#include "GPIO.h"
#include <control_reg.h>
/**
 * Initializes the required cables and registers.
 **/
void two_phase_init(){
	//Enable peripheral
	DREG->PERIP_CLK_EN.DPORT_PWM0_CLK_EN = 1;
	DREG->PERIP_RST_EN.DPORT_PWM0_RST = 1;
	DREG->PERIP_RST_EN.DPORT_PWM0_RST = 0;
	DREG->AHBLITE_MPU_TABLE_PWM0 = 1;
	//We will use Channel0.

	//==PRESCALER SUBMODULE==
	//Input to this prescaler is 160Mhz
	MOT_CTRL0->CLK_CFG.PWM_CLK_PRESCALE = 250; //Out is 640Khz

	//==SHADOW REGISTERS CONFIG==
	MOT_CTRL0->UPDATE_CFG.PWM_GLOBAL_UP_EN = 1;
	MOT_CTRL0->UPDATE_CFG.PWM_OP0_UP_EN = 1;
	MOT_CTRL0->UPDATE_CFG.PWM_OP1_UP_EN = 1;
	MOT_CTRL0->UPDATE_CFG.PWM_OP2_UP_EN = 1;

	MOT_CTRL0->UPDATE_CFG.PWM_GLOBAL_FORCE_UP ^= (1u << 1u); //Flush Shadow

	//==TIMER SUBMODULE 0,1,2==
	MOT_CTRL0->SUBMODULE_TIMER[0].CFG0.PRESCALE = 250; //Out is 2.560Khz
	MOT_CTRL0->SUBMODULE_TIMER[0].CFG1.MODE = PWM_TIMER_MODE_DECREASE;
	MOT_CTRL0->SUBMODULE_TIMER[0].CFG0.PERIOD_UPDATEMETHOD = PWM_TIMER_UPDATEMETHOD_IMMEDIATELY;
	MOT_CTRL0->SUBMODULE_TIMER[0].CFG0.PERIOD = 7;

	MOT_CTRL0->SUBMODULE_TIMER[0].SYNC.PHASE = 4;
	MOT_CTRL0->SUBMODULE_TIMER[0].SYNC.OUT_SEL = PWM_TIMER_SYNC_OUT_TIME_ZERO; //Does not really matter
	MOT_CTRL0->SUBMODULE_TIMER[0].SYNC.IN_EN = 0;

	MOT_CTRL0->TIMER_SYNCI_CFG.TIMER0_SYNCISEL = PWM_TIMER_SYNC_IN_NOT_CONNECTED;
	MOT_CTRL0->TIMER_SYNCI_CFG.EXTERNAL_SYNCI0_INVERT = 0;

	//==OPERATOR SUBMODULE==
	MOT_CTRL0->OPERATOR_TIMERSEL.OPERATOR0_TIMERSEL = PWM_TIMER_TIMERSEL_TIMER0;
	MOT_CTRL0->SUBMODULE_OPERATOR[0].GEN_A.UTEZ = PWM_ACTION_HIGH;
	MOT_CTRL0->SUBMODULE_OPERATOR[0].GEN_A.UTEA = PWM_ACTION_LOW;
	MOT_CTRL0->SUBMODULE_OPERATOR[0].GEN_A.UTEB = PWM_ACTION_NO_CHANGE;
	MOT_CTRL0->SUBMODULE_OPERATOR[0].GEN_EVENT_A.VALUE = 3;

	GPIO_set_pin_as_output(33, 32, 2); //GPIO 33 will have connected signal no. 32 at drive strength 2
	MOT_CTRL0->UPDATE_CFG.PWM_GLOBAL_FORCE_UP ^= (1u << 1u); //Flush Shadow
	//MOT_CTRL0->SUBMODULE_TIMER[0].SYNC.SOFTWARE_SYNC = 1;

	MOT_CTRL0->SUBMODULE_TIMER[0].CFG1.START = PWM_TIMER_START_RUN;
	
	//DUMP PERFORMED HERE

}
Last edited by P32Studio on Fri Feb 04, 2022 7:07 pm, edited 1 time in total.

P32Studio
Posts: 5
Joined: Wed Jul 21, 2021 4:47 pm

Re: MCPWM does not want to start

Postby P32Studio » Thu Feb 03, 2022 12:27 am

You are right. The dump shows that the mcpwm timer0's configuration registers hold their values, so that particular part of the chip is turned on correctly. But timer0 status register shows a value that would not be there, if the circuitry was running.

What you need to do is to set up RTC_CNTL_CLK_CONF_REG.RTC_CNTL_SOC_CLK_SEL to value 0b1 (PLL).
XTAL source would also work for standard wroom modules. CK8M and APLL require further configuration.
This will configure the clock line that also goes into mcpwm's timer clock input. Unfortunately, now, you need to configure some peripherals that are time dependent and are dependent on this changed clock, such as UART. Default UART gets unsynchronised and you need to resynchronize it, which is not that difficult. So the relevant initialization code would be as follows:

Code: Select all

#include <motor_control_reg.h>
#include "GPIO.h"
#include <control_reg.h>
#include <rtc_cntl_reg.h>
//This function fixes timing of UART when we change soc clock source.
static void (*uart_div_modify)(uint8_t uart_num, uint32_t DivLatchValue) = (void*)0x400090cc; //ROM function at this address

/**
 * Initializes the required cables and registers.
 **/
void two_phase_init(){
	//Enable peripheral
	
	//This is equivalent to CNTRL->CLK_CONF.SOC_CLK_SEL = CNTRL_CLK_SRC_PLL;
	(*(uint32_t*)0x3FF48070) = ((*(uint32_t*)0x3FF48070) & 0xE7FFFFFF) | (1u << 27u); //Setup timer's clock source (for whole chip)
	uart_div_modify(0, (80000000 << 4) / 115200); //Since internal clock has changed, UART got desynchronized. This fixes it.
	
	DREG->PERIP_CLK_EN.DPORT_PWM0_CLK_EN = 1; //Enable clock into this peripheral (This is different clk source from the timer)
	DREG->PERIP_RST_EN.DPORT_PWM0_RST = 1; //Reset peripheral
	DREG->PERIP_RST_EN.DPORT_PWM0_RST = 0; //Reset release
	DREG->AHBLITE_MPU_TABLE_PWM0 = 1; //set process permissions to access the mcpwm memory region
	
	//mcmpw initialized

	//We will use Channel0.		
	//Configure timer
	(*(uint32_t*)0x3FF5E004) = 0b00000000000000001111111111111111; //timer CFG0
	(*(uint32_t*)0x3FF5E008) = 0b10010; //timer CFG1. Decrease, run on.
	(*(uint32_t*)0x3FF5E00C) = 0b0; //Disable timer sync


	//mcpwm0 clock0 status at this address contains live counting value now.
	//*(volatile unsigned int *)(0x3FF5E010)
}

Who is online

Users browsing this forum: No registered users and 100 guests