printf("ho_tari\n");

8일차 본문

2025.03.13

 

오늘의 학습 목표

1. stack / interrupt review

2. 과제 review

3. PWM제어

4. Data sheet coding

 

STACK (LIFO : Last In First Out)

 

- 데이터를 차곡차곡 쌓아 올린 형태의 자료구조

- 한쪽 끝에서만 자료를 넣고 뺄 수 있는 LIFO(Last In First Out) 형 가장 마지막에 삽입된 자료가 가장 먼저 output 되는 구조를 가진다.

- Stack은 정해진 방향으로 만 쌓을 수 있다.

- Top으로 정해진 곳을 통해서만 접근 가능 새로 삽입된 data는 top이 가리키는 맨 위에 놓이게 됨 자료를 꺼낼떄도 top을 통해서 가능 하다.

- 삽입 : push, 출력: pop

- top : 가장 최근에 보관한 자료의 위치.

          N개 보관했을 때 가장 최근에 보관한 자료는 인덱스 n-1에 있다. 따라서 초기 top은 -1로 설정

int main()
 {
    printf("Main - Start\n");
    func1();
    printf("Main - End\n");
    return 0;
 }
 void func1()
 {
    printf("func1 - Start\n");
    func2();
    printf("func1 - End\n");
 }
 void func2()
 {
    printf("func2 - Start\n");
    printf("func2 - End\n");
 }

#include<stdio.h>
 #define MAX_SIZE 200
 
int stack[MAX_SIZE];
 int top=-1;
 
int is_full()
 {
    if (top >= MAX_SIZE-1)
        return 1;
    else
        return 0;
 }
 
int is_empty()
 {
    if ( top < 0)
        return 1;
    else
    {
        return 0;
    }
 }
 
  void push(int value)
 {
    if (is_full()==1)
        printf("stack overflow !!!\n");
    else
        stack[++top]=value; 
}
 
int pop()
 {
    if (is_empty()==1)
        printf("stack empty !!!\n");
    else 
        return stack[top--];
 }
 
 main()
 {
    
    push(1);
    push(3);
    push(5);
    push(7);
    push(9);
   
    printf("%d:\n",pop());
    printf("%d:\n",pop());
    printf("%d:\n",pop());
    printf("%d:\n",pop());
    printf("%d:\n",pop());
 
    return 0;
 }

 

인터럽트

 

- MCU가 특정 작업을 즉시 처리하도록 요구하는 비정상적인 사건

- 하드웨어에 의해 호출되는 “함수”다.

- 인터럽트가 발생하면 현재 실행 중인 코드를 정지하고

   인터럽트 처리 루틴(ISR)으로 즉시 이동하여 인터럽트를 먼저 처리

- 동시에 여러 개의 인터럽트가 발생하면 우선 순위가 높은 인터럽트를 우선 처리

 

ATmega128A의 인터럽트

 

- 35개의 인터럽트 사용 가능

- 인터럽트 벡터 : 인터럽트가 발생하였을 때 처리가 옮겨질 해당 ISR의 메모리 주소

- 인터럽트 벡터 테이블 : 35개 인터럽트에 대한 인터럽트 벡터를 모아놓은 테이블

- 플래시 메모리의 0x0000 번지에서 0x0045 번지까지 기록되어 있음

 

인터럽트 처리 순서

 

1. 인터럽트 발생

2. 인터럽트를 실행하고 돌아올 주소를 stack에 저장

3. 인터럽트 벡터 테이블의 해당 인터럽트 벡터가 저장된 인터럽트 벡터 테이블 위치로 이동

4. 인터럽트 벡터 값에 해당하는 ISR(Interrupt Service Routine: 인터럽트처리 함수)로 이동

5. ISR 처리

6. 인터럽트 처리 이전에 수행하던 전의 코드로 되돌아감

 

 

Timer : H//W가 S/W에게 알려주는 것

Counter : 펄스의 개수를 세는 것

 

PWM 제어

 

아날로그 - 디지털 변환

 

듀티 사이클 : HIGH 값의 비율

 

HIGH 값의 비율로 표현

- 개별 비트가 아닌 한 주기에 반응

- 주기 내의 HIGH 값 비율 (duty cycle)에 의해 동작이 제어

- LED 밝기 제어, 모터 속도 제어 등에 흔히 사용

- 아날로그 신호와 유사한 효과를 얻는 방법

 

LED 밝기 제어

- 50% 듀티 사이클에서 LED는 최대 밝기의 50%로 인식

- 잔상 효과로 가능함

 

HIGH와 LOW의 주기를 조절함으로써 PWM 신호 생성 가능

 

PWM 신호 생성을 위해 대부분의 CPU 클록 소모

 

PWM 신호 생성

- ATmega128에서 하드웨어적으로 PWM 신호 생성 지원

- 타이머/카운터에서 비교 일치 발생 시 파형 생성 기능을 통해 사용 가능

- 파형 출력 핀(OCnx)으로 PWM 신호 출력

 

PWM 모드의 종류

- 고속  PWM 모드 : 단일 경사 모드

- 위상 교정 PWM 모드 : 이중 경사 모드

- 위상 및 주파수 교정 PWM 모드 : 16비트 타이머/카운터에서만 가능

 

고속 PWM 모드

- BOTTON에서 TOP까지 카운트를 진행하는 상향 카운트만 존재 : 단일 경사 모드

- 카운트 값이 BOTTOM일 때 파형 출력 핀으로 HIGH 출력 (비반전)

