MCU
STM32F103(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
HAL driver를 사용하여 ADC를 1개를 polling mode로 single conversion 한다.

 

ADC는 크게 세가지 모드가 있다.

1. Polling

2. Interrupt

3. DMA

이번 페이지에서는 polling 모드를 사용한다.

 

1. STM32CubeMX : ADC-Polling 설정

- 'Pinout & Configuration' Tab으로 이동

1) Analog 선택

2) ADC1 항목 선택

- ADC1, 2, 3중에 사용할 ADC 선택

 

또는,

- 'Pinout view' Tab으로 이동

1) ADC로 사용하고 싶은 Pin을 직접 선택

 

- 'Clock Configuration' Tab으로 이동

1) Clock Issues 확인 하여 클럭 이슈 제거 할 것

 

2. Single Conversion

- ADC Configuration 확인

ADC_Settings >

1) Scan Convsersion Mode : Disabled

여러 채널을 샘플링 하는 경우 scan 모드를 enable 하고 한개 채널만 샘플링 하는 경우 disable

2) Continous conversion mode : Disabled

타이머의 TRGO 신호를 받을때만 샘플링 시작을 할 예정이므로 disable

3) Discontinous conversion mode : Disabled

타이머의 TRGO 신호를 받을때만 1 샘플링씩 할 예정이므로 disable

 

ADC_Regular_Conversion Mode >

1) Enable Regular Conversions : Enable

2) Number of Conversion : 1

3) Rank 1 : Channel 8(Converting 원하는 ADC 채널 선택)

4) Sampling Time : 제일 느린 시간으로 선택

- GENERATE CODE 클릭

 

3. ATOLLIC : main.c 수정

- main() : 실행 코드

while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1, 100);
	uint32_t val_pb0 = HAL_ADC_GetValue(&hadc1);
	printf("PB0 = %d\n", val_pb0);
}
/* USER CODE END 3 */
 

 
MCU
STM32F103(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
ADC를 여러개를 polling mode로 multi conversion 한다.

 

4. Multi Conversion

- ADC Configuration 확인

ADC_Settings >

1) Scan Convsersion Mode : Enabled

여러 채널을 샘플링 하는 경우 scan 모드를 enable 하고 한개 채널만 샘플링 하는 경우 disable

2) Continous conversion mode : Disabled

타이머의 TRGO 신호를 받을때만 샘플링 시작을 할 예정이므로 disable

3) Discontinous conversion mode : Enabled

여러 채널을 샘플링 하는 경우 Enable, Trigger 한번에 몇 채널 받아올지 설정해야 함

 

ADC_Regular_Conversion Mode >

1) Enable Regular Conversions : Enable

2) Number of Conversion : 3

3) 각 Rank 별로 원하는 ADC 채널 선택

4) Sampling Time : 제일 느린 시간으로 선택

- GENERATE CODE 클릭

 

5. ATOLLIC : main.c 수정

- main() : 실행 코드

while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1, 100);
	uint32_t val_pb0 = HAL_ADC_GetValue(&hadc1);
	printf("PB0 = %d ", val_pb0);

	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1, 100);
	uint32_t val_pc5 = HAL_ADC_GetValue(&hadc1);
	printf("PC5 = %d ", val_pc5);

	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1, 100);
	uint32_t val_pa2 = HAL_ADC_GetValue(&hadc1);
	printf("PA2 = %d\n", val_pa2);
}
/* USER CODE END 3 */
 

 

'ST > STM32F103C8' 카테고리의 다른 글

STM32F103C8 - ADC(DMA)  (0) 2025.08.08
STM32F103C8 - ADC(Interrupt)  (0) 2025.08.08
STM32F103C8 - printf()를 이용하여 UART로 문자열 출력  (0) 2025.08.08
STM32F103C8 - UART(DMA)  (3) 2025.08.08
STM32F103C8 - UART(Interrupt)  (1) 2025.08.08

 

MCU
STM32F103(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
HAL driver를 사용하여 UART를 2개 열어서 polling mode로 서로 TX-RX를 주고 받게 한다.

 

1. STM32F103C8 - UART 회로도

 

UART Firmware 작성시 크게 세가지 모드로 구분 할 수 있다.

1. Polling

2. Interrupt

3. DMA

이번 페이지에서는 Polling 모드를 사용

 

2. STM32CubeMX : UART-Polling 설정

- 'Pinout & Configuration' Tab으로 이동

1) Connectivity 선택

2) USART1 항목 선택

- Mode : Asynchronous, Synchronous 등

- Hardware Flow Control : Disable

- PA9(USART1_TX), PA10(USART1_RX) 자동 할당(녹색 표시)

3) USART2 도 똑같이 설정

 

- Parameter Settings 확인

1) Baud Rate : 115200 Bits/s

2) Word Length : 8 Bits

3) Parity : None

4) Stop Bits : 1

 

- GENERATE CODE 클릭


3. ATOLLIC : main.c 수정

- main() : 변수 선언

