2020-02-13 02:24 AM
It works with DMA_CIRCULAR (but I do not want or need continuous DMA transfers.)
You see by the terminal output below that reading 2 analog inputs works OK. However, after a while (< 1 minute) the 2 analog values are exchanged now. Apparently the DMA gets confused, but how and why I cannot understand.
Here is the code, all initialization was generated by STM32CubeMX, v5.5.0. (Had to omit part due to website restrictions on size of message.) NUM_AN_INPUTS=2
********** console output (about 30 seconds after swi upload / reset) ************
1959 0
1956 0
1957 0
1954 0
1955 0
1957 0
1955 0
1955 0
1954 0
1957 0
1959 0
0 1952
0 1953
3 1952
0 1952
0 1952
0 1953
0 1952
0 1953
********** usage of ADC in my code ************
volatile bool adc_done = false;
volatile uint16_t adc_buf[NUM_AN_INPUTS];
while(1) {
adc_done = false;
HAL_ADC_Start_DMA(&hadc1, (uint32_t*) adc_buf, NUM_AN_INPUTS);
while(!adc_done) {HAL_Delay(1);}
sprintf(message, "%d %d\n", adc_buf[0], adc_buf[1]);
serial.USBwrite(message);
HAL_Delay(100);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *) {
adc_done=true;
}
STMCubeMX Generated code and ADC calibration:
************ main.c: **************
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USB_DEVICE_Init();
MX_USART3_UART_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* Run the ADC calibration */
if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK)
{
/* Calibration Error */
Error_Handler();
}
Init functions generated by STM32CubeMX
/**
* @brief ADC1 Initialization Function
* @param None
* @retval None
*/
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_9;
sConfig.Rank = ADC_REGULAR_RANK_2;
sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static 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, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
STMCubeMX Generated code:
************ main.c: **************
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hadc->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ADC1 GPIO Configuration
PB0 ------> ADC1_IN8
PB1 ------> ADC1_IN9
*/
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOB, &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_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_NORMAL;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
/* 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 */
}
}
STMCubeMX Generated code:
************ stm32f1xx_hal_msp.c: **************
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hadc->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ADC1 GPIO Configuration
PB0 ------> ADC1_IN8
PB1 ------> ADC1_IN9
*/
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOB, &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_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_NORMAL;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
/* 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 */
}
}
2020-02-13 12:32 PM
Do you look at the ADC registers in debugger? If yes, then don't.
JW
2020-02-13 12:51 PM
Hello Jan,
Thanks for the advice. I do not use a debugger but just write a string to USB:
// write string of chars to USB. Must be terminated by \0.
void_serial:: USBwrite( char*data) {
CDC_Transmit_FS((uint8_t*) data, strlen(data));
Like this, all the code runs in "normal" mode =)
I tried just now an experiment where I put the ADC in continuous
conversion mode, and changed nothing else.
hadc1.Init.ContinuousConvMode= ENABLE;
Then each two/three samples the channels are reversed, showing again (I
think) a lack of synchronization between ADC and DMA controller.
John
On 2020-02-13 21:32, ST Community wrote:
2020-02-13 01:27 PM
John,
if you stop-start the DMA while conversions are started automatically, conversion results are inevitably lost while DMA is not running.
In the original case, what happens if you read out the ADC data register manually before each subsequent HAL_ADC_Start_DMA() call?
I don't Cube.
JW
2020-02-13 02:05 PM
2020-02-13 02:29 PM
2020-02-13 06:10 PM
Print out CNDTR of respective DMA channel and also the ADC status register.
JW
2020-02-14 12:41 AM
2020-02-15 12:46 AM
2020-02-16 05:40 AM
John,
Can you please do it again, this time printing ADC1->SR *before* reading ADC1->DR?
My theory is, that DMA misses one transfer, but I don't quite understand, why. What are the AHB and APB clocks, and what is the ADC clock?
JW