AVR 單片機 MEGA64A
/***************************ST7586S********************************************/
/************************PE5 ---> /CS ***************************************/
/************************PG3 ---> RS ***************************************/
/************************PG4 ---> /WR ***************************************/
/************************PA0 ---> /RD ***************************************/
/********************PF0-PF7 ---> DATA DB0-DB7 ******************************/
/************************PA1 ---> /RESET ************************************/
/************************PA2 ---> LED+(H)************************************/
#define LCD_COMMAND 0
//數據
#define LCD_DATA 1
#define DATA_IO_INIT() {DDRF=0xFF;PORTF=0x00;}
#define LCD_RST_ON() {DDRA|=BIT(1);PORTA&=~BIT(1);}
#define LCD_RST_OFF() {DDRA|=BIT(1);PORTA|=BIT(1);}
#define LCD_RS_LOW_COMMAND() {DDRG|=BIT(3);PORTG&=~BIT(3);}
#define LCD_RS_HIGH_DATA() {DDRG|=BIT(3);PORTG|=BIT(3);}
#define LCD_RD_LOW() {DDRA|=BIT(0);PORTA&=~BIT(0);}
#define LCD_RD_HIGH() {DDRA|=BIT(0);PORTA|=BIT(0);}
#define LCD_WR_LOW() {DDRG|=BIT(4);PORTG&=~BIT(4);}
#define LCD_WR_HIGH() {DDRG|=BIT(4);PORTG|=BIT(4);}
#define LCD_CS_LOW_ON() {DDRE|=BIT(5);PORTE&=~BIT(5);}
#define LCD_CS_HIGH_OFF() {DDRE|=BIT(5);PORTE|=BIT(5);}
#define LCD_LED_OFF() {DDRA|=BIT(2);PORTA|=BIT(2);}
#define LCD_LED_ON() {DDRA|=BIT(2);PORTA&=~BIT(2);}
//ST7586 COMMAND
#define LCD_NOP 0x00
#define LCD_RST 0x01 //軟件復位,RAM內容不清
#define POWER_SAVE_ON 0x10 //0X10,SLEEP,0X11,WAKE UP
#define POWER_SAVE_OFF 0x11
#define PARTIAL_DISPLAY_ON 0x12 //0x12,ON,0x13,OFF
#define PARTIAL_DISPLAY_OFF 0x13
#define INVERSE_DISPLAY_OFF 0x20 //0x20,正顯,0X21,負顯
#define INVERSE_DISPLAY_ON 0x21
#define ALL_PIXEL_ON 0x23 //0x22,off,0X23,on
#define ALL_PIXEL_OFF 0x22
#define DISPLAY_ON 0x29
#define DISPLAY_OFF 0X28
#define SET_COLUMN_ADDRESS 0x2A //0x2A+XS0000H+XE007FH 0<=XS<=XE<=7F
#define SET_ROW_ADDRESS 0x2B //0x2B+YS+YE 0<=YS<=YE<=9F
#define WRITE_DISPLAY_DATA 0x2C
//#define READ_DISPLAY_DATA 0x2E //串口模式無此功能
#define PARTIAL_DISPLAY_AREA 0x30 //個參數
#define SCROLL_AREA 0x33 // TA,SA,BA:0-A0
#define DISPLAY_CONTROL 0x36 //MY,MX1,MX0
#define START_LINE 0x37 //0-9F
#define DISPLAY_MODE 0x38 //四級灰度,39,單色顯示
#define ENABLE_DDRAM_INTERFACE 0x3A //3AH+02H
#define DISPLAY_DUTY 0xB0 //B0+9F
#define FIRST_OUTPUT_COM 0xB1 //B1H+00H
#define FOSC_DIVIDER 0xB3 //B3H+1;0:不分頻,1:2分頻,2:4分頻,3:8分頻
#define PARTIAL_DISPLAY 0xB4 //B4+A0,在局部顯示區域設置之前寫入
#define SET_VOP 0xC0 //+42H+01H
#define VOP_INCREASE 0xC1
#define VOP_DECREASE 0xC2
#define BIAS_SYSYTEM 0xC3 //C3+04;1/10
#define BOOSTER_LEVEL 0xC4 //C4+07
#define VOP_OFFSET 0xC7 //C7+1
#define ANALOG_CONTROL 0xD0 //D0+1D
#define FRAME_RATE 0xF0 //F0+06+0B+0D+12H
//舉一個例子,顯示0,像素16*24,顯示的有效數據的位數一定是要3的倍數
//16*24,寬度是2個字節16位,但是傳輸給LCD顯示時,一定要補2位,是16+2=18
/****************************取模方式******************************************/
/******點陣格式--陰碼,列行式,取模走向為逆向**********************************///舉一個例子,顯示0,像素16*24,顯示的有效數據的位數一定是要3的倍數
//16*24,寬度是2個字節16位,但是傳輸給LCD顯示時,一定要補2位,是16+2=18
/*************************************16*24*************************************/
const unsigned char Image_0[]=
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xC0,0x0E,0xE0,0x1C,0x70,
0x1C,0x70,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,
0x38,0x38,0x1C,0x70,0x1C,0x70,0x0E,0xE0,0x07,0xC0,0x00,0x00,0x00,0x00,0x00,0x00};/*0*/ /*******************************LCD寫命令**********************************/
void LCD_Cmd_Write(unsigned char cmd)
{
LCD_RS_LOW_COMMAND();
LCD_RD_HIGH();
/*LCD_ChkBusy();*/ //Call LCD_ChkBusy to Check Busy Bit
LCD_WR_LOW();
NOP();
NOP();
DDRF = 0xFF;
PORTF = cmd;
LCD_CS_LOW_ON();
NOP();
NOP();
LCD_WR_HIGH();
Some_NOP(3);
LCD_CS_HIGH_OFF();
}/*****************************LCD寫數據**********************************/
void LCD_Data_Write(unsigned char data)
{
/*LCD_ChkBusy(); */ //Call LCD_ChkBusy to Check Busy Bit
DDRF = 0xFF;
LCD_RS_HIGH_DATA();
LCD_RD_HIGH();
PORTF = data;
LCD_WR_LOW();
NOP();
NOP();
LCD_CS_LOW_ON();
NOP()
NOP();
LCD_WR_HIGH();
Some_NOP(3);
LCD_CS_HIGH_OFF();
} /***********************LCD讀數據*******************************/
unsigned char LCD_Data_Read(void)
{
unsigned char data_m=0;
DDRF = 0x00; //讀出
LCD_RS_HIGH_DATA();
LCD_RD_HIGH();
LCD_WR_LOW();
NOP();
NOP();
LCD_CS_LOW_ON();
NOP();
NOP();
LCD_WR_HIGH();
NOP();
NOP();
LCD_RD_LOW();
NOP();
NOP();
data_m = PINF;
LCD_RD_HIGH();
NOP();
NOP();
return data_m;
}/*********0<XS<XE<7F,0<YS<YE<9F************/
//LCD_Clear_RAM(0,127,0,159);//x-y
void LCD_Clear_RAM(uchar xs,uchar xe,uchar ys,uchar ye)
{
uchar x,y;
LCD_Cmd_Write(SET_COLUMN_ADDRESS);
LCD_Data_Write(0x00);
LCD_Data_Write(xs);
LCD_Data_Write(0x00);
LCD_Data_Write(xe);
LCD_Cmd_Write(SET_ROW_ADDRESS);
LCD_Data_Write(0x00);
LCD_Data_Write(ys);
LCD_Data_Write(0x00);
LCD_Data_Write(ye);
LCD_Cmd_Write(WRITE_DISPLAY_DATA);
for(y=ys;y<=ye;y++)
{
for(x=xs;x<=xe;x++)
{
LCD_Data_Write(0x00);
}
}
} //========================================================
// 畫圖函數
//========================================================
void Image_Disp(uchar x,uchar y,uchar width,uchar high,const uchar *pstr)
// 坐標(x,y),x為水平方向字符位(1個字符位占3個像素點);y為垂直方向像素行;
// width:圖片寬度,按照水平方向像素點計算;high:為圖片高度,按照像素點計算,*pstr 圖片數組名
{
uchar i,j,k,data,temp;
LCD_Cmd_Write(0x2a); // Column Address Setting
LCD_Data_Write(0x00); //
LCD_Data_Write(x); //
LCD_Data_Write(0x00);
LCD_Data_Write((width/3)+x-1); //
LCD_Cmd_Write(0x2b); // Row Address Setting
LCD_Data_Write(0x00); //
LCD_Data_Write(y);
LCD_Data_Write(0x00);
LCD_Data_Write(y+high-1);
LCD_Cmd_Write(0x2c);
for(i=0;i<high;i++)
{
for(j=0;j<width;j=j+24)
{
data = *pstr++;
for(k=0;k<2;k++)
{
temp = data&0xe0;
LCD_Data_Write(LCD_Change_Tab[temp>>5]);
data = data<<3;
}
data = data + ((*pstr)>>2);
for(k=0;k<2;k++)
{
temp = data&0xe0;
LCD_Data_Write(LCD_Change_Tab[temp>>5]);
data = data<<3;
}
data = data + (((*pstr++)&0x03)<<4);
data = data +((*pstr)>>4);
for(k=0;k<2;k++)
{
temp = data&0xe0;
LCD_Data_Write(LCD_Change_Tab[temp>>5]);
data = data<<3;
}
data = data +(((*pstr++)&0x0f)<<2);
for(k=0;k<2;k++)
{
temp = data&0xe0;
LCD_Data_Write(LCD_Change_Tab[temp>>5]);
data = data<<3;
}
}
}
} void Image18_Disp(uchar x,uchar y,uchar width,uchar high,const uchar *pstr)
// 坐標(x,y),x為水平方向字符位(1個字符位占3個像素點);y為垂直方向像素行;
// width:圖片寬度,按照水平方向像素點計算;high:為圖片高度,按照像素點計算,*pstr 圖片數組名
{
uchar i,j,k,data,temp;
LCD_Cmd_Write(0x2a); // Column Address Setting
LCD_Data_Write(0x00); //
LCD_Data_Write(x); //
LCD_Data_Write(0x00);
LCD_Data_Write((width/3)+x-1); //
LCD_Cmd_Write(0x2b); // Row Address Setting
LCD_Data_Write(0x00); //
LCD_Data_Write(y);
LCD_Data_Write(0x00);
LCD_Data_Write(y+high-1);
LCD_Cmd_Write(0x2c);
for(i=0;i<high;i++)
{
data = *pstr++;
for(k=0;k<2;k++)
{
temp = data&0xe0;
LCD_Data_Write(LCD_Change_Tab[temp>>5]);
data = data<<3;
}
data = data + ((*pstr)>>2);
for(k=0;k<2;k++)
{
temp = data&0xe0;
LCD_Data_Write(LCD_Change_Tab[temp>>5]);
data = data<<3;
}
data = data + (((*pstr++)&0x03)<<4);//指針
for(k=0;k<2;k++)
{
temp = data&0xe0;
LCD_Data_Write(LCD_Change_Tab[temp>>5]);
data = data<<3;
}
}
}
void Image8_Disp(uchar x,uchar y,uchar width,uchar high,const uchar *pstr)
// 坐標(x,y),x為水平方向字符位(1個字符位占3個像素點);y為垂直方向像素行;
// width:圖片寬度,按照水平方向像素點計算;high:為圖片高度,按照像素點計算,*pstr 圖片數組名
{
uchar i,j,k,data,temp;
LCD_Cmd_Write(0x2a); // Column Address Setting
LCD_Data_Write(0x00); //
LCD_Data_Write(x); //
LCD_Data_Write(0x00);
LCD_Data_Write((width/3)+x-1); //
LCD_Cmd_Write(0x2b); // Row Address Setting
LCD_Data_Write(0x00); //
LCD_Data_Write(y);
LCD_Data_Write(0x00);
LCD_Data_Write(y+high-1);
LCD_Cmd_Write(0x2c);
for(i=0;i<high;i++)
{
data = *pstr++;
for(k=0;k<3;k++)
{
temp = data&0xe0;
LCD_Data_Write(LCD_Change_Tab[temp>>5]);
data = data<<3;
}
}
}
/**********************LCD復位程序********************/
void LCD_Reset(void )
{
LCD_RST_OFF();
Delay_us(20);
LCD_RST_ON();
Delay_us(50);
LCD_RST_OFF();
Delay_us(20);
}
/*************************LCD初始化***********************/
void LCD_Init(void)
{
LCD_Reset();
Delay_ms(10);
LCD_Cmd_Write(POWER_SAVE_OFF);//wake up
Delay_ms(10);
LCD_Cmd_Write(0xd7); // Disable Auto Read
LCD_Data_Write(0x9F);
LCD_Cmd_Write(0xe0);// Enable OTP Read
LCD_Data_Write(0x00);
Delay_ms(10);
LCD_Cmd_Write(0xe3); // OTP Up-Load
Delay_ms(20);
LCD_Cmd_Write(0xe1); // OTP Control Out
// initail set
LCD_Cmd_Write(0x11); // Sleep Out
LCD_Cmd_Write(0x28); // Display OFF Delay_ms(50);
LCD_Cmd_Write(0xc0); // Vop = B9h
LCD_Data_Write(0x1f); // contrast value
LCD_Data_Write(0x01); // 01
LCD_Cmd_Write(0xc3); // BIAS
LCD_Data_Write(0x02); // 05 = 1/12
LCD_Cmd_Write(0xc4); // Booster = x8
LCD_Data_Write(0x07);
LCD_Cmd_Write(0xd0); // Enable Analog Circuit
LCD_Data_Write(0x1d);
LCD_Cmd_Write(0xb5); // N-Line = 13
LCD_Data_Write(0x8d); //8d
LCD_Cmd_Write(0x38); // 4 Gray
// SdCmd(0x39); // B/W Mode
LCD_Cmd_Write(0x3a); // Enable DDRAM Interface
LCD_Data_Write(0x02);
LCD_Cmd_Write(0x36); // Dispaly Control
LCD_Data_Write(0xc8); // MY=1,MX[1:0]=11;
LCD_Cmd_Write(0xb0); // Duty Setting
LCD_Data_Write(0x9f); // 160 line 9f
LCD_Cmd_Write(0x20); // Display Inversion OFF
LCD_Cmd_Write(0xb1); // First COM out
LCD_Data_Write(0x00); // com0
LCD_Clear_RAM(0,127,0,159);
LCD_Cmd_Write(0x29); // Display ON
}
實際調試時一開始使用的是電源發生器的,5V標準的電壓,當時沒發現陰影,LCD是3.3V的,實際線路板上電壓大概是5.06V,結果背景就開始有陰影了,后來重新外加弄了塊5V轉3.3V的轉接線路板。

