2026-01-05 7:48 PM
Hi!
I’m working on a motor controller design that utilizes an stm32f4 and a magnetic angular position sensor which communicates via spi.
My design currently performs current sensing; I use channel4 of timer1 to trigger ADC reads near the center of my center-aligned UVW PWM period.
This works great so far, but now I’m trying to think through how/when to schedule SPI reads of my angular sensor. I can straightforwardly perform an spi read when the ADC conversion completes, as part of my inner torque control loop. Is this reasonable to do? Is there a better way? It seems like the best time to touch off such a read is at the same time as when I trigger my ADCs. But I can’t quite figure out how I might do this.
Thanks!
Solved! Go to Solution.
2026-01-12 9:07 AM
To try to close the loop on this thread: I eventually discovered that the reason the above code doesn't quite work is because the DMA controller that tim4 uses isn't connected to the peripheral bus on which SPI1 resides. As such can't "talk" to each other on an f4, which is specific to that MCU family. I hadn't really bothered to deeply internalize this (because I've not yet experimented with having one peripheral talk to another).
Switching to use a timer with a DMA controller that CAN talk to AHB1 (which is where spi1 lives) allows the code above to work fine. In my case, this could be either timer1 or timer8.
All in all, a very neat trick that I'm glad to have learned!
2026-01-05 8:34 PM
If SPI is in two-way mode, you can trigger a transaction by a write to SPIx_DR. Any DMA trigger can do this, such as a timer channel or ADC complete. You may need to chain multiple triggers if the transaction is more than one word.
For example, set up a transfer from memory to SPIx_DR when TIM1->CH4 gets hit. Might need a different timer channel since you can only do one thing per trigger, but that's the idea.
2026-01-06 7:11 PM
Thanks so much! This is a new idea to me, and it seems pretty neat. I'll try to say that all back to you to make sure I understand:
With some help from an AI I think I understand the ins and outs of doing this, but I am struggling to implement it correctly. I'm curious if an example exists that I could refer to for this?
What I'm doing:
uint32_t angle_sensor_read_cmd = 0x0000;
uint32_t sensor_data;
// Arm the SPI Rx DMA
HAL_DMA_Start(&hdma_spi1_rx,
(uint32_t)&hspi1.Instance->DR,
(uint32_t)&sensor_data, 1);
__HAL_DMA_ENABLE_IT(&hdma_spi1_rx, DMA_IT_TC);
// Arm the timer channel2 DMA
HAL_DMA_Start(&hdma_tim4_ch2,
(uint32_t)&angle_sensor_read_cmd,
(uint32_t)&hspi1.Instance->DR, 1);
SET_BIT(&hspi1.Instance->CR2, SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN);
__HAL_SPI_ENABLE(&hspi1);
__HAL_TIM_ENABLE_DMA(&htim4, TIM_DMA_CC2);
HAL_TIM_PWM_Start_IT(&htim4, TIM_CHANNEL_1); // First, pull down CS
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2); // Second, start the SPI transaction via DMAWhen I run this code, I can see my HAL_TIM_PWM_PulseFinishedCallback() being called (which pulls CS low), and I can see what appears to be a reasonable value show up in SPI1->DR. But my HAL_SPI_RxCpltCallback() is never called, and the value I see in SPI1->DR never seems to be transferred back out to my sensor_data buffer.
I've tried checking for DMA overflow or error bits, none are set. I will keep banging on this to see if I can make it work, because it does seem like quite a useful technique to be able to leverage. But I worry I am strolling deep into the unnecessarily-complicated troubleshooting weeds and might be overlooking something simple here, so if anyone spots something I'm doing that's obviously wrong or has an example I can check out, I would be very grateful!
2026-01-12 9:07 AM
To try to close the loop on this thread: I eventually discovered that the reason the above code doesn't quite work is because the DMA controller that tim4 uses isn't connected to the peripheral bus on which SPI1 resides. As such can't "talk" to each other on an f4, which is specific to that MCU family. I hadn't really bothered to deeply internalize this (because I've not yet experimented with having one peripheral talk to another).
Switching to use a timer with a DMA controller that CAN talk to AHB1 (which is where spi1 lives) allows the code above to work fine. In my case, this could be either timer1 or timer8.
All in all, a very neat trick that I'm glad to have learned!