Ich verwende einen STM32F103C8 zum Lesen von 3 ADC-Kanälen und habe CubeMX + HAL verwendet, um den ADC so zu konfigurieren, dass die ADC-Werte in einen Puffer verschoben werden.
Ich konnte dies mit DMA und Polling erreichen: Ich habe beide als allgemein akzeptable Möglichkeiten gesehen, dies online zu tun. Ich konnte jedoch keine Konfiguration finden, mit der ich die Kanäle scannen kann, indem ich Konvertierungen im EOC-Interrupt manuell starte. Bei all meinen Versuchen werden entweder keine Kanäle vorgerückt oder der Interrupt wird überhaupt nicht ausgelöst.
Ich würde es vorziehen, Interrupts zu verwenden, da mein einziger DMA-Kanal auf dem Gerät zum Puffern einiger ziemlich hochfrequenter Audiodaten verwendet wird, aber ich bin auch nur beunruhigt, dass ich nicht verstehen kann, wie die ADC-Interrupts in Verbindung mit Scan funktionieren Modus. Ich habe die folgenden Ansätze verwendet:
DMA : Dies scheint die maßgebliche Methode zu sein, um mehrere Kanäle zu scannen und ihre jeweiligen Ergebnisse zu speichern. Insbesondere sagt das Benutzerhandbuch in §11.3.8 ¶3:
Bei Verwendung des Scan-Modus muss das DMA-Bit gesetzt werden und der Direct Memory Access Controller wird verwendet, um die konvertierten Daten der regulären Gruppenkanäle nach jeder Aktualisierung des ADC_DR-Registers an den SRAM zu übertragen.
Ich konnte es mit den intuitiven Einstellungen in CubeMX zum Laufen bringen:
plus ein kreisförmiges halbwortausgerichtetes DMA und ein direkter Aufruf von HAL_ADC_Start_DMA()
in der Quelle.
Abfrage : Ich habe versucht, dieser Antwort zu folgen , die sowohl den kontinuierlichen als auch den diskontinuierlichen Modus deaktiviert und in der Lage ist, mit aufeinanderfolgenden Anrufen allein durch die Kanäle zu gehen HAL_ADC_PollForConversion
. Ich fand, dass ich den diskontinuierlichen Modus mit Gruppengrößen von 1 aktivieren musste, dh:
hadc1.Init.DiscontinuousConvMode = ENABLE;
hadc1.Init.NbrOfDiscConversion = 1;
Das Steppen durch die Kanäle HAL_ADC_PollForConversion
funktionierte dann reibungslos.
Interrupts: Ich habe jede Permutation von Scan-Modus, diskontinuierlichem Modus und Anzahl diskontinuierlicher Konvertierungen ausprobiert, und keine davon lässt mich durch die Kanäle in der HAL_ADC_ConvCpltCallback
Interrupt-Routine gehen. Hier ist die Routine, die ich verwende:
#define NUM_ADC_BUF 8
#define NUM_ADC_CH 3
volatile uint16_t adc_buf[NUM_ADC_BUF][NUM_ADC_CH];
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
if(hadc->Instance == ADC1) {
adc_buf[adc_buf_idx & (NUM_ADC_BUF - 1)][adc_ch++] = HAL_ADC_GetValue(hadc);
if(adc_ch == NUM_ADC_CH) {
adc_ch = 0;
adc_buf_idx++;
}
HAL_ADC_Start_IT(hadc);
}
}
Innerhalb der ADC_Settings in CubeMX sind hier meine Versuche und ihre Ergebnisse:
+-------------------+-----------+--------------------+---------------------+------------------------------------------------------+
| (Continuous mode) | Scan mode | Discontinuous mode | Number of | Outcome |
| | | | discontinuous conv. | |
+-------------------+-----------+--------------------+---------------------+------------------------------------------------------+
| DISABLED | ENABLED | ENABLED | 3 | Only highest-rank-# (rank 3) channel result is given |
| DISABLED | ENABLED | ENABLED | 1 | Interrupt never fires: EOC never set |
| DISABLED | ENABLED | DISABLED | N/A | Only highest-rank-# (rank 3) channel result is given |
| DISABLED | DISABLED | ENABLED | 3 | Only lowest-rank-# (rank 1) channel result is given |
| DISABLED | DISABLED | ENABLED | 1 | Only lowest-rank-# (rank 1) channel result is given |
| DISABLED | DISABLED | DISABLED | N/A | Only lowest-rank-# (rank 1) channel result is given |
+-------------------+-----------+--------------------+---------------------+------------------------------------------------------+
Wie Sie sehen können, funktionieren keine Kombinationen richtig. Ist das einfach unmöglich? Ich nehme an, ich kann den Auszug, den ich aus dem Benutzerhandbuch zitiert habe, so verstehen, dass die einzige Möglichkeit, Scans zu verwenden, DMA ist und dass die Abfrage als eine Funktion funktioniert, die formal nicht unterstützt wird. Ist das wahr?
Als Referenz ist hier mein unberührtes automatisch generiertes adc.c
von CubeMX:
/* Includes ------------------------------------------------------------------*/
#include "adc.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
ADC_HandleTypeDef hadc1;
/* ADC1 init function */
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = ENABLE;
hadc1.Init.NbrOfDiscConversion = 3;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 3;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_9;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ADC1 GPIO Configuration
PA4 ------> ADC1_IN4
PB0 ------> ADC1_IN8
PB1 ------> ADC1_IN9
*/
GPIO_InitStruct.Pin = Xin_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(Xin_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = Zin_Pin|Yin_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* ADC1 interrupt Init */
HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */
/* USER CODE END ADC1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC1 GPIO Configuration
PA4 ------> ADC1_IN4
PB0 ------> ADC1_IN8
PB1 ------> ADC1_IN9
*/
HAL_GPIO_DeInit(Xin_GPIO_Port, Xin_Pin);
HAL_GPIO_DeInit(GPIOB, Zin_Pin|Yin_Pin);
/* ADC1 interrupt Deinit */
HAL_NVIC_DisableIRQ(ADC1_2_IRQn);
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
Im Gegensatz zur F4-Serie kann der ADC in der F1-Serie nur einen (einzelnen) Interrupt am Ende der gesamten Scansequenz erzeugen. Aus diesem Grund ist DMA ein Muss, wenn Sie den Scanmodus verwenden.
Ich denke jedoch, dass es immer noch möglich ist, ADC für Interrupt-basiertes Scannen zu konfigurieren, aber Sie müssen dies manuell tun:
Für die periodische Abtastung mehrerer Kanäle benötigen Sie wahrscheinlich 2 TIM-Module und einige TIM-Interrupts. Ein Timer (Slave) steuert das Timing zwischen jedem Kanal einer Scanning-Gruppe und triggert das ADC-Modul. Dieser Timer muss sich seiner Anzahl von Überläufen bewusst sein (unter Verwendung eines Interrupts), damit er sich selbst deaktivieren kann, nachdem alle Kanäle in der Sequenz abgetastet und konvertiert wurden. Ein weiterer Timer (Master) steuert das Timing zwischen den Scansequenzen und aktiviert den Slave-Timer (und wahrscheinlich den ersten Scan in der Sequenz).
Ich weiß nicht, wie ausgelastet Ihr DMA ist, aber ich würde auf jeden Fall versuchen, DMA zu verwenden, bevor ich die oben beschriebene Methode ausprobiere.
0___________
konkat
0___________
konkat
Tagli
konkat
Tagli
konkat