printf("ho_tari\n");
7일차 본문
2025.03.12
LED CONTROL with Bluetooth Serial Port
<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"
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 init_uart1(void);
extern void UART0_transmit(uint8_t data);
extern void UART1_transmit(uint8_t data);
extern void pc_command_processing(void);
extern void bt_command_processing(void);
extern volatile uint8_t rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
extern volatile uint8_t rx1_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
extern volatile uint8_t rx_msg_received;
extern volatile uint8_t rx1_msg_received;
volatile int msec_count = 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씩 증가
}
int main(void)
{
DDRA = 0b11111111; // PORTA를 출력 모드(1)로 설정
init_timer0();
init_uart0();
init_uart1();
stdout = &OUTPUT; // printf가 동작될 수 있도록 stdout에 OUTPUT 파일 포인터 assign
sei(); // 전역(대문)으로 interrupt 허용
// led_main();
// printf("init_uart0\n");
while(1)
{
// pc_command_processing();
bt_command_processing();
}
}
// none O/S 또는 loop monitor방식
#if 0
int main(void)
{
int button0_state=0; // 초기 상태를 0으로 출발
init_timer0();
led_main();
//fnd_main();
void (*fp[]) (void) =
{
led_all_off,
led_all_on,
shift_left_ledon,
shift_right_ledon,
shift_left_keep_ledon,
shift_right_keep_ledon,
flower_on,
flower_off
};
init_button();
// led_main();
//76543210
DDRA = 0b11111111; // PORTA를 출력 모드(1)로 설정
//---- 상위 nibble : 상위 4bits
// ---- 하위 nibble
// DDR(Data Direction Register) : 방향 설정
// 1: 출력 0: 입력을 의미
// 0b : 2진수
// 0x : hex
// DDRA = 0xff;
#if 0 // 함수 포인터 배열
while (1)
{
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state++;
button0_state %= 8;
}
fp[button0_state] ();
}
#endif
#if 0 // switch ~ case
while (1)
{
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state++;
button0_state %= 8;
}
switch (button0_state) {
case 0:
led_all_off();
break;
case 1:
led_all_on();
break;
case 2:
shift_left_ledon();
break;
case 3:
shift_right_ledon();
break;
case 4:
shift_left_keep_ledon();
break;
case 5:
shift_right_keep_ledon();
break;
case 6:
flower_on();
break;
case 7:
flower_off();
break;
}
}
#endif
#if 0 // org
while (1) // for(;;)
{
// 1 button버튼 처리 (toggle)
// button0를 1번 누르면 led_all_on
// led_all_off
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state = !button0_state; // 반전 0 <--> 1
if (button0_state)
led_all_on();
else led_all_off();
}
}
#endif
}
#endif
// 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;
}
<uart1.c>
/*
* uart1.c
*
* Created: 2025-03-11 오후 4:35:37
* Author: microsoft
*/
#include "uart1.h"
#include <string.h> // strcpy, strncmp, strcmp 등
void init_uart1(void);
void UART1_transmit(uint8_t data);
void bt_command_processing(void);
#define FUNC_NUMBER 8
extern void (*fp[FUNC_NUMBER]) (void);
/*
PC comportmaster로부터 1byte가 들어올 때마다 이곳으로 들어온다. (RX INT)
예) led_all_on\n ==> 11번 이곳으로 들어온다.
led_all_off\n
*/
volatile uint8_t rx1_msg_received = 0;
ISR(USART1_RX_vect)
{
volatile uint8_t rx1_data;
volatile static int i = 0;
rx1_data = UDR1; // uart0의 H/W register(UDR0)로부터 1byte를 읽어들인다.
// rx_data = UDR0;를 실행하면 UDR0의 내용이 빈다. (empty)
if(rx1_data == '\n')
{
rx1_buff[rear1++][i] = '\0';
rear1 %= COMMAND_NUMBER; // 0~9
i = 0; // 다음 string을 저장하기 위한 1차원 index값을 0으로
// !!!! rx_buff queue full check하는 logic 추가
}
else
{
rx1_buff[rear1][i++] = rx1_data;
// COMMAND_LENGTH를 check하는 logic 추가
}
}
/*
1. 전송 속도 : 9600bps : 총 글자수 : 9600/10bit ==> 960자
(1글자를 송, 수신하는 데 소요 시간 : 약 1ms)
2. 비동기 방식(start/stop 부호 비트로 데이터를 구분
3. RX(수신) : 인터럽트 방식으로 구현 (TX는 폴링 방식으로 구현)
*/
void init_uart1(void)
{
// 1. 9600bps로 설정
UBRR1H = 0x00;
UBRR1L = 207; // 9600bps
// 2. 2배속 통신
UCSR1A |= 1 << U2X1; // 2배속 통신
UCSR1C |= 0x06; // 비동기 / data8bits / none parity
// RXEN1 : UART1로부터 수신이 가능하도록
// TXEN1 : UART1로부터 송신이 가능하도록
// RXCIE1 : UART1로부터 1byte가 들어오면 (stop bit가 들어오면) rx interrupt를 발생시켜라
UCSR1B |= 1 << RXEN1 | 1 << TXEN1 | 1 << RXCIE1;
}
// UART0로 1byte를 전송하는 함수 (polling 방식)
void UART1_transmit(uint8_t data)
{
// 데이터 전송 중이면 전송이 끝날 때까지 기다린다.
while(!(UCSR1A & 1 << UDRE1))
; // no operation
UDR1 = data; // data를 H/W 전송 register에 쏜다.
}
void bt_command_processing(void)
{
if(front1 != rear1) // rx_buff에 data가 존재
{
printf("%s\n", rx1_buff[front1]); // &rx_buff[front][0]와 동일
if(strncmp(rx1_buff[front1], "led_all_on", strlen("led_all_on")) == NULL)
{
fp[0]();
}
else if(strncmp(rx1_buff[front1], "led_all_off", strlen("led_all_off")) == NULL)
{
fp[1]();
}
else if(strncmp(rx1_buff[front1], "shift_left_ledon", strlen("shift_left_ledon")) == NULL)
{
while(1)
{
fp[2]();
if(UCSR1A & 1 << RXC1)
break;
}
}
else if(strncmp(rx1_buff[front1], "shift_right_ledon", strlen("shift_right_ledon")) == NULL)
{
while(1)
{
fp[3]();
if(UCSR1A & 1 << RXC1)
break;
}
}
else if(strncmp(rx1_buff[front1], "shift_left_keep_ledon", strlen("shift_left_keep_ledon")) == NULL)
{
while(1)
{
fp[4]();
if(UCSR1A & 1 << RXC1)
break;
}
}
else if(strncmp(rx1_buff[front1], "shift_right_keep_ledon", strlen("shift_right_keep_ledon")) == NULL)
{
while(1)
{
fp[5]();
if(UCSR1A & 1 << RXC1)
break;
}
}
else if(strncmp(rx1_buff[front1], "flower_on", strlen("flower_on")) == NULL)
{
while(1)
{
fp[6]();
if(UCSR1A & 1 << RXC1)
break;
}
}
else if(strncmp(rx1_buff[front1], "flower_off", strlen("flower_off")) == NULL)
{
while(1)
{
fp[7]();
if(UCSR1A & 1 << RXC1)
break;
}
}
front1++;
front1 %= COMMAND_NUMBER;
// !!!! queue full check하는 logic들어가야 한다. !!!!
}
}
<uart0.c>
/*
* uart1.h
*
* Created: 2025-03-11 오후 4:35:26
* Author: microsoft
*/
#ifndef UART1_H_
#define UART1_H_
#define F_CPU 16000000L
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h> // sei()
#include "def.h"
// led_all_on\n
// led_all_off\n
volatile uint8_t rx1_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
volatile int rear1 = 0; // input index : USART1_RX_vect에서 집어 넣어주는 index
volatile int front1 = 0; // output index
#endif /* UART1_H_ */

