Using MCPWM periferal to generate desired pattern

User avatar
gunar.kroeger
Posts: 143
Joined: Fri Jul 27, 2018 6:48 pm

Using MCPWM periferal to generate desired pattern

Postby gunar.kroeger » Mon Jan 27, 2020 8:58 pm

Hi all,
We have to generate this pulse sequence on 2 pins:
chargepump.pgn.png
chargepump.pgn.png (1.71 KiB) Viewed 6166 times
this sequence should be repeated indefinitely.

What we are achieving is either another sequence being generated (1,0,1,0,0) or the correct sequence, but unsynchronized between the 2 channels. In this last case, every time we reset the board, the delay between the pulses in pin 1 and pin 2 is different. There is no "drift" of this delay during execution.

What is the correct code initialization to obtain exactly the pulses from the attached image?

Thanks
  1. /*
  2.  * ChargePump.c
  3.  *
  4.  *  Created on: 27 de jan de 2020
  5.  *      Author: joao.istchuk
  6.  */
  7. #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
  8. #include "ChargePump.h"
  9.  
  10. #define RMT_NS      12.5
  11. //times in nanoseconds
  12. #define TLO         25000 / RMT_NS
  13. #define THI         25000 / RMT_NS
  14.  
  15. static void rmt_tx_init(void);
  16.  
  17. extern Pins pins;
  18.  
  19. static bool chage_pump_instaled = false;
  20.  
  21. static rmt_item32_t items[] = {
  22.     // Setup
  23.     {{{ THI, 1, TLO, 0 }}},
  24.     {{{ TLO/2, 0, 0, 0 }}},
  25.     {{{ 0, 0, 0, 0 }}}
  26. };
  27.  
  28. static rmt_item32_t items2[] = {
  29.     // Setup
  30.     {{{ THI, 1, TLO, 0 }}},
  31.     {{{ TLO/2, 0, 0, 0 }}},
  32.     {{{ 0, 0, 0, 0 }}}
  33. };
  34.  
  35.  
  36. void chargePumpInit()
  37. {
  38.     rmt_tx_init();
  39.     chage_pump_instaled = true;
  40. }
  41.  
  42. void chargePumpDisable()
  43. {
  44.     if(chage_pump_instaled)
  45.     {
  46.         rmt_tx_stop(RMT_CHANNEL_0);
  47.         rmt_tx_stop(RMT_CHANNEL_1);
  48.     }
  49. }
  50.  
  51. void chargePumpEnable()
  52. {
  53.     if(chage_pump_instaled)
  54.     {
  55.         rmt_tx_start(RMT_CHANNEL_0,true);
  56.         rmt_tx_start(RMT_CHANNEL_1,true);
  57.     }
  58. }
  59.  
  60. /*
  61.  * Initialize the RMT Tx channel
  62.  */
  63. static void rmt_tx_init(void)
  64. {
  65.     rmt_config_t config;
  66.     config.rmt_mode = RMT_MODE_TX;
  67.     config.mem_block_num = 1;
  68.     config.tx_config.loop_en = 1;
  69.     // enable the carrier to be able to hear the Morse sound
  70.     // if the RMT_TX_GPIO is connected to a speaker
  71.     config.tx_config.carrier_en = 0;
  72.     config.tx_config.idle_output_en = 0;
  73.     config.tx_config.idle_level = 0;
  74.     config.tx_config.carrier_duty_percent = 50;
  75.     // set audible career frequency of 611 Hz
  76.     // actually 611 Hz is the minimum, that can be set
  77.     // with current implementation of the RMT API
  78.     config.tx_config.carrier_freq_hz = 611;
  79.     config.tx_config.carrier_level = 1;
  80.     // set the maximum clock divider to be able to output
  81.     // RMT pulses in range of 500ns
  82.     config.clk_div = 1;
  83.  
  84.     config.channel = RMT_CHANNEL_0;
  85.     config.gpio_num = pins.cp_ctrl_1;
  86.  
  87.     ESP_ERROR_CHECK(rmt_config(&config));
  88.     ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
  89.  
  90.     config.channel = RMT_CHANNEL_1;
  91.     config.gpio_num = pins.cp_ctrl_2;
  92.  
  93.     ESP_ERROR_CHECK(rmt_config(&config));
  94.     ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
  95.  
  96.     ESP_ERROR_CHECK(rmt_fill_tx_items(RMT_CHANNEL_0,items,sizeof(items)/sizeof(items[0]),0));
  97.     ESP_ERROR_CHECK(rmt_fill_tx_items(RMT_CHANNEL_1,items2,sizeof(items2)/sizeof(items2[0]),0));
  98.  
  99.     portDISABLE_INTERRUPTS();
  100.     rmt_tx_start(RMT_CHANNEL_0,false);
  101.     rmt_tx_start(RMT_CHANNEL_1,false);
  102. //  rmt_write_items(RMT_CHANNEL_0, items,   sizeof(items)/sizeof(items[0]), false);
  103. //  rmt_write_items(RMT_CHANNEL_1, items2,  sizeof(items)/sizeof(items[0]), false);
  104.     portENABLE_INTERRUPTS();
  105.  
  106. }
