printf("ho_tari\n");

Mini Project1 - Washing Machine 본문

(Telechips) AI 시스템 반도체 SW 개발자 교육/ATmega128A mini Project

Mini Project1 - Washing Machine

호타리 2025. 3. 16. 17:59

2025.03.04 ~ 2025.03.16

 

ATmega128A를 이용하여 세탁기 (Washing Machine) 구현 프로젝트 진행

 

🔧 프로젝트 개요

  • MCU: ATMega128A
  • 구성 요소: 버튼 4개, FND, LED, 초음파 센서, PWM 기반 DC 모터 (L298N), 부저
  • 주요 기능
    • 세탁기 3단계 작동 시퀀스 (기본/수동 모드) 구현
    • FND로 남은 시간 표시 및 애니메이션 출력
    • 초음파 센서로 장애물 감지
    • PWM 기반 모터 제어 (정회전/역회전)
    • 버튼을 통한 사용자 입력 조작

🚀 주요 동작 흐름

1. 초기화 및 대기 상태

  • 전원 ON 전까지 모든 장치는 대기 모드(system_on = 0)
  • 전원이 켜지면 Idle 상태 진입 (phase = 0)
  • FND에는 현재 선택된 phase (또는 STOP)가 표시됨

2. 버튼 기능

버튼 번호기능
버튼 0 기본 시퀀스 실행 (1→2→3 순차 작동)
버튼 1 수동 모드에서 Phase(1~3) 선택
버튼 2 수동 모드에서 선택된 Phase 실행
버튼 3 전체 시스템 전원 ON/OFF 토글

3. 세탁 동작 시퀀스 (Phase)

✅ Phase 1: 정회전 (20초)

  • 모터: 정회전
  • LED: PA0 ON
  • FND2: 정회전 애니메이션
  • FND1: 남은 시간 카운트

🔁 Phase 2: 정/역 반복 회전 (20초)

  • 모터: 3초 간격으로 방향 전환
  • LED: PA1 ON
  • FND2: 정/역 회전 애니메이션
  • FND1: 남은 시간 카운트

🔄 Phase 3: 역회전 (20초)

  • 모터: 역회전
  • LED: PA2 ON
  • FND2: 역회전 애니메이션
  • FND1: 남은 시간 카운트

⏹️ Idle 또는 종료

  • FND에 STOP 표시
  • 모든 모터, LED OFF

📟 FND 출력 구성

위치역할
FND1 (상단 2자리) 남은 시간 or 선택 Phase
FND2 (하단 2자리) 회전 애니메이션 (big_circle_forward, backward, stop)

🌊 초음파 센서 (ultrasonic.c)

  • TRIG: PG4, ECHO: PE4 (INT4)
  • 외부 인터럽트를 통해 거리 측정
  • 사용 용도:
    • 장애물 감지 가능 (현재는 FND나 buzzer와 직접 연동은 없음, 확장 가능성 有)

🔊 부저 (buzzer.c)

  • 버튼 입력 시 피드백용으로 beep() 함수 사용
  • 상태 변화 시 짧은 경고음 제공

💡 타이머 및 인터럽트 사용

  • Timer0: 1ms 단위로 msec_count, sec_count, msec2_count 증가
  • Timer3: 모터 PWM 제어 (OCR3C 사용)
  • INT4: 초음파 ECHO 신호 측정 (상승/하강 edge 감지)

⚙️ 하드웨어 포트 요약

기능핀
버튼 입력 PORTD.3~6
LED 출력 PORTA.0~2
FND 데이터 PORTC
FND 자릿수 제어 PORTB
모터 제어 PORTF.6, 7
PWM 출력 PORTE.3 (OC3C)
초음파 TRIG PORTG.4
초음파 ECHO PORTE.4 (INT4)

 

<프로젝트 정리 자료>

ATmega128A_WashingMachineProject_박성호.pdf
1.48MB

 

 

 

<main.c>

#define MAX_SPEED_FACTOR 10  // 최대 속도 배율 설정
#define MIN_SPEED_FACTOR 1   // 최소 속도 배율
#define  F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "button.h"