其中DIR的電平可以單獨再用一個I/O口去控制,不過一般實際使用時基本上都是WRITE LCD的,READ LCD RAM很少,如無READ指令的話,DIR可以直接接高電平。
/**********************LCD復位程序********************/
void LCD_Reset(void )
{
LCD_RST_OFF();
Delay_us(20);
LCD_RST_ON();
Delay_us(50);
LCD_RST_OFF();
Delay_us(20);
}
/*************************LCD初始化***********************/
void LCD_Init(void)
{
LCD_Reset();
Delay_ms(10);
LCD_Cmd_Write(POWER_SAVE_OFF);//wake up
Delay_ms(10);
LCD_Cmd_Write(0xd7); // Disable Auto Read
LCD_Data_Write(0x9F);
LCD_Cmd_Write(0xe0);// Enable OTP Read
LCD_Data_Write(0x00);
Delay_ms(10);
LCD_Cmd_Write(0xe3); // OTP Up-Load
Delay_ms(20);
LCD_Cmd_Write(0xe1); // OTP Control Out
// initail set
LCD_Cmd_Write(0x11); // Sleep Out
LCD_Cmd_Write(0x28); // Display OFF Delay_ms(50);
LCD_Cmd_Write(0xc0); // Vop = B9h
LCD_Data_Write(0x1f); // contrast value
LCD_Data_Write(0x01); // 01
LCD_Cmd_Write(0xc3); // BIAS
LCD_Data_Write(0x02); // 05 = 1/12
LCD_Cmd_Write(0xc4); // Booster = x8
LCD_Data_Write(0x07);
LCD_Cmd_Write(0xd0); // Enable Analog Circuit
LCD_Data_Write(0x1d);
LCD_Cmd_Write(0xb5); // N-Line = 13
LCD_Data_Write(0x8d); //8d
LCD_Cmd_Write(0x38); // 4 Gray
// SdCmd(0x39); // B/W Mode
LCD_Cmd_Write(0x3a); // Enable DDRAM Interface
LCD_Data_Write(0x02);
LCD_Cmd_Write(0x36); // Dispaly Control
LCD_Data_Write(0xc8); // MY=1,MX[1:0]=11;
LCD_Cmd_Write(0xb0); // Duty Setting
LCD_Data_Write(0x9f); // 160 line 9f
LCD_Cmd_Write(0x20); // Display Inversion OFF
LCD_Cmd_Write(0xb1); // First COM out
LCD_Data_Write(0x00); // com0
LCD_Clear_RAM(0,127,0,159);
LCD_Cmd_Write(0x29); // Display ON
}
實際調試時一開始使用的是電源發生器的,5V標準的電壓,當時沒發現陰影,LCD是3.3V的,實際線路板上電壓大概是5.06V,結果背景就開始有陰影了,后來重新外加弄了塊5V轉3.3V的轉接線路板。

其中DIR的電平可以單獨再用一個I/O口去控制,不過一般實際使用時基本上都是WRITE LCD的,READ LCD RAM很少,如無READ指令的話,DIR可以直接接高電平。