Last edited by gunar.kroeger on Mon Feb 10, 2020 2:49 pm, edited 1 time in total.
"Running was invented in 1612 by Thomas Running when he tried to walk twice at the same time."

User avatar
gunar.kroeger
Posts: 143
Joined: Fri Jul 27, 2018 6:48 pm

Re: RMT peripheral synchronized channels

Postby gunar.kroeger » Thu Jan 30, 2020 1:58 pm

I see there is a MCPWM component that is much better suited for this.
Still, for future applications this might be usefull to know.
"Running was invented in 1612 by Thomas Running when he tried to walk twice at the same time."

User avatar
gunar.kroeger
Posts: 143
Joined: Fri Jul 27, 2018 6:48 pm

Re: RMT peripheral synchronized channels

Postby gunar.kroeger » Mon Feb 10, 2020 2:48 pm

Actually I still am not able to generate that signal. Could someone help me out?
I don't understand how to configure the deadtime properly.

Code: Select all

static void mcpwm_example_gpio_initialize()
{
	ESP_LOGI(TAG, "initializing mcpwm gpio...\n");
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, pins.cp_ctrl_1);
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, pins.cp_ctrl_2);
}

void chargePumpInit()
{
	ESP_LOGI(TAG, "Testing brushed motor...\n");

    //1. mcpwm gpio initialization
    mcpwm_example_gpio_initialize();


    //2. initial mcpwm configuration
    ESP_LOGI(TAG, "Configuring Initial Parameters of mcpwm...\n");
    mcpwm_config_t pwm_config;
    pwm_config.frequency = 10000;    //frequency = 1000Hz,
    pwm_config.cmpr_a = 0;    //duty cycle of PWMxA = 0
    pwm_config.cmpr_b = 0;    //duty cycle of PWMxb = 0
    pwm_config.counter_mode = MCPWM_UP_COUNTER;
    pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
    mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);    //Configure PWM0A & PWM0B with above settings

	mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
	mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
//
	mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_BYPASS_RED, 500, 500); //red*100ns,  fed*100ns

    mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, 25);
    mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, 25);

    mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, MCPWM_DUTY_MODE_0);  //call this each time, if operator was previously in low/high state
    mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, MCPWM_DUTY_MODE_0);  //call this each time, if operator was previously in low/high state
}
"Running was invented in 1612 by Thomas Running when he tried to walk twice at the same time."

jmcornil
Posts: 21
Joined: Mon Feb 10, 2020 1:42 pm

Re: Using MCPWM periferal to generate desired pattern

Postby jmcornil » Wed Feb 12, 2020 2:59 pm

Hello

did you try to use MCPWM and an XOR gate ?
  • with TIMER_0, you cant get the first signal : (1 0 0 0) for pin 1
  • with TIMER_1, you can get the two signals : (1 1 0 0) and (1 1 1 0) of which the XOR gives pin 2
Is it not good for you ?

Best regards

User avatar
gunar.kroeger
Posts: 143
Joined: Fri Jul 27, 2018 6:48 pm

Re: Using MCPWM periferal to generate desired pattern

Postby gunar.kroeger » Wed Feb 12, 2020 5:02 pm

It isn't. The PCB has already been fabricated.
Honestly the docs for MCPWM were the most confusing yet. Like what the hell is a "compliment of rising edge delay"?

For example, if I set the MCPWM_UP_DOWN_COUNTER, mcpwm_set_frequency sets double the value, and there are many other things like this in this API that make it impossible for me to quickly get something working.

I eventually gave up on this and used timers and interrupts for now. If someone could better explain this API or when it is rewritten to be more user friendly I might try again.
"Running was invented in 1612 by Thomas Running when he tried to walk twice at the same time."

jmcornil
Posts: 21
Joined: Mon Feb 10, 2020 1:42 pm

Re: Using MCPWM periferal to generate desired pattern

Postby jmcornil » Thu Feb 13, 2020 9:18 am

Hello

I think than it is OK with the following init.