void init(void);

extern void init_led(void);
extern int led_main(void);
extern void init_button(void);
extern int get_button(int button_num, int button_pin);
extern void led_all_on(void);
extern void led_all_off(void);
extern void shift_left_ledon(void);
extern void shift_right_ledon(void);
extern void shift_left_keep_ledon(void);
extern void shift_right_keep_ledon(void);
extern void flower_on(void);
extern void flower_off(void);
extern int fnd_main(void);
extern void init_uart0(void);
extern void UART0_transmit(uint8_t data);
extern void (*current_function)(void);
extern init_ultrasonic();
extern void distance_ultrasonic();
extern void distance_led();
extern void fnd_distance_display();
extern void init_fnd(void);
extern void make_pwm_led_control(void);
extern void pwm_fan_control_main(void);
extern void L298N_pwm_fan_control_main(void);
extern void fnd_display(void);
extern volatile uint8_t rx_buff[80];
extern volatile uint8_t rx_msg_received;

volatile int msec_count = 0;
volatile int sec_count = 0;
volatile int msec2_count = 0;
volatile int sec2_count = 0;
volatile int msec2_speed_factor = 1;  // 기본 속도 1배
volatile uint32_t closed_check_timer = 0;
volatile uint32_t check_timer = 0; // 5000ms에 한번씩


//extern uint32_t sec_count;

FILE OUTPUT = FDEV_SETUP_STREAM(UART0_transmit, NULL, _FDEV_SETUP_WRITE);

int led_toggle=0;
ISR(TIMER0_OVF_vect)
{
	TCNT0=6;
	msec_count++;
	msec2_count += msec2_speed_factor;  // 버튼에 따라 속도 조절
	
	if(msec_count > 1000)
	{
		msec_count = 0;
		sec_count++;
	}
	if(msec2_count > 1000)
	{
		msec2_count = 0;
		sec2_count++;
	}
}


int main(void)
{
	init();
	
	//make_pwm_led_control();
	//pwm_fan_control_main();
	L298N_pwm_fan_fnd_control_main();
	
	while(1)
	{
	}
}


void init_timer0(void)
{
	TCNT0 = 6;
	TCCR0 |= 1 << CS02 | 0 << CS01 | 0 << CS00;
	TIMSK |= 1 << TOIE0;
}

void init(void)
{
	init_timer0();
	init_uart0();
	init_ultrasonic();
	
	stdout = &OUTPUT;
	sei();
	
	init_led();
	init_fnd();
}

 

<pwm.c>


#include "pwm.h"
#include "buzzer.h"

#define MAX_SPEED_FACTOR 10 
#define MIN_SPEED_FACTOR 1   
#define PRESET_TIME 8     
#define SUBPHASE_TIME 2    

extern void init_button(void);
extern int get_button(int button_num, int button_pin);
extern void fnd_display(void);  
extern void fnd_big_circle_forward(void);
extern void fnd_big_circle_backward(void);
extern void fnd_stop_display(void);      

void init_timer3(void);
void L298N_pwm_fan_fnd_control_main(void);
void init_L298N(void);

extern volatile int msec_count;
extern volatile int sec_count;
extern volatile int msec2_count;
extern volatile int sec2_count;
extern volatile int msec2_speed_factor;
extern volatile int display_time;

volatile int temp = 0;
uint8_t phase = 0;
uint8_t selected_phase = 0;
uint8_t manual_mode = 0;
uint8_t system_on = 0;

void init_timer3(void)
{
	DDRE |= (1 << 3) | (1 << 5);      
	TCCR3A |= 1 << WGM30;
	TCCR3B |= 1 << WGM32;
	TCCR3A |= 1 << COM3C1;
	TCCR3B |= (1 << CS31) | (1 << CS30);  
	OCR3C = 0;  
}

