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:
- Generate the bit pattern from the pulse sequence
- 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);
}