printf("ho_tari\n");

12일차 본문

2025.03.19

 

오늘의 학습 목표

1. I2C code review

    - protocol 분석

2. 자율주행 project 진행

    - 기구물 및 회로 조립

    - 수동 모드 coding

 

I2C LCD 출력

 

<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>
#include <stdio.h>		// printf
#include "button.h"
#include "led.h"
#include "uart0.h"
#include "extern.h"

// 선언 ----------------------------------------------------
void init_timer0(void);

volatile int msec_count = 0;
volatile int ultrasonic_check_timer = 0;

volatile int auto_mode = 1;
volatile int led_select = 0;


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

// interrupt service routine은 반드시 main함수 이전에 정의한다.
ISR(TIMER0_OVF_vect)
{
	TCNT0 = 6; // 6 ~ 256으로 돌리기 위해
	msec_count++;

	ultrasonic_check_timer++; // 초음파센서에 활용할 타이머!
}



int main(void)
{
	init_timer0();
	init_uart0();
	init_ultrasonic();
	stdout = &OUTPUT;	
	sei();				

	// dht11_main2();
	I2C_LCD_Test();
	

	return 0;
}



// AVR의 8bit counter timer0를 초기화한다.
// 임베디드/FPGA에서 가장 중요한건 초기화 -> init함수는 특히 신경쓰기!!
void init_timer0(void)
{
	// 분주 (divider / prescale)
	// 16MHz / 64 down!!
	// 16000000Hz / 64 = 250,000 Hz 로 만든다!
	// T(주기) = 1clock에 걸리는 시간 = 1 / 250000 = 0.000004sec = 0.004ms (4us)
	// 8bit timer overflow 발생 시 소요시간 = 0.004ms * 256 = 1.024ms = 0.001024sec
	//	-> interrupt를 정확하게 1ms마다 오게 하고싶으면, 
	//    사용자 지정으로 256이 아니라 250클록마다 인터럽트가 생기도록
	
	// TCMT0 초기값 설정
	TCNT0 = 6; // 정확히 1ms를 세기 위해 초기값을 0이 아니라 6으로 해준다. (250 펄스 이후에 인터럽트)
	
	// 분주비 설정 (64)
	TCCR0 |= 1 << CS02 | 0 << CS01 | 0 << CS00;
	
	// timer overflow interrupt 허용 (TIMER0 OVF)
	TIMSK |= 1 << TOIE0;	// 지역 인터럽트 허용

	
}

 

<I2C_LCD.c>

/*
 * I2C_LCD.c
 *
 * Created: 2020-01-07 오후 7:59:31
 *  Author: kccistc
 */ 
#define F_CPU	16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include "I2C.h"
#include "I2C_LCD.h"

void I2C_LCD_Test();

#define SLA_W (0x27<<1) //I2C LCD주소는 0x27 인데, <<1로 하는 이유는 wirite 모드를 유지하기 위함.
						// 0x27 : 0010 0111 ---> <<1하면 0100 1110
						//								  -------  : LCD 주소
						//								         - : write

