본문 바로가기
Programming/Embedded

[STM32F103C8] ADC: DMA AND INTERRUPT

by No Brainer 2022. 2. 20.

DMA란?

입출력 장치 제어기(IO device controller)가 CPU에 의한 프로그램의 실행없이 자료의 이동을 할 수 있도록 하는 것이 DMA이다. 이 방식에 의해서 입출력의 속도를 향상할 수 있으며, CPU와 주변 장치간의 속도차를 줄일 수 있다. 입출력 장치가 DMA를 요구하면 CPU가주메모리의 제어를 넘겨주게 되는데 CPU는 이 작업을 CPU 사이클이 끝나는 지점마다 허용할 수 있다.

출처: 네이버 백과

 

 CUBE MX 설정






scan conversion mode를 enable할 경우, 먼저 number of conversion의 수를 2 이상으로 설정해야 한다.

 





 

 

 

<truestudio>

 

 

/* USER CODE BEGIN Header */

/**

  ******************************************************************************

  * @file           : main.c

  * @brief          : Main program body

  ******************************************************************************

  * @attention

  *

  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.

  * All rights reserved.</center></h2>

  *

  * This software component is licensed by ST under BSD 3-Clause license,

  * the "License"; You may not use this file except in compliance with the

  * License. You may obtain a copy of the License at:

  *                        opensource.org/licenses/BSD-3-Clause

  *

  ******************************************************************************

  */

/* USER CODE END Header */

 

/* Includes ------------------------------------------------------------------*/

#include "main.h"

#include "adc.h"

#include "dma.h"

#include "tim.h"

#include "usart.h"

#include "usb_device.h"

#include "gpio.h"

 

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include "usbd_cdc_if.h"

#include<stdio.h>

/* USER CODE END Includes */

 

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

 

/* USER CODE END PTD */

 

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

/* USER CODE END PD */

 

/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PM */

 

/* USER CODE END PM */

 

/* Private variables ---------------------------------------------------------*/

 

/* USER CODE BEGIN PV */

ADC_HandleTypeDef hadc1;

DMA_HandleTypeDef hdma_adc1;

 

#define APP_RX_DATA_SIZE  64

#define APP_TX_DATA_SIZE  64

extern uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];

extern uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];

 

uint8_t bufftx[100] ;

 

extern int i;

 

__IO uint32_t ADCvalue[2];

volatile uint32_t data=0;

__IO float db[2];

 

 

 

/* USER CODE END PV */

 

/* Private function prototypes -----------------------------------------------*/

void SystemClock_Config(void);

/* USER CODE BEGIN PFP */

int _write(int file, unsigned char* p, int len)//printf를 위한 함수

{

HAL_UART_Transmit(&huart1, p, len, 10);

return len;

}

/* USER CODE END PFP */

 

/* Private user code ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

 

//adc interrupt callback function

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

{

/* USER CODE END WHILE */

db[0]= ADCvalue[0]*(3.3)/4095;

db[1]= ADCvalue[1]*(3.3)/4095;

 

 

 

}

/* USER CODE END 0 */

 

/**

  * @brief  The application entry point.

  * @retval int

  */

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_TIM2_Init();

  MX_USART1_UART_Init();

  MX_USB_DEVICE_Init();

  MX_DMA_Init();

  MX_ADC1_Init();

  /* USER CODE BEGIN 2 */

  HAL_TIM_OC_Start_IT(&htim2,TIM_CHANNEL_1);

  HAL_ADCEx_Calibration_Start(&hadc1);

  setvbuf(stdout, NULL, _IONBF, 0);// 즉시 pintf 가 송신될수 있도록 stdout buffer size를 0으로 만들어준다.

 

  /* USER CODE END 2 */

 

  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

    /* USER CODE END WHILE */

 

    /* USER CODE BEGIN 3 */

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADCvalue,2);//ADCvalue is the temporary buffer to store the data

//data = ADCvalue[0];

/*

sprintf(bufftx,"%d",data);

strcat(bufftx,"\n");

 

HAL_UART_Transmit(&huart1, bufftx, sizeof(bufftx), 100) ;  // send data using uart1

 

CDC_Transmit_FS((uint8_t*)bufftx, strlen((const char*)bufftx));//send data using virtual uart

memset(bufftx, 0, sizeof(bufftx));//clear the buffer

 

*/

//HAL_Delay(10);

  }

  /* USER CODE END 3 */

}

 

/**

  * @brief System Clock Configuration

  * @retval None

  */

void SystemClock_Config(void)

{

  RCC_OscInitTypeDef RCC_OscInitStruct = {0};

  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

 

  /** Initializes the CPU, AHB and APB busses clocks 

  */

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

  RCC_OscInitStruct.HSEState = RCC_HSE_ON;

  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;

  RCC_OscInitStruct.HSIState = RCC_HSI_ON;

  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;

  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;

  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  {

    Error_Handler();

  }

  /** Initializes the CPU, AHB and APB busses clocks 

  */

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;

  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;

  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

 

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

  {

    Error_Handler();

  }

  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_USB;

  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;

  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;

  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)

  {

    Error_Handler();

  }

}

 

/* USER CODE BEGIN 4 */

 

/* USER CODE END 4 */

 

/**

  * @brief  This function is executed in case of error occurrence.

  * @retval None

  */

void Error_Handler(void)

{

  /* USER CODE BEGIN Error_Handler_Debug */

  /* User can add his own implementation to report the HAL error return state */

 

  /* USER CODE END Error_Handler_Debug */

}

 

#ifdef  USE_FULL_ASSERT

/**

  * @brief  Reports the name of the source file and the source line number

  *         where the assert_param error has occurred.

  * @param  file: pointer to the source file name

  * @param  line: assert_param error line source number

  * @retval None

  */

void assert_failed(uint8_t *file, uint32_t line)

  /* USER CODE BEGIN 6 */

  /* User can add his own implementation to report the file name and line number,

     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* USER CODE END 6 */

}

#endif /* USE_FULL_ASSERT */

 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

 

ISR내부에서 USART를 통해 ADC값을 출력하거나 while안에서 출력하면 됨.

db배열에 저장된 값은 ADC값에 연산하여 얻어낸 전압값으로, 0~3.3V의 전압을 검출할 수 있다.

12비트 ADC이므로, 값은 4095 까지 나올 수 있다.