3
\$\begingroup\$

In steady state the line (output) is high and we have to create pulses that are microseconds long and are low. It is going to be a custom sequence with different pulse lengths (for example the line would look like all high, then 3 μs low, then 4 μs high, then 1 μs low, then 5 μs high - 2 μs low, then all high etc.).

We have tried standard PWM and PWM one shot, but we always failed to make it work as PWM functions do not work as intented. I mean we cannot simply close PWM and re-run it with new configs as I understood. We also tried to use DMA. We are aware that microseconds long pulses cannot be created by HAL_Delay function.

We are using NUCLEO-L452RE-P board. How can we make it work efficiently?

As I am not allowed to share work place material, I cannot post the code we wrote.

Velvet
4,9085 gold badges18 silver badges32 bronze badges
asked Sep 8, 2023 at 8:46
\$\endgroup\$
1
  • 2
    \$\begingroup\$ You could use the SPI interface to generate the output pulse on the COPI line. You just have to configure the SPI clock appropriately, and send DATA according to the pulse to be generated. \$\endgroup\$ Commented Sep 8, 2023 at 9:50

3 Answers 3

6
\$\begingroup\$

As proposed by Velvel, using SPI (in DMA mode) is probably the easiest approach.

The SPI peripheral should be configured like so:

  • Mode: Transmit Only Master
  • Hardware NSS Signal: Disable
  • Data Size: 8 Bit
  • First Bit: LSB First (to make it easier for software)
  • NSSP Mode: Disable
  • NSS Signal Type: Software

The prescaler should be set such that the baud rate is either 1 MHz exactly or the lowest possible multiple of 1 MHz.

In addition, DMA for SPI TX should be added (default settings with data width byte).

The two challenges are now:

  1. Generate the bit pattern from the pulse sequence
  2. Ensure that MOSI (the output pin) is high on idle

For the idle state: The idle state is determine by one of the bits in the SPI data shift register. For all or most of the STM32L4 chips, the shift register is 32 bit longs. So by sending 4 FF bytes initially and after each pulse sequence, this is ensured.

The code on a L476RG looks like so. SPI baud rate is 5 MHz. So the bit pattern has 5 bits per 1μs of pulse:

main.c

 pulses_init(&hspi2);
 // pulse sequence, length in μs, staring with low
 uint8_t pulse_lengths[] = { 3, 4, 1, 4, 2, 5 };
 pulses_prepare_buffer(pulse_lengths, sizeof(pulse_lengths));
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 pulses_send_buffer(&hspi2);
 HAL_Delay(1000);
 /* USER CODE END WHILE */
 /* USER CODE BEGIN 3 */
 }

pulses.h

#pragma once
#include "stm32l4xx_hal.h"
void pulses_init(SPI_HandleTypeDef* spi);
void pulses_prepare_buffer(const uint8_t* pulse_lengths, int num_pulses);
void pulses_send_buffer(SPI_HandleTypeDef* spi);

pulses.c

#include "pulses.h"
#include <stdbool.h>
#define BUFFER_LEN 1024
static uint32_t pulse_buffer[BUFFER_LEN];
static int num_words;
// SPI clock is 5 MHz; send same bit 5 times to make it 1 μs long
#define REPEAT 5
void pulses_init(SPI_HandleTypeDef* spi)
{
 // Send 4 bytes of all high bits to initialize idle state
 uint32_t data = 0xffffffff;
 HAL_SPI_Transmit(spi, (uint8_t*)&data, 4, 100);
}
void pulses_prepare_buffer(const uint8_t* pulse_lengths, int num_pulses)
{
 num_words = 0;
 uint8_t bit = 0;
 uint32_t word = 0xffffffff;
 bool is_low = true;
 for (int i = 0; i < num_pulses; i += 1)
 {
 int len = pulse_lengths[i] * REPEAT;
 for (int j = 0; j < len; j += 1) {
 if (is_low)
 word ^= 1 << bit;
 bit += 1;
 if (bit == 32) {
 pulse_buffer[num_words] = word;
 num_words += 1;
 bit = 0;
 word = 0xffffffff;
 }
 }
 is_low = !is_low;
 }
 pulse_buffer[num_words] = word;
 num_words += 1;
 // Another 4 bytes of all high bits for the idle state
 pulse_buffer[num_words] = 0xffffffff;
 num_words += 1;
}
void pulses_send_buffer(SPI_HandleTypeDef* spi)
{
 HAL_SPI_Transmit_DMA(spi, (uint8_t*)pulse_buffer, num_words * 4);
}
answered Sep 8, 2023 at 13:44
\$\endgroup\$
1
  • 1
    \$\begingroup\$ He can use also more flexible SAI in a similar way. \$\endgroup\$ Commented Sep 8, 2023 at 20:27
2
\$\begingroup\$

There are many ways of doing it - using SPI, a PLD or PIO (programmable IO state machine) hardware block if the chip has those, or even just loading new compare values into a timer using DMA. Each time a timer compare matches, timer flips the output and raises the interrupt. DMA handles the request and loads new compare value. The compare values have to be precomputed to give you the sequence you want.

answered Sep 8, 2023 at 14:13
\$\endgroup\$
2
\$\begingroup\$

Simplest method Use DMA to transfer to the GPIO. It is much more precise than SPI. Simple use a timer to trigger the DMA transmissions. It is very precise. I use it in our test devices where jitter has to be extremely minimal.

Run the code from RAM and place data in another RAM (most STM32 uC have more than one SRAM on different busses)

answered Sep 8, 2023 at 21:00
\$\endgroup\$
3
  • \$\begingroup\$ Can you explain why DMA to GPIO is more precise than SPI? \$\endgroup\$ Commented Sep 9, 2023 at 15:33
  • \$\begingroup\$ @Codo The delay between words transmitted is not guaranteed. And if you investigate more precisely you will see that sometimes there is no delay sometimes there is. When using GPIO you have everything under control and the timings are from a timer - ie very precise and consistent \$\endgroup\$ Commented Sep 9, 2023 at 17:07
  • \$\begingroup\$ In my past projects, I've never seen any delay at all (when NSSP Mode is set to Disable). It was perfectly aligned with the SPI clock. The main advantage of your method is that the base clock can be freely chosen while the choice for SPI is quite limited. \$\endgroup\$ Commented Sep 9, 2023 at 17:45

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.