본문 바로가기
# Semiconductor/[Semicon Academy]

[Harman 세미콘 아카데미] 78일차 - STM32/ARM(UltraSonic, Delay함수 만들기, UART rx & tx, 자율주행 프로젝트 시작)

by Graffitio 2023. 10. 20.
[Harman 세미콘 아카데미] 78일차 - STM32/ARM(UltraSonic, Delay함수 만들기, UART rx & tx, 자율주행 프로젝트 시작)
728x90
반응형
[UltroSonic]

 

초음파 센서에 관한 내용은 아래 링크 참조

 

[Harman 세미콘 아카데미] 39일차 - ATmega128(Ultrasonic, ADC)

[Ultrasonic] Ultra Sonic(HC-SR04) 위 센서는 초음파를 이용해 물체와의 거리를 측정할 수 있으며, 그 원리는 초음파를 발사하고 장애물과 부딪힌 뒤 반사되어 돌아오는 시간차와 음파의 속력을 측정해

rangvest.tistory.com

 

 

📌 Pin Configuration

 

좀 더 빠른 응답을 위해 Speed High로 설정

 

 

Sys Clock : 100,000,000Hz

PreScaler : 100이므로 

100,000 -> 1us counter로 사용 가능

 

TIM 동작 시 마다 인터럽트 발생

 

UART 통신마다 인터럽트 발생

 


 

📌 Code

 

/*
 * delay.h
 *
 *  Created on: Oct 20, 2023
 *      Author: Wooseong Jung
 */

#ifndef INC_DELAY_H_
#define INC_DELAY_H_
#include "tim.h" // 타이머를 쓰기 위한 헤더파일
#include "stm32f4xx_hal.h" // HAL 함수를 쓰기 위한 헤더

void delay_us(uint16_t us);


#endif /* INC_DELAY_H_ */

 

/*
 * delay.c
 *
 *  Created on: Oct 20, 2023
 *      Author: Wooseong Jung
 */

#include "delay.h"

void delay_us(uint16_t us)
{
	// __HAL_TIM_SET_COUNTER() 함수는 타이머를 원하는 숫자로 설정하는 함수
	__HAL_TIM_SET_COUNTER(&htim3, 0);

	// 타이머 값을 받아와야
    while((__HAL_TIM_GET_COUNTER(&htim3)) < us);
}

 

#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "delay.h"
#include "stdio.h"
/* USER CODE END Includes */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define TRIG_PORT GPIOA
#define TRIG_PIN GPIO_PIN_5
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#ifdef __GNUC__
  /* With GCC, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint32_t INC_Value1 = 0; // Echo High되는 순간
uint32_t INC_Value2 = 0; // Echo Low 되는 순간
uint32_t echoTime = 0; // Distance(왕복 거리)를 측정하기 위한 Echo Time()
uint8_t captureFlag = 0;
uint8_t distance = 0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
// Echo read
// Echo가 High가 되는 순간, Interrupt가 발생한다.
// 그때부터 Falling Edge가 발생하는 순간을 캡쳐하기 위한 Callback 함수.
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) // 만약 인터럽트 소스 채널이면,(우리가 1번 채널을 쓰기로 설정)
	{
		if(captureFlag == 0) // 첫 번째 캡쳐가 되지 않았다면,(Falling Edge가 발생하지 않았다면,)
		{
			INC_Value1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 첫 번째 Value를 읽어라.
			captureFlag = 1; // 첫 번째 캡쳐 완료

			// Polarity 변경(Rising -> Falling)
			__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); // Echo Falling을 캡쳐해야 하므로
		}
		else if(captureFlag == 1) // 만약 첫 번쨰 캡쳐가 이미 완료됐다면,
		{
			INC_Value2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
			__HAL_TIM_SET_COUNTER(&htim3, 0); // reset Counter

			if(INC_Value2 > INC_Value1)
			{
				echoTime = INC_Value2 - INC_Value1;
			}
			else if(INC_Value1 > INC_Value2) // 거리가 너무 길면, INC_Value2 가 Overflow되어 count가 0으로 뚝 떨어져서 INC_Value1보다 작아질 수 있다.
			{
				echoTime = (0xffff - INC_Value1) + INC_Value2;
			}
			distance = echoTime / 58;
			captureFlag = 0;

			__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); // 다시 Echo High를 캡쳐하도록 설정
			__HAL_TIM_DISABLE_IT(&htim3, TIM_IT_CC1);
		}
	}
}

void HCSR04_Read(void)
{
	HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, 1); // Trig Pin High
	delay_us(10);							   // Delay 10us
	HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, 0); // Trig Pin Low

	__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC1); // Set Timer Enable
}
/* USER CODE END PFP */