<실행 결과>
https://youtube.com/shorts/C54kKWzfsrA
초음파 센서 (Ultra Sonic)
HC-SR04
- 사람이 들을 수 있는 가청 주파수(20kHz까지)이상의 주파수를 가진 소리를 초음파
- 초음파 센서는 수신부와 송신부로 이루어짐
- 송신부에서 초음파를 쏘아 올리고 벽 혹은 물체에 반사되어 오는 초음파를 수신부에서 인식
- 이 때 보낸 시간과 반사되어 돌아온 시간을 측정하여 거리를 계산


동작 원리
1. TRIG 핀으로 최소 10us의 펄스를 주면 TRIG소자에 초음파가 발사
2. ECHO소자로 반사파가 들어 온다. (거리에 비례)
3. 거리값 구하기
- 소리 속도 : 340 m/s
- us단위 변환 : 0.034cm / us (1us 동안 0.034cm 이동)
- 초음파가 1cm 이동 소요시간 T = 2 * 0.01 / 340 = 58.824 us (왕복 시간) 편도 소요시간: 29 us
※ 초음파 속도
- 초당 340m 이동
- 29us당 1cm 이동
- 초음파 이동거리 = 왕복시간 / 1cm 이동 시간 / 2 이다.
- distance = duration / 29 / 2; <=== 센치미터로 환산
EICRA 레지스터가 INT0 ~ INT3까지의 외부 인터럽트 발생 시점을 결정한다면
EICRB (External Interrupt Control Register B) 레지스터는 INT4 ~ INT7까지의 외부 인터럽트 발생 시점을 결정한다.
Ultrasonic with UART
<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 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();
// printf("init_uart0\n");
while(1)
{
distance_ultrasonic();
//pc_command_processing();
}
}
// none O/S 또는 loop monitor방식
#if 0
int main(void)
{
int button0_state=0; // 초기 상태를 0으로 출발
init_timer0();
led_main();
//fnd_main();
void (*fp[]) (void) =
{
led_all_off,
led_all_on,
shift_left_ledon,
shift_right_ledon,
shift_left_keep_ledon,
shift_right_keep_ledon,
flower_on,
flower_off
};
init_button();
// led_main();
//76543210
DDRA = 0b11111111; // PORTA를 출력 모드(1)로 설정
//---- 상위 nibble : 상위 4bits
// ---- 하위 nibble
// DDR(Data Direction Register) : 방향 설정
// 1: 출력 0: 입력을 의미
// 0b : 2진수
// 0x : hex
// DDRA = 0xff;
#if 0 // 함수 포인터 배열
while (1)
{
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state++;
button0_state %= 8;
}
fp[button0_state] ();
}
#endif
#if 0 // switch ~ case
while (1)
{
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state++;
button0_state %= 8;
}
switch (button0_state) {
case 0:
led_all_off();
break;
case 1:
led_all_on();
break;
case 2:
shift_left_ledon();
break;
case 3:
shift_right_ledon();
break;
case 4:
shift_left_keep_ledon();
break;
case 5:
shift_right_keep_ledon();
break;
case 6:
flower_on();
break;
case 7:
flower_off();
break;
}
}
#endif
#if 0 // org
while (1) // for(;;)
{
// 1 button버튼 처리 (toggle)
// button0를 1번 누르면 led_all_on
// led_all_off
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state = !button0_state; // 반전 0 <--> 1
if (button0_state)
led_all_on();
else led_all_off();
}
}
#endif
}
#endif
// 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;
}
<ultrasonic.c>
/*
* ultrasonic.c
*
* Created: 2025-03-12 오후 2:49:17
* Author: microsoft
*/
#include "ultrasonic.h"
extern volatile int ultrasonic_check_timer;
void init_ultrasonic();
void trigger_ultrasonic();
void distance_ultrasonic();
volatile int ultrasonic_dis = 0;
volatile char scm[50];
// PE4 : 외부 INT4 초음파 센서의 상승, 하강 에지 둘다 INT가 ISR(INT4_vect)로 들어온다.
// 결국 2번 (상승 : 1번, 하강 : 1번) 들어온다.
ISR(INT4_vect)
{
// 1. 상승에지
if(ECHO_PIN & 1 << ECHO)
{
TCNT1 = 0;
}
else // 2. 하강에지
{
// ECHO 핀에 들어온 펄스 개수를 US로 환산
ultrasonic_dis = 1000000.0 * TCNT1 * 1024 / F_CPU;
// 예) TCINT에 10이 들어 있다고 가정하자
// 15.625KHz의 1주기 64us이다.
// 0.000064sec(64us) * 10 ==> 0.00064sec(640us)
// 640us / 58us(1cm 이동하는데 소요시간) ==> 11 cm이다.
// --- 1cm : 58us
sprintf(scm, "dis: %dcm\n", ultrasonic_dis / 58); // cm 환산
}
}
void init_ultrasonic(void)
{
TRIG_DDR |= 1 << TRIG; // 출력 모드로 설정
ECHO_DDR &= ~(1 << TRIG); // 입력 모드로 설정 ECHO_DDR &= 0b11110111;
// 0 1 : 상승에지(rising edge)와 하강에지(falling edge) 둘다 INT를 띄우도록 요청
EICRB |= 0 << ISC41 || 1 << ISC40;
// 16bit timer1번을 설정해서 사용 65535(max) : 0xffff
// 16MHz를 1024로 분주 16000000Hz/1024 --> 15625Hz --> 15.625KHz
// 1주기 T(주기) = 1/f = 1/15625 ==> 0.000064sec ==> 64us
TCCR1B |= 1 << CS12 | 1 << CS10; // 1024로 분주
EIMSK |= 1 << INT4; // EXTERNAL INT4 (ECHO 핀)
}
void trigger_ultrasonic(void)
{
TRIG_PORT &= ~(1 << TRIG); // low
_delay_us(1);
TRIG_PORT |= 1 << TRIG; // high
_delay_us(15); // 규격에는 10us인데 여유를 둬서 15us로 설정
TRIG_PORT &= ~(1 << TRIG); // low
}
void distance_ultrasonic(void)
{
if(ultrasonic_check_timer >= 1000) // 1초 체크
{
ultrasonic_check_timer = 0;
printf("%s", scm);
trigger_ultrasonic();
}
}
<ultrasonic.h>
/*
* ultrasonic.h
*
* Created: 2025-03-12 오후 2:49:03
* Author: microsoft
*/
#ifndef ULTRASONIC_H_
#define ULTRASONIC_H_
#define F_CPU 16000000UL // 16MHZ
#include <avr/io.h>
#include <util/delay.h> // _delay_ms _delay_us
#include <avr/interrupt.h> // sei()
#define TRIG 4
#define TRIG_PORT PORTG
#define TRIG_DDR DDRG
#define ECHO 4
#define ECHO_PIN PINE // External INT 4
#define ECHO_DDR DDRE
#endif /* ULTRASONIC_H_ */
<실행 결과>
Ultrasonic with UART LED, FND Control
<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 sonic_led(void);
extern void sonic_fnd(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();
init_fnd();
stdout = &OUTPUT; // printf가 동작될 수 있도록 stdout에 OUTPUT 파일 포인터 assign
sei(); // 전역(대문)으로 interrupt 허용
// led_main();
// printf("init_uart0\n");
while(1)
{
distance_ultrasonic();
sonic_led();
sonic_fnd();
//pc_command_processing();
}
}
// none O/S 또는 loop monitor방식
#if 0
int main(void)
{
int button0_state=0; // 초기 상태를 0으로 출발
init_timer0();
led_main();
//fnd_main();
void (*fp[]) (void) =
{
led_all_off,
led_all_on,
shift_left_ledon,
shift_right_ledon,
shift_left_keep_ledon,
shift_right_keep_ledon,
flower_on,
flower_off
};
init_button();
// led_main();
//76543210
DDRA = 0b11111111; // PORTA를 출력 모드(1)로 설정
//---- 상위 nibble : 상위 4bits
// ---- 하위 nibble
// DDR(Data Direction Register) : 방향 설정
// 1: 출력 0: 입력을 의미
// 0b : 2진수
// 0x : hex
// DDRA = 0xff;
#if 0 // 함수 포인터 배열
while (1)
{
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state++;
button0_state %= 8;
}
fp[button0_state] ();
}
#endif
#if 0 // switch ~ case
while (1)
{
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state++;
button0_state %= 8;
}
switch (button0_state) {
case 0:
led_all_off();
break;
case 1:
led_all_on();
break;
case 2:
shift_left_ledon();
break;
case 3:
shift_right_ledon();
break;
case 4:
shift_left_keep_ledon();
break;
case 5:
shift_right_keep_ledon();
break;
case 6:
flower_on();
break;
case 7:
flower_off();
break;
}
}
#endif
#if 0 // org
while (1) // for(;;)
{
// 1 button버튼 처리 (toggle)
// button0를 1번 누르면 led_all_on
// led_all_off
if (get_button(BUTTON0, BUTTON0PIN))
{
button0_state = !button0_state; // 반전 0 <--> 1
if (button0_state)
led_all_on();
else led_all_off();
}
}
#endif
}
#endif
// 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;
}
<ultrasonic.c>
/*
* ultrasonic.c
*
* Created: 2025-03-12 오후 2:49:17
* Author: microsoft
*/
#include "ultrasonic.h"
#include "fnd.h"
extern volatile int ultrasonic_check_timer;
void init_ultrasonic();
void trigger_ultrasonic();
void distance_ultrasonic();
void sonic_led(void);
void sonic_fnd(void);
volatile int ultrasonic_dis = 0;
volatile int ultrasonic_cm = 0;
volatile char scm[50];
// PE4 : 외부 INT4 초음파 센서의 상승, 하강 에지 둘다 INT가 ISR(INT4_vect)로 들어온다.
// 결국 2번 (상승 : 1번, 하강 : 1번) 들어온다.
ISR(INT4_vect)
{
// 1. 상승에지
if(ECHO_PIN & 1 << ECHO)
{
TCNT1 = 0;
}
else // 2. 하강에지
{
// ECHO 핀에 들어온 펄스 개수를 US로 환산
ultrasonic_dis = 1000000.0 * TCNT1 * 1024 / F_CPU;
// 예) TCINT에 10이 들어 있다고 가정하자
// 15.625KHz의 1주기 64us이다.
// 0.000064sec(64us) * 10 ==> 0.00064sec(640us)
// 640us / 58us(1cm 이동하는데 소요시간) ==> 11 cm이다.
// --- 1cm : 58us
ultrasonic_cm = ultrasonic_dis / 58;
sprintf(scm, "dis: %dcm\n", ultrasonic_cm); // cm 환산
}
}
void init_ultrasonic(void)
{
TRIG_DDR |= 1 << TRIG; // 출력 모드로 설정
ECHO_DDR &= ~(1 << TRIG); // 입력 모드로 설정 ECHO_DDR &= 0b11110111;
// 0 1 : 상승에지(rising edge)와 하강에지(falling edge) 둘다 INT를 띄우도록 요청
EICRB |= 0 << ISC41 || 1 << ISC40;
// 16bit timer1번을 설정해서 사용 65535(max) : 0xffff
// 16MHz를 1024로 분주 16000000Hz/1024 --> 15625Hz --> 15.625KHz
// 1주기 T(주기) = 1/f = 1/15625 ==> 0.000064sec ==> 64us
TCCR1B |= 1 << CS12 | 1 << CS10; // 1024로 분주
EIMSK |= 1 << INT4; // EXTERNAL INT4 (ECHO 핀)
}
void trigger_ultrasonic(void)
{
TRIG_PORT &= ~(1 << TRIG); // low
_delay_us(1);
TRIG_PORT |= 1 << TRIG; // high
_delay_us(15); // 규격에는 10us인데 여유를 둬서 15us로 설정
TRIG_PORT &= ~(1 << TRIG); // low
}
void distance_ultrasonic(void)
{
if(ultrasonic_check_timer >= 1000) // 1초 체크
{
ultrasonic_check_timer = 0;
printf("%s", scm);
trigger_ultrasonic();
}
}
void sonic_led(void)
{
DDRA = 0xff;
PORTA = 0x00;
if(ultrasonic_cm >= 1 && ultrasonic_cm < 3)
PORTA = 0x01;
if(ultrasonic_cm >= 3 && ultrasonic_cm < 6)
PORTA = 0x03;
if(ultrasonic_cm >= 6 && ultrasonic_cm < 9)
PORTA = 0x07;
if(ultrasonic_cm >= 9 && ultrasonic_cm < 12)
PORTA = 0x0f;
if(ultrasonic_cm >= 12 && ultrasonic_cm < 14)
PORTA = 0x1f;
if(ultrasonic_cm >= 14 && ultrasonic_cm < 16)
PORTA = 0x3f;
if(ultrasonic_cm >= 16 && ultrasonic_cm < 18)
PORTA = 0x7f;
if(ultrasonic_cm >= 18)
PORTA = 0xff;
}
void sonic_fnd(void)
{
uint8_t fnd_font[] = {~0xc0,~0xf9,~0xa4,~0xb0,~0x99,~0x92,~0x82,~0xd8,~0x80,~0x90,~0x7f};
int fnd_number=ultrasonic_cm;
int f0=fnd_number/1000; //천의 자리 숫자 0~9;
int f1=(fnd_number/100) % 10; //백의 자리
int f2=(fnd_number/10) % 10; //십의 자리
int f3=(fnd_number) % 10; //일의자리
static int digit_select=0;
FND_DATA_PORT=0x00;
switch(digit_select)
{
case 0: // FND 첫번째칸
FND_DIGIT_PORT=~0x10;
FND_DATA_PORT = fnd_font[f0];
break;
case 1: // FND 두번째칸
FND_DIGIT_PORT=~0x20;
FND_DATA_PORT = fnd_font[f1];
break;
case 2: // FND 세번째칸
FND_DIGIT_PORT=~0x40;
FND_DATA_PORT =fnd_font[f2];
break;
case 3: // FND 네번째칸
FND_DIGIT_PORT=~0x80;
FND_DATA_PORT=fnd_font[f3];
break;
}
digit_select ++;
digit_select %=4;
}
<ultrasonic.h>
/*
* ultrasonic.h
*
* Created: 2025-03-12 오후 2:49:03
* Author: microsoft
*/
#ifndef ULTRASONIC_H_
#define ULTRASONIC_H_
#define F_CPU 16000000UL // 16MHZ
#include <avr/io.h>
#include <util/delay.h> // _delay_ms _delay_us
#include <avr/interrupt.h> // sei()
#define TRIG 4
#define TRIG_PORT PORTG
#define TRIG_DDR DDRG
#define ECHO 4
#define ECHO_PIN PINE // External INT 4
#define ECHO_DDR DDRE
#endif /* ULTRASONIC_H_ */
<실행 결과>
회로도

'(Telechips) AI 시스템 반도체 SW 개발자 교육 > ATmega128A 마이크로컨트롤러 프로그래밍' 카테고리의 다른 글
9일차 (0) | 2025.03.16 |
---|---|
8일차 (0) | 2025.03.13 |
6일차 (0) | 2025.03.11 |
5일차 (0) | 2025.03.10 |
4일차 (0) | 2025.03.07 |