- 비교 일치가 발생하면 파형 출력 핀으로 LOW 출력 (비반전)

- 비교 일치 값 조정에 의해 듀티 사이클 조정

 

TCCR 레지스터 : 파형 생성 방법 설정

- WGM 비트에 의해 고속 및 위상 교정 PWM 모드 선택

    - PWM 관련 모드 중 하나 선택

- COM 비트에 의해 반전 및 비반전 모드 선택

    - 비반전 모드 : 비교 일치 시 LOW, BOTTOM에서 HIGH 출력

    - 반전 모드 : 비교 일치 시 HIGH, BOTTOM에서 LOW 출력

 

Timer INT 종류

- Overflow Interrupt : TCNT0 (H/W 8bit counter register)

- 비교 일치 Interrupt : 사용자가 지정한 펄스 개수만큼 만나면 INT를 띄운다. (H/W가 S/W한테 알려줌)

 

PWM LED control

 

<led.c>

/*
 * led.c
 *
 * Created: 2025-03-05 오전 10:21:53
 *  Author: microsoft
 */ 

#include "led.h"
#include "button.h"

int led_main(void);   // 선언
void led_all_on(void);
void led_all_off(void);
void shift_left_ledon(void);
void shift_right_ledon(void);
void shift_left_keep_ledon(void);
void shift_right_keep_ledon(void);
void flower_on(void);
void flower_off(void);
void state_transition(void);
void make_pwm_led_control(void);
void led_on_pwm(int dim);

extern void init_button(void);
extern int get_button(int button_num, int button_pin);

extern volatile int msec_count;
#define FUNC_NUMBER 8

int state = 0;
int button0_state = 0;
int button1_state = 0;

void (*fp[]) (void)= 
{
	led_all_on,
	led_all_off,
	shift_left_ledon,
	shift_right_ledon,
	shift_left_keep_ledon,  // _delay_ms(200)
	shift_right_keep_ledon,
	flower_on,
	flower_off
};

int led_main(void)   // 정의 
{
	DDRA = 0xff;   // PORTA에 연결된 pin8개를 output mode로 
	init_button();

#if 1
	while(1)
	{
		if (get_button(BUTTON0, BUTTON0PIN))
		{
			button0_state = !button0_state;
		}
		if (get_button(BUTTON1, BUTTON1PIN))
		{
			button1_state++;
			button1_state %= FUNC_NUMBER;
		}
		
		fp[state]();
		
	}
#else
	while(1) 
	{
		shift_left_ledon();
		shift_right_ledon();
		shift_left_keep_ledon();  // _delay_ms(200)
		shift_right_keep_ledon();
		flower_on(); 
		flower_off();
	}	
#endif
}

void shift_left_ledon(void)
{
#if 1
	// (1) for문 제거 (2) _delay_ms(30) 제거
	static int i = 0;
	
	if(msec_count >= 100)
	{
		msec_count = 0;
		if(i >= 8)
		{
			i = 0;
			PORTA = 0;
			state_transition();
			
		}
		else
		{
			PORTA = 0b00000001 << i++; // (1) PORTA = 0b00000001 << i (2) i++
		}
	}
#else // org
	for (int i=0; i < 8; i++)
	{
		PORTA = 0b00000001 << i;
		//               1 i=0;
	    //        00000010 i=1;
	    //        10000000 i=7;
		_delay_ms(30);
	}	
#endif
}

void shift_right_ledon(void)
{
#if 1
	static int i = 0;
	
	if(msec_count >= 100)
	{
		msec_count = 0;
		if(i >= 8)
		{
			i = 0;
			PORTA = 0;
			state_transition();
		}
		else
		{
			PORTA = 0b10000000 >> i++;
		}
	}
#else
	PORTA=0;
	for (int i=0; i < 8; i++)
	{
		PORTA = 0b10000000 >> i;
		//        10000000 i=0;
		//        01000000 i=1;
		_delay_ms(30);
	}
#endif
}

void shift_left_keep_ledon(void)
{
#if 1
	static int i = 0;
	
	if(msec_count >= 100)
	{
		msec_count = 0;
		if(i >= 8)
		{
			i = 0;
			PORTA = 0;
			state_transition();
		}
		else
		{
			PORTA |= 0b00000001 << i;
			i++;
		}
	}
#else
	for (int i=0; i < 8; i++)
	{
		PORTA |= 0b00000001 << i;
		//               1 i=0;
		//        00000010 i=1;
		//        10000000 i=7;
		_delay_ms(30);
	}
#endif
}

void shift_right_keep_ledon(void)
{
#if 1
	static int i = 0;
	
	if(msec_count >= 100)
	{
		msec_count = 0;
		if(i >= 8)
		{
			i = 0;
			PORTA = 0;
			state_transition();
		}
		else
		{
			PORTA |= 0b10000000 >> i;
			i++;
		}
	}
#else
	PORTA=0;
	for (int i=0; i < 8; i++)
	{
		PORTA |= 0b10000000 >> i;
		//        10000000 i=0;
		//        01000000 i=1;
		_delay_ms(30);
	}
#endif
}

void flower_on(void)
{
#if 1
	// (1) for문 제거 (2) _delay_ms(30) 제거
	static int i = 0;

	if(msec_count >= 100)
	{
		msec_count = 0;
		if(i >= 4)
		{
			i = 0;
			PORTA = 0;
			state_transition();
		}
		else
		{
			PORTA |= 0x10 << i | 0x08 >> i;
			i++;
		}
	}
#else
	PORTA=0;
	for (int i=0; i < 4; i++)
	{
		PORTA |= 0x10 << i | 0x08 >> i;
		_delay_ms(30);
	}
#endif
}