void init_L298N(void)
{
	DDRF |= (1 << 6) | (1 << 7);
	PORTF &= ~((1 << 6) | (1 << 7));
	PORTF |= (1 << 6);  
}
void L298N_pwm_fan_fnd_control_main(void)
{
	uint8_t forward = 1; 
	unsigned int phase_start_sec = 0;

	init_button();
	init_timer3();
	init_L298N();
	init_buzzer();


	DDRA |= (1 << 0) | (1 << 1) | (1 << 2);

	while(1)
	{
		
		if(get_button(BUTTON3, BUTTON3PIN)) {
			beep(100);
			system_on = !system_on;
			_delay_ms(200); 
		}
		
		
		if(system_on == 0) {
			OCR3C = 0;             
			fnd_stop_display();     
			PORTA &= ~((1 << 0) | (1 << 1) | (1 << 2)); 
			phase = 0;             
			selected_phase = 0;
			continue;
		}
		
		if(phase == 0) {

			OCR3C = 0;
			display_time = selected_phase;
			fnd_stop_display();
			PORTA &= ~((1 << 0) | (1 << 1) | (1 << 2));

	
			if(get_button(BUTTON0, BUTTON0PIN)) {
				beep(100);
				phase = 1;
				manual_mode = 0;  
				phase_start_sec = sec2_count;
				display_time = PRESET_TIME;
				forward = 1; 
				PORTF &= ~((1 << 6) | (1 << 7));
				PORTF |= (1 << 6);
				OCR3C = 250;
				PORTA |= (1 << 0);  
				_delay_us(100);   
			}

			if(get_button(BUTTON1, BUTTON1PIN)) {
				beep(100);
				selected_phase++;
				if(selected_phase > 3) selected_phase = 1;
				_delay_us(100);  
			}

			if(get_button(BUTTON2, BUTTON2PIN)) {
				beep(100);
				if(selected_phase != 0) {
					phase = selected_phase;
					manual_mode = 1; 
					phase_start_sec = sec2_count;
					display_time = PRESET_TIME;
					if(phase == 1) {
						forward = 1;
						PORTF &= ~((1 << 6) | (1 << 7));
						PORTF |= (1 << 6);
						OCR3C = 250;
						PORTA |= (1 << 0);
						} else if(phase == 2) {
						forward = 1;
						PORTF &= ~((1 << 6) | (1 << 7));
						PORTF |= (1 << 6);
						OCR3C = 250;
						PORTA |= (1 << 1); 
						} else if(phase == 3) {
						forward = 0;
						PORTF &= ~((1 << 6) | (1 << 7));
						PORTF |= (1 << 7);
						OCR3C = 250;
						PORTA |= (1 << 2); 
					}
					_delay_us(100); 
				}
			}
		}
		else if(phase == 1) {
			int elapsed = sec2_count - phase_start_sec;
			int remaining = PRESET_TIME - elapsed;
			if(remaining > 0) {
				display_time = remaining;
				} else {
				display_time = 0;
				fnd_stop_display();
				OCR3C = 0;
				PORTA &= ~(1 << 0);
				beep(100);
				_delay_us(100);

				if(manual_mode == 0) {
					phase = 2;
					phase_start_sec = sec2_count;
					display_time = PRESET_TIME;
					forward = 1;
					PORTF &= ~((1 << 6) | (1 << 7));
					PORTF |= (1 << 6);
					OCR3C = 250;
					PORTA |= (1 << 1); 
					} else {
					phase = 0;
					selected_phase = 0;
				}
			}
			fnd_big_circle_forward();
		}
		else if(phase == 2) {  
			int elapsed = sec2_count - phase_start_sec;
			int remaining = PRESET_TIME - elapsed;
			if(remaining > 0) {
				display_time = remaining;
				int subphase = (elapsed / SUBPHASE_TIME) % 2;
				if(subphase == 0) {
					if(forward != 1) {
						forward = 1;
						PORTF &= ~((1 << 6) | (1 << 7));
						PORTF |= (1 << 6);
					}
					} else {
					if(forward != 0) {
						forward = 0;
						PORTF &= ~((1 << 6) | (1 << 7));
						PORTF |= (1 << 7);
					}
				}
				OCR3C = 250;
				if(forward)
				fnd_big_circle_forward();
				else
				fnd_big_circle_backward();
				} else {
				display_time = 0;
				fnd_stop_display();
				OCR3C = 0;
				PORTA &= ~(1 << 1);
				beep(100);
				_delay_us(100);
				if(manual_mode == 0) { 
					phase = 3;
					phase_start_sec = sec2_count;
					display_time = PRESET_TIME;
					forward = 0;
					PORTF &= ~((1 << 6) | (1 << 7));
					PORTF |= (1 << 7);
					OCR3C = 250;
					PORTA |= (1 << 2);
					} else {
					phase = 0;
					selected_phase = 0;
				}
			}
		}
		else if(phase == 3) { 
			int elapsed = sec2_count - phase_start_sec;
			int remaining = PRESET_TIME - elapsed;
			if(remaining > 0) {
				display_time = remaining;
				} else {
				display_time = 0;
				fnd_stop_display();
				OCR3C = 0;
				PORTA &= ~(1 << 2);
				beep(100);
				_delay_us(100);
				phase = 0;
				selected_phase = 0;
			}
			fnd_big_circle_backward();
		}
		
		fnd_display();
		_delay_us(500);
	}
}

 