void I2C_LCD_Test()
{
	I2C_LCD_init();
#if 0
	while(1)
	{
		I2C_LCD_write_string_XY(0,0,"Hello !!!"); //개행문자 쓰지마.
	}
#else  // org 
	uint8_t toggle=0;
	char sbuf[20];
	int i=0;
	
	//while(1)
	//{
		//toggle = !toggle;
		//i++;
		//i %= 100;
		//sprintf(sbuf, "%3d", i);
		//I2C_LCD_clear();
		//if (toggle)
		//{
			//I2C_LCD_write_string_XY(0,0,"Hello !!!"); //개행문자 쓰지마.
		//}
		//else
		//{
			//I2C_LCD_write_string_XY(0,0,"SEONGHO "); //개행문자 쓰지마.
		//}
		//I2C_LCD_write_string_XY(0,10,sbuf);
		//
		//_delay_ms(100); 
	//}
	while(1)
	{	
		I2C_LCD_write_string_XY(0,0,"Hello !!!"); //개행문자 쓰지마.
		I2C_LCD_write_string_XY(1,0,"SEONGHO");
		_delay_ms(100); //프로토콜에 의해 실행되므로, 데이터를 다 받을때까지 기다려야한다.
	}
#endif 
}
// 1byte를 write
void I2C_LCD_write_data(uint8_t data)
{
	char data_u, data_l;
	uint8_t data_t[4] = {0,};
		
	data_u = (data&0xf0);      // 상위 4bit 데이터
	data_l = ((data<<4)&0xf0); // 하위 4bit 데이터
	data_t[0] = data_u|0x0D;   //en=1, rs=1           |D7|D6|D5|D4|X|E|RW|RS|
	data_t[1] = data_u|0x09;   //en=0, rs=1
	data_t[2] = data_l|0x0D;   //en=1, rs=1
	data_t[3] = data_l|0x09;   //en=0, rs=1

	for(char i=0;i<4;i++){
		I2C_write_byte(SLA_W, data_t[i]);
	}
}

void I2C_LCD_write_command(uint8_t command)
{
	char data_u, data_l;
	uint8_t data_t[4];
	data_u = (command&0xf0);      // command의 상위 4bit 저장
	data_l = ((command<<4)&0xf0); // command의 하위 4bit 저장
	data_t[0] = data_u|0x0C;  //en=1, rs=0           |D7|D6|D5|D4|X|E|RW|RS|
	data_t[1] = data_u|0x08;  //en=0, rs=0
	data_t[2] = data_l|0x0C;  //en=1, rs=0
	data_t[3] = data_l|0x08;  //en=0, rs=0
	
	for(char i=0;i<4;i++){
		I2C_write_byte(SLA_W, data_t[i]);
	}
}

// 화면 clear
// 화면에 있는 내용만 지운다. 
void I2C_LCD_clear(void)
{
	I2C_LCD_write_command(COMMAND_CLEAR_DISPLAY);
	_delay_ms(2);
}

// LCD를 초기화
void I2C_LCD_init(void)
{
	I2C_init(10000);
	_delay_ms(50);
	//Initialization of HD44780-based LCD (4-bit HW)
	I2C_LCD_write_command(0x33);
	I2C_LCD_write_command(0x32);
	I2C_LCD_write_command(0x28);   //Function Set 4-bit mode
	I2C_LCD_write_command(0x0c);   //Display On/Off Control
	I2C_LCD_write_command(0x06);   //Entry mode set
	I2C_LCD_write_command(0x01);   //Clear Display
	//Minimum delay to wait before driving LCD module
	_delay_ms(10);
}
// 현재의 xy좌표에 printf처럼 스트링 값을 출력 
void I2C_LCD_write_string(char *string)
{
	uint8_t i;
	for(i=0; string[i]; i++) //"hello !!\0" 마지막 널문자에서 조건 거짓이 되어 빠져나온다.
	I2C_LCD_write_data(string[i]);
}

// 커서를 x,y좌표로 이동
void I2C_LCD_goto_XY(uint8_t row, uint8_t col)
{
	col %= 16;
	row %= 2;
	
	uint8_t address = (0x40 * row) + col;
	uint8_t command = 0x80 + address;
	
	I2C_LCD_write_command(command);
}

// x,y좌표로 이동을 하고 string값을 출력 한다. 
void I2C_LCD_write_string_XY(uint8_t row, uint8_t col, char *string)
{
	I2C_LCD_goto_XY(row, col);
	I2C_LCD_write_string(string);
}

 

<I2C.c>

/*
 * I2C.c
 *
 * Created: 2020-01-07 오후 7:58:00
 *  Author: kccistc
 */ 
#include <avr/io.h>
#include "I2C.h"

void I2C_init(unsigned int baud){
	TWBR = baud;
}

void I2C_start(void)
{
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	while (!(TWCR & (1<<TWINT)));  // 시작 완료 대기
}

