원래 KUT-128에는 LCD가 바로 연결되지 않고 LCD 커넥터에다가 연결하도록 되어있다.

시뮬레이터 상에서는 만들 필요 없으니 그냥 바로 LCD 를 연결시켜서 만들었다.

회로 자체 만드는건 어렵지는 않았는데,

LCD 써보려고 하니까 글이 잘 안읽히더라

조금 찾아보니까 이분이 LCD 내용을 잘 정리하셨더라.

https://kogun.tistory.com/23

 

AVR(ATmaga128)로 텍스트LCD 장난하기

안녕하세요 고군입니다~ 이번 시간에는 텍스트LCD에 대하여 알아보고 AVR을 사용하여 화면을 표시해보도록 하겠습니다. LCD는 저렴한 가격으로 인해 산업현장에서는 아직도 많이 사용되고 있으

kogun.tistory.com

 

내가 정리한것 보다는 위 링크 보는게 이해하기 좋을거같다.

 

 

LCD 핀

- RS 핀 : 0은 명령어, 1은 데이터  

- DB4~7핀 : 8비트 or 4비트 모드 사용가능한데 4비트 모드 사용시 이 4개 핀 사용

- RW 핀 : 0이 W, 1이 R

 

LCD 

- 레지스터들 : 8비트 IR(명령), DR(데이터)가 존재. RS핀으로 선택

- IR : DDRAM(디스플래이 데이터), CGRAM(문자 생성) 주소 설정,메모리클리어, 디스플래이 onoff 명령어 등 CPU가 쓴다.

- DR : DDRAM, CGRAM에 쓴 데이터나 읽은 데이터 일시 저장. DR에 쓴 데이터는 DDRAM, CGRAM에 써진다. 렘으로부터 읽을때 IR에 주소를 넣으면 DR에 들어옴

- Busy Flag BF : LCD가 명령 받을수 있는지 표현. BF가 1이면 못받음. 0이면 받을 수 있음.RS=0, RW=1이면 BF 읽음

- DDRAM display data ram : 8비트 문자 데이터 표시. 16 x 2 lcd이나 실제로는 최대 80글자 기억

- CGROM 문자 생성 롬 : 5x7, 5x10 크기의 도트 문자 보관. CGROM의 문자 코드를 DDRAM에 써주면 표시됨.

- CGRAM 문자 생성 램 : 사용자가 정의한 문자를 보관. 8개 까지.

- 어드레스 카운터 : DDRAM, CGRAM 어드레스 지정시 사용. 

 

아래의 링크에서도 정말 잘 정리되어 있으니 참고하는게 좋겠다.

https://electronicsdo.tistory.com/entry/ATmega128%EC%8B%A4%EC%A0%84-CLCD-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

https://electronicsdo.tistory.com/entry/ATmega128%EC%8B%A4%EC%A0%84-CLCD-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

 

코드는 그대로 한거같은데 무슨이유에서인지 동작하지 않는다..

 

아무래도 비트 단위로 적어가면서 확인해봐야겠다.

 

명령어 하나 당 실행 시간이 40us인데 40us를 대기하는게하나도 없다.

 

일단 이제 0617 00시 됬으니 잠시 중지

 

-----

 

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

//function set 인터페이스 길이, 표시 행 수, 문자 폰트 설정
// DL=1은 8비트, DL=0은 4비트, 1 or 2줄, 도트 크기
//   dl NF    -> DL = 0, NF : 10 -> 2 라인, 5 x 7도트 
// 0010 1000 
#define FUNCSET	0x28
//entry mode set 110
//I/D = 1 어드레스 + 1하고 커서 우측 이동
//S = 0, DDRAM에 데이터 쓴 후 화면 좌 또는 우이동?
#define ENTMODE	0x06	// entry mode set
#define ALLCLR	0x01	//all clear
//display on/off
// 0000 1100 = 0x0c
// 0000 1DCB 
// D= 1 display on, C=1 cursor on, B=1 cursor Blink
#define DISPON	0x0E	//display on
#define DISPOFF 0x04	//display off
#define LINE1	0x80
#define LINE2	0xC0	//2nd line move