<fnd.c>

#include "fnd.h"

void init_fnd(void);
int fnd_main(void);
void fnd_display(void);
void fnd_big_circle_forward(void);
void fnd_big_circle_backward(void);
void fnd_stop_display(void);

extern volatile int msec_count;
extern volatile int sec_count;
extern volatile int msec2_count;
extern volatile int sec2_count;
volatile int temp;

volatile int display_time = 0;

int fnd_main(void)
{
	while (1)
	{
		fnd_display();
		_delay_ms(1);
		fnd_big_circle_backward();
		_delay_ms(1);
	}
	
	return 0;
}

void init_fnd(void)
{
	FND_DATA_DDR = 0xff;
	FND_DIGIT_DDR |= 1 << FND_DIGIT_D1 | 1 << FND_DIGIT_D2 | 1 << FND_DIGIT_D3 | 1 << FND_DIGIT_D4;
	FND_DIGIT_DDR |= 1 << New_FND_DIGIT_D1 | 1 << New_FND_DIGIT_D2 | 1 << New_FND_DIGIT_D3 | 1 << New_FND_DIGIT_D4;
	FND_DIGIT_PORT = ~0x00;
}

void fnd_display(void)
{
	uint8_t fnd_font[] = {0xc0, 0xf9, 0xA4, 0xb0, 0x99, 0x92, 0x82, 0xd8, 0x80, 0x90, 0x7f};

	static int digit_select = 0;
	
	int value = display_time;
	
	switch(digit_select)
	{
		case 0:
		FND_DIGIT_PORT = 0x80;
		FND_DATA_PORT = fnd_font[value % 10];
		break;
		case 1:
		FND_DIGIT_PORT = 0x40;
		FND_DATA_PORT = fnd_font[(value / 10) % 10];
		break;
		case 2:
		FND_DIGIT_PORT = 0x20;
		FND_DATA_PORT = fnd_font[0];
		break;
		case 3:
		FND_DIGIT_PORT = 0x10;
		FND_DATA_PORT = fnd_font[0];
		break;
	}
	digit_select++;
	digit_select %= 4;
}


void fnd_big_circle_forward(void)
{
	uint8_t fnd_font0[] = {0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf4, 0xf0};
	uint8_t fnd_font1[] = {0xff, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6};
	uint8_t fnd_font2[] = {0xff, 0xff, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6};
	uint8_t fnd_font3[] = {0xff, 0xff, 0xff, 0xf7, 0xe7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6};

	static int digit_select = 0;
	
	int frame = (msec2_count / 50) % 12;
	
	switch(digit_select)
	{
		case 0:
		FND_DIGIT_PORT = 0x08;
		FND_DATA_PORT = fnd_font0[frame];
		break;
		case 1:
		FND_DIGIT_PORT = 0x04;
		FND_DATA_PORT = fnd_font1[frame];
		break;
		case 2:
		FND_DIGIT_PORT = 0x02;
		FND_DATA_PORT = fnd_font2[frame];
		break;
		case 3:
		FND_DIGIT_PORT = 0x01;
		FND_DATA_PORT = fnd_font3[frame];
		break;
	}
	digit_select++;
	digit_select %= 4;
}