void I2C_transmit(uint8_t data)
{
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);
	while (!(TWCR & (1<<TWINT)));
}

void I2C_write_byte(uint8_t address, uint8_t data)
{
	I2C_start();
	I2C_transmit(address);
	I2C_transmit(data);
	I2C_stop();
}

void I2C_stop(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN)| (1<<TWSTO);
}

uint8_t I2C_receive_ACK(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN) |(1<<TWEA);
	
	while( !(TWCR & (1<<TWINT)));             // 수신 완료 대기
	
	return TWDR;
}

uint8_t I2C_receive_NACK(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN);
	
	while( !(TWCR & (1<<TWINT)));             // 수신 완료 대기
	
	return TWDR;
}

 

<I2C.h>

/*
 * I2C.h
 *
 * Created: 2020-01-07 오후 7:57:06
 *  Author: kccistc
 */ 


#ifndef I2C_H_
#define I2C_H_
void I2C_init(unsigned int baud);
void I2C_start(void);
void I2C_transmit(uint8_t data);
void I2C_write_byte(uint8_t address, uint8_t data);
void I2C_stop(void);
uint8_t I2C_receive_ACK(void);
uint8_t I2C_receive_NACK(void);




#endif /* I2C_H_ */

 

<I2C_LCD.h>

/*
 * I2C_LCD.h
 *
 * Created: 2020-01-07 오후 8:00:34
 *  Author: kccistc
 */ 


#ifndef I2C_LCD_H_
#define I2C_LCD_H_
#define COMMAND_CLEAR_DISPLAY	0X01

#define COMMAND_DISPLAY_ON_OFF_BIT	2
#define COMMAND_CURSOR_ON_OFF_BIT	1
#define COMMAND_BLINK_ON_OFF_BIT	0

#define START 0x08
#define SLA_W (0x27<<1)    // I2C LCD 주소 0x27 , <<1 이유는 write모드 유지
#define SLA_R (0x27<<1 | 0x01)       // I2C LCD 주소 0x27 , Read모드 유지

void I2C_LCD_init(void);
void I2C_LCD_write_data(uint8_t data);
void I2C_LCD_write_command(uint8_t command);
void I2C_LCD_clear(void);
void I2C_LCD_write_string(char *string);
void I2C_LCD_goto_XY(uint8_t row, uint8_t col);
void I2C_LCD_write_string_XY(uint8_t row, uint8_t col, char *string);




#endif /* I2C_LCD_H_ */

 

<extern.h>

/*
 * extern.h
 *
 * Created: 2025-03-19 오전 10:25:53
 *  Author: microsoft
 */ 


#ifndef EXTERN_H_
#define EXTERN_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 void init_ultrasonic(void);
extern void distance_ultrasonic(void);

extern int led_state;
extern void (*funcs[])(void);
extern volatile uint8_t rx_message_received;

extern void dht11_main(void);
extern void I2C_LCD_Test(void);



#endif /* EXTERN_H_ */

 

<실행 결과>

 

I2C 오실로스코프 파형 분석 (전화번호 뒷자리 2개)

<I2C_LCD.c>

/*
 * I2C_LCD.c
 *
 * Created: 2020-01-07 오후 7:59:31
 *  Author: kccistc
 */ 
#define F_CPU	16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include "I2C.h"
#include "I2C_LCD.h"

void I2C_LCD_Test();

#define SLA_W (0x27<<1) //I2C LCD주소는 0x27 인데, <<1로 하는 이유는 wirite 모드를 유지하기 위함.
						// 0x27 : 0010 0111 ---> <<1하면 0100 1110
						//								  -------  : LCD 주소
						//								         - : write