void flower_off(void)
{
#if 1
	unsigned char h=0xf0;
	unsigned char l=0x0f;
	static int i = 0;

	if(msec_count >= 100)
	{
		msec_count = 0;
		if(i >= 4)
		{
			i = 0;
			PORTA = 0;
			state_transition();
		}
		else
		{
			PORTA = (h >> i) & 0xf0 | (l << i) & 0x0f;
			i++;
		}
	}
#else
	unsigned char h=0xf0;
	unsigned char l=0x0f;
	
	PORTA=0;
	for (int i=0; i < 4; i++)
	{
		PORTA = (h >> i) & 0xf0 | (l << i) & 0x0f;
		_delay_ms(30);
	}
	PORTA=0;
	_delay_ms(30);
#endif
}

void led_all_on(void)
{
	PORTA = 0xff;
}

void led_all_off(void)
{
	PORTA = 0x00;
}

void state_transition(void)
{
	if(!button0_state)
	{
		state++;
		state = state % FUNC_NUMBER;
	}
	else
	{
		state = button1_state;
	}
}

void make_pwm_led_control(void)
{
	int dim = 0; // LED 밝기 조정 변수
	int direction = 1; // 1 : 밝기 증가 모드, -1 : 밝기 감소 모드
	
	DDRA = 0xff;
	PORTA = 0xff; // led all on
	// dark --> bright, bright --> dark
	
	while(1)
	{
		led_on_pwm(dim);
		dim += direction;
		if(dim == 255) direction = -1;
		if(dim == 0) direction = 1;
	}
}

void led_on_pwm(int dim)
{
	PORTA = 0xff;
	
	for(int i = 0; i < 256; i++)
	{
		if(i > dim)
		{
			PORTA = 0; // duty cycle이 max를 넘어가면 led all off
		}
		_delay_us(20);
	}
}

 

<main.c>

/*
 * 01.LED_CONTROL.c
 *
 * Created: 2025-03-04 오후 4:25:34
 * Author : microsoft
 */ 

#define  F_CPU 16000000UL  // 16MHZ
#include <avr/io.h>
#include <util/delay.h>  // _delay_ms _delay_us
#include <avr/interrupt.h> // sei()
#include <stdio.h> // printf, scanf, fgets, puts, gets 등이 들어있다.

#include "button.h"
#include "def.h"
#include "ultrasonic.h"

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 init_ultrasonic(void);
extern void distance_ultrasonic(void);
extern void make_pwm_led_control(void);
extern void led_on_pwm(int dim);

extern volatile uint8_t rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
extern volatile uint8_t rx_msg_received;

volatile int msec_count = 0;
volatile int ultrasonic_check_timer = 0;
// for printf 
FILE OUTPUT = FDEV_SETUP_STREAM(UART0_transmit, NULL, _FDEV_SETUP_WRITE);
// volatile 변수 type 앞에 volatile을 선언하는 이유 : compiler한테 최적화 방지 지시
// cc -O
/*
예) 1 ~ 10까지 합을 구하는 프로그램
int i = 0; int sum = 0;
while( i < 10)
{
	i++; sum += i;	
}
printf("sum-->%d\n", sum); // 1 ~ 10 : 55가 된다.
*/

// TIMER0_OVF_vect
// ISR(Interrupt Service Routine)(함수)
// --> H/W가 S/W한테 event(상황변화)가 일어났다고 알려 주는 공간
// 250개의 pulse를 count(1ms)하면 이곳으로 자동적으로 들어온다.
// ISR루틴(함수)는 가능한 짧게 작성한다.
int led_toggle = 0;
ISR(TIMER0_OVF_vect)
{
	// 6 ~ 256 : 250(1ms) 그래서 TCNT0를 6으로 설정하는 것이다.
	TCNT0 = 6;
	msec_count++; // 1ms마다 1씩 증가
	ultrasonic_check_timer++;
}

int main(void)
{
	// DDRA = 0b11111111;   // PORTA를 출력 모드(1)로 설정
	
	init_timer0();
	init_uart0();
	init_ultrasonic();
	stdout = &OUTPUT; // printf가 동작될 수 있도록 stdout에 OUTPUT 파일 포인터 assign
	sei(); // 전역(대문)으로 interrupt 허용
	
	// led_main();
	make_pwm_led_control();
	
	// printf("init_uart0\n");
	
	while(1)
	{
		distance_ultrasonic();
		//pc_command_processing();
	}
}

