|
本帖最后由 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;
}
|
|