ADC (Analog to Digital Converter)
아날로그를 Digital 신호로 변경
아날로그 전압을 몇 단계로 쪼개서 디지털화
기준전압 - VREF, 쪼개는 단계 - ADC Resoultion

ADC 계산방법
VREF = 3.3V
4096단계 = 12bit resolution
ADC결과 1500이면

프로그래밍 하는 방식
- 기존에는 AD 변환이 완료되면 Value[0] ~ Value[3] 까지 하나씩 저장해달라고 CPU 클럭이 소모됨
- 위와 다르게 DMA 방식으로 AD변환이 완료되면 ADC결과를 DMA 컨트롤러가 자동으로 원하는 변수에 저장
정리
특정메모리의 값을 가져올 때 사용
몇개데이터를 가져와서 복사할떄는 효과가 거의 없지만 1kbyte 크기일때는 for문 돌려서 복사하느라 CPU가 다른일을 하지 못하지만 DMA 이용하면 바로 복사함. CPU의 클럭 소요 없이 DMA가 복사하기 때문에 좋음.
Uart로 데이터가 100byte씩 들어올때 보통 1byte씩 인터럽트 걸어서 배열에 차곡차곡 쌓아놓는데 그러면 100번의 인터럽트가 걸린다. 하지만 DMA를 이용하면 100byte를 DMA가 알아서 저장하고 다 받아지면 인터럽트 한번 걸어서 수행함
4개의 ADC를 설정함
ADC1 - IN9,10,12,13
ADC의 계산 방법 등을 설정함

소스코드
HAL_ADC_Start_DMA(&hadc1, &adcval[0], 4);
uint16_t adcval[4];
while (1)
{
printf(str, "%4d %4d",adcval[0],adcval[1]);
printf(str, "%4d %4d",adcval[2],adcval[3]);
}
#include "main.h"
// ADC와 DMA를 다루기 위한 핸들러 구조체입니다.
ADC_HandleTypeDef hadc1; // ADC1 핸들러 구조체
DMA_HandleTypeDef hdma_adc1; // ADC1과 연결된 DMA 핸들러 구조체
/* USER CODE BEGIN PV */
// ADC로부터 읽어온 값을 저장하는 변수입니다.
uint32_t adcBuf = 0; // DMA를 통해 ADC 값이 이 변수에 저장됩니다.
/* USER CODE END PV */
// 시스템 클럭 설정 함수의 선언입니다.
void SystemClock_Config(void);
// GPIO 초기화 함수의 선언입니다.
static void MX_GPIO_Init(void);
// DMA 초기화 함수의 선언입니다.
static void MX_DMA_Init(void);
// ADC1 초기화 함수의 선언입니다.
static void MX_ADC1_Init(void);
// 시스템 틱(Tick) 증가 함수입니다. 시스템 시간의 카운트를 증가시킵니다.
void HAL_IncTick(void)
{
// uwTick 전역 변수를 uwTickFreq 주기로 증가시킵니다.
uwTick += uwTickFreq;
// uwTick이 200의 배수가 되면 scan 플래그를 SET으로 설정합니다.
if(uwTick % 200 == 0){
scan = SET;
}
}
int main(void)
{
// HAL 라이브러리를 초기화합니다. 시스템 설정을 초기화하는 중요한 함수입니다.
HAL_Init();
// 시스템 클럭을 설정합니다.
SystemClock_Config();
// GPIO를 초기화합니다.
MX_GPIO_Init();
// DMA를 초기화합니다.
MX_DMA_Init();
// ADC1을 초기화합니다.
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
// DMA를 사용하여 ADC 변환을 시작합니다.
// 변환된 값은 adcBuf에 저장되며, 1개의 데이터만 수신합니다.
HAL_ADC_Start_DMA(&hadc1, &adcBuf, 1);
/* USER CODE END 2 */
while (1)
{
// scan 플래그가 SET이면, 즉 시스템 시간이 200틱(주기)마다 이 조건을 만족하면 실행됩니다.
if(scan){
// scan 플래그를 리셋하여 다음 200틱까지는 실행되지 않도록 합니다.
scan = RESET;
// DMA의 연속 요청이 비활성화된 경우, 다시 ADC 변환을 시작해야 합니다.
// 위의 주석에서 이 부분을 설명하고 있으며, 필요한 경우 주석을 해제하여 사용합니다.
//HAL_ADC_Start_DMA(&hadc1, &adcBuf, 1);
}
// __NOP()는 No Operation 명령어로, CPU를 잠시 대기 상태로 만듭니다.
// 이 함수는 주로 디버깅 목적으로 사용됩니다.
__NOP();
}
}
ADC_HandleTypeDef hadc1; 및 DMA_HandleTypeDef hdma_adc1;:
- ADC 및 DMA 모듈을 제어하기 위해 사용되는 핸들러입니다. 이 구조체들은 HAL 라이브러리 함수에서 ADC 및 DMA 하드웨어를 제어하는 데 사용됩니다.
uint32_t adcBuf = 0;:
- ADC 변환 결과를 저장하는 변수입니다. DMA를 통해 이 변수에 변환된 아날로그 값이 저장됩니다.
HAL_IncTick(void):
- 이 함수는 시스템의 틱 카운터를 증가시키는 역할을 합니다. 여기서 uwTick은 주기적으로 증가하며, 200틱마다 scan 플래그가 SET됩니다. 이 플래그는 이후의 작업에서 신호로 사용됩니다.
main() 함수:
- HAL_Init()를 통해 HAL 라이브러리와 하드웨어를 초기화한 후, 시스템 클럭, GPIO, DMA, ADC를 각각 초기화합니다.
- 초기화 후, HAL_ADC_Start_DMA(&hadc1, &adcBuf, 1);를 호출하여 DMA를 사용해 ADC 변환을 시작합니다. 변환된 값은 adcBuf에 저장됩니다.
- 메인 루프에서는 200틱마다 scan 플래그를 체크하여 추가 작업이 필요할 경우 해당 작업을 수행합니다.
DAC는 ADC의 반대로, Digital값을 Analog 값으로 변환하는 모듈입니다.
STM32F103에는 12bit DAC 모듈을 가지고 있습니다.
0부터 Vref+ 에 인가된 전압을 4096(2^12)개로 쪼개어 출력으로 보냅니다.
DAC 출력 공식!