int main(void)
{
  /* USER CODE BEGIN 1 */
	HAL_StatusTypeDef status = HAL_ERROR;

	int i = 0;

	char tx_data[62] = {};
	char rx_data[60] = {};
  /* USER CODE END 1 */
 

- main() : 실행 코드

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RTC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
for(i = 0 ; i < 60 ; i++)
{
	tx_data[i] = i+48;
}
HAL_UART_Transmit(&huart1, &tx_data[0], 1, 1000);

for(i = 0 ; i < 60 ; i++)
{
	status = HAL_UART_Receive(&huart2, &rx_data[i], 1, 500);
	if(status == HAL_OK)
	{
		HAL_UART_Transmit(&huart1, &tx_data[i+1], 1, 1000);
	}
	else
	{
		__HAL_UART_CLEAR_OREFLAG(&huart2); 
	}
}

for(i = 0 ; i < 60 ; i++)
{
	printf("%c ", rx_data[i]);
}

/* USER CODE END 2 */
 

- printf()를 사용하기 위해서는 아래 링크를 참조

STM32F103C8 - printf()를 이용하여 UART로 문자열 출력

 

- 일정 사이즈(60)개가 들어와야 인터럽트 발생. 59 or 61개가 들어오면 정상적인 동작을 기대 할 수 없음

- 위와 같이 60개 보내고 60개 받을때는 문제 없이 동작하는 것 처럼 보이나, 내가 컨트롤 할 수 없는 Delay가 발생할때 Overrun Error 발생될 수 있음

 

'ST > STM32F103C8' 카테고리의 다른 글

STM32F103C8 - UART(DMA)  (3) 2025.08.08
STM32F103C8 - UART(Interrupt)  (1) 2025.08.08
STM32F103C8 - GPIO(EXTI)  (2) 2025.08.08
STM32F103C8 - GPIO  (0) 2025.08.08
STM32F103C8 - Project 생성 및 기본 설정  (1) 2025.08.08

 

MCU
STM32F746G DISCOVERY(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
HAL driver를 사용하여 UART각 Polling, Interrupt mode 입출력 Test(Receive Overrun 방지)

 

- UART Firmware 작성시 아래와 같이 크게 세가지 모드로 구분 할 수 있음

- 각 mode별로 입출력 Test

1. Polling

2. Interrupt

3. DMA

이번 페이지에서는 Polling, Interrupt mode 사용


1. STM32CubeMX : UART-Polling 설정

- Configuration은 STM32F746 DISCOVERY - printf()를 이용하여 UART로 문자열 출력 참고

 

- ATOLLIC : main.c

- 터미널에서 Keyboard 입력을 받아 다시 Terminal로 출력 코드 작성

- 필요한 변수 선언

#define RX_MAX    1

uint8_t rcv_data[RX_MAX];
 

- while() 작성

  while (1)
  {
	HAL_UART_Receive(&huart1, rcv_data, RX_MAX, 1000);

    if(rcv_data[0])   {
        printf("Rx OK = %c\r\n", rcv_data[0]);
        rcv_data[0] = 0x00;
    }

    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))   {
        printf("UART1 Overrun Error occurred.\r\n");
        __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
 

- 위와 같이 작성하였지만 Keyboard 입력이 빠르지 않기 때문에 Overrun Error가 발생하지 않는다.

때문에 HAL_Delay만 추가 해서 Test

  while (1)
  {
	HAL_Delay(50);
	HAL_UART_Receive(&huart1, rcv_data, RX_MAX, 1000);

    if(rcv_data[0])   {
        printf("Rx OK = %c\r\n", rcv_data[0]);
        rcv_data[0] = 0x00;
    }

    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))   {
        printf("UART1 Overrun Error occurred.\r\n");
        __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
 

- Delay 하나로, Keyboard 입력이 쌓임 - 바로 처리 하지 못하고 Delay - Overrun Error 발생함을 확인


2-1. STM32CubeMX : UART-Interrupt(1) 설정

- Configuration은 STM32F746 DISCOVERY - printf()를 이용하여 UART로 문자열 출력 참고

- USART1 NVIC enable 할 것

 

- ATOLLIC : main.c

- 터미널에서 Keyboard 입력을 받아 다시 Terminal로 출력 코드 작성

- 필요한 변수 및 callback() 작성

#define RX_MAX    1

uint8_t rcv_data[RX_MAX];

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)  {
	if(huart->Instance == USART1)
	{
		HAL_UART_Receive_IT(&huart1, rcv_data, RX_MAX);
	}
}
 

- while() 작성

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_UART_Receive_IT(&huart1, rcv_data, RX_MAX);
  
  while (1)
  {
	  if(rcv_data[0])
	  {
		  printf("Rx OK = %c\r\n", rcv_data[0]);
		  rcv_data[0] = 0x00;
	  }
	  
	  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))
	  {
		  printf("UART1 Overrun Error occurred.\r\n");
		  __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
	  }
	/* USER CODE END WHILE */
	
	/* USER CODE BEGIN 3 */
	}
 