// timer0를 초기화시키는 부분
// AVR에서 8bit timer 0/2번 중 0번을 초기화
// 임베디드/FPGA 등에서 제일 중요한 것은 초기화를 정확히 해주는 것이다.
// 그래서 이 부분을 특별히 신경을 써서 작성한다.
void init_timer0(void)
{
	// 16MHz/64 분주(down) : divider / prescale
	// ---------- 분주비 계산 ----------
	// (1) 16000000Hz/64 ==> 250,000Hz
	// (2) T(주기) 1clock의 소요 시간 : 1/f = 1/250,000 ==> 0.000004sec(4us) : 0.004ms
	// (3) 8bit timer OV(OVflow) : 0.004ms x 256 = 0.001024sec --> 1.024ms
	// 1ms마다 정확하게 INT를 띄우고 싶으면 0.004ms x 250개를 count = 0.001sec ==> 1ms
	TCNT0 = 6; // TCNT : 0 ~ 256 1ms마다 TIMER0_OVF_vect로 진입한다.
			   // TCNT0 = 6으로 설정한 이유 : 6 ~ 256 : 250개의 펄스를 count하기 때문에 정확히 1ms가 된다.
	// (4) 분주비 설정 64분주 (250,000Hz --> 250KHz)
	TCCR0 |= 1 << CS02 | 0 << CS01 | 0 << CS00; // TCCR0 |= 0xf4; 보다는 좌측 코드 권장
	// (5) Timer0 overflow INT를 허용(enable)
	TIMSK |= 1 << TOIE0; // TIMSK |= 0x01;
}

 

<실행 결과>

https://youtube.com/shorts/n-vE1Lm6gMU

https://youtu.be/KUm9bew963U

 

PWM 모터제어 : timer3 활용

<main.c>

/*
 * 01.LED_CONTROL.c
 *
 * Created: 2025-03-04 오후 4:25:34
 * Author : microsoft
 */ 

#define  F_CPU 16000000UL  // 16MHZ
#include <avr/io.h>
#include <util/delay.h>  // _delay_ms _delay_us
#include <avr/interrupt.h> // sei()
#include <stdio.h> // printf, scanf, fgets, puts, gets 등이 들어있다.

#include "button.h"
#include "def.h"
#include "ultrasonic.h"

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 init_ultrasonic(void);
extern void distance_ultrasonic(void);
extern void make_pwm_led_control(void);
extern void led_on_pwm(int dim);
extern void pwm_fan_control_main(void);

extern volatile uint8_t rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
extern volatile uint8_t rx_msg_received;

volatile int msec_count = 0;
volatile int ultrasonic_check_timer = 0;
// for printf 
FILE OUTPUT = FDEV_SETUP_STREAM(UART0_transmit, NULL, _FDEV_SETUP_WRITE);
// volatile 변수 type 앞에 volatile을 선언하는 이유 : compiler한테 최적화 방지 지시
// cc -O
/*
예) 1 ~ 10까지 합을 구하는 프로그램
int i = 0; int sum = 0;
while( i < 10)
{
	i++; sum += i;	
}
printf("sum-->%d\n", sum); // 1 ~ 10 : 55가 된다.
*/

// TIMER0_OVF_vect
// ISR(Interrupt Service Routine)(함수)
// --> H/W가 S/W한테 event(상황변화)가 일어났다고 알려 주는 공간
// 250개의 pulse를 count(1ms)하면 이곳으로 자동적으로 들어온다.
// ISR루틴(함수)는 가능한 짧게 작성한다.
int led_toggle = 0;
ISR(TIMER0_OVF_vect)
{
	// 6 ~ 256 : 250(1ms) 그래서 TCNT0를 6으로 설정하는 것이다.
	TCNT0 = 6;
	msec_count++; // 1ms마다 1씩 증가
	ultrasonic_check_timer++;
}

int main(void)
{
	// DDRA = 0b11111111;   // PORTA를 출력 모드(1)로 설정
	
	init_timer0();
	init_uart0();
	init_ultrasonic();
	stdout = &OUTPUT; // printf가 동작될 수 있도록 stdout에 OUTPUT 파일 포인터 assign
	sei(); // 전역(대문)으로 interrupt 허용
	
	// led_main();
	// make_pwm_led_control();
	pwm_fan_control_main();
	
	// printf("init_uart0\n");
	
	while(1)
	{
		distance_ultrasonic();
		//pc_command_processing();
	}
}

// timer0를 초기화시키는 부분
// AVR에서 8bit timer 0/2번 중 0번을 초기화
// 임베디드/FPGA 등에서 제일 중요한 것은 초기화를 정확히 해주는 것이다.
// 그래서 이 부분을 특별히 신경을 써서 작성한다.
void init_timer0(void)
{
	// 16MHz/64 분주(down) : divider / prescale
	// ---------- 분주비 계산 ----------
	// (1) 16000000Hz/64 ==> 250,000Hz
	// (2) T(주기) 1clock의 소요 시간 : 1/f = 1/250,000 ==> 0.000004sec(4us) : 0.004ms
	// (3) 8bit timer OV(OVflow) : 0.004ms x 256 = 0.001024sec --> 1.024ms
	// 1ms마다 정확하게 INT를 띄우고 싶으면 0.004ms x 250개를 count = 0.001sec ==> 1ms
	TCNT0 = 6; // TCNT : 0 ~ 256 1ms마다 TIMER0_OVF_vect로 진입한다.
			   // TCNT0 = 6으로 설정한 이유 : 6 ~ 256 : 250개의 펄스를 count하기 때문에 정확히 1ms가 된다.
	// (4) 분주비 설정 64분주 (250,000Hz --> 250KHz)
	TCCR0 |= 1 << CS02 | 0 << CS01 | 0 << CS00; // TCCR0 |= 0xf4; 보다는 좌측 코드 권장
	// (5) Timer0 overflow INT를 허용(enable)
	TIMSK |= 1 << TOIE0; // TIMSK |= 0x01;
}

 

<pwm.c>

/*
 * pwm.c
 *
 * Created: 2025-03-13 오후 12:48:52
 *  Author: microsoft
 */ 

#include "pwm.h".

