high-voltage-pulse-generator

Slave ESP32

The ESP32 in high voltage pulse generator board operates as an SPI slave and receives data from an SPI master. Based on the received data, it performs two main functions:

1.- Configure Digital Potentiometers: Sets the position of digital potentiometers to define the high voltage level. The channel and pot_value bytes are read.

2.- Activate PWM Signal: Activates a PWM signal on the selected channel with specified frequency and pulse width.

You can download the code here. This code is a modification of the code initially described in C++ in the previous section.


1.- Variable Declaration and Initial Configuration

The main variables to use for receive SPI data from master are:


static constexpr uint32_t BUFFER_SIZE {32}; // Buffer size for SPI communication
uint8_t spi_slave_tx_buf[BUFFER_SIZE]; // Transmission buffer
uint8_t spi_slave_rx_buf[BUFFER_SIZE]; // Reception buffer
uint8_t lastChannel = 7; // Initial value outside valid range to save the last channel
uint8_t lastPotValue = 256; // Initial value outside valid range to save the last potentiometer value

2.- Initialize SPI Slave

Configure, initialize and clear SPI buffers.


void setup() {
  slave.setDataMode(SPI_MODE0);
  slave.begin(VSPI);
  memset(spi_slave_tx_buf, 0, BUFFER_SIZE);
  memset(spi_slave_rx_buf, 0, BUFFER_SIZE);
  // Initialize SPI slave and buffers
}

3.- Main function

The function waits until an SPI transaction arrives, then reads the received data and assigns it to the Freq, pot_value, pulse width, and channel variables. Then it is confirmed if pot_value has previously been set in the respective channel to avoid assigning again and save processing time. Finally, the PWM signal of the respective channel is activated at the frequency and pulse width selected by the user. SPI transfer time is measured and displayed.


void loop() {
    slave.wait(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE);
    
    if (slave.available()) {
        unsigned long startTime = micros();
        uint8_t Freq = spi_slave_rx_buf[0];
        uint8_t pot_value = spi_slave_rx_buf[1];
        uint8_t pulse_width = spi_slave_rx_buf[2];
        uint8_t channel = spi_slave_rx_buf[3];
        if (channel != lastChannel || pot_value != lastPotValue) {
            configurePotentiometer(channel, pot_value);
            lastChannel = channel;
            lastPotValue = pot_value;
        }
        activatePWMChannel(channel, Freq, pulse_width);
        unsigned long endTime = micros();
        unsigned long transferTime = endTime - startTime;
        Serial.print("SPI Transfer Time: ");
        Serial.println(transferTime);
    }
    // End of loop function
}

!! Warning !! !! If you use the maximum voltage at a frequency of 80KHz, consider that you should not send data at a rate higher than 4KHz since the activation and deactivation constants of the high voltage signal reach a total time of 250us. This was described in the previous section Pulse Width Adjustment.

4.- Activate PWM Channels

The PWM signal of the selected channel is activated for a time proportional to the pulse width, to subsequently disable the PWM that controls the inverter switching signals. The alternative of using the mcpwm_stop() function is discarded; as it requires longer processing time by having to disable the timer and duty cycle settings.


void activatePWMChannel(uint8_t channel, uint8_t Freq, uint8_t pulse_width) {
    uint32_t frequency = Freq * 1000;
    switch (channel) {
        case 1: // CH1
            mcpwm(MCPWM_UNIT_0, MCPWM0A, MCPWM0B, MCPWM_TIMER_0, GPIO_U0_PWM0A_OUT, GPIO_U0_PWM0B_OUT, frequency);
            delayMicroseconds(pulse_width);
            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);
            break;
            ...
        case 0: // All channels
            for (int i = 1; i <= 6; i++) {
                activatePWMChannel(i, Freq, pulse_width);
            }
            return;
        default:
            Serial.println("Invalid channel selected.");
            break;
    }
}

i Note i i If all channels or 0 are selected, the 6 channels are activated/deactivated sequentially in a similar way to the SADA system.

5.- Configure potentiometers

The position of the digital potentiometer of the channel selected by the user is set. stemp_amp is defined as the inverse of pot_value since a value of 0 corresponds to the maximum voltage and vice versa.


void configurePotentiometer(uint8_t channel, uint8_t pot_value) {
    uint8_t step_amp = 255 - pot_value; // Maximum voltage step_amp = 0

    switch (channel) {
        case 1: 
            assign_potentiometer(CHIP1_ADDR, CHANNEL3, step_amp);
            break;
          ...
        case 0: 
            for (int i = 1; i <= 6; i++) {
                configurePotentiometer(i, pot_value);
            }
            return;
        default: 
            Serial.println("Invalid channel");
            break;
    }
}