(출처: Reference Manual)
DACoutput: DAC 출력
VREF: VREF+ 핀에 인가된 전압
DOR: Data Output Register로 여기에 0~4095값이 쓰여짐
Vref+ 가 3.3V일경우
3.3*1 / 4095 = 0.000806 이 됩니다.
즉 Digital Value 1당 0.000806V가 출력으로 나가게 됩니다.
HAL 드라이버에서 초기화를 제외한 DAC에 필요한 함수는 아래와 같습니다.
HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef *hdac, uint32_t Channel);
// DAC Output Enable 함수
// hdac: dac 인스턴스
// Channel: DAC_CHANNEL_1 or DAC_CHANNEL_2
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef *hdac, uint32_t Channel);
// DAC Output Disable 함수
// hadc: dac 인스턴스
// Channel: DAC_CHANNEL_1 or DAC_CHANNEL_2
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data);
// DAC Value 설정 함수
// hadc: dac 인스턴스
// Channel: DAC_CHANNEL_1 or DAC_CHANNEL_2
// Alignment: DAC_ALIGN_12B_R or DAC_ALIGN_12B_L or DAC_ALIGN_8B_R
// data: 0 ~ 4095 값
사용법
목표: DAC 채널 1의 출력을 2V로 설정하고 싶다.
DOR = 2V * 4095 / 3.3V = 2481.82 ≒ 2482
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2482);
HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);
HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
while (1)
{
//HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2047);
//삼각파 : 4095까지 증가 후 0으로 감소.
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dacval);
dacval++;
if(dacval > 4095) dacval = 0;
}
사인파
#include <math.h>
MX_TIM7_Init(void)
{
htim7.Instance = TIM7;
htim7.Init.Prescaler = 9;
htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
htim7.Init.Period = 8399;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static unsigned char cnt =0;
if(htim->Instance == TIM7)
{
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, (sinf(2*3.1415926535f*50*cnt / 1000.f)+1)*2047);
cnt++;
if(cnt>999) cnt=0;
}
}
Ref
https://louie0724.tistory.com/362
https://www.youtube.com/watch?v=o6hZEyv6s88&list=PLUaCOzp6U-RqMo-QEJQOkVOl1Us8BNgXk&index=13
https://wikidocs.net/168981
https://superbaik.tistory.com/entry/STM32F-ADC-with-DMA