extern void init_button(void);
extern int get_button(int button_num, int button_pin);

void init_timer3(void);
void pwm_fan_control_main(void);

/*
	16bit timer 3번 활용
	PWM 출력 신호(3EA)
	============
	PE3 : OC3A
	PE4 : OC3B / INT4 --> 사용중(초음파 echo 펄스 esternal INT용)
	PE5 : OC3C ---> 모터 연결 
	BTN0 : start/stop
	BTN1 : speed-up(OC3C : 20씩 증가, max 250)
	BTN2 : speed-down(OC3C : 20씩 감소, min 60)
*/

void init_timer3(void)
{
	DDRE |= 1 << 3 | 1 << 5; // 현재 4번 핀은 초음파 echo
	// 16bit timer 3번의 모드5 : 8비트 고속 PWM
	TCCR3A |= 1 << WGM30;
	TCCR3B |= 1 << WGM32;
	// 비반전 모드
	// 사용자가 설정한 PWM 값이 일치시 OC3C의 출력핀이 LOW로 바뀌고 BOTTON에서 HIGH로 바뀐다.
	TCCR3A |= 1 << COM3C1;
	// 분주비 : 64분주 16000000 / 64 ---> 250000Hz(250KHz)
	// T = 1/f = 1/250000Hz ==> 0.000004sec (4us)
	// 250000Hz에서 256개의 펄스를 count하면 소요시간 1.02ms
	//              127개                            0.5ms
	TCCR3B |= 1 << CS31 | 1 << CS30; // 분주비 : 64
	OCR3C = 0; // OCR(Output Compare Register) : PWM 값 
}

// PE5 모터 연결
void pwm_fan_control_main(void)
{
	uint8_t start_button = 0;
	
	init_button();
	init_timer3();
	
	while(1)
	{
		if(get_button(BUTTON0, BUTTON0PIN)) // start/stop
		{
			start_button = !start_button; // toggle
			OCR3C = (start_button) ? 250 : 0; // 250 : 모터 회전의 기본값, 0 : stop
		}
		else if(get_button(BUTTON1, BUTTON1PIN)) // speed-up
		{
			OCR3C = (OCR3C >= 250) ? 250 : OCR3C + 20;
		}
		else if(get_button(BUTTON2, BUTTON2PIN)) // speed-down
		{
			OCR3C = (OCR3C <= 70) ? 60 : OCR3C - 20;
		}
	}
}

 

<pwm.h>

/*
 * pwm.h
 *
 * Created: 2025-03-13 오후 12:48:42
 *  Author: microsoft
 */ 


#ifndef PWM_H_
#define PWM_H_

#define  F_CPU 16000000UL  // 16MHZ
#include <avr/io.h>
#include <util/delay.h>  // _delay_ms _delay_us
#include <avr/interrupt.h> // sei()
#include <stdio.h> // printf, scanf, fgets, puts, gets 등이 들어있다.

#include "button.h"



#endif /* PWM_H_ */

 

<실행 결과>

https://youtube.com/shorts/6DBCi9AZ7c8

https://youtu.be/fdWCeAjGv7Y

 

 

PWM FAN CONTROL with BUTTON forward <==> backward

 

<main.c>

/*
 * 01.LED_CONTROL.c
 *
 * Created: 2025-03-04 오후 4:25:34
 * Author : microsoft
 */ 

#define  F_CPU 16000000UL  // 16MHZ
#include <avr/io.h>
#include <util/delay.h>  // _delay_ms _delay_us
#include <avr/interrupt.h> // sei()
#include <stdio.h> // printf, scanf, fgets, puts, gets 등이 들어있다.

#include "button.h"
#include "def.h"
#include "ultrasonic.h"

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 init_ultrasonic(void);
extern void distance_ultrasonic(void);
extern void make_pwm_led_control(void);
extern void led_on_pwm(int dim);
extern void pwm_fan_control_main(void);
extern void init_L298N(void);
extern void L298N_pwm_fan_control_main(void);

extern volatile uint8_t rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
extern volatile uint8_t rx_msg_received;

volatile int msec_count = 0;
volatile int ultrasonic_check_timer = 0;
// for printf 
FILE OUTPUT = FDEV_SETUP_STREAM(UART0_transmit, NULL, _FDEV_SETUP_WRITE);
// volatile 변수 type 앞에 volatile을 선언하는 이유 : compiler한테 최적화 방지 지시
// cc -O
/*
예) 1 ~ 10까지 합을 구하는 프로그램
int i = 0; int sum = 0;
while( i < 10)
{
	i++; sum += i;	
}
printf("sum-->%d\n", sum); // 1 ~ 10 : 55가 된다.
*/

// TIMER0_OVF_vect
// ISR(Interrupt Service Routine)(함수)
// --> H/W가 S/W한테 event(상황변화)가 일어났다고 알려 주는 공간
// 250개의 pulse를 count(1ms)하면 이곳으로 자동적으로 들어온다.
// ISR루틴(함수)는 가능한 짧게 작성한다.
int led_toggle = 0;
ISR(TIMER0_OVF_vect)
{
	// 6 ~ 256 : 250(1ms) 그래서 TCNT0를 6으로 설정하는 것이다.
	TCNT0 = 6;
	msec_count++; // 1ms마다 1씩 증가
	ultrasonic_check_timer++;
}