void lcd_init();
//flash 는 flash 변수. flash 메모리에 저장
void lcd_string(char*);
void busy();
void command(unsigned char);
void data(unsigned char);

void main(){
	lcd_init();
	
	/*
	while(1){
		command(DISPOFF);
		_delay_us(100);
		command(DISPON);
		_delay_us(100);
	}
	*/
	
	lcd_string("hello! kut-128");
	command(LINE2);
	lcd_string("atmel atmega128");
	while(1);
}

void lcd_init(){
	DDRD = 0xff;
	PORTD &= 0xfb;
	
	_delay_ms(20);
	command(0x30);
	_delay_ms(10);
	command(0x30);
	_delay_ms(1);
	command(0x30);
	_delay_ms(1);
	command(0x20);
	_delay_ms(1);
	
	command(FUNCSET);
	command(DISPON);
	command(ALLCLR);
	command(ENTMODE);
}

void lcd_string(char *str){
	char *pstr = 0;
	
	pstr = str;
	while(*pstr)
		data(*pstr++);
}

void command(unsigned char byte){
	busy();	
	PORTD = (byte & 0xf0);
	PORTD &= 0xfe;
	PORTD &= 0xfd;
	_delay_ms(1);
	PORTD |= 0x04;
	_delay_ms(1);
	PORTD &= 0xfb;
	
	
	PORTD = ((byte<<4) & 0xf0);
	PORTD &= 0xfe;
	PORTD &= 0xfd;
	_delay_ms(1);
	PORTD |= 0x04;
	_delay_ms(1);
	PORTD &= 0xfb;

	/*
	busy();
	
	// 인스트럭션 상위 바이트
	PORTD = (byte & 0xf0);	//데이터
	PORTD &= 0xfe;			//rs=0
	PORTD &= 0xfd;			//rw=0;
	_delay_us(1);
	PORTD |= 0x04;			//e = 1;
	_delay_us(1);
	PORTD &= 0xfb;			//e = 0;
	
	//인스트럭션 하위 바이트
	PORTD = ((byte << 4) & 0xf0);
	PORTD &= 0xfe;
	PORTD &= 0xfd;
	_delay_us(1);
	PORTD |= 0x04;
	_delay_us(1);
	PORTD &= 0xfb;
	*/
}

void data(unsigned char byte){
	busy();
	PORTD = (byte & 0xf0);
	PORTD |= 0x01;
	PORTD &= 0xfd;
	_delay_ms(1);
	PORTD |= 0x04;
	_delay_ms(1);
	PORTD &= 0xfb;
	
	PORTD = ((byte<<4) & 0xf0);
	PORTD |= 0x01;
	PORTD &= 0xfd;
	_delay_ms(1);
	PORTD |= 0x04;
	_delay_ms(1);
	PORTD &= 0xfb;
	/*
	busy();
	
	PORTD = (byte & 0xf0);
	PORTD |= 0x01;
	PORTD &= 0xfd;
	_delay_us(1);
	PORTD |= 0x04;
	_delay_us(40);
	PORTD &= 0xfb;
	
	PORTD = ((byte << 4) & 0xf0);
	PORTD |= 0x01;
	PORTD &= 0xfd;
	_delay_us(1);
	PORTD |= 0x04;
	_delay_us(40);
	PORTD &= 0xfb;
*/
}


void busy(){
	_delay_ms(2);
	//_delay_us(400);
}

 

----------

지금 시간이 일요일 새벽 1시

결국 lcd 4비트 모드로 제어하려고 별짓을 다해봤지만 안됬다.

 

구글링해서 봐도 영어 실력이 부족해서 잘 읽혀지지가 않았다.