I use MCPWM but with only one timer : TIMER_0
The three Operator are linked to TIMER_0
  • On GPIO_PWM0A_OUT, I get your pin1 signal
  • On GPIO_PWM1A_OUT, I get your pin2 signal
  • The third pin is just for me to control
I used huge prescaler for i have no oscillo here and I can just test with leds.
I let you adjust PMW_CLK_PRESCALER and PMW_TIMER_PRESCALER macros.

Code: Select all

#define GPIO_PWM0A_OUT 17   //Set GPIO 17 as PWM0A 
#define GPIO_PWM1A_OUT 18   //Set GPIO 18 as PWM1A 
#define GPIO_PWM2A_OUT 19   //Set GPIO 19 as PWM2A 

#define PMW_CLK_PRESCALER   128-1   // Prescal I for pwm
#define PMW_TIMER_PRESCALER 150-1   // Prescal II for pwm
#define PMW_TIMER_PERIOD 40000UL    // Period of the pwm

enum{MCPWM_CMPA,MCPWM_CMPB};
	
void MCPWM_Init(){
	mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
   	mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_PWM1A_OUT);
    	mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2A, GPIO_PWM2A_OUT);

	// MCPWM  Prescaler Initialisation
	// First The clock prescale
	mcpwm_ll_set_clock_prescale(&MCPWM0, PMW_CLK_PRESCALER);
	
	// Timer0 initialisation
	mcpwm_ll_timer_set_prescale(&MCPWM0, MCPWM_TIMER_0, PMW_TIMER_PRESCALER);
	mcpwm_ll_timer_set_count_mode(&MCPWM0, MCPWM_TIMER_0, 1);  // 1 pour incrémenter le timer
	mcpwm_ll_timer_set_period(&MCPWM0, MCPWM_TIMER_0, PMW_TIMER_PERIOD);
	
	// Choix des timers pour chacun des trois opérateur
	enum { OP0, OP1, OP2};
	mcpwm_ll_operator_select_timer(&MCPWM0, OP0, MCPWM_TIMER_0);
	// I let OP1 and OP2 on timer_0
	mcpwm_ll_operator_select_timer(&MCPWM0, OP1, MCPWM_TIMER_0);
	mcpwm_ll_operator_select_timer(&MCPWM0, OP2, MCPWM_TIMER_0);
	
	// Operator 0 initialisation
	mcpwm_ll_operator_set_compare(&MCPWM0, OP0, MCPWM_CMPA, 10000);
	mcpwm_ll_gen_set_zero_action (&MCPWM0, OP0, MCPWM_GEN_A, MCPWM_ACTION_FORCE_HIGH);
	mcpwm_ll_gen_set_cmp_action  (&MCPWM0, OP0, MCPWM_GEN_A, MCPWM0A, MCPWM_ACTION_FORCE_LOW,MCPWM_ACTION_NO_CHANGE);
		
	// Operator 1 initialisation
	mcpwm_ll_operator_set_compare(&MCPWM0, OP1, MCPWM_CMPA, 20000);
	mcpwm_ll_operator_set_compare(&MCPWM0, OP1, MCPWM_CMPB, 30000);
	mcpwm_ll_gen_set_zero_action (&MCPWM0, OP1, MCPWM_GEN_A, MCPWM_ACTION_FORCE_LOW);
	mcpwm_ll_gen_set_cmp_action  (&MCPWM0, OP1, MCPWM_GEN_A, MCPWM0A, MCPWM_ACTION_FORCE_HIGH,MCPWM_ACTION_NO_CHANGE);
	mcpwm_ll_gen_set_zero_action (&MCPWM0, OP1, MCPWM_GEN_A, MCPWM_ACTION_FORCE_LOW);
	mcpwm_ll_gen_set_cmp_action  (&MCPWM0, OP1, MCPWM_GEN_A, MCPWM0B, MCPWM_ACTION_FORCE_LOW,MCPWM_ACTION_NO_CHANGE);

	// Operator 2 initialisation (just to see)
	mcpwm_ll_operator_set_compare(&MCPWM0, OP2, MCPWM_CMPA, 30000);
	mcpwm_ll_gen_set_zero_action (&MCPWM0, OP2, MCPWM_GEN_A, MCPWM_ACTION_FORCE_HIGH);
	mcpwm_ll_gen_set_cmp_action  (&MCPWM0, OP2, MCPWM_GEN_A, MCPWM0A, MCPWM_ACTION_FORCE_LOW,MCPWM_ACTION_NO_CHANGE);

	// Timer(s) start
	mcpwm_ll_timer_start(&MCPWM0,MCPWM_TIMER_0);
	
	}
	
Best regards

Who is online

Users browsing this forum: Google [Bot] and 125 guests