int main(void)
{
	// DDRA = 0b11111111;   // PORTA를 출력 모드(1)로 설정
	
	init_timer0();
	init_uart0();
	init_ultrasonic();
	stdout = &OUTPUT; // printf가 동작될 수 있도록 stdout에 OUTPUT 파일 포인터 assign
	sei(); // 전역(대문)으로 interrupt 허용
	
	// led_main();
	// make_pwm_led_control();
	// pwm_fan_control_main();
	L298N_pwm_fan_control_main();
	
	// printf("init_uart0\n");
	
	while(1)
	{
		distance_ultrasonic();
		//pc_command_processing();
	}
}

// timer0를 초기화시키는 부분
// AVR에서 8bit timer 0/2번 중 0번을 초기화
// 임베디드/FPGA 등에서 제일 중요한 것은 초기화를 정확히 해주는 것이다.
// 그래서 이 부분을 특별히 신경을 써서 작성한다.
void init_timer0(void)
{
	// 16MHz/64 분주(down) : divider / prescale
	// ---------- 분주비 계산 ----------
	// (1) 16000000Hz/64 ==> 250,000Hz
	// (2) T(주기) 1clock의 소요 시간 : 1/f = 1/250,000 ==> 0.000004sec(4us) : 0.004ms
	// (3) 8bit timer OV(OVflow) : 0.004ms x 256 = 0.001024sec --> 1.024ms
	// 1ms마다 정확하게 INT를 띄우고 싶으면 0.004ms x 250개를 count = 0.001sec ==> 1ms
	TCNT0 = 6; // TCNT : 0 ~ 256 1ms마다 TIMER0_OVF_vect로 진입한다.
			   // TCNT0 = 6으로 설정한 이유 : 6 ~ 256 : 250개의 펄스를 count하기 때문에 정확히 1ms가 된다.
	// (4) 분주비 설정 64분주 (250,000Hz --> 250KHz)
	TCCR0 |= 1 << CS02 | 0 << CS01 | 0 << CS00; // TCCR0 |= 0xf4; 보다는 좌측 코드 권장
	// (5) Timer0 overflow INT를 허용(enable)
	TIMSK |= 1 << TOIE0; // TIMSK |= 0x01;
}

 

<pwm.c>

/*
 * pwm.c
 *
 * Created: 2025-03-13 오후 12:48:52
 *  Author: microsoft
 */ 

#include "pwm.h".

extern void init_button(void);
extern int get_button(int button_num, int button_pin);

void init_timer3(void);
void init_L298N(void);
void pwm_fan_control_main(void);
void L298N_pwm_fan_control_main(void);

/*
	16bit timer 3번 활용
	PWM 출력 신호(3EA)
	============
	PE3 : OC3A
	PE4 : OC3B / INT4 --> 사용중(초음파 echo 펄스 esternal INT용)
	PE5 : OC3C ---> 모터 연결 
	BTN0 : start/stop
	BTN1 : speed-up(OC3C : 20씩 증가, max 250)
	BTN2 : speed-down(OC3C : 20씩 감소, min 60)
*/

void init_timer3(void)
{
	DDRE |= 1 << 3 | 1 << 5; // 현재 4번 핀은 초음파 echo
	// 16bit timer 3번의 모드5 : 8비트 고속 PWM
	TCCR3A |= 1 << WGM30;
	TCCR3B |= 1 << WGM32;
	// 비반전 모드
	// 사용자가 설정한 PWM 값이 일치시 OC3C의 출력핀이 LOW로 바뀌고 BOTTON에서 HIGH로 바뀐다.
	TCCR3A |= 1 << COM3C1;
	// 분주비 : 64분주 16000000 / 64 ---> 250000Hz(250KHz)
	// T = 1/f = 1/250000Hz ==> 0.000004sec (4us)
	// 250000Hz에서 256개의 펄스를 count하면 소요시간 1.02ms
	//              127개                            0.5ms
	TCCR3B |= 1 << CS31 | 1 << CS30; // 분주비 : 64
	OCR3C = 0; // OCR(Output Compare Register) : PWM 값 
}

// PF6 : IN1 (L298N)
// PF7 : IN2 (L298N)
// PF6(IN1) PF7(IN2)
//    0        0     : 역회전
//    1        0     : 정회전
//    1        1     : stop
void init_L298N(void)
{
	DDRF |= 1 << 6 | 1 << 7; // 출력 모드 설정
	PORTF &= !(1 << 6 | 1 << 7);
	PORTF |= 1 << 6; // 정회전
}

// PE5 모터 연결
void pwm_fan_control_main(void)
{
	uint8_t start_button = 0;
	
	init_button();
	init_timer3();
	
	while(1)
	{
		if(get_button(BUTTON0, BUTTON0PIN)) // start/stop
		{
			start_button = !start_button; // toggle
			OCR3C = (start_button) ? 250 : 0; // 250 : 모터 회전의 기본값, 0 : stop
		}
		else if(get_button(BUTTON1, BUTTON1PIN)) // speed-up
		{
			OCR3C = (OCR3C >= 250) ? 250 : OCR3C + 20;
		}
		else if(get_button(BUTTON2, BUTTON2PIN)) // speed-down
		{
			OCR3C = (OCR3C <= 70) ? 60 : OCR3C - 20;
		}
	}
}