코드를 다 뜯어고쳐서 써보자니 이럴때는 아얘 다날리고 쓰는게 맞긴한데

그러기는 너무 싫더라

코드 바꾸기는 싫고 이래저래 시뮬레이션 시간, 딜레이 시간 조절해가면서 이래저래 시도해보다보니 시간이 이렇게 됬다.

 

며칠간 이걸로 한참 삽질했는데

그냥 디버깅 툴이랑 주변 장치 컨트롤러를 여러 라인에 딜레이 주는 식으로 어떻게 초기화하고 값을 입력시키는지 이해한걸로 만족해야 될거같다.

나중에 기회되면 다시 시도해야지.

 

이 다음에는 모터 시뮬도 돌리려했지만 타이머/카운터를 다루는 내용이 나온다.

이 부분은 학교 수업에서 진행하니 나중에 진도 나가고 하고 싶어지면 해야지.

 

일단 프로테우스 아트메가 시뮬레이션 글은 여기까지로 마쳐야겠다.

이 다음에는 임베디드 간단하고 넓게 정리해보고,

나빌로스 만들기 나 학교에 있는 임베디드 키트 가지고 놀아볼 생각이다.

아날로그 비교기 추가

- 우측 아래에 가변 저항 2개가 AIN0, AIN1에 연결되어 있다.

 

아날로그 비교기 예제 1

- 풀링 방식으로 비교 후 LED shift

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>


/*
AIN1 핀 가변 저항과 AIN0 핀 가변저항 조절
AIN0 >= AIN1 -> LED 순차 점멸
아날로그 비교기 출력 ACO를 풀링으로 체크
*/
unsigned char led = 0xfe;

int main()
{
	DDRC = 0xff;
	DDRE = 0b00000010; //AIN(PE2), AIN1(PE3) 입력 설정
	ACSR = 0x0; // Analog Comparator Control and Status Registor
	SFIOR = 0x0; // Special Function IO Registor 
				 // 3번째 비트 ACME = 0 -> AIN1에 음 입력 전압
	
	PORTC = led;
	
	while(1){
		if(ACSR & 0x20){ // ACSR의 0x20 = ACO 아날로그 비교기 출력 비트가 1인지 확인
			led <<= 1;
			led |= 0x01;
			if(led == 0xff)
				led = 0xfe;
			PORTC = led;
			_delay_ms(1000);
		}
		
	}
}

 

풀링 방식은 잘 되는데,

인터럽트 방식에서는 토글이 잘 안되선지 인터럽트가 한번만 동작하고 만다. 그냥 pass

 

 

ADC도 fnd 쓰기도하고, 입력 받는데서 자꾸 워닝뜨길래 pass

 

 

 

USART

- atmega128에서는 USART 2개 내장, 클럭부, 송신부, 수신부로 구성

- 관련 레지스터로 UDR(IO 데이터 레지스터), UCSRA/B/C(제어 상태 레지스터) 3개, 보레이트 레지스터 UBRRH, UBRRL 2개로 총 6개

- UDR을 송신 데이터 버퍼 레지스터, 수신 데이터 버퍼 레지스터 두 용도로 사용, 같은 주소를 가지나 실제로는 분리되어 송신시 송신 데이터 버퍼 TXBn에 쓰고, 수신시 수신 데이터 버퍼 RXBn의 값을 읽는다.

 

USART 데이터 포맷과 통신 순서

1. 제어 레지스터로 통신 설정(동기/비동기, 데이터 비트 수, 정지 비트수 등)

2. 보레이트 레지스터로 전송속도 설정

 

 

 

USART를 추가한 KUT-128 회로

- 주의사항 : virtual terminal에서 rx/tx polarity를 inverted로 설정해야한다. normal로 했더니 보드레이트나 클럭을 잘 맞춰도 글자가 깨진다.

- 이걸 normal로 해놓고 rxd, txd를 거꾸로 연결해도 동작이 안된다.

 

