I'm free
[DSP6K] I2C 통신 (TMS320C6747 to LCD) 본문
급하게 DSP에 I2C통신으로 LCD(1.5inch OLED Module)를
제어해야 할 일이 생겨서 자료를 남기기 위해 작성
운영체제 : Windows 10 pro x64
프로그램 :
- uVision5(MDK-ARM 5.27)C6000 Code Generation Tools 7.4.23
- AISgen for D800K005
제어장치 :
- TMS320C6747
- 1.5inch OLED Module
직접 해보면서 글을 쓰는 것이기 때문에 틀린 부분이 있을 수 있습니다.
잘못된 점은 언제든 삭제되거나 수정될 수 있습니다.
제품 정보
1) TMS320C6747
http://www.ti.com/product/TMS320C6747
2) 1.5inch OLED Module
https://www.waveshare.com/wiki/1.5inch_OLED_Module
하드웨어 연결 상태.
DSP의 핀 연결
- SPI1_SIMO[0]/I2C1_SDA/GP5[6]/BOOT[6]
- SPI1_SOMI[0]/I2C1_SCL/GP5[5]/BOOT[5]
LCD의 핀 연결
- DIN(SDA)
- CLK(SCL)
- BS 1pin
(구매 당시 BS-0번 0옴으로 연결됨 -> 제거 후 BS-1번 0옴으로 연결)
세팅
(다른 하드웨어 세팅도 들어가 있음. 데이터 시트 확인 필수)
//i2C1 Output (bit21, bit22)
*( volatile unsigned int* ) 0x01E26064 = 0x0000ffff;
//PINMUX8 (0-3,4-7)
*(unsigned int*) 0x01C14140 = 0x28888022; // UART2, McASP1, I2C0, I2C1
#define LCD_ON 0xAF
#define LCD_OFF 0xAE
#define LCD_REMAP 0xA0
//0 1 1 1 1 0 D(1)/C(0) R(1)/W(0)
#define LCD_ADDR_data (0x3E)
#define LCD_ADDR_comm (0x3C)
volatile long lcdWaitCnt = 0;
void f_lcdDelay(int n){
long i;
for (i = 0; i<n*2200; i++) lcdWaitCnt++;// 1cnt 0.022us.. 5500cnt 120us..
//100kHz 10us 1bit
//data 8bit + startbit +ackbit = 10bit : 100us
// data sand delay
//5300 x
//5400 o
}
/*
#define I2C1_BASE 0x01E28000
#define I2C1_ICOAR *( volatile Uint32* )( I2C1_BASE + 0x00 )
#define I2C1_ICIMR *( volatile Uint32* )( I2C1_BASE + 0x04 )
#define I2C1_ICSTR *( volatile Uint32* )( I2C1_BASE + 0x08 )
#define I2C1_ICCLKL *( volatile Uint32* )( I2C1_BASE + 0x0C )
#define I2C1_ICCLKH *( volatile Uint32* )( I2C1_BASE + 0x10 )
#define I2C1_ICCNT *( volatile Uint32* )( I2C1_BASE + 0x14 )
#define I2C1_ICDRR *( volatile Uint32* )( I2C1_BASE + 0x18 )
#define I2C1_ICSAR *( volatile Uint32* )( I2C1_BASE + 0x1C )
#define I2C1_ICDXR *( volatile Uint32* )( I2C1_BASE + 0x20 )
#define I2C1_ICMDR *( volatile Uint32* )( I2C1_BASE + 0x24 )
#define I2C1_ICIVR *( volatile Uint32* )( I2C1_BASE + 0x28 )
#define I2C1_ICEMDR *( volatile Uint32* )( I2C1_BASE + 0x2C )
#define I2C1_ICPSC *( volatile Uint32* )( I2C1_BASE + 0x30 )
#define I2C1_ICPID1 *( volatile Uint32* )( I2C1_BASE + 0x34 )
#define I2C1_ICPID2 *( volatile Uint32* )( I2C1_BASE + 0x38 )
#define I2C1_ICPFUNC *( volatile Uint32* )( I2C1_BASE + 0x48 )
#define I2C1_ICPDIR *( volatile Uint32* )( I2C1_BASE + 0x4C )
#define I2C1_ICPDIN *( volatile Uint32* )( I2C1_BASE + 0x50 )
#define I2C1_ICPDOUT *( volatile Uint32* )( I2C1_BASE + 0x54 )
#define I2C1_ICPDSET *( volatile Uint32* )( I2C1_BASE + 0x58 )
#define I2C1_ICPDCLR *( volatile Uint32* )( I2C1_BASE + 0x5C )
*/
void f_lcdI2C_init() {
//1// I2C 클럭 활성 //-//
//2// I2C 재설정 //-//
I2C1_ICMDR &= 0xFFDF; //IRS(5)=0
//3//ICMDR 구성 설정 //-//
//마스터 구성
I2C1_ICMDR |= 0x0400; //MST(10)=1
//I2C 송신모드
I2C1_ICMDR |= 0x0200; //TRX(9)=1
//7비트 주소설정
I2C1_ICMDR &= 0xFEFF; //XA(8)=0
//반복모드
I2C1_ICMDR &= 0xFF7F; //RM(7)=0
//루프백모드 비활성화
I2C1_ICMDR &= 0xFFBF; //DLM(6)=0
//데이터형식 비활성화 ????//free data mode //lcd보드쪽이지원안하는듯?
I2C1_ICMDR &= 0xFFF7; //FDF(3)=0
//옵션:시작바이트비활성
//I2C1_ICMDR &= 0xFFEF; //STB(4)=0
//전송비트수설정//0:8bit 1:1bit~7:7bit
I2C1_ICMDR &= 0xFFF8; //BC(0-2)=0
//-//-//-//
//4// 슬레이브 주소구성 //-//
//슬레이브주소 7bit
I2C1_ICSAR = LCD_ADDR_comm & 0xFF;
//5// 클럭주파수 설정 //-//
//Prescaler frequency 6.7MHz~13.3MHz
//I2C clock frequency = I2C input clock frequency/(IPSC + 1)
//30MHz/(0x02+1) = 10MHz /0x03:7.5MHz/0x04:6MHz
//계산상 0x03인데 오실로로 찍어보면 0x0A(최종 출력 클럭 100kHz 기준)
I2C1_ICPSC = 0x0A;
//6// 마스터 클럭주파수 설정 //-//
//I2C serial clock frequency = prescaled module clock frequency/((ICCL + d) + (ICCH + d))
//10MHz/((45+5)+(45+5) = 100kHz
I2C1_ICCLKL = 0x19;// 0x2D;
I2C1_ICCLKH = 0x41;//0x2D;
//7// 인터럽트레지스터 확인//-//
//ICSTR 읽고 다시 쓰기
I2C1_ICSTR = I2C1_ICSTR;
//ICSTR 초기화는 1
//I2C1_ICSTR=1; //clear
//I2C1_ICIVR이 0이될때까지 읽기
while (I2C1_ICIVR != 0);
//8// 컨트롤러 리셋 취소/활성화 //-//
I2C1_ICMDR |= 0x0020; //IRS(5)=1
//설정 끝.
}
int f_lcdI2C_Command(Uint16* data) {
I2C1_ICMDR &= 0xF7FF; //STP(11)=0
I2C1_ICMDR |= 0x2020; //STT(13)=1 IRS(5)=1
//STOP 상태 확인 STP(11)==1?
// if (I2C1_ICMDR & 0x0800) return 1;
//9// 버스사용중비트 확인 BB(12)==1?
// if ((I2C1_ICSTR & 0x1000)) return I2C1_ICSTR;
/* 전송할 데이터의 Slave Address 설정 */
/* D/C 설정 */
*(short*)(0x62000000) = 0x00;//(D[3]:0x08) 0:high normal //(D[2]:0x04) 0:Command
I2C1_ICSAR = LCD_ADDR_comm & 0xFF;
I2C1_ICMDR = 0x2E20;
f_lcdDelay(2);
//C0 onlyData(0) nextControl(1)
//DC C(0) D(1)-GDRAM(++)
//C0 DC 000000
I2C1_ICDXR = data[0];
I2C1_ICMDR = 0x0E20;
f_lcdDelay(2);
I2C1_ICDXR = data[1];
I2C1_ICMDR = 0x0E20;
f_lcdDelay(2);
f_lcdDelay(1);
I2C1_ICMDR &= 0xFFDF; //IRS(5)=0
return 0;
}
int f_lcdI2C_Command_select(int select) {
I2C1_ICMDR &= 0xF7FF; //STP(11)=0
I2C1_ICMDR |= 0x2020; //STT(13)=1 IRS(5)=1
//STOP 상태 확인 STP(11)==1?
// if (I2C1_ICMDR & 0x0800) return 1;
//9// 버스사용중비트 확인 BB(12)==1?
// if ((I2C1_ICSTR & 0x1000)) return I2C1_ICSTR;
/* 전송할 데이터의 Slave Address 설정 */
/* D/C 설정 */
*(short*)(0x62000000) = 0x00;//(D[3]:0x08) 0:high normal //(D[2]:0x04) 0:Command
I2C1_ICSAR = LCD_ADDR_comm & 0xFF;
I2C1_ICMDR = 0x2E20;
f_lcdDelay(2);
//C0 onlyData(0) nextControl(1)
//DC C(0) D(1)-GDRAM(++)
//C0 DC 000000
I2C1_ICDXR = 0x00;
I2C1_ICMDR = 0x0E20;
f_lcdDelay(2);
switch (select)
{
case LCD_ON:
I2C1_ICDXR = LCD_ON;
I2C1_ICMDR = 0x0E20;
f_lcdDelay(2);
break;
case LCD_OFF:
I2C1_ICDXR = LCD_OFF;
I2C1_ICMDR = 0x0E20;
f_lcdDelay(2);
break;
case LCD_REMAP:
/*
A[0] = 0b, Disable Column Address Re-map(RESET) (우좌)
A[0] = 1b, Enable Column Address Re-map (좌우) EN
A[1] = 0b, Disable Nibble Re-map (RESET) EN
A[1] = 1b, Enable Nibble Re-map
A[2] = 0b, Enable Horizontal Address Increment(RESET)(가로) EN
A[2] = 1b, Enable Vertical Address Increment(세로)
A[3] = 0b, Reserved (RESET)
A[4] = 0b, Disable COM Re-map (RESET)(아래서위로)
A[4] = 1b, Enable COM Re-map(반전)(위에서아래로) EN
A[5] = 0b, Reserved (RESET)
A[6] = 0b, Disable COM Split Odd Even (RESET)(홀수/짝수)
A[6] = 1b, Enable COM Split Odd Even(순서대로) EN
A[7] = 0b, Reserved (RESET)
*/
I2C1_ICDXR = LCD_REMAP;
I2C1_ICMDR = 0x0E20;
f_lcdDelay(2);
I2C1_ICDXR = 0x51;// = 0x01 | 0x10 | 0x40;
I2C1_ICMDR = 0x0E20;
f_lcdDelay(2);
break;
default:
break;
}
f_lcdDelay(1);
I2C1_ICMDR &= 0xFFDF; //IRS(5)=0
return 0;
}
int f_lcdI2C_Data(Uint16 numOfByte, Uint16* data) {
int i;
I2C1_ICMDR &= 0xF7FF; //STP(11)=0
I2C1_ICMDR |= 0x2020; //STT(13)=1 IRS(5)=1
//STOP 상태 확인 STP(11)==1?
// if (I2C1_ICMDR & 0x0800) return 1;
//9// 버스사용중비트 확인 BB(12)==1?
// if ((I2C1_ICSTR & 0x1000)) return I2C1_ICSTR;
/* 전송할 데이터의 Slave Address 설정 */
/* D/C 설정 */
// *(short*)(0x62000000) = 0x02;//(D[3]:0x08) 0:high normal //(D[2]:0x04) 1:Data
// I2C1_ICSAR = LCD_ADDR_data & 0xFF;
*(short*)(0x62000000) = 0x00;//(D[3]:0x08) 0:high normal //(D[2]:0x04) 0:Command
I2C1_ICSAR = LCD_ADDR_comm & 0xFF;
I2C1_ICMDR = 0x2E20;
f_lcdDelay(2);
//C0 onlyData(0) nextControl(1)
//DC C(0) D(1)-GDRAM(++)
//C0 DC 00 0000
I2C1_ICDXR = 0x40;
I2C1_ICMDR = 0x2E20;
f_lcdDelay(2);
//I2C1_ICCNT = numOfByte & 0xFF;
//lcdDelay(2);
for (i = 0; i < numOfByte; i+=2) {
I2C1_ICDXR = (Uint8)((((data[i] << 4) & 0xF0) | (data[i+1] & 0x0F))&0xFF);
I2C1_ICMDR = 0x2E20;
f_lcdDelay(2);
}
f_lcdDelay(1);
I2C1_ICMDR &= 0xFFDF; //IRS(5)=0
return 0;
}
문제점
아래 사진은 LCD ON 명령을 I2C로 보내는 것을 Oscilloscope로 측정한 것이다.
DataSheet에 나와있는 대로 신호가 잘 가다가 마지막 Stop bit 처리하는 부분에서 DataSheet와 상이하게 동작한다. 이는 DSP쪽 코드 문제로 보이며 그냥 진행하면 한 번만 보내고 Stop 이벤트를 인식하지 못해 이후 출력을 새로 보내지 않고 Data정보만 보내는 문제가 있다,
이를 해결하지 못했다.(누군가 알려주었으면 좋겠지만)
그래서 위 코드상에 보면 인터럽트를 강제 리셋시키고 다시 시작하는 방식으로 구현되어있다.
(내가 생각해도 정상적인 방법은 아니지만 동작은 잘되니...)
코드 적용
- 이전에 만들어둔 이미지를 HAX 변환하여 만든 이미지 배열을 적용
[Labview] Image to RGB(hax) ver.10
LCD가 4bit 방식이라 변환작업을 코드상에서 구현.
(변환 프로그램에서 4bit도 추가하면 좋겠지만, 귀찮으니 나중에 하는 걸로...)
#define Uint16 unsigned short
//LCD
#define XByte 128 //1 BYTE = 2dot,X max Dot = 128
#define YByte 128 //1 Byte = 1Dot,y max Dot = 128
#define OLED_BUFSIZ XByte * YByte
extern Uint16 f_lcdBuffer[OLED_BUFSIZ];
int imageToColor[16384] = {
0x434343,0x696969,0x797979, //변환 HAX코드 적용(생략)
};
void main(void) {
int k;
j = 0;
//임시 테스트용
//다른방식 사용 필요.
for (k = 0; k < OLED_BUFSIZ; k++) {
j = (imageToColor[k] & 0xFF)
+ ((imageToColor[k] >> 8) & 0xFF)
+ ((imageToColor[k] >> 16) & 0xFF);
j /= 3;
f_lcdBuffer[k] = (j >>4)&0x0F;
}
f_lcdI2C_init();
//LCD 초기 설정-- LCD속도 설정 필요
fftestff = f_lcdI2C_Command_select(LCD_REMAP);
fftestff = f_lcdI2C_Command_select(LCD_ON);
fftestff = f_lcdI2C_Data(OLED_BUFSIZ, f_lcdBuffer);
while (1) {
//
}
}
동작 화면
Gray 스케일 OLED라 조금 아쉽다.
RGB(color) OLED도 조만간 구매한다니.(소스 수정해야 돼 귀찮...)
칼라로도 해보면 나름 괜찮겠다 싶다.