void L298N_pwm_fan_control_main(void)
{
	uint8_t start_button = 0;
	uint8_t forward = 1; // forward <--> backward
	
	init_button();
	init_timer3();
	init_L298N();
	
	while(1)
	{
		if(get_button(BUTTON0, BUTTON0PIN)) // start/stop
		{
			start_button = !start_button; // toggle
			OCR3C = (start_button) ? 250 : 0; // 250 : 모터 회전의 기본값, 0 : stop
		}
		else if(get_button(BUTTON1, BUTTON1PIN)) // speed-up
		{
			OCR3C = (OCR3C >= 250) ? 250 : OCR3C + 20;
		}
		else if(get_button(BUTTON2, BUTTON2PIN)) // speed-down
		{
			OCR3C = (OCR3C <= 70) ? 60 : OCR3C - 20;
		}
		else if(get_button(BUTTON3, BUTTON3PIN)) // forward <--> backward
		{
			forward = !forward;
			PORTF &= ~(1 << 6 | 1 << 7); // IN1, IN2 reset
			PORTF = (forward) ? (PORTF | 1 << 6) : (PORTF | 1 << 7);
			//                    정회전              역회전
		}
	}
}

 

<실행 결과>

https://youtube.com/shorts/yZNklxjqMkY

 

FAN and 2 FND CONTROL with BUTTON and PWM

 

<main.c>

/*
 * 01.LED_CONTROL.c
 *
 * Created: 2025-03-04 오후 4:25:34
 * Author : microsoft
 */ 
#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배


//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)
	{
		//fnd_main();
	}
}


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();
}

 

<fnd.c>

/*
 * fnd.c
 *
 * Created: 2025-03-06 오후 12:23:41
 *  Author: microsoft
 */ 
#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;

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;										
	
	switch(digit_select)
	{
		case 0:
			FND_DIGIT_PORT = 0x80;										
			FND_DATA_PORT = fnd_font[sec_count % 10];					
			break;
		case 1:
			FND_DIGIT_PORT = 0x40;										
			FND_DATA_PORT = fnd_font[sec_count / 10 % 6];
			break;
		case 2:
			FND_DIGIT_PORT = 0x20;										
			FND_DATA_PORT = fnd_font[sec_count / 60 % 10];				
			break;
		case 3:
			FND_DIGIT_PORT = 0x10;										
			FND_DATA_PORT = fnd_font[sec_count / 600 % 6];				
			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;
		
		switch(digit_select)
		{
			case 0:
			FND_DIGIT_PORT = 0x08;
			FND_DATA_PORT = fnd_font0[sec2_count % 12];
			break;
			case 1:
			FND_DIGIT_PORT = 0x04;
			FND_DATA_PORT = fnd_font1[sec2_count % 12];
			break;
			case 2:
			FND_DIGIT_PORT = 0x02;
			FND_DATA_PORT = fnd_font2[sec2_count % 12];
			break;
			case 3:
			FND_DIGIT_PORT = 0x01;
			FND_DATA_PORT = fnd_font3[sec2_count % 12];
			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;
	
	switch(digit_select)
	{
		case 0:
		FND_DIGIT_PORT = 0x08;
		FND_DATA_PORT = fnd_font0[sec2_count % 12];
		break;
		case 1:
		FND_DIGIT_PORT = 0x04;
		FND_DATA_PORT = fnd_font1[sec2_count % 12];
		break;
		case 2:
		FND_DIGIT_PORT = 0x02;
		FND_DATA_PORT = fnd_font2[sec2_count % 12];
		break;
		case 3:
		FND_DIGIT_PORT = 0x01;
		FND_DATA_PORT = fnd_font3[sec2_count % 12];
		break;
	}
	digit_select++;
	digit_select %= 4;
}

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;
}

 

<fnd.h>

/*
 * fnd.h
 *
 * Created: 2025-03-06 오후 12:23:18
 *  Author: microsoft
 */ 


#ifndef FND_H_
#define FND_H_
#define  F_CPU 16000000UL  // 16MHZ
#include <avr/io.h>
#include <util/delay.h>  // _delay_ms _delay_us

#define FND_DATA_PORT  PORTC
#define FND_DATA_DDR   DDRC

#define FND_DIGIT_PORT PORTB
#define FND_DIGIT_DDR  DDRB
#define New_FND_DIGIT_D1  0
#define New_FND_DIGIT_D2  1
#define New_FND_DIGIT_D3  2
#define New_FND_DIGIT_D4  3
#define FND_DIGIT_D1  4
#define FND_DIGIT_D2  5
#define FND_DIGIT_D3  6
#define FND_DIGIT_D4  7


#endif /* FND_H_ */

 

<pwm.c>

/*
 * pwm.c
 *
 * Created: 2025-03-13 오후 12:48:53
 *  Author: microsoft
 */ 

#define MAX_SPEED_FACTOR 10  // 최대 속도 배율 설정
#define MIN_SPEED_FACTOR 1   // 최소 속도 배율

#include "pwm.h"

// button 함수
extern void init_button(void);
extern int get_button(int button_num, int button_pin);
// fnd 함수
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 pwm_fan_control_main(void);
void L298N_pwm_fan_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;

volatile int temp = 0;