USART 송신 하기

- 보레이트 9600bps, 정지비트 1비트, 패리티 x, 비동기 데이터 8비트

- 추가 : 아 그냥 문자열만 주면 줄바꿈이 안되서 뒤에 \r\n을 추가했다.

ref : https://www.avrfreaks.net/forum/uart-newline-n-issue

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

void putch(char);

int main()
{
	char str[] = "This is USART0 control program\r\n";
	char *pstr;
	
	UCSR0A = 0x0;
	UCSR0B = 0b00001000;	//송신 인에이블 txen = 1 ->TxDn이 범용 IO포트가 아닌 송신 단자로 동작
	UCSR0C = 0b00000110;	// 비동기 데이터 8비트 모드	bit2/1 전송 데이터 비트 수 설정 11 -> 데이터 전송 8비트
	//UBRR 보 레이트 레지스터
	UBRR0H =0;				//Xtal = 16hz시 buad = 9600
	UBRR0L = 103;
	
	pstr = str;
	while(1){
		while(*pstr){
			putch(*pstr++);
			_delay_ms(1000);
		}
		pstr = str;
		_delay_ms(1000);
	}
}


void putch(char data){
	/*
	UDRE0 = 1?   UCRS0A의 5번째 비트 UDRE
	USART Data Register Emtry로 비어있으면 1 = 송신 준비 완료 인터럽트 발생
	*/
	while(!(UCSR0A & 0x20)){
		_delay_ms(1000);
	}
	// st -> 0 ~ 7(8개 데이터비트) -> (페리티 설정 x) -> stop bit(현재 1개)
	UDR0 = data;				// USART IO register에 데이터 전송
}

 

 

 

virtual terminal test 하기

 USART 송신 후 송수신 실험을 하려고 했는데, 입력을 해도 제대로 동작이 안됬다.

terminal에 입력한게 재대로 전송이 되고 있는지 확인하기 위해 잠깐 구글링 해봤더니

두 터미널을 연결해서 타이핑하면 반영이 되더라 아래 링크는 그 내용

ref: https://electronics.stackexchange.com/questions/420487/cant-type-on-virtual-terminal-proteus

추가 : 또 보다보니 실제 COM 포트를 프로테우스 시뮬레이션 상으로 연동해주는게 있었다.

기회되면 한번 만져봐도 좋을거같다.

ref : https://www.theengineeringprojects.com/2013/05/how-to-use-virtual-terminal-in-proteus.html

 

 

USART 송수신 테스트

- 지금 실제 kut-128 키트가 아닌 proteus 시뮬레이션 환경에서만 사용하다보니, 예제처럼 제대로 동작되지 않는다.

- 애초에 가상 터미널 1개에다가 송신 모드로 잡아놓고  쓰다보니 그 터미널을 echo type 설정을 해서 쓴다고 USART0로부터 값을 수신을 못하니 원하는데로 동작안하는게 당연했다.

- 그래서 atmega128에서는 USART 2개가 제공되므로 가상 터미널 2개를 놓고, 하나는 타이핑 입력 송신 터미널, 남은 하나는 결과 수신 터미널로 잡고 약간 회로와 코드를 변경해서 구현했더니 잘 동작하더라.

 

 

- 천천히 칠땐 괜찮지만 비동기 통신에다가 패리티 체크도 안하다보니, 타이핑을 빠르게 하면 수신 터미널에서 제대로 전달받지 못한다.

 

 

 

마지막으로 이걸 인터럽트로 바꿔보자

고치다가 찾은건데 USART 하나로 송수신 할수 있더라.

이게 전이중 방식이 가능한걸 잊었다.

----

인터럽트 처리가 잘 안되서 일단 여기까지 하고 해결하면 추가 예정

 

--------

220616-1821

아직 인터럽트 문제는 해결 못했고, 아래 표의 포멧처럼 USART 데이터가 날라가는지 보기위해 한번 실험해보았다.