void I2C_LCD_Test()
{
	I2C_LCD_init();
	
#if 1
	while(1)
	{
		I2C_write_byte(SLA_W, '5');
		I2C_write_byte(SLA_W, '1');
		_delay_ms(1000);
	}
#else  // org 
	uint8_t toggle=0;
	char sbuf[20];
	int i=0;
	
	while(1)
	{
		toggle = !toggle;
		i++;
		i %= 100;
		sprintf(sbuf, "%3d", i); // itoa 또는 sprintf를 통해 아스키로 전환해야00함
		I2C_LCD_clear();
		if (toggle)
		{
			I2C_LCD_write_string_XY(0,0,"Hello !!!"); //개행문자 쓰지마.
		}
		else
		{
			I2C_LCD_write_string_XY(0,0,"SEONGHO "); //개행문자 쓰지마.
		}
		I2C_LCD_write_string_XY(0,10,sbuf);
		
		_delay_ms(1000); 
	}
	//while(1)
	//{	
		//I2C_LCD_write_string_XY(0,0,"Hello !!!"); //개행문자 쓰지마.
		//I2C_LCD_write_string_XY(1,0,"PARK SEONG HO");
		//_delay_ms(100); //프로토콜에 의해 실행되므로, 데이터를 다 받을때까지 기다려야한다.
	//}
#endif 
}
// 1byte를 write
void I2C_LCD_write_data(uint8_t data)
{
	char data_u, data_l;
	uint8_t data_t[4] = {0,};
		
	data_u = (data&0xf0);      // 상위 4bit 데이터
	data_l = ((data<<4)&0xf0); // 하위 4bit 데이터
	data_t[0] = data_u|0x0D;   //en=1, rs=1           |D7|D6|D5|D4|X|E|RW|RS|
	data_t[1] = data_u|0x09;   //en=0, rs=1
	data_t[2] = data_l|0x0D;   //en=1, rs=1
	data_t[3] = data_l|0x09;   //en=0, rs=1

	for(char i=0;i<4;i++){
		I2C_write_byte(SLA_W, data_t[i]);
	}
}

void I2C_LCD_write_command(uint8_t command)
{
	char data_u, data_l;
	uint8_t data_t[4];
	data_u = (command&0xf0);      // command의 상위 4bit 저장
	data_l = ((command<<4)&0xf0); // command의 하위 4bit 저장
	data_t[0] = data_u|0x0C;  //en=1, rs=0           |D7|D6|D5|D4|X|E|RW|RS|
	data_t[1] = data_u|0x08;  //en=0, rs=0
	data_t[2] = data_l|0x0C;  //en=1, rs=0
	data_t[3] = data_l|0x08;  //en=0, rs=0
	
	for(char i=0;i<4;i++){
		I2C_write_byte(SLA_W, data_t[i]);
	}
}

// 화면 clear
// 화면에 있는 내용만 지운다. 
void I2C_LCD_clear(void)
{
	I2C_LCD_write_command(COMMAND_CLEAR_DISPLAY);
	_delay_ms(2);
}

// LCD를 초기화
void I2C_LCD_init(void)
{
	I2C_init(10000);
	_delay_ms(50);
	//Initialization of HD44780-based LCD (4-bit HW)
	I2C_LCD_write_command(0x33);
	I2C_LCD_write_command(0x32);
	I2C_LCD_write_command(0x28);   //Function Set 4-bit mode
	I2C_LCD_write_command(0x0c);   //Display On/Off Control
	I2C_LCD_write_command(0x06);   //Entry mode set
	I2C_LCD_write_command(0x01);   //Clear Display
	//Minimum delay to wait before driving LCD module
	_delay_ms(10);
}
// 현재의 xy좌표에 printf처럼 스트링 값을 출력 
void I2C_LCD_write_string(char *string)
{
	uint8_t i;
	for(i=0; string[i]; i++) //"hello !!\0" 마지막 널문자에서 조건 거짓이 되어 빠져나온다.
	I2C_LCD_write_data(string[i]);
}

// 커서를 x,y좌표로 이동
void I2C_LCD_goto_XY(uint8_t row, uint8_t col)
{
	col %= 16;
	row %= 2;
	
	uint8_t address = (0x40 * row) + col;
	uint8_t command = 0x80 + address;
	
	I2C_LCD_write_command(command);
}

