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 |