전송 속도는 9600bps이니까

1bit 전송에 대강 0.1ms 정도 걸리겠다.

아래의 코드 대로 first, second, third 처럼 찍어서 보내게 했다.

 

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>


unsigned char first = 0b00110001;
unsigned char second = 0b00110010;
unsigned char third = 0b10000001;
unsigned char fourth = 0b10100101;

int main()
{
	DDRC = 0xff;
	PORTC = 0xff;
	// 송신 to ocsil
	UCSR0A = 0x0;
	//송신 완료 인터럽트 벡터 인에이블, 송신 인에이블 txen = 1 ->TxDn이 범용 IO포트가 아닌 송신 단자로 동작
	UCSR0B = 0b01001000;
	// 비동기 데이터 8비트 모드	bit2/1 전송 데이터 비트 수 설정 11 -> 데이터 전송 8비트
	UCSR0C = 0b00000110;
	//UBRR 보 레이트 레지스터
	UBRR0H = 0;	
	UBRR0L = 103;

	while(1){
		putch(first);
		_delay_ms(20);
		putch(second);
		_delay_ms(20);
		putch(third);
		_delay_ms(20);
		putch(fourth);
		_delay_ms(1000);
	}
}

void putch(char data){
	while(!(UCSR0A & 0x20));
	UDR0 = data;
}

 

프로테우스에서 제공하는 오실로스코프 크기 조절이 안되서

일단 전체 USART 데이터를 볼수 있도록 스케일을 낮춰놨다.

대강 1bit당 0.1ms 가까이 나온다.

 

 

unsigned char first = 0b00110001;
unsigned char second = 0b00110010;
unsigned char third = 0b10000001;
unsigned char fourth = 0b10100101;

 

first 변수에는 1을 2진화 한 값을 담아 보냈다.

이걸 USART 프레임으로 만들면 데이터는 8비트, 스탑 비트 스타트비트는 각각 1개이니 총 10비트로

9600bps이므로 1개는 0.1ms 이고, 10개이므로 1ms 정도 걸린다.

 

max232에서는 반전된 결과가 나오고 있어서 원래 뒤집히도록 나오는걸 알고 있었지만

왜 데이터 비트자리에 0b00110001를 반전해서 안나오나 싶었는데

위의 프레임 포멧을 자세히 보면 데이터 비트 LSB가 가장 앞에서 나오고 있으므로

00110001이 반대로 10001100으로 나오고 앞뒤로 스타트 스탑 비트가 붙는거였다.

  

 

 

 

나머지 값도 이런식으로  나올테니 넘어가고

원래는 왜 인터럽트 처리가 안되는지 보려고 했는데

지금 와서 보니까 이전에 저장한 프로젝트의 코드까지 현재 코드의 내용들로 덮어씌워져서 다날라갔다.

여기다가 중간 중간에 코드블록 해서 올렸는데, 폴링 방식으로 송수신한 코드도 사라졌다.

 

일단 USART는 여기까지 봤으니 통신은 넘어가고 나머지 하드웨어도 다뤄봐야겠다. 

프로테우스로 ATmega128 개발 키트 KUT-128 구현

- 인터럽트 처리를 위한 스위치, LED, FND 4개 정도만 일단 구현함

기본 블링크 예제

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>


int main()
{
	DDRC = 0xff;
	while(1){
		PORTC = 0xf0;
		_delay_ms(500);
		PORTC = 0x0f;
		_delay_ms(500);
	}
}

 

 

기본 FND 예제

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

int main()
{ 
   char i, val;

   DDRB = 0xff;
   DDRF = 0xf0;
   PORTF = 0b11100000;
   
   while(1){
      val = 0b00000001;
      for(i = 0; i < 7;i++){
	 PORTB = val;
	 _delay_ms(500);
	 val = val << 1;
      }
   }
   
   return 0;
}

 

 

 

 