void fnd_big_circle_backward(void)
{
	uint8_t fnd_font0[] = {0xfb, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf0};
	uint8_t fnd_font1[] = {0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xf6, 0xf6};
	uint8_t fnd_font2[] = {0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xf6, 0xf6, 0xf6};
	uint8_t fnd_font3[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6};

	static int digit_select = 0;

	int frame = (msec2_count / 50) % 12;
	
	switch(digit_select)
	{
		case 0:
		FND_DIGIT_PORT = 0x08;
		FND_DATA_PORT = fnd_font0[frame];
		break;
		case 1:
		FND_DIGIT_PORT = 0x04;
		FND_DATA_PORT = fnd_font1[frame];
		break;
		case 2:
		FND_DIGIT_PORT = 0x02;
		FND_DATA_PORT = fnd_font2[frame];
		break;
		case 3:
		FND_DIGIT_PORT = 0x01;
		FND_DATA_PORT = fnd_font3[frame];
		break;
	}
	digit_select++;
	digit_select %= 4;S
}

void fnd_stop_display(void)
{
	uint8_t fnd_font0 = 0x8c;
	uint8_t fnd_font1 = 0xc0;
	uint8_t fnd_font2 = 0x87;
	uint8_t fnd_font3 = 0x92;

	static int digit_select = 0;
	
	switch(digit_select)
	{
		case 0:
		FND_DIGIT_PORT = 0x08;
		FND_DATA_PORT = fnd_font0;
		break;
		case 1:
		FND_DIGIT_PORT = 0x04;
		FND_DATA_PORT = fnd_font1;
		break;
		case 2:
		FND_DIGIT_PORT = 0x02;
		FND_DATA_PORT = fnd_font2;
		break;
		case 3:
		FND_DIGIT_PORT = 0x01;
		FND_DATA_PORT = fnd_font3;
		break;
	}
	digit_select++;
	digit_select %= 4;
	
	msec2_count = temp;
}

 

<buzzer.h>

/*
 * buzzer.h
 *
 * Created: 2025-03-15 오후 1:22:37
 *  Author: microsoft
 */ 


#ifndef BUZZER_H_
#define BUZZER_H_

#include <util/delay.h>

#define BUZZER_PIN 3
#define BUZZER_PORT PORTE
#define BUZZER_DDR  DDRE

// 부저를 초기화합니다.
void init_buzzer(void) {
	BUZZER_DDR |= (1 << BUZZER_PIN);       // 부저 핀 출력으로 설정
	BUZZER_PORT &= ~(1 << BUZZER_PIN);     // 부저 OFF (low)
}

// duration_ms 동안 부저로 beep음을 발생시킵니다.
// 예: 2kHz 사각파 (약 250us high, 250us low)
void beep(uint16_t duration_ms) {
	// 500us period = 2kHz, 따라서 duration_ms*1000/500번 반복
	uint16_t cycles = (duration_ms * 1000UL) / 500;
	for(uint16_t i = 0; i < cycles; i++){
		BUZZER_PORT |= (1 << BUZZER_PIN);
		_delay_us(250);
		BUZZER_PORT &= ~(1 << BUZZER_PIN);
		_delay_us(250);
	}
}


#endif /* BUZZER_H_ */

 

<speaker.c>

#define F_CPU 16000000L
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define DO_01   1911
#define DO_01_H 1817
#define RE_01   1703
#define RE_01_H 1607
#define MI_01   1517
#define FA_01   1432
#define FA_01_H 1352
#define SO_01   1276
#define SO_01_H 1199
#define LA_01   1136
#define LA_01_H 1073
#define TI_01   1012
#define DO_02   956
#define DO_02_H 909
#define RE_02   851
#define RE_02_H 804
#define MI_02   758
#define FA_02   716
#define FA_02_H 676
#define SO_02   638
#define SO_02_H 602
#define LA_02   568
#define LA_02_H 536
#define TI_02   506
#define DO_03   478
#define DO_03_H 450
#define RE_03	425
#define RE_03_H 401
#define MI_03	378

