printf("ho_tari\n");
5일차 본문
2025.03.10
오늘의 학습 목표
1. UART 통신
- RX INT
- Circular Queue 적용
- uart command programming
2. 회로도 작성
Comport Master 다운로드
UART 시리얼 통신
비동기 vs. 동기
- 비동기 : (동기가 아님) clock 신호와 무관하게 별도의 부호를 가지고 Data 송수신 시 Data를 구분하는 방식
- 동기 : clock 신호에 맞춰서 Data 송수신하는 방식
보율(Baud Rate) : 변조 속도, 신호가 변화한 횟수
BPS(Bits Per Second) : 전송 속도
데이터 전송 규칙
- 데이터의 시작과 끝을 표시하기 위한 표시 필요
- 데이터 비트 포함 10비트 데이터를 전송하는 것이 일반적
- 시작 비트 : 1비트의 LOW
- 데이터 비트 : 8비트의 데이터
- 정지 비트 : 1비트의 HIGH
ATmega128A의 UART 통신

벡터 번호 : 19
프로그램 주소 : 0x0024
소스 : USART0 RX
인터럽트 정의 : USART0 Rx Complete
UBRR 레지스터와 보율
UCSRnA 레지스터 비트
UCSRnB 레지스터 비트
<uart0.h>
/*
* uart0.h
*
* Created: 2025-03-10 오전 10:32:09
* Author: microsoft
*/
#ifndef UART0_H_
#define UART0_H_
#define F_CPU 16000000L
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h> // sei()
// led_all_on\n
// led_all_off\n
volatile uint8_t rx_buff[80]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
#endif /* UART0_H_ */
<uart0.c>
/*
* uart0.c
*
* Created: 2025-03-10 오전 10:32:24
* Author: microsoft
*/
#include "uart0.h"
void init_uart0(void);
void UART0_transmit(uint8_t data);
/*
PC comportmaster로부터 1byte가 들어올 때마다 이곳으로 들어온다. (RX INT)
예) led_all_on\n ==> 11번 이곳으로 들어온다.
led_all_off\n
*/
volatile uint8_t rx_msg_received = 0;
ISR(USART0_RX_vect)
{
volatile uint8_t rx_data;
volatile static int i = 0;
rx_data = UDR0; // uart0의 H/W register(UDR0)로부터 1byte를 읽어들인다.
// rx_data = UDR0;를 실행하면 UDR0의 내용이 빈다. (empty)
if(rx_data == '\n')
{
rx_msg_received = 1; // 완전한 message 수신
rx_buff[i] = '\0'; // 문장의 끝을 NULL로 집어 넣는다.
i = 0;
}
else
{
rx_buff[i++] = rx_data;
}
}
/*
1. 전송 속도 : 9600bps : 총 글자수 : 9600/10bit ==> 960자
(1글자를 송, 수신하는 데 소요 시간 : 약 1ms)
2. 비동기 방식(start/stop 부호 비트로 데이터를 구분
3. RX(수신) : 인터럽트 방식으로 구현 (TX는 폴링 방식으로 구현)
*/
void init_uart0(void)
{
// 1. 9600bps로 설정
UBRR0H = 0x00;
UBRR0L = 207; // 9600bps
// 2. 2배속 통신
UCSR0A |= 1 << U2X0; // 2배속 통신
UCSR0C |= 0x06; // 비동기 / data8bits / none parity
// RXEN0 : UART0로부터 수신이 가능하도록
// TXEN0 : UART0로부터 송신이 가능하도록
// RXCIE0 : UART0로부터 1byte가 들어오면 (stop bit가 들어오면) rx interrupt를 발생시켜라
UCSR0B |= 1 << RXEN0 | 1 << TXEN0 | 1 << RXCIE0;
}
// UART0로 1byte를 전송하는 함수 (polling 방식)
void UART0_transmit(uint8_t data)
{
// 데이터 전송 중이면 전송이 끝날 때까지 기다린다.
while(!(UCSR0A & 1 << UDRE0))
; // no operation
UDR0 = data; // data를 H/W 전송 register에 쏜다.
}
<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"
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 volatile rx_buff[80]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
extern volatile uint8_t rx_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();
stdout = &OUTPUT; // printf가 동작될 수 있도록 stdout에 OUTPUT 파일 포인터 assign
sei(); // 전역(대문)으로 interrupt 허용
// led_main();
printf("init_uart0\n");
while(1)
{
if(rx_msg_received)
{
printf("%s\n", rx_buff);
led_all_on();
rx_msg_received = 0;
}
}
}
// 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;
}
<실행 결과>
원형큐
<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"
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 pc_command_processing(void);
extern volatile uint8_t rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
extern volatile uint8_t rx_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();
stdout = &OUTPUT; // printf가 동작될 수 있도록 stdout에 OUTPUT 파일 포인터 assign
sei(); // 전역(대문)으로 interrupt 허용
// led_main();
printf("init_uart0\n");
while(1)
{
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;
}
<uart0.c>
/*
* uart0.c
*
* Created: 2025-03-10 오전 10:32:24
* Author: microsoft
*/
#include "uart0.h"
#include <string.h> // strcpy, strncmp, strcmp 등
void init_uart0(void);
void UART0_transmit(uint8_t data);
void pc_command_processing(void);
/*
PC comportmaster로부터 1byte가 들어올 때마다 이곳으로 들어온다. (RX INT)
예) led_all_on\n ==> 11번 이곳으로 들어온다.
led_all_off\n
*/
volatile uint8_t rx_msg_received = 0;
ISR(USART0_RX_vect)
{
volatile uint8_t rx_data;
volatile static int i = 0;
rx_data = UDR0; // uart0의 H/W register(UDR0)로부터 1byte를 읽어들인다.
// rx_data = UDR0;를 실행하면 UDR0의 내용이 빈다. (empty)
if(rx_data == '\n')
{
rx_buff[rear++][i] = '\0';
rear %= COMMAND_NUMBER; // 0~9
i = 0; // 다음 string을 저장하기 위한 1차원 index값을 0으로
// !!!! rx_buff queue full check하는 logic 추가
}
else
{
rx_buff[rear][i++] = rx_data;
// COMMAND_LENGTH를 check하는 logic 추가
}
}
/*
1. 전송 속도 : 9600bps : 총 글자수 : 9600/10bit ==> 960자
(1글자를 송, 수신하는 데 소요 시간 : 약 1ms)
2. 비동기 방식(start/stop 부호 비트로 데이터를 구분
3. RX(수신) : 인터럽트 방식으로 구현 (TX는 폴링 방식으로 구현)
*/
void init_uart0(void)
{
// 1. 9600bps로 설정
UBRR0H = 0x00;
UBRR0L = 207; // 9600bps
// 2. 2배속 통신
UCSR0A |= 1 << U2X0; // 2배속 통신
UCSR0C |= 0x06; // 비동기 / data8bits / none parity
// RXEN0 : UART0로부터 수신이 가능하도록
// TXEN0 : UART0로부터 송신이 가능하도록
// RXCIE0 : UART0로부터 1byte가 들어오면 (stop bit가 들어오면) rx interrupt를 발생시켜라
UCSR0B |= 1 << RXEN0 | 1 << TXEN0 | 1 << RXCIE0;
}
// UART0로 1byte를 전송하는 함수 (polling 방식)
void UART0_transmit(uint8_t data)
{
// 데이터 전송 중이면 전송이 끝날 때까지 기다린다.
while(!(UCSR0A & 1 << UDRE0))
; // no operation
UDR0 = data; // data를 H/W 전송 register에 쏜다.
}
void pc_command_processing(void)
{
if(front != rear) // rx_buff에 data가 존재
{
printf("%s\n", rx_buff[front]); // &rx_buff[front][0]와 동일
if(strncmp(rx_buff[front], "led_all_on", strlen("led_all_on")) == NULL)
{
printf("Find: led_all_on\n");
}
front++;
front %= COMMAND_NUMBER;
// !!!! queue full check하는 logic들어가야 한다. !!!!
}
}
<uart0.h>
/*
* uart0.h
*
* Created: 2025-03-10 오전 10:32:09
* Author: microsoft
*/
#ifndef UART0_H_
#define UART0_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 rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
volatile int rear = 0; // input index : USART0_RX_vect에서 집어 넣어주는 index
volatile int front = 0; // output index
#endif /* UART0_H_ */
<def.h>
/*
* def.h
*
* Created: 2025-03-10 오후 3:05:49
* Author: microsoft
*/
#ifndef DEF_H_
#define DEF_H_
#define COMMAND_NUMBER 10
#define COMMAND_LENGTH 40
#endif /* DEF_H_ */
<실행 결과>
KiCAD 사용하여 회로도 제작
LED CONTROL 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"
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 pc_command_processing(void);
extern volatile uint8_t rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
extern volatile uint8_t rx_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();
stdout = &OUTPUT; // printf가 동작될 수 있도록 stdout에 OUTPUT 파일 포인터 assign
sei(); // 전역(대문)으로 interrupt 허용
// led_main();
printf("init_uart0\n");
while(1)
{
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;
}
<uart0.c>
/*
* uart0.c
*
* Created: 2025-03-10 오전 10:32:24
* Author: microsoft
*/
#include "uart0.h"
#include <string.h> // strcpy, strncmp, strcmp 등
void init_uart0(void);
void UART0_transmit(uint8_t data);
void pc_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 rx_msg_received = 0;
ISR(USART0_RX_vect)
{
volatile uint8_t rx_data;
volatile static int i = 0;
rx_data = UDR0; // uart0의 H/W register(UDR0)로부터 1byte를 읽어들인다.
// rx_data = UDR0;를 실행하면 UDR0의 내용이 빈다. (empty)
if(rx_data == '\n')
{
rx_buff[rear++][i] = '\0';
rear %= COMMAND_NUMBER; // 0~9
i = 0; // 다음 string을 저장하기 위한 1차원 index값을 0으로
// !!!! rx_buff queue full check하는 logic 추가
}
else
{
rx_buff[rear][i++] = rx_data;
// COMMAND_LENGTH를 check하는 logic 추가
}
}
/*
1. 전송 속도 : 9600bps : 총 글자수 : 9600/10bit ==> 960자
(1글자를 송, 수신하는 데 소요 시간 : 약 1ms)
2. 비동기 방식(start/stop 부호 비트로 데이터를 구분
3. RX(수신) : 인터럽트 방식으로 구현 (TX는 폴링 방식으로 구현)
*/
void init_uart0(void)
{
// 1. 9600bps로 설정
UBRR0H = 0x00;
UBRR0L = 207; // 9600bps
// 2. 2배속 통신
UCSR0A |= 1 << U2X0; // 2배속 통신
UCSR0C |= 0x06; // 비동기 / data8bits / none parity
// RXEN0 : UART0로부터 수신이 가능하도록
// TXEN0 : UART0로부터 송신이 가능하도록
// RXCIE0 : UART0로부터 1byte가 들어오면 (stop bit가 들어오면) rx interrupt를 발생시켜라
UCSR0B |= 1 << RXEN0 | 1 << TXEN0 | 1 << RXCIE0;
}
// UART0로 1byte를 전송하는 함수 (polling 방식)
void UART0_transmit(uint8_t data)
{
// 데이터 전송 중이면 전송이 끝날 때까지 기다린다.
while(!(UCSR0A & 1 << UDRE0))
; // no operation
UDR0 = data; // data를 H/W 전송 register에 쏜다.
}
void pc_command_processing(void)
{
if(front != rear) // rx_buff에 data가 존재
{
printf("%s\n", rx_buff[front]); // &rx_buff[front][0]와 동일
if(strncmp(rx_buff[front], "led_all_on", strlen("led_all_on")) == NULL)
{
fp[0]();
}
else if(strncmp(rx_buff[front], "led_all_off", strlen("led_all_off")) == NULL)
{
fp[1]();
}
else if(strncmp(rx_buff[front], "shift_left_ledon", strlen("shift_left_ledon")) == NULL)
{
while(1)
{
fp[2]();
if(UCSR0A & 1 << RXC0)
break;
}
}
else if(strncmp(rx_buff[front], "shift_right_ledon", strlen("shift_right_ledon")) == NULL)
{
while(1)
{
fp[3]();
if(UCSR0A & 1 << RXC0)
break;
}
}
else if(strncmp(rx_buff[front], "shift_left_keep_ledon", strlen("shift_left_keep_ledon")) == NULL)
{
while(1)
{
fp[4]();
if(UCSR0A & 1 << RXC0)
break;
}
}
else if(strncmp(rx_buff[front], "shift_right_keep_ledon", strlen("shift_right_keep_ledon")) == NULL)
{
while(1)
{
fp[5]();
if(UCSR0A & 1 << RXC0)
break;
}
}
else if(strncmp(rx_buff[front], "flower_on", strlen("flower_on")) == NULL)
{
while(1)
{
fp[6]();
if(UCSR0A & 1 << RXC0)
break;
}
}
else if(strncmp(rx_buff[front], "flower_off", strlen("flower_off")) == NULL)
{
while(1)
{
fp[7]();
if(UCSR0A & 1 << RXC0)
break;
}
}
front++;
front %= COMMAND_NUMBER;
// !!!! queue full check하는 logic들어가야 한다. !!!!
}
}
<uart0.h>
/*
* uart0.h
*
* Created: 2025-03-10 오전 10:32:09
* Author: microsoft
*/
#ifndef UART0_H_
#define UART0_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 rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
volatile int rear = 0; // input index : USART0_RX_vect에서 집어 넣어주는 index
volatile int front = 0; // output index
#endif /* UART0_H_ */
<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);
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;
}
}
<실행 결과>
https://youtube.com/shorts/6XBp6Kz4Ejc
Stack Calculator
<stack_calculator.c>
#include <stdio.h> // 표준 입출력 함수들 (printf, fgets 등)을 사용하기 위한 헤더 파일
#include <stdlib.h> // 표준 라이브러리 함수들 (exit 등)을 사용하기 위한 헤더 파일
#include <ctype.h> // 문자 분류 함수들 (isdigit 등)을 사용하기 위한 헤더 파일
// 스택의 최대 크기를 정의합니다.
#define MAX_SIZE 100
// 숫자들을 저장할 스택을 위한 배열과, 스택의 꼭대기를 나타내는 인덱스를 초기화합니다.
double numStack[MAX_SIZE];
int numTop = -1; // -1은 스택이 비어있음을 의미합니다.
// 연산자들을 저장할 스택을 위한 배열과, 스택의 꼭대기를 나타내는 인덱스를 초기화합니다.
char opStack[MAX_SIZE];
int opTop = -1; // -1은 스택이 비어있음을 의미합니다.
// 주어진 연산자(op)의 우선순위를 반환하는 함수입니다.
// '+'와 '-'의 우선순위는 1, '*'와 '/'의 우선순위는 2로 설정합니다.
// 다른 문자의 경우 0을 반환합니다.
int precedence(char op) {
return (op == '+' || op == '-') ? 1 : (op == '*' || op == '/') ? 2 : 0;
}
// 연산자 op를 이용하여 두 피연산자 a, b에 대해 연산을 수행하고 결과를 숫자 스택에 push하는 함수입니다.
void applyOperator() {
// 숫자 스택에서 가장 최근에 push한 숫자(b)를 pop합니다.
double b = numStack[numTop--];
// 그 다음 숫자(a)를 pop합니다.
double a = numStack[numTop--];
// 연산자 스택에서 연산자를 pop합니다.
char op = opStack[opTop--];
double result; // 연산 결과를 저장할 변수
// 연산자에 따라 적절한 계산을 수행합니다.
if (op == '+')
result = a + b;
else if (op == '-')
result = a - b;
else if (op == '*')
result = a * b;
else if (op == '/')
result = a / b;
// 계산된 결과를 숫자 스택에 push합니다.
numStack[++numTop] = result;
}
int main() {
// 사용자로부터 입력받을 수식을 저장할 배열을 선언합니다.
char expr[256];
// 사용자에게 수식 입력을 요청합니다.
printf("수식을 입력하세요: ");
// 표준 입력으로부터 한 줄을 읽어 expr에 저장합니다.
if (fgets(expr, sizeof(expr), stdin) == NULL) {
// 입력이 실패할 경우 에러 메시지 출력 후 프로그램 종료
printf("입력 오류!\n");
return 1;
}
// 수식의 각 문자를 순차적으로 처리하기 위한 인덱스 변수
int i = 0;
// 문자열의 끝(NULL 문자)을 만날 때까지 반복합니다.
while (expr[i] != '\0') {
// 공백 문자는 무시합니다.
if (expr[i] == ' ') {
i++; // 인덱스를 증가시키고 다음 문자로 넘어갑니다.
continue;
}
// 현재 문자가 숫자인 경우 (정수 또는 실수)
if (isdigit(expr[i])) {
double num = 0; // 숫자 값을 저장할 변수 초기화
// 정수 부분을 처리합니다: 연속된 숫자들을 읽어 num에 누적합니다.
while (isdigit(expr[i])) {
num = num * 10 + (expr[i] - '0'); // 문자 -> 숫자 변환 후 누적
i++; // 다음 문자로 이동
}
// 소수점이 있을 경우 소수 부분을 처리합니다.
if (expr[i] == '.') {
i++; // 소수점 문자 건너뛰기
double factor = 0.1; // 소수점 이하 자릿수를 계산하기 위한 배수
// 소수점 이하의 숫자들을 처리합니다.
while (isdigit(expr[i])) {
num += (expr[i] - '0') * factor; // 각 자리수 값을 누적
factor *= 0.1; // 다음 자리수의 가중치를 줄임
i++; // 다음 문자로 이동
}
}
// 완성된 숫자 값을 숫자 스택에 push합니다.
numStack[++numTop] = num;
continue; // 다음 문자 처리로 넘어갑니다.
}
// 현재 문자가 왼쪽 괄호 '('인 경우, 연산자 스택에 push합니다.
if (expr[i] == '(') {
opStack[++opTop] = expr[i];
i++; // 다음 문자로 이동
continue;
}
// 현재 문자가 오른쪽 괄호 ')'인 경우,
// 왼쪽 괄호 '('가 나올 때까지 연산자 스택에 있는 연산자들을 이용하여 계산을 수행합니다.
if (expr[i] == ')') {
// opStack의 top이 '('가 될 때까지 반복합니다.
while (opTop >= 0 && opStack[opTop] != '(') {
applyOperator(); // 연산자 적용
}
// 왼쪽 괄호 '('를 스택에서 제거합니다.
opTop--;
i++; // 다음 문자로 이동
continue;
}
// 현재 문자가 연산자('+', '-', '*', '/')인 경우 처리합니다.
if (expr[i] == '+' || expr[i] == '-' || expr[i] == '*' || expr[i] == '/') {
// 스택에 있는 연산자 중, '('가 아닌 것이고 현재 연산자보다 우선순위가 높거나 같은 경우 계산을 먼저 수행합니다.
while (opTop >= 0 && opStack[opTop] != '(' &&
precedence(opStack[opTop]) >= precedence(expr[i])) {
applyOperator();
}
// 현재 연산자를 연산자 스택에 push합니다.
opStack[++opTop] = expr[i];
i++; // 다음 문자로 이동
continue;
}
// 위의 조건에 해당하지 않는 다른 문자는 무시하고 다음 문자로 넘어갑니다.
i++;
}
// 수식 전체를 처리한 후, 연산자 스택에 남아있는 모든 연산자에 대해 계산을 수행합니다.
while (opTop >= 0) {
applyOperator();
}
// 최종 결과는 숫자 스택의 top에 남아 있으므로 이를 출력합니다.
printf("결과: %lf\n", numStack[numTop]);
// 프로그램을 정상 종료합니다.
return 0;
}
<실행 결과>
'(Telechips) AI 시스템 반도체 SW 개발자 교육 > ATmega128A 마이크로컨트롤러 프로그래밍' 카테고리의 다른 글
7일차 (0) | 2025.03.12 |
---|---|
6일차 (0) | 2025.03.11 |
4일차 (0) | 2025.03.07 |
3일차 (0) | 2025.03.06 |
2일차 (0) | 2025.03.05 |