I try to control 4 different lasers at a very fast rate (~1MHz). I need to choose which of the four are on/off for each 1us (laser diodes are capable of toggling at these speeds, that's not a problem). I can control the lasers by just giving a 3.3V-5V signal. Is there a way to do this WITHOUT using FPGA.
I tried pin manipulation and it can achieve high speed toggling but it is just toggle. I want to control which pins are HIGH for each 1us.
how do you know which one to turn on when ?
you need to account for the time it takes to do those decisions as well (if that's the case)
how strict is the 1µs duration?
It is a pre-defined sequence and I want it as big as possible (depends on the storage on the controller).
The thing with using direct register access, I could not get it work time-precise. According to their initial state (on/off), direct register access takes longer/shorter.
If the sequence is predefined then try out the RP2040 and it's PIO. It's cheap and you can have quite large lookup tables on it. I have worked successfully with the PIO up to 3MHz while doing DSP .
I agree, rp2040 is great for this. His PIO is act as small FPGA inside the processor. I'm just trying to output predefined sequences to pins through dma and pio, in this case frequencies of tens of megahertz are easily achievable (theoretically up to half of the system clock)
It seems not very fast... Maybe it's because reading from progmem? Have you tried from RAM?
I didn’t try it on the AVR, but on the STM32 bluepil, when writing directly to the registers, I got about 4-5 MHz, and on the rp2040 at least 15 MHz (I can’t check further, since I haven't so fast oscilloscope )
A 500kHz square wave is a transition every 1us, which was the request.
1us is 16 clocks on the AVR, and the progmem access takes 3 clocks, so that’s probably “reasonable”; you might shave off a couple cycles if you’re very careful/sneaky or use asm, but I’m pretty sure a new sample every 330ns would be impossible.
A faster clock rate and easier pointer math certainly makes going faster much easier.
Did the all transitions take same duration? When I tried it (with a little different code) some transitions took more time than the others and which board did you try on?
Code for what board? I tried it for STM32F1/F4 bluepill and RPi 2040.
I am playing with RGB diode panels and this task is typical for this application, so I will be glad to help.
But it's delayed until the end of weekend.
As far as I could tell. For my test, I didn't disable interrupts, which means that every ~1ms, a timer interrupt is going to come along and mess up the timing. Or if I sent it Serial Traffic.
The test was done on an Uno.
Good evening, I have prepared for you an example code that switches two pins using a bit mask with a frequency of 1 MHz
The board stm32F401CC (blackpill)
/* This example demonstrates using of two matched DMA transfers
* for switching two pins according to a bit pattern. One DMA channel
* is used to turn on the pins in accordance with the values read from
* the array, the second channel resets the state of the pins before
* the next switch. The switching frequency 1 MHz as set by the timer setup.
*
* b707 2022 board: STM32F401cc blackpill
* Arduino STM32 support from: https://github.com/rogerclarkmelbourne/Arduino_STM32
*/
// Pins for output pulses
#define PIN1 PA0
#define PIN2 PA1
// Array of pin states
const byte nRows = 6; // number of pin states in array
uint8_t mux_mask[nRows] = {0b00000001,
0b00000011,
0b00000010,
0b00000011,
0b00000001,
0b00000000};
volatile uint32 *muxsetreg; // pin register placeholder
uint32 clr_mask; // bitmask placeholder
// DMA channel config
const dma_dev* spiDmaDev = DMA2; // we use DMA2 because only it can transfer from memory to GPIO on STM32F4
dma_channel spiDmaChannel = DMA_CH6; // DMA channel for TIM1 requests
dma_stream datTxDmaStream = DMA_STREAM2; // TIM1 CH2
dma_stream clkTxDmaStream = DMA_STREAM6; // TIM1 CH3
#define DMD_TIMER TIMER1
#define DMD_TIMER_BASE TIMER1_BASE
#define TIM_PERIOD 84 // TIM_PERIOD = F_CPU/ 1000000
void setup() {
// GPIO setup
pinMode(PIN1, OUTPUT);
pinMode(PIN2, OUTPUT);
muxsetreg = portSetRegister(PIN1); // pin set/reset register GPIOx->BSRR
clr_mask = digitalPinToBitMask(PIN1) | digitalPinToBitMask(PIN2); // bitmask for clearing pins
clr_mask = clr_mask << 16;
// timer setup
timer_init(DMD_TIMER);
timer_pause(DMD_TIMER);
DMD_TIMER_BASE->DIER = (1 << 11)|(1 << 10); //CH2 & CH3 DMA request enable
DMD_TIMER_BASE->PSC = 0; // Timer with system clock
DMD_TIMER_BASE->ARR = (TIM_PERIOD - 1); // F_CPU/ 1 000 000 = 1 MHz cycle
DMD_TIMER_BASE->CCR2 = TIM_PERIOD/2 -10 ; // dma request for set pins states
DMD_TIMER_BASE->CCR3 = TIM_PERIOD - 10; // dma request for reset
// dma setup
// 1st DMA stream
dma_init(spiDmaDev);
dma_setup_transfer(spiDmaDev, datTxDmaStream, spiDmaChannel, DMA_SIZE_8BITS,(uint32_t*)muxsetreg, (uint8_t*)mux_mask, NULL, (DMA_MINC_MODE | DMA_CIRC_MODE | DMA_FROM_MEM));
dma_set_num_transfers(spiDmaDev, datTxDmaStream, nRows);
dma_enable(spiDmaDev, datTxDmaStream);
// 2 nd dma stream
dma_setup_transfer(spiDmaDev, clkTxDmaStream, spiDmaChannel, DMA_SIZE_32BITS, (uint32_t*)muxsetreg, (uint32_t*)&clr_mask, NULL, (DMA_MINC_MODE | DMA_CIRC_MODE | DMA_FROM_MEM));
dma_set_num_transfers(spiDmaDev, clkTxDmaStream, 1);
dma_enable(spiDmaDev, clkTxDmaStream);
DMD_TIMER_BASE->CNT = 0;
DMD_TIMER_BASE->CR1 = (1 << 0);// Start TIM1
}
void loop() {
// put your main code here, to run repeatedly:
}