int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  
  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  HCSR04_Read();
	  printf("%d cm\r\n", distance);
	  HAL_Delay(200);
  }
  /* USER CODE END 3 */
}

 

✅ HAL 함수 가져오기 tip

    STM32IDE에서 HAL함수들이 제공되고

    이 HAL 함수는 Header file과 Source file에 1:1 매칭되어 있다.

    Header에는 프로토타입만 선언

    Source에는 함수 Source 전체가 작성되어 있다.

 

    __weak : 니가 함수를 가져가서 재정의한 뒤 사용해라.

 

 

 

우리는 Rising 때 캡쳐하라고 설정했다.

근데 우리는 Falling도 캡쳐해야 하는데 그때마다 ioc 파일 설정을 바꿔주기는 좀 곤란하다.

 

그럴 때 쓰는 명령어가 있다.

__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);

 

✅ Debug

벌레 버튼을 눌러 디버그 모드를 실행한다.

우측에 있는 Debug 셋팅 창에서 Lve Expression 배너 클릭

내가 받을 변수들을 Expression에 넣어주고

Resume 버튼을 눌러주면,

아래와 같이 우리가 확인하고자 하는 변수들의 값이 실시간으로 출력된다.

 

 


 

📌 Operation

Comport master와 같은 터미널 프로그램을 실행시킨다.

Baudrate는 115200으로 설정했으므로, 똑같이 셋팅해주고

포트를 맞춰준 뒤, Open port 클릭.

 

현재 거리가 1us마다 출력된다.

 


 

[UART rx & tx]

 

📌 Pin Configuration

 

UART1 셋팅(블루투스)

 

UART2 셋팅(Terminal)

 


 

📌 Code

 

통신 입력이 발생하면, 인터럽트가 동작하도록 설정하였고

인터럽트 발생 시 마다 callback함수가 호출된다.

HAL_Init() 이후에 uart1과 uart2의 입력이 한 번씩

Callback 함수에는 UART를 통해 입력을 받는 함수와 데이터를 전송하는 함수가 포함되어 있다.

이렇게 간단한 함수만으로도 통신이 가능하다.

 

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
	uint8_t rxData[1];
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

// 통신을 통해 입력이 들어오면, 인터럽트 발생
// 인터럽트가 발생되면 Callback 함수가 호출되어 아래 기능들이 실행된다.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_UART_Receive_IT(&huart2, rxData, sizeof(rxData)); // UART2로부터 입력받음
	HAL_UART_Receive_IT(&huart1, rxData, sizeof(rxData)); // UART1으로부터 입력받음(블루투스)
	HAL_UART_Transmit_IT(&huart1, rxData, sizeof(rxData)); // UART1으로 데이터 보냄
	HAL_UART_Transmit_IT(&huart2, rxData, sizeof(rxData)); // UART2로 데이터 보냄
}
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart2, rxData, sizeof(rxData));
  HAL_UART_Receive_IT(&huart1, rxData, sizeof(rxData));
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE BEGIN 3 */
	  if(rxData[0] == '1')
	  {
		  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 1);
	  }
	  else if(rxData[0] == '0')
	  {
		  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 0);
	  }
  }
  /* USER CODE END 3 */
}

 


 

 

자율주행 이동체 프로젝트 시작

 

728x90
반응형