CLCD를 사용하여 좌우명 새기기 라는 강좌를 진행하며 정리한 글입니다.
출처 : cafe.naver.com/laydigital/
안녕하세요 8.2 Clcd 강좌를 진행후 궁금증이 생겨서 질문드립니다.
강좌에서 Write timing diagram을 토대로 timing 에따라 코드를 작성하다 실제로 제가 주는 _delay(sec)시간이 과연
정확한 시간인가하고 의아해졌습니다.
실제로 init_clcd 에서는 그대로 _delay()라이브러리 함수의 기능을사용하여 값을 datasheet value 그대로 주었고,
_delay_ms(1.53) or _delay_ms(2), _delay_us(39).. 등등 Write timing diagram에서는 시간단위가 ns 인데 MPU: 16MHZ
1clock당 62.5ns 로 계산하면 기본적으로 1clock에서 ~? clock 까지 한코드를 실행하는데 드는 clock 만 가지고도 딜레이 없이 사용해도 괜찮은건지 궁금합니당. 제가 작성한 코드는 아래와 같이.. write_dsp_timing 에서 table 순서대로만 코드를 적고 마지막에 data를 입력 시키는 delay만 길게 잡고 실행해서 data를 화면에 표시를 했습니다. 마지막 delay 값을 ns 단위로 줬는데 data가 다 입력되지 않고 잘려서 들어갑니다.
여기서 궁금증을 정리하면..
질문1) 정확히 확인해보지는 못했지만 사실 방법도 아직 잘 모르지만 코드가 컴파일되면 1clock당 수행되는 코드형태로
바뀐다고 알고 있는데요 (assembly형태) 순서대로 딜레이 함수 추가 하지않고 기본적으로 한코드문 실행할경우
1clock~ 3clcok(16MHz = 62.5ns/1clock )으로 가정하고 순서대로 코드하여 사용해도 괜찮은건지 일단 궁금합니다.
여기서 좀 문제되는건 제가 작성한 코드가 각코드문에서 몇 clock씩 소요되는지 아직 확인 방법을 몰라 좀 애매합니다..
질문2) write_dsp_timing 마지막 delay값으로 그냥 적당한 값을 넣어주었는데요 (몇번 해보고 되는 값으로..) 실제로
delay 함수를 사용하여 write_dsp_timing 함수상에서 마지막으로 넣어줘야 하는 delay 값을 어림잡아서라도 계산값 으로 해보고
싶은데 영 어떻게 계산할지 방법이 않떠오릅니다 어떤식으로 계산해서 넣어주면 괜찮을지 힌트라도 주시면 감사하겠습니다.
질문3) datasheet 상에서 E Rise/Fall time 이값의 의미는 강좌에 설명되어져 있는데요, 실제로 이값은
어떤식으로 사용되는건지 궁금합니다. Max 20 (ns), 설명은 있는데 활용하는 경우가 없어서(?), 몰라서 이해하기 좀 난해합니다.
코딩할때나 하드웨어를 구성할때나 어떤경우에 이용되는건지 아니면 그냥 참고사항인지, 실제로 wiki나 각종블로그에는
화려한 공식들이 등장하는데요 이값이 어떤 쓰임이 있는지 알려주시면 감사하겠습니다.
-------------------------------------------------------------------------------------------------
아래는 연습용으로 첫번째 줄의 문자열이 한칸씩 >> 쉬프트하는 연습을 코드로 해보았습니다. 더 괜찮은 방법이 있다면 알려주시면 감사하겠습니다. 쉬프트하면 전에 있던 첫주소의 문자가 계속 중첩되서 다시 지우고 새로 쓰는 형태로 해보았습니다.
-----------------------------------------------------------------------------------------------------------------
#include <avr/io.h>
#define kilo 1000UL
#define Mega kilo*kilo
#define F_CPU 16*Mega // 1/16us = 1clock=0.0625(us), 1clock=62.5(ns)
#include <util/delay.h>
#define Enable PG4 //1: Enable , 0:disable
#define R_W PG3 //1: Read, 0: Write , normal: 0 (write)
#define RS PG2 //1: DR(Data reg), 0: IR(Instruction reg)
#define DB0 PA0
#define DB1 PA1
#define DB2 PA2
#define DB3 PA3
#define DB4 PA4
#define DB5 PA5
#define DB6 PA6
#define DB7 PA7
#define IR 0
#define DR 1
#define READ 1
#define WRITE 0
void init_clcd(void);
void write_dsp_timing(int8_t,int8_t,uint8_t); //1cycle: 500ns = 8clock (upper)
void write_string(uint8_t *p);
uint8_t arr[]="Dyne Practice";
uint8_t brr[]="Good job !";
uint8_t crr[]={20,20,20,20,20,20,20,20,20,20,20,20};
int main(void)
{
DDRA = 0xff;
DDRG = 0xff;
init_clcd();
uint8_t i=0;
//write_dsp_timing(DR,WRITE,'D');
while(1)
{ /*--set ADD---*/
write_dsp_timing(IR,WRITE,(3<<DB6)); //2-line first address.
write_string(brr);
for(i=0; i<8; i++)
{
write_dsp_timing(IR,WRITE,((2<<DB6)+i)); //1-line first address.
write_string(arr);
/*--set ADD---*/
_delay_ms(400);
write_dsp_timing(IR,WRITE,((2<<DB6)+i)); //clear.
write_string(crr); //disp clear.
}
}
}
void init_clcd(void)
{
/*----wait more 30ms, minimum (30ms) -----*/
_delay_ms(30);
/*---funcition set 2-line mode(1), 5x7dot mode(0)----*/
write_dsp_timing(IR,WRITE,(7<<DB3)); // PORTA = (7<<DB3);
/*----wait more 39us, minimum (39us) -----*/
_delay_us(39);
/*----Display on(1), curs off(0), Blink off(0)---*/
write_dsp_timing(IR,WRITE,(3<<DB2));//PORTA = (3<<DB2);
/*----wait more 39us, minimum (39us) -----*/
_delay_us(30);
/*----Display clear(1)-----*/
write_dsp_timing(IR,WRITE,(1<<DB0));//PORTA = (1<<DB0);
/*----wait more 1.53ms, minimum (1.53ms) -----*/
_delay_ms(2);
/*----Entry mode set -----*/
write_dsp_timing(IR,WRITE,(3<<DB1));//PORTA = (3<<DB2);
}
void write_dsp_timing(int8_t rs, int8_t r_w, uint8_t Data)
{
PORTG = (rs<<RS); //IR
PORTG |= (r_w<<R_W); //Write
PORTG |= (1<<Enable); //1
PORTA = Data; //Data
PORTG &= ~(1<<Enable); //0
_delay_ms(1); //valid data timing
}
void write_string(uint8_t *p)
{
while(*p)
{
write_dsp_timing(DR,WRITE,*p++);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
질문1) 정확히 확인해보지는 못했지만 사실 방법도 아직 잘 모르지만 코드가 컴파일되면 1clock당 수행되는 코드형태로
바뀐다고 알고 있는데요 (assembly형태) 순서대로 딜레이 함수 추가 하지않고 기본적으로 한코드문 실행할경우
1clock~ 3clcok(16MHz = 62.5ns/1clock )으로 가정하고 순서대로 코드하여 사용해도 괜찮은건지 일단 궁금합니다.
그렇게 가정해서 하셔도 상관없습니다.
단 C로 만든 코드가 assembly 몇개 명령어로 만들어질지, 기계어를 보면서(lss화일) 확인 하시면 됩니다.
PORTA = 1;
PORTA = 0;
적어도 포트 A의 핀 0는 62.5 * 2 이상의 high pulse를 가집니다.(io register에 쓰기 assembly code
가 2 clock을 먹으므로)
타이밍상 100ns high 라면 위 두개의 assembly를 바로 사용해서도 delay가 만족합니다.
일반적으로는 IO핀의 변화에 대한 최소 시간이 있습니다.
특정포트의 핀을 0으로 만듬
특정포트의 핀을 1로 만듬
의 두 기계어가 있다고 하고 위 두 명령어 각각이 2 clock의 실행시간이 소요된다고 해서
실제로 IO핀이 해당 시간만큼만큼 따라주지 경우도 맣습니다.
(예를 들어 1Ghz cpu 가 bus 가 200Mhz 를 통해서 io 레지스터에 200MHz로 쓰기가 가능하다고 해서
io핀이 100Mhz(토글을 하므로 bus 속도의 반)이 되지 못합니다.
핀은 최대 움직일수 있는 속도가 있습니다.
만약 60ns 보다 작은 setup 타임이 필요한 타이밍이라면
PORTy = data; // 데이타를 싣고
PORTx = 0;
PORTx = 1; // 제어신호가 ->0->1 로 바뀌어야 하고 data의 setup 타임이 60ns 라면
// 위 같이 delay 없이도 충분함... io register에 쓰기/읽기 최소 2clock이라 최소 130 ns 이상은 나옴.
와 같이 하셔도 충분히 0인 시간이 위 조건을 만족합니다.(따로 delay가 필요없음)
C 로 작성한 코드가 asembly로 생성되는 걸 보시고 필요한 경우
asm("nop");// 62.5 ns 추가
와 같은 assembly를 C 에 삽입하면 됩니다.
PORTG = (rs<<RS); //IR
PORTG |= (r_w<<R_W); //Write
PORTG |= (1<<Enable); //1
PORTA = Data; //Data
PORTG &= ~(1<<Enable); //0
_delay_ms(1); //valid data timing
[출처] 8.2 CLCD에 내 좌우명을 새겨라 [강좌]질문. (임베디드홀릭) |작성자 다인
위는
시작
RS신호만 1로 만듬
최소 62.5 * 2 이상 대기
R_W 신호 1로 만듬
최소 62.6 * 2 이상 대기
Enable 신호를 1로 만듬
최소 62.5 * 2 이상 대기
data를 씀
최소 62.5 * 2 이상 대기
Enable 신호 0으로 만듬
최소 62.5 * 2 이상 대기
---여기에서 더 필요한 시간은
Enable이 0이 된후 다시 위 과정이 반복되는걸 cycle time 이라고 하는데
위 시간을 다 합한것과 cycle time의 차(cycle 타임이 더 적으면 delay 필요없음,
cycle타임이 더 크면 해당 시간만큼 대기)
그런데 위에서
atmega128의 경우 PORTG는 io read write(비트 세트/clear) 명령어가 제공되지 않는 레지스터입니다.
즉 메모리 읽기/쓰기로 일어납니다.
PORTG |= 1 << Enable
이건
reg = PORTG;
reg = reg | (1 << Enable);
PORTG = reg
이렇게 세 단계로 이루어집니다.
메모리에서 데티아 읽기, 쓰기, 그리고 레지스터에 OR 시키는 시간만큼 소요됩니다.
따라서 io bit set/clear 가 되는
PORTA |= 1 << Enable ;
가 sbis 같은 2cycle 명령어로 변환되는것도 다르게 시간이 훨씬 더 걸립니다.
기계어 생성된걸 봐야 하며 컴파일 옵션(옵티마이저)에 따라 다르므로
제가 위에서 최소라고 적은 겁니다. 실제 시간은 더 걸림.
강좌에 있는 CLCD 타이밍으로 본다면
RS와 R/W는 같은 타이밍에 쓸수 있음
E가 1로 바뀔때까지 RS, R/W는 Tsu1 시간이 필요하며, 예의 경우 40ns 이므로 delay 필요없음
E의 high 시간은 Tw이며 230 ns 이므로 명령어 실행시간 62.5 * 2 = 130과의 차 100ns 이므로 두개의 기계어
asm("nop"); // 62.5
asm("nop"); // 62.5
다시 E를 0으로 하면 130ns(최소 62.5 * 2) 인데
새로 E를 high 할 수 있는 시간 Tc (cycle이 500 ns 이므로)
500 - (62.5 * 2 + 62.5 * 2(nop) + 62.5 * 2(0으로 쓰기)) =
500 - (130 + 130 + 130) = 500 - 390 = 110
그런데 새로 시작할때, 다시 RS/RW를 쓰기 하고 다시 E를 1로 올리므로 이미 시간에 다 포함.
(또한 함수로 구현되어 있어서 함수 리턴 및 call 시간이 추가로 붙음)
Tr/Tf 은 길어야 20ns 입니다. 일반적으로 회로에서 mcu에 바로 clcd를 연결할 경우 mcu가 특정 핀을 쓰기 하면 신호가 바로 올라가므로 별로 신경 쓸 필요는 없는데,
혹시라도 해당 핀에 cap같은걸 달아서 천천히 올라가는 완만한 신호가 생성된다면 해당 규격에 어긋나죠
만약 clcd를 긴 케이블을 통해서 구동한다면 선이 길어지면서
저항과 cap 성분이 크져서 신호가 완만히 올라갈경우 위 시간이 meet하지 않는다면 다른
방안(케이블을 좋은걸 사용하거나, 길이를 줄이거나) 을 강구해야 겠죠.
데이타쉬트 보시면
테이블 6에
bus timing (bus상에서 시간) 말고 명령어별로 delay시간이 우측에 있습니다.
즉 명령어에 따라 추가 시간이 필요합니다.
예를 들어 RS가 0인 것중에서도
clear display 명령어가 보낸후에는 최소 1.53 ms 까지 대기해야 하고
entry mode는 39 us 등
각 동작에 따른 delay 테이블을 두시거나,
해당 갑중 가능 큰값을 일괄 적용하시면 되겠죠.
bus 의 타이밍은 물리적은(전기적은) 규격이고, 그것을 통해 lcd에 명령이 전달되어서
해당 명령이 실행될때까지의 시간은 lcd 에서 내부적으로 더 필요한 시간입니다.
아 그래서 8-bit Initialization의 표와 같이 중간마다 명령어 수행후 최소 delay 값을 명시한거군요
그렇죠.
bus 타이밍은 전기적으로 상대편이 신호를 잘 받기 위한 규격이고
그렇게 전달된 명령/데이타가 내부에서 처리하는 시간이 있는거죠.
No comments:
Post a Comment