Hallöchen, ich habe eine Frage bzw. benötige etwas Hilfe beim Einstellen der SampleTime beim STM32F4 Disco. Vielleicht könnt ihr mir da ein wenig helfen. Ich brauche eine stabile Abtastfrequenz von 160kHz. Aber ich finde nichts, wo man die Abtastfrequenz beim ADC selbst so genau einstellen kann. In den Registern gibt es leider immer nur Prescaler, Resolution, SampleTime(3,5,...,480 Cycles) und ADC_TwoSamplingDelay. Wobei mir auch nicht 100% klar ist, wie sich letzendlich die komplette Zeit zusammensetzt. Bisher habe ich es so gelöst, dass ich den ADC auf Channel 10 im continuouse mode laufen lasse, und mit einem Timer IRQ in einer Frequenz von 160kHz einen Interrupt auslöse und mir dann den momentanwert des AD-Wandlers hole. Aber das muss doch auch irgendiwe mit dem ADC selbst möglich sein! Wäre ja schon irgendwie ideal. Schon mal Danke im Voraus. Wenn gewünscht, kann ich auch gerne mal meinen Code mitschicken. Gruß Justus
Die ADC's haben keine präzisen Timer. Aber du kannst einen der normalen Timer auf 160 kHz einstellen und damit den ADC triggern lassen. Im Reference Manual steht wie das geht.
Dr. Sommer schrieb: > Die ADC's haben keine präzisen Timer. Aber du kannst einen der normalen > Timer auf 160 kHz einstellen und damit den ADC triggern lassen. Im > Reference Manual steht wie das geht. Naja, das habe ich ja oben geschrieben. So mache ich es ja bisher. Ich dachte nur, dass müsste doch auch direkt mittels ADC gehen. Oder hat jemand noch eine adequate Lösung, wie man das doch über den ADC direkt machen kann? Aber vielleicht kann mir noch jemand behilflich sein, wie ich die Zeit berechne, die mein ADC benötigt, um einen Wert umzuwandeln. Und ja, ich weiß im HAnd-Manual steht diese Gleichung auf Seite 400. Aber die lässt mich da nicht ganz hintersteigen.
Du kannst den adc entweder einschalten oder ausschalten. Samplerate richtet sich dann nach der von dir erwähnten Gleichung. Je schneller du dir die werte abholst, auf welchem Weg auch immer, desto schwieriger wird es sicherzustellen, dass du genau den Wert abgeholt hast, den du auch haben wolltest. Zumindest, wenn nebenher noch anderes zeug läuft. Da musst du dann die Priorität für den adc-teil hochsetzen.
Justus Z. schrieb: > Naja, das habe ich ja oben geschrieben Nein, du hast continuous mode und interrupts geschrieben. Ich habe gesagt, dass der Timer automatisch eine ADC Konvertierung ohne Interrupt auslösen kann, über die interne Synchronisations Verschaltung im Controller. Nein, es gibt keine andere Möglichkeit, weil dies die dafür vorgesehene Möglichkeit ist. Zusammen mit DMA kann man auch ganz auf Interrupts verzichten.
Dr. Sommer schrieb: > Nein, du hast continuous mode und interrupts geschrieben. Ich habe > gesagt, dass der Timer automatisch eine ADC Konvertierung ohne > Interrupt auslösen kann, über die interne Synchronisations Verschaltung > im Controller. Nein, es gibt keine andere Möglichkeit, weil dies die > dafür vorgesehene Möglichkeit ist. Zusammen mit DMA kann man auch ganz > auf Interrupts verzichten. Und wo kann ich das genau nachlesen? Das würde mich auch nochmal interessieren, wenigstens um mal davon gehört zuhaben und noch eine ausweich Möglichkeit zuhaben. Welche Vorzüge hätte denn diese Variante? Edit: Oder würde das Gedanklich so funktionieren, dass der Timer dann alle 160kHz einen Signal an den ADC gibt eine Konvertierung zu starten? Würde mich über guten Input freuen. Momentan habe ich das Problem, dass irgendwas mit der Zeit nicht so genau funktioniert. Ich habe den Timer 3 (84MHz) mit einem Prescaler von 105 auf 800kHz. Einen Counter mit Auto-Reloader habe ich auf 800kHz / 160kHz - 1 = 4 gestellt. Somit sollte der Timer einen Interrupt alle 6,25µs auslösen. In dem Timer Interrupt steht folgender Code: [...] if( iBufCho ) { aiBuffer1[iBufPos] = ADC1->DR; iBufPos++; if( iBufPos == iAnzahlAbtast) { iBufPos = 0; iBufCho = 0; fFrequenz = iCalc(); } } else { aiBuffer2[iBufPos] = ADC1->DR; iBufPos++; if( iBufPos == iAnzahlAbtast ) { iBufPos = 0; iBufCho = 1; fFrequenz = iCalc(); } } [...] Es wird also der konvertierte Wert des AD Wandlers in ein Puffer geschrieben. Wenn der Puffer voll ist, wird eine Rechenfunktion aufgerufen. die ist etwas länger und benötigt ca. 60000 Takten. Habe gerade nicht im Kopf, wie groß die Rechenleistung beim F4 ist. Aber müsste diese Zeit dann noch irgendwie mit beachtet werden? Weil ich habe das Gefühl, das der Timer nicht richtig läuft. in der Calc() lasse ich alle 50 Durchgänge (das müsste eigentlich eine Zeit von genau 1s entsprechen) eine LED togglen und die toggled um ca. das 1.7-Fache zu langsam. Oder vergesse ich bei dem ganzen irgendwas.
:
Bearbeitet durch User
Justus Z. schrieb: > Aber müsste diese Zeit dann > noch irgendwie mit beachtet werden? Na klar, genauso wie die ADC_Conversion_Time. Der ADC sampled natürlich durchgehend. Aber du holst dir die Daten ja ab.
Reginald L. schrieb: > Na klar, genauso wie die ADC_Conversion_Time. Der ADC sampled natürlich > durchgehend. Aber du holst dir die Daten ja ab. Gut. Hehe. Das dachte ich mir schon. Ich bin mir halt nur relativ unsicher, wie viele Dinge der Microcontroler parallel laufen haben kann, ohne das zeitlich etwas beeinflusst wird. Also die Funktionen die bearbeitet werden sind: - ADC - Beschreiben zweier Buffer (abwechselnd) - Verrechnen der Buffer ADC: Der ADC läuft meines Erachtens im Hintergrund (also die Wandlung selbst). Wenn diese vorrüber ist, wird ein Interrupt ausgelöst und der Wert in eine "Warte-Variable" geschrieben. Das dürfte mit einer Geschwindigkeit von t_ADC passieren. Der ADC läuft über den APB2 mit 84MHz und wird mit einem Prescaler von 6 auf 14MHz runter "gedrosselt". Bedient wird nur ein Channel, ein ADC und eine Wandlung. Die Zeit der Conversion selbst berechnet sich mit: T_conv = SamplingTime + 12 Cycles -> 12 Cycles werden denke mal die Resolution sein. Leider weiß ich nicht, woraus sich die SampleTime zusammen setzt. Dazu kommen noch die ADC_TwoSamplingDelay, die man einstellen kann (mind. 5 * T_ADCCLK). Auch hier weiß ich leider nicht, warum man die benötigt. Also berechnet sich die Zeit für einen kompletten Durchgang des AD-Wandlers mit: T_ADC = T_conv + T_delay. Richtig? Wobei halt die Wandlung selbst im Hintergrund läuft. Die interrupt Funktion des ADC selbst beinhaltet wie erwähnt, nur das beschreiben einer Variable und das zurücksetzen eines Bits. Also 3 Takte Timer/Verrechnen der Buffer: Der läuft wie oben schon Berechnet mit einer Geschwindigkeit von 160kHz. Der Code darin benötigt 6 Takte, wenn der Buffer noch nicht gefüllt ist und ca. 50000 (nochmal genau nachgerechnet). Somit Berechnet sich die Zeit für den Timer zu: T_timer = 6 bzw. 50000 * T_HCLK. Korrekt? Und da sehe ich auch gleich eine Frage. Wenn diese timer Zeit über 160kHz kommt, wird dann ein Interrupt in einen Interrupt ausgelöst? Und wie könnte ich dieses Problem umgehen. Gibt es eine Möglichkeit den Buffer zu befüllen, währenddessen schön gerechnet wird? Hoffentlich bekomme ich jetzt nicht zu stark den Kopf gewaschen. Gruß Justus
:
Bearbeitet durch User
Ich würde sie sache ganz anders angehen: Adc clock: würde ich versuchen auf das mögliche Maximum zu stellen. Je höher, desto höher wird die Chance, dass du genau den Sample abholst den du haben möchtest. Bei den relativ niedrigen sampling raten die du erzielen möchtest reichen die 14mhz aber alle mal. Conversion time: je höher, desto länger wird vom adc die "anliegende Spannung gemessen und gemittelt". Die sollte man also auch auf das mögliche Maximum einstellen. Der wert hängt natürlich von deiner gewünschten frequenz und der adc clock ab. Die Abholung der Daten würde ich bei der sampling rate schon fast dem dma überlassen. Double buffering und du kannst zwischendurch gemütlich deine Werte berechnen. Justus Z. schrieb: > Hoffentlich bekomme ich jetzt nicht zu stark den Kopf gewaschen. Zu der Sorte microcontroller.net-Menschen gehöre ich nicht ;)
Hier mal ein alter Codeschnipsel von mir. Da triggert ein Timer den DMA, der die Daten abholt. Aber pass auf, da sind noch einige Fehler enthalten, so darf z.B. Timer.Prescaler nur 16bit groß sein. Aber vom Prinzip her hilft dir das vllt weiter. Und der DMA läuft im manuellen DoubleBuffer, der kann das natürlich auch von selber.
1 | bool _DAQ::Init() |
2 | {
|
3 | // Clocks
|
4 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); |
5 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); |
6 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); |
7 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); |
8 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); |
9 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); |
10 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE); |
11 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); |
12 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); |
13 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE); |
14 | |
15 | //Pins
|
16 | GPIO_InitTypeDef h_gpio; |
17 | ZEROINIT(h_gpio); |
18 | h_gpio.GPIO_Mode = GPIO_Mode_IN; |
19 | h_gpio.GPIO_OType = GPIO_OType_OD; |
20 | h_gpio.GPIO_PuPd = GPIO_PuPd_NOPULL; |
21 | h_gpio.GPIO_Speed = GPIO_High_Speed; |
22 | h_gpio.GPIO_Pin = |
23 | GPIO_Pin_15; // Trigger Input |
24 | GPIO_Init(GPIOA, &h_gpio); |
25 | SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource15); |
26 | |
27 | EXTI_InitTypeDef h_exti; |
28 | ZEROINIT(h_exti); |
29 | h_exti.EXTI_Line = EXTI_Line15; |
30 | h_exti.EXTI_LineCmd = ENABLE; |
31 | h_exti.EXTI_Mode = EXTI_Mode_Interrupt; |
32 | h_exti.EXTI_Trigger = EXTI_Trigger_Rising; |
33 | EXTI_Init(&h_exti); |
34 | |
35 | h_gpio.GPIO_Mode = GPIO_Mode_AN; |
36 | h_gpio.GPIO_OType = GPIO_OType_OD; |
37 | h_gpio.GPIO_PuPd = GPIO_PuPd_NOPULL; |
38 | h_gpio.GPIO_Pin = |
39 | GPIO_Pin_0; // ADC1 Channel 8 |
40 | GPIO_Init(GPIOB, &h_gpio); |
41 | h_gpio.GPIO_Pin = |
42 | GPIO_Pin_1; // ADC2 Channel 11 |
43 | GPIO_Init(GPIOC, &h_gpio); |
44 | |
45 | // DMA ADC
|
46 | DMA_InitTypeDef h_dma; |
47 | ZEROINIT(h_dma); |
48 | h_dma.DMA_BufferSize = 0; |
49 | h_dma.DMA_Channel = DMA_Channel_0; |
50 | h_dma.DMA_DIR = DMA_DIR_PeripheralToMemory; |
51 | h_dma.DMA_FIFOMode = DMA_FIFOMode_Enable; |
52 | h_dma.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; |
53 | h_dma.DMA_Memory0BaseAddr = (uint32_t)&rx_buffer1; |
54 | h_dma.DMA_MemoryBurst = DMA_MemoryBurst_Single; |
55 | h_dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; |
56 | h_dma.DMA_MemoryInc = DMA_MemoryInc_Enable; |
57 | h_dma.DMA_Mode = DMA_Mode_Circular; |
58 | h_dma.DMA_PeripheralBaseAddr = (uint32_t)&ADC->CDR; |
59 | h_dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; |
60 | h_dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; |
61 | h_dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; |
62 | h_dma.DMA_Priority = DAQ_ADC_DMA_PRIORITY; |
63 | DMA_Init(DMA2_Stream0, &h_dma); |
64 | DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)&rx_buffer2, DMA_Memory_0); |
65 | DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE); |
66 | DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE); |
67 | |
68 | // ADC1
|
69 | ADC_InitTypeDef h_adc1; |
70 | ZEROINIT(h_adc1); |
71 | h_adc1.ADC_ContinuousConvMode = DISABLE; |
72 | h_adc1.ADC_DataAlign = ADC_DataAlign_Right; |
73 | h_adc1.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; |
74 | h_adc1.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; |
75 | h_adc1.ADC_NbrOfConversion = 1; |
76 | h_adc1.ADC_Resolution = ADC_Resolution_12b; |
77 | h_adc1.ADC_ScanConvMode = DISABLE; |
78 | ADC_Init(ADC1, &h_adc1); |
79 | ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_480Cycles); |
80 | |
81 | // ADC2
|
82 | ADC_InitTypeDef h_adc2; |
83 | ZEROINIT(h_adc2); |
84 | h_adc2.ADC_ContinuousConvMode = DISABLE; |
85 | h_adc2.ADC_DataAlign = ADC_DataAlign_Right; |
86 | h_adc2.ADC_ExternalTrigConv = 0; |
87 | h_adc2.ADC_ExternalTrigConvEdge = 0; |
88 | h_adc2.ADC_NbrOfConversion = 1; |
89 | h_adc2.ADC_Resolution = ADC_Resolution_12b; |
90 | h_adc2.ADC_ScanConvMode = DISABLE; |
91 | ADC_Init(ADC2, &h_adc2); |
92 | ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_480Cycles); |
93 | |
94 | // ADC Multimode
|
95 | ADC_CommonInitTypeDef h_adc_multi; |
96 | ZEROINIT(h_adc_multi); |
97 | h_adc_multi.ADC_DMAAccessMode = ADC_DMAAccessMode_2; |
98 | h_adc_multi.ADC_Mode = ADC_DualMode_RegSimult; |
99 | h_adc_multi.ADC_Prescaler = ADC_Prescaler_Div4; // 22.5MHz at 90MHz APB |
100 | h_adc_multi.ADC_TwoSamplingDelay = DISABLE; |
101 | ADC_CommonInit(&h_adc_multi); |
102 | |
103 | ADC_Cmd(ADC1, ENABLE); |
104 | ADC_Cmd(ADC2, ENABLE); |
105 | ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); |
106 | |
107 | // Timer 5 - Sync Timer for Data and Trigger
|
108 | TIM_TimeBaseInitTypeDef h_tim5; |
109 | ZEROINIT(h_tim5); |
110 | h_tim5.TIM_ClockDivision = TIM_CKD_DIV1; |
111 | h_tim5.TIM_CounterMode = TIM_CounterMode_Up; |
112 | h_tim5.TIM_Period = 0xFFFFFFFF; // 5 days Period |
113 | h_tim5.TIM_Prescaler = 0; // Variable Interval |
114 | TIM_TimeBaseInit(TIM5, &h_tim5); |
115 | |
116 | // Timer 13 - Trigger
|
117 | TIM_TimeBaseInitTypeDef h_tim13; |
118 | ZEROINIT(h_tim13); |
119 | h_tim13.TIM_ClockDivision = TIM_CKD_DIV1; |
120 | h_tim13.TIM_CounterMode = TIM_CounterMode_Up; |
121 | h_tim13.TIM_Period = 65536 - 1; // ~1.3s Period |
122 | h_tim13.TIM_Prescaler = 1800 - 1; // Interval 20 µs |
123 | TIM_TimeBaseInit(TIM13, &h_tim13); |
124 | TIM_SelectOnePulseMode(TIM13, TIM_OPMode_Single); |
125 | |
126 | // Timer 2 - ADC Trigger
|
127 | TIM_TimeBaseInitTypeDef h_tim2; |
128 | ZEROINIT(h_tim2); |
129 | h_tim2.TIM_ClockDivision = TIM_CKD_DIV1; |
130 | h_tim2.TIM_CounterMode = TIM_CounterMode_Up; |
131 | h_tim2.TIM_Period = 0; // Variable Period |
132 | h_tim2.TIM_Prescaler = 0; // Interval 0.011 µs |
133 | TIM_TimeBaseInit(TIM2, &h_tim2); |
134 | TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); |
135 | TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); |
136 | |
137 | // Interrupts
|
138 | NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_PriorityGroup_3, DAQ_TRIGGER_IT_PRIORITY, 1)); |
139 | NVIC_EnableIRQ(EXTI15_10_IRQn); |
140 | NVIC_SetPriority(DMA2_Stream0_IRQn, NVIC_EncodePriority(NVIC_PriorityGroup_3, DAQ_BUFFERCOMPLETE_IT_PRIORITY, 1)); |
141 | NVIC_EnableIRQ(DMA2_Stream0_IRQn); |
142 | |
143 | return OK; |
144 | }
|
145 | // On Trigger
|
146 | void EXTI15_10_IRQHandler() |
147 | {
|
148 | // Clear IT
|
149 | EXTI->PR = EXTI_Line15; |
150 | |
151 | // Start Trigger Timer
|
152 | TIM13->CR1 |= TIM_CR1_CEN; |
153 | |
154 | // Check if Trigger dead time passed
|
155 | if (TIM13->CNT > 1500) |
156 | {
|
157 | // Stop Trigger Timer 13
|
158 | TIM13->CR1 &= (uint16_t)~TIM_CR1_CEN; |
159 | |
160 | // Get data
|
161 | DAQ.roundcounter++; |
162 | DAQ.triggervalue = TIM13->CNT; |
163 | |
164 | DAQ.p_triggerbufferempty[DAQ.triggerbuffercounter] = TIM5->CNT; |
165 | DAQ.triggerbuffercounter++; |
166 | |
167 | // Set Flags
|
168 | DAQ.triggerready = true; |
169 | |
170 | // Reset and start Trigger Timer 13
|
171 | TIM13->CNT = 0; |
172 | TIM13->CR1 |= TIM_CR1_CEN; |
173 | }
|
174 | }
|
175 | // On Buffer written
|
176 | void DMA2_Stream0_IRQHandler() |
177 | {
|
178 | // Clear IT
|
179 | DMA2->LIFCR |= 0x20; |
180 | |
181 | // Switch Buffers
|
182 | if ((DMA2_Stream0->CR & DMA_SxCR_CT) != 0) |
183 | {
|
184 | DAQ.p_accdatabuffercomplete = (uint32_t*)DAQ.rx_buffer1; |
185 | DAQ.p_triggerbuffercomplete = (uint32_t*)DAQ.triggerbuffer1; |
186 | DAQ.p_triggerbufferempty = (uint32_t*)DAQ.triggerbuffer2; |
187 | |
188 | DAQ.dmatriggervalcompleteSOC = DAQ.dmatriggerval1; |
189 | DAQ.dmatriggervalcompleteEOC = DAQ.dmatriggerval2; |
190 | DAQ.dmatriggerval2 = TIM5->CNT; |
191 | }
|
192 | else
|
193 | {
|
194 | DAQ.p_accdatabuffercomplete = (uint32_t*)DAQ.rx_buffer2; |
195 | DAQ.p_triggerbuffercomplete = (uint32_t*)DAQ.triggerbuffer2; |
196 | |
197 | DAQ.p_triggerbufferempty = (uint32_t*)DAQ.triggerbuffer1; |
198 | DAQ.dmatriggervalcompleteSOC = DAQ.dmatriggerval2; |
199 | DAQ.dmatriggervalcompleteEOC = DAQ.dmatriggerval1; |
200 | DAQ.dmatriggerval1 = TIM5->CNT; |
201 | }
|
202 | |
203 | DAQ.lasttriggerbuffercounter = DAQ.triggerbuffercounter; |
204 | DAQ.triggerbuffercounter = 0; |
205 | DAQ.accdataready = true; |
206 | }
|
:
Bearbeitet durch User
Justus Z. schrieb: > Und wo kann ich das genau nachlesen? Im Reference Manual natürlich. S. 400-402. Justus Z. schrieb: > Edit: Oder würde das Gedanklich so funktionieren, dass der Timer dann > alle 160kHz einen Signal an den ADC gibt eine Konvertierung zu starten? Ja. Justus Z. schrieb: > Welche Vorzüge hätte denn diese Variante? Dass du so die Samplerate auf fast jeden beliebigen Wert einstellen kannst (alle Teiler des Systemtakts von max. 168MHz). Durch Kaskadierung mehrerer Timer auch auf extrem langsame Werte, falls nötig. Reginald L. schrieb: > Adc clock: würde ich versuchen auf das mögliche Maximum zu stellen. Je > höher, desto höher wird die Chance, dass du genau den Sample abholst den > du haben möchtest. Dadurch Jittert die Sample-Rate aber im Endeffekt. Nicht gut für DSP-Anwendungen. Dafür ist die Timer-Synchronisation da, für exakte Sample-Rates, warum die nicht einfach benutzen?! Reginald L. schrieb: > Aber pass auf, da sind noch einige Fehler > enthalten Ja, z.B. ist ein Klassenname von _DAQ in C++ verboten (Unterstrich + Großbuchstabe, oder Doppelunterstrich, am Anfang, sind der Standard Library vorbehalten).
Hier mal ein Codeschnippsel, hier wird Timer2 dazu genutzt den ADC mit 100kHz zu trigger (Samplerate), der DMA holt die Daten ab und wenn der Buffer voll ist gibts n Interrupt:
1 | void Init_ADC ( void ) |
2 | {
|
3 | /* Set clock */
|
4 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); |
5 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE); |
6 | RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE); |
7 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); |
8 | |
9 | GPIO_InitTypeDef GPIO_InitStructure; |
10 | GPIO_StructInit(&GPIO_InitStructure); |
11 | ADC_InitTypeDef ADC_InitStructure; |
12 | ADC_StructInit(&ADC_InitStructure); |
13 | ADC_CommonInitTypeDef ADC_CommonInitStructure; |
14 | ADC_CommonStructInit(&ADC_CommonInitStructure); |
15 | DMA_InitTypeDef DMA_InitStructure; |
16 | DMA_StructInit(&DMA_InitStructure); |
17 | TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure; |
18 | TIM_TimeBaseStructInit (&TIM_TimeBase_InitStructure); |
19 | NVIC_InitTypeDef NVIC_InitStructure; |
20 | NVIC_Init(&NVIC_InitStructure); |
21 | |
22 | TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; |
23 | TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; |
24 | TIM_TimeBase_InitStructure.TIM_Period = 840 - 1; |
25 | TIM_TimeBase_InitStructure.TIM_Prescaler = 0; |
26 | TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure); |
27 | |
28 | TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); |
29 | TIM_SelectOutputTrigger(TIM2,TIM_TRGOSource_Update); |
30 | TIM_ARRPreloadConfig(TIM2, ENABLE); |
31 | |
32 | NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); |
33 | NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; |
34 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; |
35 | NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; |
36 | NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; |
37 | NVIC_Init(&NVIC_InitStructure); |
38 | |
39 | /* ADC Kanäle */
|
40 | /* IO initialisieren */
|
41 | GPIO_StructInit(&GPIO_InitStructure); |
42 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2; |
43 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; |
44 | GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; |
45 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; |
46 | GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; |
47 | GPIO_Init(GPIOC, &GPIO_InitStructure); |
48 | |
49 | /* DMA Controll */
|
50 | DMA_InitStructure.DMA_Channel = DMA_Channel_0; |
51 | DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); |
52 | DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DataBuffer; |
53 | DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; |
54 | DMA_InitStructure.DMA_BufferSize = BUFFERSIZE; |
55 | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; |
56 | DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; |
57 | DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; |
58 | DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; |
59 | DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; |
60 | DMA_InitStructure.DMA_Priority = DMA_Priority_High; |
61 | DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; |
62 | DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; |
63 | DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; |
64 | DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; |
65 | DMA_Init(DMA2_Stream0, &DMA_InitStructure); |
66 | DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE ); |
67 | |
68 | |
69 | /* ADC */
|
70 | ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; |
71 | ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8; |
72 | ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; |
73 | ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; |
74 | ADC_CommonInit(&ADC_CommonInitStructure); |
75 | |
76 | ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; |
77 | ADC_InitStructure.ADC_ScanConvMode = DISABLE; |
78 | ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; |
79 | ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; |
80 | ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; |
81 | ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; |
82 | ADC_InitStructure.ADC_NbrOfConversion = 1; |
83 | ADC_Init(ADC1, &ADC_InitStructure); |
84 | |
85 | ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE); |
86 | ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_28Cycles); |
87 | |
88 | DMA_Cmd(DMA2_Stream0, ENABLE); |
89 | ADC_Cmd(ADC1, ENABLE); |
90 | ADC_DMACmd(ADC1, ENABLE); |
91 | ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); |
92 | }
|
93 | |
94 | void DMA2_Stream0_IRQHandler ( void ) |
95 | {
|
96 | DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); |
97 | TIM_Cmd(TIM2, DISABLE); |
98 | Parameter.Messtatus = ENDED; |
99 | }
|
Zum Starten einfach
1 | TIM_Cmd(TIM2, ENABLE); |
Timer2 läuft "nur" auf 84MHz, daher Prescaler 840-1 (84MHz/840 = 100kHz).
Damit der Code von Ingo oben bei mir lief, musste ich einige Anpassungen vornehmen. siehe im Kommentar // muths
1 | #include "main.h" |
2 | #include "stm32f4xx_tim.h" |
3 | #include "misc.h" |
4 | #include "stm32f4xx_gpio.h" |
5 | #include "stm32f4xx_rcc.h" |
6 | #include "stm32f4xx_adc.h" |
7 | #include "stm32f4xx_dma.h" |
8 | |
9 | #define BUFFERSIZE 3
|
10 | volatile uint32_t DataBuffer[BUFFERSIZE]; |
11 | |
12 | volatile uint8_t Messtatus = 0; |
13 | |
14 | |
15 | void Init_ADC ( void ) |
16 | {
|
17 | /* Set clock */
|
18 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); |
19 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE); |
20 | RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE); |
21 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); |
22 | |
23 | GPIO_InitTypeDef GPIO_InitStructure; |
24 | GPIO_StructInit(&GPIO_InitStructure); |
25 | ADC_InitTypeDef ADC_InitStructure; |
26 | ADC_StructInit(&ADC_InitStructure); |
27 | ADC_CommonInitTypeDef ADC_CommonInitStructure; |
28 | ADC_CommonStructInit(&ADC_CommonInitStructure); |
29 | DMA_InitTypeDef DMA_InitStructure; |
30 | DMA_StructInit(&DMA_InitStructure); |
31 | TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure; |
32 | TIM_TimeBaseStructInit (&TIM_TimeBase_InitStructure); |
33 | NVIC_InitTypeDef NVIC_InitStructure; |
34 | NVIC_Init(&NVIC_InitStructure); |
35 | |
36 | /* Timer2 läuft "nur" auf 84MHz, daher Prescaler 840-1 (84MHz/840 = 100kHz).*/
|
37 | |
38 | TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; |
39 | TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; |
40 | TIM_TimeBase_InitStructure.TIM_Period = 840 - 1; |
41 | TIM_TimeBase_InitStructure.TIM_Prescaler = 0; |
42 | TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure); |
43 | |
44 | TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); |
45 | TIM_SelectOutputTrigger(TIM2,TIM_TRGOSource_Update); |
46 | TIM_ARRPreloadConfig(TIM2, ENABLE); |
47 | |
48 | NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); |
49 | NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; |
50 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; |
51 | NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; |
52 | NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; |
53 | NVIC_Init(&NVIC_InitStructure); |
54 | |
55 | /* ADC Kanäle */
|
56 | /* IO initialisieren */
|
57 | GPIO_StructInit(&GPIO_InitStructure); |
58 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2; |
59 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; |
60 | GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; |
61 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; |
62 | GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; |
63 | GPIO_Init(GPIOC, &GPIO_InitStructure); |
64 | |
65 | /* DMA Controll */
|
66 | DMA_InitStructure.DMA_Channel = DMA_Channel_0; |
67 | DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); |
68 | DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DataBuffer; // muths DataBuffer |
69 | DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; |
70 | DMA_InitStructure.DMA_BufferSize = BUFFERSIZE; |
71 | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; |
72 | DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; |
73 | DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // muths DMA_PeripheralDataSize_HalfWord; |
74 | DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // muths DMA_MemoryDataSize_HalfWord; |
75 | DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; |
76 | DMA_InitStructure.DMA_Priority = DMA_Priority_High; |
77 | DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; |
78 | DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; |
79 | DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; |
80 | DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; |
81 | DMA_Init(DMA2_Stream0, &DMA_InitStructure); |
82 | DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE ); |
83 | |
84 | |
85 | /* ADC */
|
86 | ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; |
87 | ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8; |
88 | ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; |
89 | ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; |
90 | ADC_CommonInit(&ADC_CommonInitStructure); |
91 | |
92 | ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; |
93 | ADC_InitStructure.ADC_ScanConvMode = ENABLE; // muths DISABLE; |
94 | ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; |
95 | ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; |
96 | ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; |
97 | ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; |
98 | ADC_InitStructure.ADC_NbrOfConversion = BUFFERSIZE; // muths 1; |
99 | ADC_Init(ADC1, &ADC_InitStructure); |
100 | |
101 | ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE); |
102 | ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_28Cycles); // muths |
103 | ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_28Cycles); |
104 | ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 3, ADC_SampleTime_28Cycles); // muths |
105 | // für alle 6 benötigte Zeit= 1 us
|
106 | |
107 | DMA_Cmd(DMA2_Stream0, ENABLE); |
108 | ADC_Cmd(ADC1, ENABLE); |
109 | ADC_DMACmd(ADC1, ENABLE); |
110 | ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); |
111 | }
|
112 | |
113 | void DMA2_Stream0_IRQHandler ( void ) |
114 | {
|
115 | DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); |
116 | TIM_Cmd(TIM2, DISABLE); |
117 | Messtatus = 1; |
118 | }
|
119 | |
120 | |
121 | |
122 | int main(void){ |
123 | //----------------------------------------------------
|
124 | |
125 | |
126 | SystemInit(); // Quarz Einstellungen aktivieren |
127 | |
128 | Init_ADC(); |
129 | TIM_Cmd(TIM2, ENABLE); // Zum Starten |
130 | /*
|
131 | Timer2 läuft "nur" auf 84MHz, daher Prescaler 840-1 (84MHz/840 =
|
132 | 100kHz).
|
133 | */
|
134 | while(1) |
135 | {
|
136 | |
137 | if (Messtatus == 1) { |
138 | // auswerten etc.
|
139 | }
|
140 | |
141 | |
142 | |
143 | |
144 | }
|
145 | }
|
:
Bearbeitet durch User
Bei gut kommentierten Programmen kann man doch gleich erkennen, wie es funktionieren soll.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.