cancel
Showing results for 
Search instead for 
Did you mean: 

UART DMA sends sometimes random data when using ADC

ibruno26
Associate II

Hi everyone,

I'm relatively new to STM, i'm making my diploma thesis with it. It's going to be a mini, portable oscilloscope, but at this point I'm totally stuck. I started using ADC with DMA, but soon I realised this might be not the perfect option, so I dumped the idea, and started using instead the UART with DMA. It all worked well, until I started working with ADC too. I tried interrupt and poll mode too, but I always end up with random spikes, when I monitor the transmitted data. It goes well, if there are small, slow changes in voltage, but every time, when there is a relatively fast(now I do it with a 10K potentiometer, so fast means a half turn with a fast move). It just goes up from 2-3000mVs instantly to over a million, and immediately falls back to the previous value. I checked in code, the measured and calculated values are ok, they never go up, so it seems like the problem is with UART. I don't have an oscilloscope, or any equipment(just a cheap crap multimeter), and I can't go to the university thanks to covid, so I would really appreciate any help, because i'm out of ideas. Already tried changing baudrate, adc sampling time, modifying/deleting IT handlers etc. but none of them seemed to help.

I'm using a Nucleo-L452RE-P board.

Code in the link(everything is written in main.c: https://github.com/ibruno26/szakdolgozat)

Thanks for everything.

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru
HAL_UART_Receive_DMA(&huart2, uart_data_in, 1);
//adc_Voltage = 500;
mode = uart_data_in[0];

DMA functions are not blocking. It is very likely you're reading from uart_data_in before data has been sent there by the DMA.

If you want to use a blocking function, use HAL_UART_Receive. If you want to use DMA, you'll need to program logic to wait until it actually has data before trying to read it.

HAL_UART_Transmit_DMA(&huart2, (uint8_t*)msg, strlen(msg));

Same thing. You are likely calling this before the previous call completes. Switch to blocking, or implement logic to ensure it's available.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

7 REPLIES 7

How do you have that potentiometer connected, exactly? Post a photo and description.

JW

Side pins connected to 3.3V and GND, middle to A0, just like in this picture(without the pwm out, sorry, for some reason I can't attach picture from phone)

http://www.emcu.eu/tim14-pwm-output-duty-cycle-regulated-using-a-potentiometer/

What is A0 in terms of STM32 pin names? (the Nucleo UM/schematics is your friend)?

Where is it initialized in GPIO?

Is it the pin which corresponds to the ADC channel you are using? (see datasheet of your mcu).

Read out the GPIO registers in the debugger and check. You're surely familiar with the GPIO chapter of RM.

And, while at it, you can also check the ADC registers. If you have set it to continuous conversion, you should simply see the ADC data register changing as appropriate. Again, ADC chapter in RM.

> can't attach picture from phone

You have to click on the "mountains and sun" icon below and upload it as a file.

JW

Sorry, it's the PA0 PIN, assigned to ADC1_IN5, as it is in the datasheet.

I checked the gpio and adc registers, everything seems fine, it looks like to me that something goes wrong where i calculate the adcVoltage and then sprintf it to msg, and then call uart dma transmit.

Sidenote: i'm using mpaland's printf library(https://github.com/mpaland/printf), but it does the same with the stdio.h too.

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* 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_USART2_UART_Init();
  MX_ADC1_Init();
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_ADC_Start(&hadc1);
  //adcVoltage = 60000;
  while (1)
  {
	  if(HAL_ADC_PollForConversion(&hadc1, 5) == HAL_OK) {
		  adcValue = HAL_ADC_GetValue(&hadc1);
	  }
	  //adcValue = 3500;
	  HAL_UART_Receive_DMA(&huart2, uart_data_in, 1);
	  //adc_Voltage = 500;
	  mode = uart_data_in[0];
 
	  switch(mode) {
 
	  case 1:
		  adcMultiplier = 1;
		  break;
 
	  case 2:
		  adcMultiplier = 2;
		  break;
 
	  case 3:
		  adcMultiplier = 4;
		  break;
 
	  default:
		  __NOP();
		  //adcMultiplier = 0;
	  }
 
	  adcVoltage = (uint16_t)((adcValue * REFVOLT * adcMultiplier) / ADCRES);
	  sprintf(msg, "%u\n", adcVoltage);
	  HAL_UART_Transmit_DMA(&huart2, (uint8_t*)msg, strlen(msg));
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Do you wait until msg is sent, before you sprintf into it again?

JW

TDK
Guru
HAL_UART_Receive_DMA(&huart2, uart_data_in, 1);
//adc_Voltage = 500;
mode = uart_data_in[0];

DMA functions are not blocking. It is very likely you're reading from uart_data_in before data has been sent there by the DMA.

If you want to use a blocking function, use HAL_UART_Receive. If you want to use DMA, you'll need to program logic to wait until it actually has data before trying to read it.

HAL_UART_Transmit_DMA(&huart2, (uint8_t*)msg, strlen(msg));

Same thing. You are likely calling this before the previous call completes. Switch to blocking, or implement logic to ensure it's available.

If you feel a post has answered your question, please click "Accept as Solution".

Thanks for your help, I replaced transmit_dma with the blocking one, and it works perfectly now.