Ich lese ADC über DMA auf dem STM32 Nucleo F334R8. Nachdem ich jedoch den DMA mit dem folgenden Code gestartet habe, wird die Haupt-while-Schleife nicht ausgeführt
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4);
Ich verstehe, dass die DMA-Interrupts möglicherweise so oft auftreten, dass andere Interrupts möglicherweise nicht auftreten können. Daher habe ich die Priorität für den DMA so geändert, dass sie so niedrig wie möglich ist:
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 15);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
Ich weiß, dass andere Interrupts behandelt werden, da die folgenden Module in Betrieb sind:
Ich verstehe nicht, warum die Haupt-While-Schleife nicht ausgeführt wird, wenn der ADC-DMA aktiviert ist. Wenn ich den folgenden Code auskommentiere, wird die Haupt-While-Schleife ausgeführt
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4);
Hier ist die ADC-Konfiguration:
void MX_ADC1_Init(void)
{
ADC_MultiModeTypeDef multimode;
ADC_ChannelConfTypeDef sConfig;
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
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 = 4;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if(HAL_ADC_Init(&hadc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if(HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the ADC multi-mode
*/
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 1;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 2;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = 3;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_7;
sConfig.Rank = 4;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(adcHandle->Instance == ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC12_CLK_ENABLE()
;
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN6
PC1 ------> ADC1_IN7
PA0 ------> ADC1_IN1
PA1 ------> ADC1_IN2
*/
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
if(HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(adcHandle, DMA_Handle, hdma_adc1);
/* ADC1 interrupt Init */
HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 14);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
}
Hier ist die DMA-Konfiguration:
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 15);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
Hier ist das Wichtigste:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
CPU_CACHE_Enable();
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_TIM1_Init();
MX_TIM3_Init();
MX_TIM6_Init();
MX_USART2_UART_Init();
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&ADC_Raw, 4);
my_printf("Version 1.0\n");
InitializeControllerCommand();
while (1)
{
if(start_pwm == true)
{
fade_LED();
}
if(readInput == 1)
{
readInputPin();
}
}
}
Ich verstehe, dass die DMA-Interrupts möglicherweise so oft auftreten, dass andere Interrupts möglicherweise nicht auftreten, daher habe ich die Priorität für den DMA so geändert, dass sie so niedrig wie möglich ist
[...]
Ich verstehe nicht, warum die Haupt-While-Schleife nicht ausgeführt wird, wenn der ADC-DMA aktiviert ist.
Wenn DMA-Interrupts so oft auftreten würden, dass sie andere Interrupt-Handler am Laufen hindern würden, würden sie den Hauptcode nicht mitlaufen lassen.
Es ist möglich, das Hauptprogramm mit der CMSIS-Funktion so einzustellen, dass es mit einer höheren Priorität als bestimmte Interrupts ausgeführt wird, __set_PRIMASK()
was in eine Anweisung übersetzt würde MSR PRIMASK, Rx
, aber dies würde effektiv alle Interrupts mit niedrigerer Priorität deaktivieren, da das Hauptprogramm niemals beendet wird (es sei denn, es trifft zu ein nicht behebbarer Fehler).
Meiner Meinung nach solltest du
Erhöhung der Abtastzeit
Die Abtastzeit für jeden Kanal kann in den Registern ADC->SMPR1
und ADC->SMPR2
oder durch Einstellen des SamplingTime
Felds in der Kanalinitialisierungsstruktur von 1,5 bis 601,5 ADC-Taktzyklen eingestellt werden.
Reduzierung der ADC-Taktfrequenz
CKMODE
Der ADC-Takt kann aus dem AHB-Takt getaktet werden, optional dividiert durch 2 oder 4. Dies wird durch die Felder des ADC->CCR
Registers oder durch das Init.ClockPrescaler
Feld der ADC-Initialisierungsstruktur gesteuert .
Alternativ kann der ADC von der Haupt-PLL getaktet werden, optional geteilt durch 2,4,6,8,10,12,16,32,64,128 oder 256. Die CKMODE
Bits oder Init.ClockPrescaler
müssen 0 ( ADC_CLOCK_ASYNC_DIV1
) sein, und der Divisor kann ausgewählt werden in RCC->CFGR2
, oder eingestellt durchHAL_RCCEx_PeriphCLKConfig()
Die oben genannten Methoden beruhen auf der Erhöhung der Zeit, die für jede Konvertierung in der Sequenz benötigt wird, dies ist möglicherweise nicht geeignet für einige Anwendungen, bei denen die Kanäle in einem kurzen Zeitintervall abgetastet werden sollten, um mehr oder weniger synchrone Ergebnisse zu erhalten.
Erhöhen des Intervalls zwischen Konvertierungssequenzen
Die ADC-Konvertierungssequenz kann auch durch ein Timer-Ereignis gestartet werden. In diesem Fall sollte der kontinuierliche Konvertierungsmodus deaktiviert werden, und eine Ereignisquelle muss in den Bits EXTEN
und EXTSEL
des ADC->CFGR
Registers ausgewählt werden. Mögliche Ereignisquellen sind in Kapitel 13.3.18 Konvertierung bei externem Trigger und Triggerpolarität im Referenzhandbuch aufgeführt . Timer bieten eine große Flexibilität bei der Auswahl einer geeigneten Abtastfrequenz, aber sie sind eine begrenzte Ressource. Wenn eine Abtastfrequenz von 1 kHz oder weniger ausreichen würde, wäre es am einfachsten, die Konvertierung jedes Mal einfach vom periodischen Interrupt-Handler aus zu starten, falls Sie einen haben.
keine Transienten
folgte Monica zu Codidact
keine Transienten
folgte Monica zu Codidact
Michael Braun