/*
	16bit timer 3번 활용
	PWM 출력 신호 (3ea) p342 표.15-1 참고
	============
	PE3: OC3A
	PE4: OC3B, INT4(사용중-->초음파 echo펄스 external INT용)
	PE5: OC3C --> 모터 연결
	BTN0: start/stop
	BTN1: speed up (OC3C: 20씩 증가, max = 250)
	BTN2: speed down (OC3C: 20씩 감소, min = 60)
*/
void init_timer3(void)
{
	DDRE |= 1 << 3 | 1 << 5;		// 현재 4번핀은 초음파 echo
	// 16 timer 3번의 모드 5: 8비트 고속 PWM (p346 표.15-6)
	TCCR3A |= 1 << WGM30;
	TCCR3B |= 1 << WGM32;
	// 비반전 모드
	// 사용자가 설정한 PWM값에 일치 시 OC3C의 출력핀이 LOW로 바뀌고 BOTTOM에 HIGH로 바뀐다.
	// p350 표.15-7 참고
	TCCR3A |= 1 << COM3C1;
	// 분주비: 64, 16000000/64 --> 2500000Hz(250KHz)
	// T = 1/f = 1/2500000 = 0.000004sec = 4us
	// 2500000Hz에서 256개의 펄스를 count하면 소요시간 1.02ms
	//				 127개                           0.50ms
	// p318 표.14-1 참고  
	TCCR3B |= 1 << CS31 | 1 << CS30;		// 분주비: 64
	OCR3C = 0;								// OCR(Output Compare Register): PWM 값
}

// PF6: IN1 (L298N)
// PF7: IN2 (L298N)
// PF6(IN1) PF7(IN2)
//    0        0	: 역회전
//    1        0	: 정회전
//    1		   1    : stop
void init_L298N(void)
{
	DDRF |= 1 << 6 | 1 << 7;				// 출력 모드로 설정, hard coding
	PORTF &= ~(1 << 6 | 1 << 7);
	PORTF |= 1 << 6;						// 정회전
}

// PE5에 모터연결
void pwm_fan_control_main(void)
{
	uint8_t start_button = 0;
	
	init_button();
	init_timer3();
	
	while(1)
	{
		// 만약에 최소로 감소시키고 계속 증가시키면 250을 딱 못 맞춰서 아예 멈춰버린다.
		if(get_button(BUTTON0, BUTTON0PIN))			// start,stop
		{
			start_button = !start_button;			// toggle
			OCR3C = (start_button) ? 250 : 0;		// 250: 모터 회전의 기본값, 0: stop
		}
		else if(get_button(BUTTON1, BUTTON1PIN))	// speed up
		{
			OCR3C = (OCR3C >= 250) ? 250 : OCR3C + 20;
		}
		else if(get_button(BUTTON2, BUTTON2PIN))	// speed down
		{
			OCR3C = (OCR3C <= 70) ? 70 : OCR3C - 20;
		}
	}	
}

// PE5에 L298N_모터연결
void L298N_pwm_fan_fnd_control_main(void)
{
	int temp = 0;
	
    uint8_t start_button = 0;
    uint8_t forward = 1;		// 모터 회전 방향
    uint8_t fnd_forward = 1;	// FND 회전 방향		// forward <--> backward
	uint8_t fnd_running = 0;    // FND 동작 여부 (0: 회전, 1: 정지)
	
	init_button();
	init_timer3();
	init_L298N();
	
	while(1)
	{	
		fnd_display();
		_delay_ms(1);
		
        // FND 동작 여부에 따라 실행
        if (fnd_running) 
		{
	        if (fnd_forward)
				fnd_big_circle_forward();
	        else
				fnd_big_circle_backward();
	    }
		else 
		{
	        fnd_stop_display();
        }
        _delay_ms(1);		
		
		if(get_button(BUTTON0, BUTTON0PIN))			// start,stop
		{
			temp = msec2_count;
            fnd_running = !fnd_running;				// FND 실행/정지 토글
			start_button = !start_button;			// toggle
			OCR3C = (start_button) ? 250 : 0;		// 250: 모터 회전의 기본값, 0: stop
		}
		else if(get_button(BUTTON1, BUTTON1PIN))	// speed up
		{
			if (msec2_speed_factor < MAX_SPEED_FACTOR)
				msec2_speed_factor++;  // 속도 증가		
			OCR3C = (OCR3C >= 250) ? 250 : OCR3C + 20;
		}
		else if(get_button(BUTTON2, BUTTON2PIN))	// speed down
		{
		    if (msec2_speed_factor > MIN_SPEED_FACTOR)
				msec2_speed_factor--;  // 속도 감소	
			OCR3C = (OCR3C <= 70) ? 70 : OCR3C - 20;
		}
		else if(get_button(BUTTON3, BUTTON3PIN))	// 방향 설정
		{
			sec2_count = msec2_count = 0;
            fnd_forward = !fnd_forward; // FND 방향 토글
            forward = !forward; // 모터 방향 토글
			PORTF &= ~(1 << 6 | 1 << 7);			// IN1, IN2 reset
			PORTF = (forward) ? (PORTF | 1 << 6) : (PORTF | 1 << 7);
			//					 정회전				역회전 
		}
	}
}

 

<pwm.h>

/*
 * pwm.h
 *
 * Created: 2025-03-13 오후 12:48:40
 *  Author: microsoft
 */ 


#ifndef PWM_H_
#define PWM_H_
#define  F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "button.h"

#endif /* PWM_H_ */

 

<실행 결과>

https://youtu.be/_LNRH-EldHU

 

'(Telechips) AI 시스템 반도체 SW 개발자 교육 > ATmega128A 마이크로컨트롤러 프로그래밍' 카테고리의 다른 글

11일차  (0) 2025.03.18
9일차  (0) 2025.03.16
7일차  (0) 2025.03.12
6일차  (0) 2025.03.11
5일차  (0) 2025.03.10