[STM32#07] TIM3의 ARR와 CCR을 설정하고 PWM출력을 해서 부저(buzzer) 사운드 재생하는 방법 알아보기!(녹칸다 내맘대로 STM32)
프로그래밍/STM32 2025. 11. 3. 23:05
https://youtube.com/live/QPbT5pfgztc
[STM32#07] TIM3의 ARR와 CCR을 설정하고 PWM출력을 해서 부저(buzzer) 사운드 재생하는 방법 알아보기!(녹칸다 내맘대로 STM32)
심심한녹칸다의 내맘대로 STM32시리즈이다!
STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g39f7ee2e2d4_1_0#slide=id.g39f7ee2e2d4_1_0
이번편은 녹칸다의 STM32쉴드에 붙어있는 부저를 작동시켜보도록 하자!
1.STM32에서 구조체와 포인터타입의 구조체에 대해서 차이를 알수있는 예시를 보이시오!

/* USER CODE BEGIN 2 */
char buff[50];
typedef struct{
uint16_t num1;
int32_t num2;
}Mystruct;
//Mystruct type의 구조체를 s1이라는 이름으로 만들겠다!
Mystruct s1;
//Mystruct type의 구조체의 주소를 담는 포인터를 생성한다!
Mystruct *s2;
//s2에다가 s1의 주소를 넣겠다!
//s2는 s1을 참조하게된다!
s2 = &s1;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//s1의 멤버인 num1에 10이라는 값을 대입하겠다!
s1.num1 = 10;
s1.num2 = 20;
sprintf(buff,"CASE1 num1=%d, num2=%d\n",s1.num1,s1.num2);
HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
HAL_Delay(1000);
//s2를 통해서 s1의 num1의 값을 30으로 바꾸고, num2의 값을 40으로 바꾼다!
(*s2).num1 = 30;
(*s2).num2 = 40;
sprintf(buff,"CASE2 num1=%d, num2=%d\n",s1.num1,s1.num2);
HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
HAL_Delay(1000);
s2->num1 = 50;
s2->num2 = 60;
sprintf(buff,"CASE3 num1=%d, num2=%d\n",s1.num1,s1.num2);
HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
2.IOC를 설정해서 부저에 1초 간격으로 440hz의 부저음이 발생할 수 있도록 하시오!(PWM은 제일 처음 1회 호출하는 코드가 있다)

/* USER CODE BEGIN 0 */
static void Tone(uint32_t Frequency)
{
TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
/* USER CODE END 0 */
//중략
/* USER CODE BEGIN 2 */
//TIM3_CH3의 PWM기능을 활성화하겠습니다!
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
//주파수값을 440hz로 설정한다
Tone(440);
//시작하면 일단 부저는 끈상태라
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//부저의 소리를 재생한다!(1초)
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
HAL_Delay(1000);
//부저의 소리를 끈다!(1초)
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
3.녹칸다가 쉴드에 붙혀놓은 8개의 버튼이 있다! 8개의 버튼을 도,레,미,파,솔,라,시,도까지를 버튼을 누를때 소리가 나오도록 하시오!(피아노 만들기)
/* USER CODE BEGIN 0 */
static void Tone(uint32_t Frequency)
{
TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
#define NOTE_C6 1047 //도
#define NOTE_D6 1175 //레
#define NOTE_E6 1319 //미
#define NOTE_F6 1397 //파
#define NOTE_G6 1568 //솔
#define NOTE_A6 1760 //라
#define NOTE_B6 1976 //시
#define NOTE_C7 2093 //도
/* USER CODE END 0 */
//중략
/* USER CODE BEGIN 2 */
//TIM3_CH3의 PWM기능을 활성화하겠습니다!
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
//시작하면 일단 부저는 끈상태라
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_C6);
}else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_D6);
}else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_E6);
}else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_F6);
}else if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_G6);
}else if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_A6);
}else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_B6);
}else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_C7);
}else{
//소리를 끈다
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
4.로터리엔코더를 이용해서 로터리엔코더의 회전이 감지되면 감지될때마다 아주 짧은 비프음을 발생시키시오!(시계 6옥타브미, 반시계는 6옥타브솔,로터리엔코더눌려지면 7옥타브도)
/* USER CODE BEGIN 0 */
static void Tone(uint32_t Frequency)
{
TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
#define NOTE_C6 1047 //도
#define NOTE_D6 1175 //레
#define NOTE_E6 1319 //미
#define NOTE_F6 1397 //파
#define NOTE_G6 1568 //솔
#define NOTE_A6 1760 //라
#define NOTE_B6 1976 //시
#define NOTE_C7 2093 //도
/* USER CODE END 0 */
//중략
/* USER CODE BEGIN 2 */
//TIM3_CH3의 PWM기능을 활성화하겠습니다!
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
//시작하면 일단 부저는 끈상태라
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
uint8_t old_sensor_A = 1;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
uint8_t sensor_A = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11); //A값
if(sensor_A == 0 && old_sensor_A == 1){
//falling edge
uint8_t sensor_B = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12); //B값
if(sensor_B == 0){
//시계
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_E6);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
}else{
//반시계
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_G6);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
}
}
old_sensor_A = sensor_A;
//PC7이 눌려지면 7옥타브 도 음이 재생된다!
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_C7);
}else{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
5.PA0에 연결된 가변저항을 이용해서 부저의 피치를 6옥타브도부터 7옥타브 도까지를 설정하는데, 버튼1인 PB1을 누르면 부저가 재생되고, 버튼2인 PA8을 누르면 부저가 재생되지 않도록 하시오!

/* USER CODE BEGIN 0 */
static void Tone(uint32_t Frequency)
{
TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
#define NOTE_C6 1047 //도
#define NOTE_D6 1175 //레
#define NOTE_E6 1319 //미
#define NOTE_F6 1397 //파
#define NOTE_G6 1568 //솔
#define NOTE_A6 1760 //라
#define NOTE_B6 1976 //시
#define NOTE_C7 2093 //도
/* USER CODE END 0 */
//중략
/* USER CODE BEGIN 2 */
//TIM3_CH3의 PWM기능을 활성화하겠습니다!
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_ADC_Start(&hadc1); //딱 한번만 호출
uint16_t adc_value0 = 0;
//시작하면 일단 부저는 끈상태라
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//PB1을 누르면 부저의 소리가 난다
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
}
//PA8을 누르면 부저의 소리가 꺼진다
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
}
//측정할때마다 호출
if(HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
{
// 값 처리
adc_value0 = HAL_ADC_GetValue(&hadc1); //0~4095
//NOTE_C6 1047 ~ NOTE_C7 2094
adc_value0 = NOTE_C6+(NOTE_C7-NOTE_C6)*(adc_value0/4095.0);
Tone(adc_value0);
}
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
6.예제5에서 했던 가변저항측정값을 CDS측정값으로 바꾸고, 빛의 세기에 따라서 음높이가 달라지는 예시를 구현하시오!

/* USER CODE BEGIN 0 */
static void Tone(uint32_t Frequency)
{
TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
#define NOTE_C6 1047 //도
#define NOTE_D6 1175 //레
#define NOTE_E6 1319 //미
#define NOTE_F6 1397 //파
#define NOTE_G6 1568 //솔
#define NOTE_A6 1760 //라
#define NOTE_B6 1976 //시
#define NOTE_C7 2093 //도
/* USER CODE END 0 */
//중략
/* USER CODE BEGIN 2 */
//TIM3_CH3의 PWM기능을 활성화하겠습니다!
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_ADC_Start(&hadc1); //딱 한번만 호출
uint16_t adc_value0 = 0;
//시작하면 일단 부저는 끈상태라
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//PB1을 누르면 부저의 소리가 난다
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
}
//PA8을 누르면 부저의 소리가 꺼진다
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
}
//측정할때마다 호출
if(HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
{
// 값 처리
adc_value0 = HAL_ADC_GetValue(&hadc1); //0~4095
//NOTE_C6 1047 ~ NOTE_C7 2094
adc_value0 = NOTE_C6+(NOTE_C7-NOTE_C6)*(adc_value0/4095.0);
Tone(adc_value0);
}
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
7.PA1에 연결된 CDS의 아날로그 값을 측정해서 녹칸다가 센서를 손으로 가리면 부저의 소리가 발생하고 안가리면 소리가 안나도록 하시오!
/* USER CODE BEGIN 0 */
static void Tone(uint32_t Frequency)
{
TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
#define NOTE_C6 1047 //도
#define NOTE_D6 1175 //레
#define NOTE_E6 1319 //미
#define NOTE_F6 1397 //파
#define NOTE_G6 1568 //솔
#define NOTE_A6 1760 //라
#define NOTE_B6 1976 //시
#define NOTE_C7 2093 //도
/* USER CODE END 0 */
//중략
/* USER CODE BEGIN 2 */
//TIM3_CH3의 PWM기능을 활성화하겠습니다!
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_ADC_Start(&hadc1); //딱 한번만 호출
uint16_t adc_value0 = 0;
//시작하면 일단 부저는 끈상태라
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
Tone(NOTE_G6);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//측정할때마다 호출
if(HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
{
// 값 처리
adc_value0 = HAL_ADC_GetValue(&hadc1); //0~4095
//측정값이 1000보다 크면 부저에 소리가 안나고
//그게 아니라면 소리가 난다
if(adc_value0 > 1000){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
}else{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
}
}
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
8.로터리엔코더를 돌리면 세탁메뉴가 변경되는데 LED8개중에 1개가 선택되는것으로 시각화한다! 그리고 로터리엔코더의 스위치를 누르면 실행이 되었다는 의미로 세탁 완료음을 부저로 재생하시오! 그리고 로터리엔코더가 회전할때 회전하는 사운드도 추가하시오!
/* USER CODE BEGIN 0 */
static void Tone(uint32_t Frequency)
{
TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
#define NOTE_C6 1047 //도
#define NOTE_D6 1175 //레
#define NOTE_E6 1319 //미
#define NOTE_F6 1397 //파
#define NOTE_G6 1568 //솔
#define NOTE_A6 1760 //라
#define NOTE_B6 1976 //시
#define NOTE_C7 2093 //도
#define NOTE_D7 2349 //레
void play_melody(){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2,1);
Tone(NOTE_G6);
HAL_Delay(500);
Tone(NOTE_C7);
HAL_Delay(500/3);
Tone(NOTE_B6);
HAL_Delay(500/3);
Tone(NOTE_A6);
HAL_Delay(500/3);
Tone(NOTE_G6);
HAL_Delay(500);
Tone(NOTE_E6);
HAL_Delay(500);
//파솔라 레미파 미 솔
Tone(NOTE_F6);
HAL_Delay(500/3);
Tone(NOTE_G6);
HAL_Delay(500/3);
Tone(NOTE_A6);
HAL_Delay(500/3);
Tone(NOTE_D6);
HAL_Delay(500/3);
Tone(NOTE_E6);
HAL_Delay(500/3);
Tone(NOTE_F6);
HAL_Delay(500/3);
Tone(NOTE_E6);
HAL_Delay(500);
Tone(NOTE_G6);
HAL_Delay(500);
//
Tone(NOTE_G6);
HAL_Delay(500);
Tone(NOTE_C7);
HAL_Delay(500/3);
Tone(NOTE_B6);
HAL_Delay(500/3);
Tone(NOTE_A6);
HAL_Delay(500/3);
Tone(NOTE_G6);
HAL_Delay(500);
Tone(NOTE_C7);
HAL_Delay(500);
//도레도 시라시 도~
Tone(NOTE_C7);
HAL_Delay(500/3);
Tone(NOTE_D7);
HAL_Delay(500/3);
Tone(NOTE_C7);
HAL_Delay(500/3);
Tone(NOTE_B6);
HAL_Delay(500/3);
Tone(NOTE_A6);
HAL_Delay(500/3);
Tone(NOTE_B6);
HAL_Delay(500/3);
Tone(NOTE_C7);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2,0);
}
GPIO_TypeDef *mygroup[] = {GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOC,GPIOB};
uint16_t mypin[] = {GPIO_PIN_3,GPIO_PIN_13,GPIO_PIN_5,GPIO_PIN_14,GPIO_PIN_4,GPIO_PIN_15,GPIO_PIN_4,GPIO_PIN_7};
void set_led(int num){
//LED의 상태 업데이트 현재 켜져야하는 LED는 CNT번째꺼
for(int i = 0;i<8;i++){
if(i == num){
HAL_GPIO_WritePin(mygroup[i],mypin[i],GPIO_PIN_SET);
}else{
HAL_GPIO_WritePin(mygroup[i],mypin[i],GPIO_PIN_RESET);
}
}
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2,1);
Tone(NOTE_G6);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2,0);
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
//TIM3_CH3의 PWM기능을 활성화하겠습니다!
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
//시작하면 일단 부저는 끈상태라
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
uint8_t old_A = 0;
int cnt = 0;
//시작하자마자 일단 12시방향의 LED는 1개 켜고 출발한다!
HAL_GPIO_WritePin(mygroup[0],mypin[0],GPIO_PIN_SET);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//PA11의 상승엣지가 발생했을때
uint8_t now_A = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
if(old_A == 0 && now_A == 1){
//PA12의 값을 측정해서 LOW면 시계, HIGH면 반시계
uint8_t now_B = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
if(now_B){
//반시계
cnt--;
if(cnt < 0) cnt = 7;
set_led(cnt);
}else{
//시계
cnt++;
if(cnt > 7) cnt = 0;
set_led(cnt);
}
}
old_A = now_A;
//PC7을 누르면 세탁기 완료 사운드가 재생된다
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7) == 0){
play_melody();
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}