#define F_CLK       16000000L //클럭
#define F_SCALER	8 //프리스케일러
#define BEAT_1_32	42
#define BEAT_1_16	86
#define BEAT_1_8	170
#define BEAT_1_4	341
#define BEAT_1_2	682
#define BEAT_1		1364
					   
int laundry[] = {DO_02_H, FA_02_H, FA_02, RE_02_H, DO_02_H, LA_01_H, 
					TI_01, DO_02_H, RE_02_H, SO_01, LA_01_H, TI_01, LA_01_H, DO_02_H,
					DO_02_H, FA_02_H, FA_02, RE_02_H, DO_02_H, FA_02_H,
					FA_02_H, SO_02_H, FA_01_H, FA_02, RE_02_H, FA_02, FA_02_H, '\0'};
					
const int laundry_beats[] = {BEAT_1_4, BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_4, BEAT_1_4,
								BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_4, BEAT_1_4,
								BEAT_1_4, BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_4, BEAT_1_4,
								BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_8, BEAT_1_2};

void Music_Player(int *tone, int *Beats)
{
	while(*tone != '/0')
	{
		OCR3A = *tone;
		delay_ms(*Beats);
		tone++;
		Beats++;
		OCR3A = 0;
		_delay_ms(10);		
	}
	return;
}  
// Timer3 위상교정 PWM
void init_speaker(void)
{
	DDRE |= 0x08;   // PWM CHANNEL  OC3A(PE3) 출력 모드로 설정 한다. 
	TCCR3A = (1<<COM3A0); // COM3A0 : 비교일치시 PE3 출력 반전 (P328 표14-6 참고)
	TCCR3B = (1<<WGM32) | (1<<CS31);   // WGM32 : CTC 4(P327 표14-5) CS31: 8분주(P318)
	// CTC mode : 비교일치가 되면 카운터는 reset되면서 PWM 파형 출력 핀의 출력이 반전 됨. 
	// 정상모드와 CTC모드의 차이점은 비교일치 발생시 TCNT1의 레지스터값을 0으로 설정 하는지 여부 이다. 
	// 정상모드를 사용시 TCNT1이 자동으로 0으로 설정 되지 않아 인터럽트 루틴에서 TCNT1을 0으로 설정 해 주었다. 
	// 위상교정 PWM mode4  CTC 분주비 8  16000000/8 ==> 2,000,000HZ(2000KHZ) : 
	// up-dounting: 비교일치시 LOW, down-counting시 HIGH출력
	// 1/2000000 ==> 0.0000005sec (0.5us)
	// P599 TOP 값 계산 참고
	// PWM주파수 = OSC(16M) / ( 2(up.down)x N(분주율)x(1+TOP) ) 
	// TOP =  (fOSC(16M) /  2(up.down)x N(분주율)x 원하는주파수 )) -1 
	//-----------------------------------------------------------
	// - BOTTOM :  카운터가 0x00/0x0000 일때를 가리킨다.
    // - MAX : 카운터가 0xFF/0xFFFF 일 때를 가리킨다.
    // - TOP?:  카운터가 가질 수 있는 최대값을 가리킨다. 오버플로우 인터럽트의 경우 TOP은 0xFF/0xFFFF
    //          이지만 비교일치 인터럽트의 경우 사용자가 설정한 값이 된다. 
    
	TCCR3C = 0;  // P328 그림 14-11 참고 
	OCR3A = 0;   // 비교 일치값을 OCR3A에 넣는다. 
	
	return;
}

void delay_ms(int ms)
{
	while(ms-- != 0)_delay_ms(1);
}

 

<실행 결과>

https://youtube.com/shorts/i7M0yNZkVg0

 

washing_machine.zip
0.19MB