STM32 hat je nach Chipvariante einen oder mehrere Analog-Digital-Wandler (ADC).
Der ADC konvertiert die am analogen Eingang anliegende Spannung in einen binären Wert. Dafür braucht er allerdings ein Paar Zyklen- je nach Einstellung aber mindestens um die 3 Zyklen. Zudem läuft ADC mit niedrigerer Frequenz als der Cortex-Kern. Man muss also wissen, dass eine Konvertierung einige Prozessorzyklen lang dauert.
Unter dem Aspekt gibt es (wie für viele andere Hardwarekomponenten) hauptsächlich drei Möglichkeiten einen Analog-Digital-Wandler zu steuern bzw. auszulesen:
- Normaler Modus (Blockierend)
- Interrupt-Modus (Nicht-blockierend)
- Per DMA auslesen (Nicht-blockierend)
Normaler Modus
In diesem Modus läuft das Sampling im Hauptprogramm. Das heisst, man triggert das Sampling an, wartet(!) darauf, dass das Sampling fertig ist und macht weiter im Hauptprogramm. Das bedeutet viele verlorene Zyklen und darf nur in Ausnahmefällen bzw. in nicht leistungskritischen Applikationen eingesetzt werden.
Interrupt Modus
Im Interrupt-Modus stößt das Hauptprogramm das Sampling an und macht weiter mit seinen Aufgaben. Wenn das Sampling fertig ist, löst die ADC-Hardware einen Interrupt aus. Mit Hilfe dieses Interrupts kann dann dem Hauptprogramm mitgeteilt werden, dass nun das Register den neuen Digitalwert beinhaltet.
DMA MODUS
Im DMA-Modus schaufelt die DMA-Hardware im Hintergrund die ADC-Werte. Dabei wird die ADC-Hardware in den kontinuierlichen Modus gesetzt. Das heißt, sie fängt gleich mit dem nächsten Sampling an, wenn sie mit einem fertig ist. Das ist oft der effizienteste Modus.
Beim Projekt Flex 500 wird z. B. das Expression-Pedal mit einem ADC interrupt-gesteuert gelesen. Der Grund dafür ist, dass man die Frequenz mit einem Timer einstellen möchte. Die ADC-Hardware ist folgendermaßen konfiguriert. Das
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | void c_sysconfig::MX_ADC1_Init(void) {   ADC_ChannelConfTypeDef sConfig;     /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)     */   hadc1.Instance = ADC1;   hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;   hadc1.Init.Resolution = ADC_RESOLUTION_12B;   hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;   hadc1.Init.ContinuousConvMode = ENABLE;   hadc1.Init.DiscontinuousConvMode = DISABLE;   hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;   hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;   hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;   hadc1.Init.NbrOfConversion = 1;   hadc1.Init.DMAContinuousRequests = ENABLE;   hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;   if (HAL_ADC_Init(&hadc1) != HAL_OK)   { //    _Error_Handler(__FILE__, __LINE__);   }     /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.     */   sConfig.Channel = ADC_CHANNEL_3;   sConfig.Rank = ADC_REGULAR_RANK_1;   sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)   { //    _Error_Handler(__FILE__, __LINE__);   } } | 
Nach jedem Lesezyklus triggert man dabei das nächste Sampling. Das Interrupt wird dabei allerdings nicht benutzt, da der Zyklus kurz ist und das Interrupt sonst das Hauptprogramm unnötig oft unterbrechen würde.
| 1 2 3 4 5 6 7 8 9 | uint16_t c_exppedal::get_value(void){ 	//Interrupt mode 	HAL_ADC_Start_IT(&hadc1); 	adc_val=HAL_ADC_GetValue(&hadc1); 	return (uint16_t) adc_val; } | 
Andere Möglichkeit wäre eben per DMA, dass man dabei die Hardware nur starten und stoppen muss:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void c_exppedal::start(void){ 	//Request using interrupt mode 	HAL_ADC_Start_DMA(&hadc1, &adc_val,1); } void c_exppedal::stop(void){ 	//Request using interrupt mode 	HAL_ADC_Stop_DMA(&hadc1); } | 
Die Konfiguration des Timers ist folgendermaßen. Bei einer CPU-Frequenz von 216MHz ergibt sich eine Timerfrequenz von:
 (1)    
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | void c_sysconfig::MX_TIM3_Init(void)   {     TIM_ClockConfigTypeDef sClockSourceConfig;     TIM_MasterConfigTypeDef sMasterConfig;     htim3.Instance = TIM3;     htim3.Init.Prescaler = 10000;     htim3.Init.CounterMode = TIM_COUNTERMODE_UP;     htim3.Init.Period = 1600;     htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;     htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;     if (HAL_TIM_Base_Init(&htim3) != HAL_OK)     { //      _Error_Handler(__FILE__, __LINE__);     }     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;     if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)     { //      _Error_Handler(__FILE__, __LINE__);     }     sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;     if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)     { //      _Error_Handler(__FILE__, __LINE__);     }   } | 
Ressourcen
Der vollständige Code vom Communication Stack befindet sich auf den Repositories vom Controller und DSP unter den Ordnern „hw„.
 
						 herunterladen
 herunterladen










 haben. D.h. für den Kopfhörerausgang ist ein Verstärker erforderlich, der Lasten bis
 haben. D.h. für den Kopfhörerausgang ist ein Verstärker erforderlich, der Lasten bis  bedienen kann. Das bedeutet eine Effektivspannung von
 bedienen kann. Das bedeutet eine Effektivspannung von  , was wiederum bei einer
, was wiederum bei einer  Last einer Effektivleistung von
 Last einer Effektivleistung von



 und
 und  einigermaßen gleichmäßig, nach dem Differenzieren am Zielgerät eliminieren sie sich.
 einigermaßen gleichmäßig, nach dem Differenzieren am Zielgerät eliminieren sie sich.

 und
 und  durch die Position des Potentiometer VR_VC1 resultierende Widerstände und
 durch die Position des Potentiometer VR_VC1 resultierende Widerstände und  die Verstärkung durch die zweite Stufe ist, in dem Fall
 die Verstärkung durch die zweite Stufe ist, in dem Fall  .
.
 und
 und  berechnet sich als
 berechnet sich als![Rendered by QuickLaTeX.com \begin{equation*} V_O=\frac{V_1 R_2+V_2 R_1}{R_1+R_2} [1+\frac{R_{f1}}{R_{f2}}] \end{equation*}](https://www.cankosar.com/wp-content/ql-cache/quicklatex.com-91fc04fef83decdde605775c8a9ec9b9_l3.png)
 Potentiometer gemischt, sodass
 Potentiometer gemischt, sodass


 beschreibt die Kompressionsrate, mit der das Signal abgeschwächt wird. Dies greift ab dem Überschreiten eines Threshold-Werts.
 beschreibt die Kompressionsrate, mit der das Signal abgeschwächt wird. Dies greift ab dem Überschreiten eines Threshold-Werts.





 arbeitet. Danach wird die Impedanz vom Signal mit dem Opamp U_Inp1A angepasst und 11-fach verstärkt (Passive Gitarre). Mit dem Aktiv/Passiv-Schalter SW1 wird in die Rückkopplungsschleife zusätzlich der Widerstand R_I3 zugeschaltet, der den effektiven Widerstand der Rückkopplung von
 arbeitet. Danach wird die Impedanz vom Signal mit dem Opamp U_Inp1A angepasst und 11-fach verstärkt (Passive Gitarre). Mit dem Aktiv/Passiv-Schalter SW1 wird in die Rückkopplungsschleife zusätzlich der Widerstand R_I3 zugeschaltet, der den effektiven Widerstand der Rückkopplung von  auf
 auf  senkt. Die Verstärkung sinkt dadurch also auf das 6-fache (aktive Gitarre).
 senkt. Die Verstärkung sinkt dadurch also auf das 6-fache (aktive Gitarre).

