本帖最后由 MSP430G2553 于 2019-2-18 09:39 編輯
單片機以串行方式驅動LCD,所用的程序是借鑒別人的,漢字可以正常顯示,之后增加自己寫的ReadByte(讀RAM數據)和DrawPoint函數,不論怎么改變DrawPoint輸入的xy坐標參數,LCD只在固定位置顯示一部分點。不知道是不是ReadByte寫的有問題?(附上了源代碼)
盼望有經驗的高手賜教!
/* Main.c file generated by New Project wizard
*
* Created: 周四 二月 7 2019
* Processor: MSP430G2553
* Compiler: GCC for MSP430
*/
#include<math.h>
#include <msp430g2553.h>
#define uchar unsigned char
#define uint unsigned int
/*************************************************************************************************/
/*---------------------------------------8*16字模------------------------------------------------*/
/*************************************************************************************************/
uchar font1[]= //code就是告訴單片機這個數據放入ROM中,51單片機特有!
{
/*-- 文字: 測 --*/
/*-- 宋體12; 此字體下對應的點陣為:寬x高=16x16 --*/
0x08,0x31,0x86,0x60,0x00,0xFE,0x02,0xF2,0x02,0xFE,0x00,0xF8,0x00,0x00,0xFF,0x00,
0x04,0xFC,0x03,0x00,0x80,0x47,0x30,0x0F,0x10,0x67,0x00,0x07,0x40,0x80,0x7F,0x00,
/*-- 文字: 試 --*/
/*-- 宋體12; 此字體下對應的點陣為:寬x高=16x16 --*/
0x40,0x42,0xDC,0x08,0x00,0x90,0x90,0x90,0x90,0x90,0xFF,0x10,0x12,0x1C,0x10,0x00,
0x00,0x00,0x7F,0x20,0x10,0x20,0x20,0x1F,0x10,0x10,0x01,0x06,0x18,0x20,0x78,0x00,
/*-- 文字: 測 --*/
/*-- 宋體12; 此字體下對應的點陣為:寬x高=16x16 --*/
0x08,0x31,0x86,0x60,0x00,0xFE,0x02,0xF2,0x02,0xFE,0x00,0xF8,0x00,0x00,0xFF,0x00,
0x04,0xFC,0x03,0x00,0x80,0x47,0x30,0x0F,0x10,0x67,0x00,0x07,0x40,0x80,0x7F,0x00,
/*-- 文字: 試 --*/
/*-- 宋體12; 此字體下對應的點陣為:寬x高=16x16 --*/
0x40,0x42,0xDC,0x08,0x00,0x90,0x90,0x90,0x90,0x90,0xFF,0x10,0x12,0x1C,0x10,0x00,
0x00,0x00,0x7F,0x20,0x10,0x20,0x20,0x1F,0x10,0x10,0x01,0x06,0x18,0x20,0x78,0x00,
/*-- 文字: 測 --*/
/*-- 宋體12; 此字體下對應的點陣為:寬x高=16x16 --*/
0x08,0x31,0x86,0x60,0x00,0xFE,0x02,0xF2,0x02,0xFE,0x00,0xF8,0x00,0x00,0xFF,0x00,
0x04,0xFC,0x03,0x00,0x80,0x47,0x30,0x0F,0x10,0x67,0x00,0x07,0x40,0x80,0x7F,0x00,
/*-- 文字: 試 --*/
/*-- 宋體12; 此字體下對應的點陣為:寬x高=16x16 --*/
0x40,0x42,0xDC,0x08,0x00,0x90,0x90,0x90,0x90,0x90,0xFF,0x10,0x12,0x1C,0x10,0x00,//每個數組對應8bit二進制數
0x00,0x00,0x7F,0x20,0x10,0x20,0x20,0x1F,0x10,0x10,0x01,0x06,0x18,0x20,0x78,0x00,//每個漢字占據兩頁
};
/*************************************************************************************************/
/*--------------------------------------LCD功能定義----------------------------------------------*/
/*************************************************************************************************/
//從顯示 RAM 中讀取數據。可以連續讀出數據。在串行模式下,讀命令無效。
/*LCM顯示開關設定*/
#define DISPLAY_ON() WriteCommand(0xaf) // Display on, 顯示開
#define DISPLAY_OFF() WriteCommand(0xae) // Display off,顯示關
/*LCM顯示ADC設定*///本命令能夠使顯示 RAM 的列地址和段驅動的輸出反向。相當于左右反轉。當 ADC 為正常時,列
//地址從左到右為 0-127,當 ADC 為反向時,列地址從左到右為 131-4。防止模塊安裝反向
/*LCM反顯設定*/
#define REVERSE_DISPLAY_ON() WriteCommand(0xa7) // Reverse display : 0 illuminated,白字黑背景,D0變為1即可反白顯示
#define REVERSE_DISPLAY_OFF() WriteCommand(0xa6) // Normal display : 1 illuminated, 黑字白背景,D7~D0為10100110,對應串行數據為0xa6
/*LCM顯示黑屏設定*/
#define ENTIRE_DISPLAY_ON() WriteCommand(0xa5) // Entire dislay Force whole LCD point,D0位變為1即可變成全屏顯示狀態
//不能改寫顯示 RAM 里面的數據
#define ENTIRE_DISPLAY_OFF() WriteCommand(0xa4) // Normal display,D7~D0數據為10100100,對應串行輸入數據為0xa4
/*LCM顯示偏壓設定 */
#define SET_BIAS() WriteCommand(0xa3) // bias 1,D0變成1為1/7 bias
#define CLEAR_BIAS() WriteCommand(0xa2) // bias 0并行數據為10100010
/*LCM */
#define SET_MODIFY_READ() WriteCommand(0xe0) // Stop automatic increment of the column address by the read instruction
#define RESET_MODIFY_READ() WriteCommand(0xee) // Cancel Modify_read, column address return to its initial value just before the Set Modify Read instruction is started
/*LCM復位 */
#define RESET() WriteCommand(0xe2)
/*ADC*/ //ADC 表示列地址和端地址驅動器的關系
#define SET_ADC() WriteCommand(0xa1) // Normal disrect (SEG131-SEG0,當反向安裝時,ADC:0xa1,
//列范圍是從左到右4-131
#define CLEAR_ADC() WriteCommand(0xa0) // Reverse disrect (SEG0-SEG131),當正向安裝時,ADC:0xa0,
//此時行范圍為 0、63、……2、1,列范圍是 0-127。
/*LCM顯示輸出模式選擇*/
#define SET_SHL() WriteCommand(0xc8) // SHL 1,COM63-COM0 逆方向->從下到上顯示
#define CLEAR_SHL() WriteCommand(0xc0) // SHL 0,COM0-COM63 順方向->從上到下顯示
/*LCMLCM工作頻率選擇*/
#define SET_OSC() WriteCommand(0xE4) //
#define CLEAR_OSC() WriteCommand(0xE5) //
/**************************************************************************************************/
/*----------------------------------------延時函數------------------------------------------------*/
/**************************************************************************************************/
void Delay(uint t)
{
uint i,j;
for(i=0;i<t;i++)
{
for(j=0;j<=10;j++)
{
}
}
}
/*************************************************************************************************/
/*--------------------------------------LCM寫數據函數--------------------------------------------*/
/*************************************************************************************************/
void Write_Data(uchar dat) //寫一個字節數據
{
uchar i;
P3OUT|=BIT1;//SCLK = 1;
P3OUT&=~BIT1;//SCLK = 0;
P3OUT&=~BIT6;//CS= 0;使能從機,進行數據命令操作
P3OUT|=BIT4;//A0 = 1;命令數據選擇端,高電平:數據,低電平:命令
for(i=0;i<8;i++)
{
if(dat & 0x80)
{
P3OUT|=BIT0; //SID為1
}
else
{
P3OUT&=~BIT0; //SID為0
}
//SID = dat & 0x80;//取最高位
P3OUT|=BIT1;//SCLK = 1;
P3OUT&=~BIT1;//SCLK = 0;給一個時鐘來操作一位數據
dat <<= 1;//左移,準備發送下一個數據
}
Delay(20);//讀狀態函數也可以不寫,只用簡短的延時函數替換即可
P3OUT|=BIT6;//CS= 1; 停止寫的操作
}
/*************************************************************************************************/
/*------------------------------------LCM寫指令函數----------------------------------------------*/
/*************************************************************************************************/
void WriteCommand(uchar cmd)//往LCD控制器里面寫命令
{
uchar i;
P3OUT|=BIT1;//SCLK = 1;
P3OUT&=~BIT1;//SCLK = 0;
P3OUT&=~BIT6;//CS= 0;
P3OUT&=~BIT4;//A0 = 0;
for(i=0;i<8;i++)
{
if(cmd & 0x80)
{
P3OUT|=BIT0; //SID為1
}
else
{
P3OUT&=~BIT0; //SID為0
}
//SID = cmd & 0x80;//取最高位10000000
P3OUT|=BIT1;//SCLK = 1;
P3OUT&=~BIT1;//SCLK = 0;
cmd <<= 1;//左移,準備發送下一個數據
}
Delay(20);//讀狀態函數也可以不寫,只用簡短的延時函數替換即可
P3OUT|=BIT6;//CS= 1;
}
//通過頁地址(page address)和列地址(column address)共同來確定數據在顯示 RAM 中的位置。
/*************************************************************************************************/
/*--------------------------------------LCM頁(行)基址設置--------------------------------------------*/
/*************************************************************************************************/
void Set_Page_Address(uchar add)
{
add=0xb0|add;//設置顯示起始頁
WriteCommand(add);//一共有8頁(行)
}
/*************************************************************************************************/
/*--------------------------------------LCM設置列地址函數--------------------------------------------*/
/*************************************************************************************************/
void Set_Column_Address(uchar add)//列地址分成兩部分(高四位和低四位)寫入,顯示 RAM 每訪問一次,列地址自動加一
{
WriteCommand(0x10|(0x0f&(add>>4)));//取出高四位00001111
WriteCommand(0x0f&add);//取出低四位
}
/*************************************************************************************************/
/*----------------------------------------LCM清屏函數--------------------------------------------*/
/*************************************************************************************************/
void LcmClear( void )
{ //寫地址也是寫命令
uchar i,j;
WriteCommand(0x00); //Set Display Start Line = com0
for(i=0;i<8;i++)//總共有8頁
{//每個字節存儲一列(8行)點陣信息
WriteCommand(0xb0+i); //Set Page Address,起始地址為0xb0
WriteCommand(0x10); //Set Column Address = 0 16 00010000,列的起始地址為0x10
// WriteCommand(0x00); //1 -> 129 因為列地址(column address)在數據寫入后自動加 1,因
//此用戶可以連續向顯示 RAM 寫入數據
for(j=0;j<143;j++)
{
Write_Data( 0x00); //寫入0,地址指針自動加一,將要顯示的內容寫入顯示 RAM
}
if(i==4)
{
for(j=0;j<143;j++)//顯示橫坐標
{
Write_Data( 0x00); //寫入0,地址指針自動加一,將要顯示的內容寫入顯示 RAM
}
}
}
}
/**************************************************************************************************/
/*-------------------------------------LCM電源控制項設定函數-------(開關)----------------------------*/
/**************************************************************************************************/
void Power_Control(uchar vol)
{
WriteCommand((0x28|vol));
}
/**************************************************************************************************/
/*----------------------------------------LCM偏置電壓函數-----------------------------------------*/
/**************************************************************************************************/
void Regulor_Resistor_Select(uchar r)
{
WriteCommand((0x20|r));
}
/**************************************************************************************************/
/*----------------------------------------LCM電量設置函數-----------------------------------------*/
/**************************************************************************************************/
void Set_Contrast_Control_Register(uchar mod)
{
WriteCommand(0x81);
WriteCommand(mod);
}
/**************************************************************************************************/
/*----------------------------------------顯示起始行定義函數--------------------------------------*/
/**************************************************************************************************/
void Initial_Dispay_Line(uchar line)//8行,顯示RAM的行地址LineAddress
{ //行掃描方向是從 0,63,62 一直到 2,1 逐漸減小的當設定起始行后,從起始行開始的8 行是 PAGE0,當行地址到 1 之后,自動轉到第 0,63……
line|=0x40;//一般情況下,本命令設置為 0X40,通過有規律的改變起始行,可以實現上下滾屏,但要注意在滾屏結束后,將原先設定的起始行重新設定。
WriteCommand(line);
return;
}
/**************************************************************************************************/
/*----------------------------------------LCM初始化函數-------------------------------------------*/
/**************************************************************************************************/
void LcmInitial( void )
{
P3OUT&=~BIT5; //RES=0;
Delay(150);
P3OUT|=BIT5; //RES=1;復位操作,RST出現一個上升沿
Delay(10);
WriteCommand(0x30);
WriteCommand(0x02);
WriteCommand(0x0c);
WriteCommand(0x01);
WriteCommand(0x06);
DISPLAY_OFF(); //關顯示
LcmClear();
P3OUT|=BIT6; // CS=1;
SET_ADC(); /*LCM顯示ADC設定,順方向 */ //ADC為normal
CLEAR_SHL(); /*LCM公共輸出模式選擇,順方向 */ //顯示輸出模式為REVERSE,從左到右顯示
SET_BIAS(); /*LCM顯示偏壓設定(取中間值) */
Power_Control(0x07); /*LCM電源控制項設定,這里全開 */
Regulor_Resistor_Select(0x04); /*調整V0電阻比率,這里折中*/
Set_Contrast_Control_Register(0x28); //NT7534綠0x28//ST7565藍0x0a
Initial_Dispay_Line(0x00); /*開顯示*/
DISPLAY_ON();//進入省電模式?
}
/**************************************************************************************************/
/*--------------------------------------LCM漢字顯示函數-------------------------------------------*/
/**************************************************************************************************/
void Display_Chinese(uchar x,uchar y)//(DDRAM)最多可以控制16字元*4行,LCD的顯示范圍為16字元*2行
{
uchar i,j,k,num=0;
i=x;
for(i=x;i<x+2;i++) //xy只是起始位置,一個漢字占據兩頁
{
num=i<<7;
num=num>>3;
Set_Page_Address(i);//頁地址
Set_Column_Address(y);//列地址,找到對應8bit存儲單元的地址
for(j=0;j<6;j++) //6個漢字
{
for(k=0;k<16;k++)//讀寫GDRAM時一次最少寫2個字節,一次最少讀2個字節
{
Write_Data(font1[num+k]);//縱向取模
}
num+=32;//因為漢字分為上下兩部分,所以在數組中移位32位進行下一個漢字的顯示
}
}
}
/*************************************************************************************************/
/*--------------------------------------LCD引腳定義----------------------------------------------*/
/*************************************************************************************************/
void int_port(void) //管腳初始化,串行模式時,DB0-DB5 沒有作用
{
P3SEL&=~BIT0; //P3.0模擬SID,設置為i/o口輸出方向
P3DIR|=BIT0; //DB7(SI) :串行模式數據端
P3OUT&=~BIT0;
P3SEL&=~BIT1; //P3.1模擬SCLK,設置為i/o口輸出方向
P3DIR|=BIT1; //DB6(SCL):串行模式時鐘端
P3OUT&=~BIT1;
P3DIR|=BIT4; //A0 命令數據選擇端,高電平:數據,低電平:命令
P3OUT&=~BIT4; //A0 = 0;
P3DIR|=BIT5; //RES
P3OUT|=BIT5; //RES=1;
P3DIR|=BIT6; //CS
P3OUT|=BIT6; //CS= 1;
P3DIR|=BIT2; //RD使能控制配合WR使用
P3OUT|=BIT2; //向RAM寫數據
P3DIR|=BIT3; //WR讀寫控制線
P3OUT&=~BIT3; //置0為向RAM寫數據
}
void clrgdram()
//清除GDRAM中的的隨機數據。因為上電后GDRAM中的數據是隨機的,如果不清除而直接打開GDRAM顯示時,會顯示亂碼
//所以在局部使用GDRAM顯示圖形時,要先清除隨機數據。如果是全局使用GDRAM,即整個lcd屏全部設置顯示數據,則可以
//不必清除,因為新數據會把隨機數據給覆蓋掉
{
unsigned char i,j,k;
WriteCommand(0x34);//在寫GDRAM的地址之前一定要打開擴充指令集否則地址寫不進去!!
i = 0x80;//寫GDRAM的指令是0x80+地址
for(j = 0;j < 32;j++)//分上下兩個屏幕寫,上半屏32行,128列,且上下兩個半屏行地址相同,列地址由0~7
{
WriteCommand(i++);
WriteCommand(0x80);//列地址起始是0x80,一共16列
for(k = 0;k < 16;k++)//16個字節,每個字節8bit
{
Write_Data(0x00); //寫入空字符,就相當于清零
}
}
i = 0x80;
for(j = 0;j < 32;j++)//下半屏32行,上下兩個半屏行地址相同,列地址由8~15
{
WriteCommand(i++);//0x80+地址(兩字節16位,15bit~0bit)
WriteCommand(0x88);//列地址,由0x80+8得到下半屏起始列地址
for(k = 0;k < 16;k++)//列地址有8個,每個地址兩個字節,寫入數據是8bit,故需要2*8=16次寫入每一行(橫著寫)
{
Write_Data(0x00);//先高字節再低字節
}
}
WriteCommand(0x30);//回到基本指令集
}
//////////串口讀GDRAM數據/////////////A0是命令數據選擇端,高電平:數據,低電平:命令
uchar ReadByte(void) //這個函數是我自己寫的
{
uchar i,DIN;
uchar DATA;
/*********************************設置為讀數據狀態*************************************/
P3OUT&=~BIT2;//RD置0
P3OUT|=BIT3;//WR置1,選擇讀控制線
P3DIR&=~BIT0; //DB7(SI) :串行模式數據端,設置為輸入!!!!!讀數據
/***********************************************************************************/
P3OUT|=BIT4;//A0 = 1;操作數據
P3OUT |=BIT6; //LCD_CS=0;
for(i=0;i<8;i++)
{
//寫一個時鐘下降沿
P3OUT|=BIT1;//en=1,SCLK = 1;
P3OUT&=~BIT1;//SCLK = 0;
if(P3IN&BIT0)//讀SID數據,如果為高的話
DIN |=1; //寫數據
else
DIN |=0;
//再來一個時鐘
P3OUT|=BIT1;//en=1,SCLK = 1;
P3OUT&=~BIT1;//SCLK = 0;
DIN<<=1;//進行下一位的輸入,向高位移一位
}
DATA=DIN;
/************************************恢復為寫狀態*************************************/
P3DIR|=BIT0; //DB7(SI) :串行模式數據端
P3OUT|=BIT2;//RD置1
P3OUT&=~BIT3;//WR置0,選擇讀控制線,方便進行后續寫的操作
/***********************************************************************************/
P3OUT &=~BIT6;//LCD_CS=1;
return(DATA);
}
//12864串行連接寫數據,寫命令函數按照手冊上的時序進行編程
void DrawPoint(unsigned char X,unsigned char Y,unsigned char Color)//操作兩字節數據
{ //GDRAM是32行×16字
unsigned char Row,Tier,Tier_bit;
unsigned char ReadOldH,ReadOldL ;
WriteCommand(0x34);//使用擴展指令集,關閉繪圖顯示 打開擴展指令集
Tier=X>>4 ;//去16*16首地址,算出它在哪一個字節(地址)
Tier_bit=X&0x0f ;//算出它在哪一個位
if(Y<32)
{
Row=Y ;
}
else
{
Row=Y-32 ;//垂直坐標有32位,以1為單位
Tier+=8 ;//0x88,確定是上半屏韓式下半屏
}
WriteCommand(Row+0x80);//Y = 0x80 + y;
WriteCommand(Tier+0x80);//
ReadByte();//假讀!!!!!!!!!!!!
ReadOldH=ReadByte();//讀高字節
ReadOldL=ReadByte();//讀低字節
WriteCommand(Row+0x80);//寫入所確定的點的行位地址//讀操作會改變AC,所以重新設置一次
WriteCommand(Tier+0x80); //寫入所確定的點的列字地址
if(Tier_bit<8) //點在高字節
{
switch(Color) //畫點類型,1黑或0白或2取反
{
case 0 ://置0
ReadOldH&=(~(0x01<<(7-Tier_bit)));//修改該點同時保持其他位不變
break ;
case 1 ://置1
ReadOldH|=(0x01<<(7-Tier_bit));//修改該點同時保持其他位不變
break ;
case 2 ://取反
ReadOldH^=(0x01<<(7-Tier_bit));//修改該點同時保持其他位不變
break;
default :
break ;
}
//最后再把data重新寫進去就行了
Write_Data(ReadOldH);//將高字節數據寫回
Write_Data(ReadOldL);//將低字節數據不變寫回
}
else //點在低字節
{
switch(Color) //畫點類型,1黑或0白或2取反
{
case 0 ://置0
ReadOldL&=(~(0x01<<(15-Tier_bit)));//修改該點同時保持其他位不變
break ;
case 1 ://置1
ReadOldL|=(0x01<<(15-Tier_bit));//修改該點同時保持其他位不變
break ;
case 2 ://取反
ReadOldL^=(0x01<<(15-Tier_bit));//修改該點同時保持其他位不變
break ;
default :
break ;
}
//最后再把data重新寫進去就行了
Write_Data(ReadOldH);//將高字節數據不變寫回
Write_Data(ReadOldL);//將低字節數據寫回
}
WriteCommand(0x36);//打開繪圖顯示
WriteCommand(0x30);//回到基本指令集模式,轉回普通指令,畢竟ST7920是以字符為主的
//對DDRAM寫數據時,確保在基本指令集下(使用指令0x30開啟),然后寫入地址
}
/*************************************************************************************************/
/***************************************正弦函數計算程序*********************************************/
/*************************************************************************************************/
float tsin(float x)
{ //為了和標準庫中的sin()函數區別,所以取名為tsin()函數
float n = x,sum=0;
double TINY_VALUE = 1e-10; //計算精度
int i = 1;
do
{
sum += n;
i++;
n = (-n) * x*x / (2 * i - 1) / (2 * i - 2);
} while (fabs(n)>=TINY_VALUE);
return sum;
}
void fsin()
{
float x,y;//輸入的坐標范圍為128x64
uchar x1,y1;
for(x=0;x<(4*3.1415);x+=0.1)
{
float k;
//if (r*r <= s * s)
// k = sqrt(tsin(r)*tsin(r) + tsin(s)*tsin(s));
//else
k = tsin(x)/2 ;
//y=sin(x);
//x=r*s;
y=2*k;
x1=10*x;
y1=31-(10*y+0.5);//這條語句是對y值進行四舍五入!
DrawPoint(x1,y1,1);
}
}
/*------------------------------------------主程序------------------------------------------------*/
/**************************************************************************************************/
int main()
{
WDTCTL = WDTPW + WDTHOLD;
int_port();
LcmInitial();//LCD屏幕初始
LcmClear();
//Display_Chinese(4,0);//16x16小方
clrgdram();
DrawPoint(1,1,1); //畫一個點
while(1)
{
//fsin();
};
return 0;
}
|