// x,y좌표로 이동을 하고 string값을 출력 한다. 
void I2C_LCD_write_string_XY(uint8_t row, uint8_t col, char *string)
{
	I2C_LCD_goto_XY(row, col);
	I2C_LCD_write_string(string);
}

 

<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>
#include <stdio.h>		// printf
#include "button.h"
#include "led.h"
#include "uart0.h"
#include "extern.h"

// 선언 ----------------------------------------------------
void init_timer0(void);

volatile int msec_count = 0;
volatile int ultrasonic_check_timer = 0;

volatile int auto_mode = 1;
volatile int led_select = 0;


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

// interrupt service routine은 반드시 main함수 이전에 정의한다.
ISR(TIMER0_OVF_vect)
{
	TCNT0 = 6; // 6 ~ 256으로 돌리기 위해
	msec_count++;

	ultrasonic_check_timer++; // 초음파센서에 활용할 타이머!
}



int main(void)
{
	init_timer0();
	init_uart0();
	init_ultrasonic();
	stdout = &OUTPUT;	
	sei();				

	// dht11_main2();
	I2C_LCD_Test();
	

	return 0;
}



// AVR의 8bit counter timer0를 초기화한다.
// 임베디드/FPGA에서 가장 중요한건 초기화 -> init함수는 특히 신경쓰기!!
void init_timer0(void)
{
	// 분주 (divider / prescale)
	// 16MHz / 64 down!!
	// 16000000Hz / 64 = 250,000 Hz 로 만든다!
	// T(주기) = 1clock에 걸리는 시간 = 1 / 250000 = 0.000004sec = 0.004ms (4us)
	// 8bit timer overflow 발생 시 소요시간 = 0.004ms * 256 = 1.024ms = 0.001024sec
	//	-> interrupt를 정확하게 1ms마다 오게 하고싶으면, 
	//    사용자 지정으로 256이 아니라 250클록마다 인터럽트가 생기도록
	
	// TCMT0 초기값 설정
	TCNT0 = 6; // 정확히 1ms를 세기 위해 초기값을 0이 아니라 6으로 해준다. (250 펄스 이후에 인터럽트)
	
	// 분주비 설정 (64)
	TCCR0 |= 1 << CS02 | 0 << CS01 | 0 << CS00;
	
	// timer overflow interrupt 허용 (TIMER0 OVF)
	TIMSK |= 1 << TOIE0;	// 지역 인터럽트 허용

	
}

 

<실행 결과>

 

자율주행차 조립 및 납땜

 

 

ATmega128A 이용

 

정면 초음파

- ECHO : PORTE 5

- TRIG : PORTA 1

 

왼쪽 츠음파

- ECHO : PORTE 4

- TRIG : PORTA 0

 

오른쪽 초음파

- ECHO : PORTE 6

- TRIG : PORTA 2

 

부저

- VCC : PORTE 3

- GND : GND

 

블루투스

- RX : RX

- TX : TX

- VCC : VCC

- GND : GND

 

FND

- D1, D2, D3, D4 : PORTF 4, 5, 6, 7

- A, B, C, D, E, F, G, DP : PORTC 0, 1, 2, 3, 4, 5, 6, 7

 

I2D_LCD

- SCL : PORTD 0

- SDA : PORTD 1

 

LED

- VCC : PORTG 3

- GND : GND

 

BUTTON

- PORTA 6

- VCC

 

MOTOR DRIVER

- IN1, IN2, IN3, IN4 : PORTF 0, 1, 2, 3

- ENA : PORTB 5

- ENB : PORTB 6

 

BUZZER

- VCC : PORTE 3

- GND : GND

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

13일차  (0) 2025.03.20
11일차  (0) 2025.03.18
9일차  (0) 2025.03.16
8일차  (0) 2025.03.13
7일차  (0) 2025.03.12