Thanks for making this video. I have to go back to it every time I migrate my project to a different chip and set everything up all over again. It would be nice if STM32cubeIDE would just let you select a different chip, and highlight issues that need to be solved manually.
*Summary* *Goal:* (Starts at 0:00) * Sample an ADC input at a specific rate (40kHz in the example) using an STM32F303K8 microcontroller. * Use timer events to automatically trigger ADC conversions, eliminating the need for software-triggered conversions. * Provide a debug output signal synchronized with the ADC sampling rate. *ADC Setup:* (Starts at 1:16) * *Disable Scan Conversion Mode:* Ensures that only a single ADC channel is sampled. * *Disable Continuous Conversion Mode:* Prevents continuous ADC conversions; conversions will be triggered by the timer. * *Set External Trigger Conversion Source:* Select the timer output event that will trigger the ADC conversions (Timer 1 Trigger Out Event in this case). * *Configure Trigger Polarity:* Choose whether the ADC conversion should be triggered on the rising edge, falling edge, or both edges of the timer output. *Timer Setup:* (Starts at 3:05) * *Select Internal Clock Source:* Use the internal clock to drive the timer. * *Configure Timer Period (ARR Register):* Set the timer period to achieve the desired ADC sampling rate. The example uses a period of 1000 clock cycles for a 40kHz sampling rate with a 40MHz clock. * *Enable Update Event Generation:* This event is triggered when the timer counter resets, which will be used to trigger the ADC conversions. * *Configure Output Compare Channel (Optional):* This can be used to generate a debug output signal synchronized with the ADC sampling rate. The example configures Output Compare Channel 1 to toggle on every timer counter reset, generating a square wave at the sampling frequency. *Software Implementation:* (Starts at ~5:22 - not explicitly stated but code walkthrough begins) * *Initialize Timer and ADC:* Set up the timer and ADC peripherals with the desired configurations. * *Start Timer and Output Compare (if used):* Enable the timer and output compare channel to begin generating trigger events and debug output. * *Start ADC DMA:* Configure and enable DMA to transfer ADC conversion data to a memory buffer. * *Handle DMA Conversion Complete Callback:* This callback function is called when the DMA buffer is full. It can be used to process the acquired ADC data and restart the DMA transfer for the next buffer. *Key Points:* * The code leverages hardware features (timers and DMA) to automate the ADC sampling process, minimizing software overhead. * No interrupt service routines are required for ADC conversions. * A debug output signal allows for visual verification of the sampling rate. i used gemini 1.5 pro to summarize the transcript.
Nice presentation! Your code only works on an 32F407VG if I enable DMA Continuous, which in the manual says "Bit 9 DDS: DMA disable selection (for single ADC mode) 1: DMA requests are issued as long as data are converted and DMA=1". Without that it just does one set of samples. I can see the timer3 OC toggle at the right frequency, so not sure why it doesn´t work without.
Hi Rick, thank you for the useful video. Can I clarify one thing? I’m fairly new to programming STM32s. The yellow trace on the oscilloscope (channel 1) is generated by the GPIO toggle in the HAL_ADC_ConvCpltCallback(…), right?
Hi your video is really good, but I need to write these results by using usart but I don't know in which function can I write, I an writing in convcallback function,I could not see any output, I write in while it has no output also, what can I do to make it write by usart?
I'm facing a bug if i follow the video exactly only the first element of the array is getting updated and the other 9 are still at 0 Update: it got fixed after using HAL_TIM_PWM... instead of HAL_TIM_Base..
@@randomrickUK well apparently even if my ADC can run at 2.4 msps it will not get triggered with the timer from 1-2.4msps but will works very well from below 1msps
You need to change the settings of the timer (i.e. not the ADC). You need to set both the Prescaler (the PSC register) and the Counter Period (the ARR register) so that together they slow down the system clock to your 1000Hz (or 1 millisecond). This doesn't slow down the whole device - it just affects that timer, which is what we want. Although one part is called a prescaler and the other is called a counter, they both have the same effect of dividing down the main clock for your timer. So the timer runs at "system clock / prescaler / counter". Remember to reduce the value of both parameters by 1 due to the way the timer circuit works. For example, if you want the prescaler to be 50, then the value in the register should be 49. There is often more than one combination of Prescaler/Counter values that will give the same result. But for 1000Hz, it should be pretty easy unless you have a strange main system clock like 12.321 MHz, but you probably haven't. Let's say your system clock is 8MHz. That needs dividing (slowing) down by 8000 to get to your target of 1000Hz. Here are a couple of combinations that could do that: Prescaler 8000, Counter 1 (but that would be 7999 and 0 in the registers) Prescaler 1000, Counter 8 (999 and 7 in the registers) Be careful not to choose values that are too big for the registers! On some parts and in some timers, the Prescaler is only 16 bit, so you can't choose numbers bigger than the maximum for 16 bits. If you want to get a 1Hz pulse, you can't just put 8000000 in to the Prescaler because that number is too big.
Thank you so much, for example if I choose 10 MHz and I did not divide it(so I chose divider 0), I arranged the ARR to 999, is this divider calculation true?but I have another question, I am getting EEG signals, do not I need to use Contunious Conversion instead of single conversion?
Thank you , it worked for me , the only issue is the adc is giving wrong reading in the buffer for indexes 1, 3,5...... when i use this method and the error is fixed where i get 2920 instead of 3520 for all of them ( im using a fixed dc signal just to test which corresponds to 3520) , but when i use the normal case without timers i dont get that error , so for indexes 0,2,4.... i get correct values 3520 . any idea why this is happening ?
No, sorry, I don't have an immediate idea of what's happening here. It doesn't make sense to me that only certain ADC acquisitions would give the wrong ADC reading. I'd really have to see both the code and the circuit to help further with this issue. I hope you manage to find an answer.
Hi Johan. First thing to check is the automatically generated code. Near the beginning of main() in main.c you will find the calls to the initialisation functions. Check that the call to MX_DMA_Init() comes BEFORE the call to MX_ADC_Init(). Sometimes CubeIDE gets this wrong, and you have to manually edit it. Though of course if you re-run the code generator, it will overwrite your change and you'll have to re-apply it.
@@randomrickUK Hi I got everything working except the dma buffer variables, the compiler just wont accept these ones: uint16_t dmaBuffer[DMABUFLEN]; char txBuf[TXBUFLEN]; So could you please help me out with those aswell?
@@johansvensson6542 Do you mean the compiler complains about the 2 lines of code you showed me? Or their use somewhere else? It looks to me like one buffer is for ADC and the other is for UART but I don't know if you're using DMA for both or just for the ADC. Have you declared (#define) DMABUFLEN and TXBUFLEN? If so, tell me what the error message is that you're getting from the compiler, and then maybe I can help. Or do you mean that it compiles OK but just doesn't work when you run your code?
The DMA buffer can be any size you want, as long as you have enough spare RAM for it. You then pass that size to the HAL_ADC_Start_DMA() call when beginning the conversion.
@@randomrickUK Sir, maybe Roopesh means max of dma buffer, I try STM32H743(RM0433 page.622) max of dma buffer is 65535. Other series of stm need to check the manual.
Hi - yes, it'll potentially work at higher speeds, but of course as the speeds go up, you need to consider the sample time setup of your adc - the more cycles, the more time each conversion will take. So you have to make sure that the conversion time is not longer than the (time) spacing of the ADC acquistions you want. Also remember that there's a certain number of (ADC clock) cycles over the number you choose in setting up the ADC - maybe they're all 12.5 cycles, or maybe that's just the last device I checked! So if you select "1.5 cycles" as your acquisition time, it's really 1.5 + 12.5 = 14 cycles of your ADC clock per ADC reading.
@@randomrickUK in the black pill it takes 15 ADC clock cycles for 1 sample, so suppose I set up a DMA for 100 samples and I set the Timer such that the time between edges is 15 ADC clock cycles. According to the video the DMA is ready untill all the buffer is filled right so i thought is it possible to vary sampling rate from max sampling rate to a very low sampling rate(32bit timer), also do we need to consider the time the DMA will take to send the sample from ADC to memory?
@@joshuachettiar86 If you set the timer to 15 ADC clock cycles, you're not achieving much - you may as well just set up the ADC to Continuous mode, which means "don't wait to be told to start the next ADC conversion - do it immediately". Using the timer is to achieve precise timing at a rate you want that's slower than the maximum possible (that being Continous Mode). Yes, the DMA Conversion Complete callback happens when the buffer is full. You can also have a callback for when the buffer is half full too. If you gate each separate conversion into the DMA buffer on the timer, I see no reason why you can't vary that timer as you wish, and of course the callback will simply happen when that buffer is full. I don't know the exact number of cycles the DMA takes to put each ADC value into each element of the DMA buffer, but it puts each sample in as it comes, so it's very quick - it doesn't wait until the end and then fill it up. What CAN take time is your callback at the end of the DMA buffer. What you do in your callback is unknown, of course, but keep in mind that if you want to immediately start the next DMA buffer fill (so you've configured your DMA as a circular buffer), you won't have a lot of time to process the current buffer contents before the start of it begins to be filled again. It's for this reason that we can use the half-buffer method. Say you want to add up all the ADC readings once the DMA buffer is full: it may take more time to do that than is available before the ADC starts to fill up the buffer over again. In this case, add up the first half of the buffer half way through and then complete it at the end of the DMA at which point you've already added up the first half so it doesn't matter if the ADC overwites the last values.
@@randomrickUK thanks!!! so i will just configure the code such that when i want the MAX sampling the ADC will switch to continuous conversion and when i want to lower it i will switch it such that the timer triggers it. thank you alot!!!! for the explanation i will test it and get back