본문 바로가기
DSP, MCU/STM32 (ARM Cortex-M)

STM32 , UART 통신으로 피아노 연주하기 , PWM Frequency 제어 ( Passive Buzzer )

by eteo 2022. 5. 27.

 

STM32F429 보드와 수동부저를 사용하여 멜로디를 연주해 보았다.

 

인터럽트 방식 UART 통신으로 시리얼 모니터에 숫자를 입력하는 해당하는 음계가 연주된다.

 

 

 

 

 

회로와 timer 설정은 지난번에 올린글과 동일하다. 거기서 uart3 설정만 켰다.

 

2022.05.27 - [MCU/STM32 (ARM Cortex-M)] - STM32 , PWM 주기 변경으로 수동 부저 ( Passive Buzzer ) 제어 , 실시간으로 ARR 변경시 동작 멈추는 현상 해결 , 멜로디 출력

 

 

 

소스코드

 

전역변수 구간

/* USER CODE BEGIN PV */
typedef enum {
	N = 0,
    C = 956,
    D = 852,
    E = 758,
    F = 716,
    G = 638,
    A = 568,
    B = 506,
    C6 = 478
}_NOTE;
_NOTE note;
uint8_t rx_data=0;
uint32_t IRcalled=0;
/* USER CODE END PV */

 

ARR값은 본인의 클락설정과 PSC값에 따라 달라질 수 있다.

typedef enum으로 음계를 묶고 _NOTE 로 정의해둔 뒤 자료형처럼 사용하였다. 그외에는 uart 데이터를 수신할 변수와 인터럽트 처리함수가 호출된 횟수를 셀 변수를 만들어 두었다.

 

 

 

함수의 원형 선언 구간

/* USER CODE BEGIN PFP */
void piano(_NOTE note);
_NOTE numtoscale(uint8_t strnum);
/* USER CODE END PFP */

 

uart 수신데이터를 받아 처리한 뒤 _NOTE 형으로 반환하는 numtoscale 함수와 다시 이 값을 받아 실제 부저를 울릴 piano 함수를 만들었다.

 

 

 

 

USER CODE BEGIN 2 부분

  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart3, &rx_data, 1);
//  uint8_t test[8]={'1','2','3','4','5','6','7','8'};
  uint32_t temp;
  /* USER CODE END 2 */

 

HAL_UART_Receive_IT 함수를 쓰고 일시적으로 사용할 temp 변수도 만든다.

 

 

 

 

 

while문

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

	  temp=IRcalled;
	  if(rx_data!=0){
		  note = numtoscale(rx_data);
		  piano(note);
		  if(temp==IRcalled){
			  rx_data=0;
		  }
	  }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

 

rx_data 가 0이 아니라면, 즉 uart 통신으로 수신된 데이터가 있다면 numtoscale함수를 호출하여 rx_data 를 인수로 넘기고 반환값을 note 변수로 받는다. 다시 piano함수를 호출하며 note를 인수로 전달해 사용자가 입력한 숫자에 매칭되는 음계를 부저를 통해 출력한다.

if문이 실행되는 중간에 또 uart 수신 인터럽트가 발생한게 아니라면 rx_data를 0으로 만든다. 이 부분은 시리얼 모니터에 키보드를 너무 빨리 연속해서 눌렀을 때 씹히는 부분을 보완하려고 추가했다.

 

 

 

 

 

USER CODE BEGIN 4 부분

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance == huart3.Instance){
	  HAL_UART_Receive_IT(&huart3, &rx_data, 1);
	  HAL_UART_Transmit(&huart3, &rx_data, 1, 10);

	  IRcalled++;
  }
}

 

HAL_UART_RxCpltCallback 함수 내에서 uart 통신으로 수신된 데이터를 버퍼 rx_data에 쓰고 터미널에서 체크할 용도로 다시 송신한다. 그때 HAL_UART_Transmit 함수는 폴링방식을 사용했다. 그리고 IRcalled++ 한다.

 

 

_NOTE numtoscale(uint8_t strnum){
	if(strnum=='1'){
		return C;
	}else if(strnum=='2'){
		return D;
	}else if(strnum=='3'){
		return E;
	}else if(strnum=='4'){
		return F;
	}else if(strnum=='5'){
		return G;
	}else if(strnum=='6'){
		return A;
	}else if(strnum=='7'){
		return B;
	}else if(strnum=='8'){
		return C6;
	}else
		return N;
}

 

매개변수는 uint8_t 형이고 입력받은 문자의 값에 따라 typedef enum으로 정의된 _NOTE형으로 반환한다.

 

 

void piano(_NOTE scale){

	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
	TIM2->ARR = scale;
	TIM2->CCR1 = TIM2->ARR / 2;
	HAL_Delay(300);
	HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);

}

 

HAL_TIM_PWM_Start 하고 매개변수로 받은 값을 TIM->ARR에 대입해준뒤 듀티비인 TIM2->CCR1은 그 절반으로 해준다. 300ms 동안 출력한 후 다시 HAL_TIM_PWM_Stop 한다.