- 위와 같이 작성하였지만 Keyboard 입력이 빠르지 않기 때문에 Overrun Error가 발생하지 않는다.

때문에 HAL_Delay만 추가 해서 Test

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_UART_Receive_IT(&huart1, rcv_data, RX_MAX);
  
  while (1)
  {
	  HAL_Delay(50);

	  if(rcv_data[0])
	  {
		  printf("Rx OK = %c\r\n", rcv_data[0]);
		  rcv_data[0] = 0x00;
	  }
	  
	  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))
	  {
		  printf("UART1 Overrun Error occurred.\r\n");
		  __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
	  }
	/* USER CODE END WHILE */
	
	/* USER CODE BEGIN 3 */
	}
 

- HAL_UART_Receive_IT()를 사용하였음에도 Overrun Error 발생

 

Q. printf()가 시간이 오래 걸리는데 printf()를 사용해서 문제가 발생하지 않았나? Delay가 너무 길다.

A. printf()를 사용하지 않아 문제가 발생하지 않았다 하더라도, Polling 방식과 마찬가지로, 내가 컨트롤 할 수 없는 Delay가 발생할때 Overrun Error 발생될 수 있음(데이터가 들어오면 처리 해야 하는데, delay 때문에 처리는 못하고 데이터가 또 들어오면 Overrun)

 

Q. HAL_UART_Receive_IT 버퍼 사이즈를 키우면 되지 않나?

A. 수신 사이즈 만큼의 데이터가 들어와야 인터럽트 발생되며 사이즈를 무한정 키울수도 없음.

수신 사이즈를 1000으로 키웠는데 999개가 들어온다면? 1001개가 들어온다면?

 

아래에 Overrun Error를 방지하기 위한 예제를 설명합니다.


2-2. STM32CubeMX : UART-Interrupt(2) 설정

- Configuration은 STM32F746 DISCOVERY - printf()를 이용하여 UART로 문자열 출력 참고

- USART1 NVIC enable 할 것

 

- ATOLLIC : main.c

- 터미널에서 Keyboard 입력을 받아 다시 Terminal로 출력 코드 작성

- main.c : main() 수정

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_UART_Receive_IT(&huart1, rcv_data, RX_MAX);
  
  while (1)
  {
	  HAL_Delay(50);
	  
	  if(rcv_data[0])
	  {
  		  printf("Rx OK = %c\r\n", rcv_data[0]);
  		  rcv_data[0] = 0x00;
  	  }

  	  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))
  	  {
  		  printf("UART1 Overrun Error occurred.\r\n");
  		  __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
  	  }
  	/* USER CODE END WHILE */

  	/* USER CODE BEGIN 3 */
  }
 

- stm32f7xx_it.c : USART1_IRQHandler 생성됨

void USART1_IRQHandler(void)
{
	/* USER CODE BEGIN USART1_IRQn 0 */

	/* USER CODE END USART1_IRQn 0 */
	HAL_UART_IRQHandler(&huart1);
	/* USER CODE BEGIN USART1_IRQn 1 */

	/* USER CODE END USART1_IRQn 1 */
}
 

- stm32f7xx_it.c : USART1_IRQHandler 수정

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	if((huart1.Instance->ISR & UART_FLAG_RXNE) != RESET)
	{
		rcv_data[0] =  huart1.Instance->RDR;
	}

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}
 

위와 같이 rx 데이터가 들어올 때 바로 읽어야 Overrun Error 발생 하지 않음

 

※ UART Interrupt mode 추가 설명(꼭 읽고 가면 좋은)

- HAL_UART_Receive_IT()를 호출하면 UART 수신 인터럽트에 관한 설정을 자동으로 해주며, UART 수신 인터럽트 대기 상태가 됨(while()이나 다른 Timer 인터럽트에서 호출 하면 데이터를 잃어 버릴수 있음)

 

- HAL_UART_Receive_IT()에서 데이터 size가 고정되어 있는것이 아니라면 한 바이트씩 수신 추천

 

- UART 수신 인터럽트가 발생(데이터가 들어오면)하면 USARTx_IRQHandler() 실행됨

 

2-1. STM32CubeMX : UART-Interrupt(1) 추가 설명

- USARTx_IRQHandler()HAL_UART_IQRHandler() 호출하여 인터럽트 관련 처리를 수행함

(STM32CubeMX에서 USART1 NVIC enable 하면 자동 생성됨)

 

- HAL_UART_IQRHandler()의 수신 인터럽트에서 HAL_UART_RxCpltCallback() 호출

 

- HAL_UART_RxCpltCallback()는 weak로 선언 되어 있으며 사용자가 원하는대로 작성, 마지막에 HAL_UART_Receive_IT()를 호출하여 다시 UART 수신 인터럽트 대기 상태로 만들 것

 

2-2. STM32CubeMX : UART-Interrupt(2) 추가 설명

- 위 USARTx_IRQHandler()의 huart1.Instance->RDR 넣는 어레이는 링버퍼로 작성 권장(데이터 잃어 버리고 싶지 않으면)

+ Recent posts