FND 9999 시뮬레이션 실패?

- FND 4개로 9999찍으려 했지만 _delay_ms를 낮게 설정해서 real time으로 동작되지 않는다.

- 거기다 실제 하드웨어처럼 잔상이 남지 않아 4개의 수가 동시에 나오지 않는다.

 

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

char seg_pat[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
		  0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};

void seg4_out(int);
		  
int main()
{ 
	int num = 0;
	
	DDRB = 0xff;
	DDRF = 0b11110000;
	while(1){
		seg4_out(num);
		num++;
		if (num > 9999)
			num = 0;
	}
}

void seg4_out(int num){
	int i, N1000, N100, N10, N1, buf;
	
	N1000 = num / 1000;
	buf = num % 1000;
	N100 = buf / 100;
	buf = buf % 100;
	N10 = buf / 10;
	N1 = buf % 10;
	
	for(i=0;i < 10;i++){
		PORTF = 0b11100000;
		PORTB = seg_pat[N1];
		_delay_ms(10);
		
		PORTF = 0b11010000;
		PORTB = seg_pat[N10];
		_delay_ms(10);
		
		PORTF = 0b10110000;
		PORTB = seg_pat[N100];
		_delay_ms(10);
		
		PORTF = 0b01110000;
		PORTB = seg_pat[N1000];
		_delay_ms(10);
	}
}

 

fnd 2개도 제대로 안된다. 인터럽트랑 같이 쓰고싶었는데

 

 

 

FND, INT 예제

- FND 2개도 못쓰니 그냥 FND 여러개 달린걸 써야할거같긴한데 그러면 원래 KUT-128 보드를 바꾸는거라 어떻게 할지 고민이다.

- 대신 외부 인터럽트로 FND 1개의 숫자 올리는 예제를 구현했다.

- 근데 mega128.h가 아닌 avr/interrupt.h를 사용해서 기존 보던 책과는 조금 다르게 ISR(INT4_ vect)로 인터럽트 정의한다.

- 근데 전역 변수 N1을 선언하고 썻는데 제대로 인터럽트 동작이 안되더라.

 => 아래의 링크를 보니 전역 변수에 volatile을 줬더니 문제없이 동작한다. 찾아보니 최적화 과정에서 지워졌나보다.

https://exploreembedded.com/wiki/AVR_External_Interrupts

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

char seg_pat[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
		  0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};

volatile int N1 = 0;
		  
int main()
{
	DDRB = 0xff;
	DDRC = 0xff;
	DDRF = 0b11110000;
	
	EICRB = 0b00000010;
	EIMSK = 0b00010000;
	SREG = 0x80;
	PORTF = 0b11100000;
	
	while(1){
		PORTB = seg_pat[N1];
		PORTC = 0xff;
		_delay_ms(1000);
	}
}

ISR(INT4_vect){
	N1 = (N1 + 1) % 10;
}

 

진도를 더 나가고 싶긴한데, 책을 두고와서 더 나갈수가 없다 ;;

내일 타이머랑 통신으로 넘어가야지

프로테우스 짱짱맨

아두이노, 센서 없어도 시뮬레이션으로 돌려볼수 있다.

아두이노 뿐만 아니라 다른 MCU, 라즈베리파이도 가능

 

 

LCD LM016L 사용하기

- RS 레지스터 선택 : 12번 핀
- E Enable : 쓰기
- 4비트 or 8비트 모드 사용 가능 : 4비트 모드 사용

 


'

 

 

 

초음파 센서 시뮬레이션

 

 

DC 모터를 이용한 선풍기

 

 

 

 

서보모터 제어하기

- SG90 서보모터 : 180도 회전, PW가 1 ~ 2ms 변화-> HIGH 1ms는 -90도, 2ms는 90도

- 이건 잘 동작안하는데, 올바른 서보모터 파츠를 찾아야할거같다.

 

 

+ Recent posts