Page 1 of 1

Using MCPWM periferal to generate desired pattern

Posted: Mon Jan 27, 2020 8:58 pm
by gunar.kroeger
Hi all,
We have to generate this pulse sequence on 2 pins:
chargepump.pgn.png
chargepump.pgn.png (1.71 KiB) Viewed 1082 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. }

Re: RMT peripheral synchronized channels

Posted: Thu Jan 30, 2020 1:58 pm
by gunar.kroeger
I see there is a MCPWM component that is much better suited for this.
Still, for future applications this might be usefull to know.

Re: RMT peripheral synchronized channels

Posted: Mon Feb 10, 2020 2:48 pm
by gunar.kroeger
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
}

Re: Using MCPWM periferal to generate desired pattern

Posted: Wed Feb 12, 2020 2:59 pm
by jmcornil
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

Re: Using MCPWM periferal to generate desired pattern

Posted: Wed Feb 12, 2020 5:02 pm
by gunar.kroeger
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.

Re: Using MCPWM periferal to generate desired pattern

Posted: Thu Feb 13, 2020 9:18 am
by jmcornil
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