再论Atmega8L-LCD12864,带画点函数,希望对想学点阵液晶的朋友有所帮助。(注:GCC的)
前一段时间想搞一下点阵LCD,于是就到本网站上寻找,找到了好多资料,xiaotanlan朋友的资料很好,http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=468165&bbs_page_no=1&sub_kind_id=2065&bbs_id=1000但是看了已后,觉得自已做的时候还是有很多要注意的地方.于是就写了下面的内容:
LCD12864液晶说明(个人跟椐datasheet的理解):
lcd12864分为左半屏和右半屏,每半个屏幕竖着排列着64(0~63)个字节,每个字节最低位在最上面,高位在最下面,一个字节的8位构成了lcd12864一页中一列的8个点,而它共有8页,即在y方向有8*8=64个点.如下图:
http://cache.amobbs.com/bbs_upload782111/files_5/armok01104631.jpg
画点的重点:
1,通过x坐标所属的左右半屏.
2,通过y坐标算出点所属的页,及一页内y坐标(即一个字节中一位的位置).
3,读出原来要画点位字节内容,再设要画的点(否则会擦除原来画的点).
程序如下:
环境:ATmega8L, WinAVR GCC, LCD12864, Proteus6.7
/*
ATmega8L LCD12864
作者:zhb2000
*/
#include <avr/io.h>
#define uchar unsigned char
#define uintunsigned int
#define boolunsigned char
#define true1
#define false 0
#define setbit(sfr,bit) (sfr|=(1<<bit))
#define clrbit(sfr,bit) (sfr&=~(1<<bit))
#define RS 0
#define RW 1
#define CS12
#define CS23
#define E 4
#define RST5
#define DATA PORTB
#define CTRL PORTC
void init(void);
uchar getState(void); //得到LCD状态字
bool lcdBusy(void);
void lcdCommand(uchar command); //发送命令字
void lcdOpen(void); //打开LCD
void lcdStartLine(uchar line); //设置起始行
void lcdSetPage(uchar page); //设置页地址
void lcdSetRow(uchar row); //设置列地址
void lcdWrite(uchar dat,uchar cs); //写显示数据,注'0'亮'1'暗
uchar lcdRead(uchar cs); //读数据
void pixel(uchar x,uchar y); //画点
int main(void)
{
init();
lcdOpen();
lcdStartLine(0);
lcdSetPage(0);
lcdSetRow(0);
pixel(30,30);
pixel(100,50);
pixel(15,40);
while(1)
{
}
return 0;
}
void init()
{
PORTB=0xff;
DDRB=0xff;
PORTC=0xff;
DDRC=0xff;
PORTD=0xff;
DDRD=0xff;
}
uchar getState()
{
PORTB=0xff;
DDRB=0x0;
clrbit(CTRL,CS1);
clrbit(CTRL,CS2);
clrbit(CTRL,RS);
setbit(CTRL,RW);
setbit(CTRL,E); //下降沿
clrbit(CTRL,E);
return PINB;
}
bool lcdBusy()
{
uchar state=0;
state=getState();
if(state&0x80)
{ //BUSY
return true;
}
else
{ //idle
return false;
}
}
void lcdCommand(uchar command)
{
DDRB=0xff;
clrbit(CTRL,CS1);
clrbit(CTRL,CS2);
clrbit(CTRL,RS);
clrbit(CTRL,RW);
DATA=command;
setbit(CTRL,E); //下降沿
clrbit(CTRL,E);
}
void lcdOpen()
{
while(lcdBusy());
lcdCommand(0x3f);
}
void lcdStartLine(uchar line)
{
line|=0xc0;
while(lcdBusy());
lcdCommand(line);
}
void lcdSetPage(uchar page)
{
page&=0x7;
page|=0xb8;
while(lcdBusy());
lcdCommand(page);
}
void lcdSetRow(uchar row)
{ //设置列地址
row&=0x3f;
row|=0x40;
while(lcdBusy());
lcdCommand(row);
}
void lcdWrite(uchar dat,uchar cs)
{ //写显示数据
while(lcdBusy());
DDRB=0xff;
if(cs==1)
{ //选择左半屏
clrbit(CTRL,CS1);
setbit(CTRL,CS2);
}
else
{ //选择右半屏
clrbit(CTRL,CS2);
setbit(CTRL,CS1);
}
setbit(CTRL,RS);
clrbit(CTRL,RW);
DATA=dat;
setbit(CTRL,E); //下降沿
clrbit(CTRL,E);
}
uchar lcdRead(uchar cs)
{ //读数据
uchar dat;
while(lcdBusy());
DDRB=0x00;
PORTB=0xff;
if(cs==1)
{ //选择左半屏
clrbit(CTRL,CS1);
setbit(CTRL,CS2);
}
else
{ //选择右半屏
clrbit(CTRL,CS2);
setbit(CTRL,CS1);
}
setbit(CTRL,RS);
setbit(CTRL,RW);
setbit(CTRL,E); //下降沿
clrbit(CTRL,E);
setbit(CTRL,E); //高
dat=PINB;
return dat;
}
void pixel(uchar x,uchar y)
{
uchar page,dX,dY;
uchar cs=2;
uchar dot=0;
if(x>=64)
{
x-=64;
}
else
{
cs=1;
}
dX=x; //算出x坐标
page=y/8; //算出页号
dY=y%8; //算出y坐标
lcdSetPage(page);
lcdSetRow(dX);
dot=lcdRead(cs);
clrbit(dot,dY);
lcdSetPage(page);
lcdSetRow(dX);
lcdWrite(dot,cs);
}
Proteus6.7仿真:
http://cache.amobbs.com/bbs_upload782111/files_5/armok01104628.jpg
原程序及仿真文件:
点击此处下载armok01104632.rar
希望对想学LCD12864点阵液晶的朋友有所帮助.
-----此内容被zhb2000于2006-03-01,10:56:12编辑过 好贴,顶。 好呀!谢谢!! 今天花了一整天时间才搞好LCD12863显示2色BMP图片(累啊 :) )。
自已还写了一个2色BMP图片到程序数组的转换小程序bmp2txt.exe
读取2色BMP图片,BMP的头结构我就不多说了,很多书上都有。
这里我们主要注意以下几个地方。
BMP文件偏移
0x0a 图像数据的开始位置
0x12 图像的宽度
0x16 图像的高度
0x1e 图像是否压缩
(这里我们只讨论不压缩的,用windows的画笔画一幅画存盘的时候选单色就行了)
BMP的2色图像是倒着存放的,即第一个数据字节表示是的图像最后一行的前8
个点"1"表示白色,"0"表示黑色(当然也可跟椐调色板来设置)
BMP图片数据每行要DWORD对齐,也是一行的字节数必须为4的倍数,如一行的数
据只有8个点为0xff 而BMP表示为 0xff 0x00 0x00 0x00
然后用bmp2txt.exe 将所画的2色图转为程序代码,copy到程序中编译就OK了。
http://cache.amobbs.com/bbs_upload782111/files_5/armok01104906.jpg
关键代码:
/*
读BMP图像 2色 未压缩
*/
#define BMP_HEIGHT 0x16
#define BMP_WIDTH0x12
#define BMP_COMPRESSION 0x1e
#define BMP_bfOffBits 0x0a
//COMPRESSION
#define BI_RGB 0
#define BI_RLE8 1
#define BI_RLE4 2
#define BI_BITFIELDS 3
const unsigned char Bitmap PROGMEM={//bmp2txt.exe生成的图像数据
......
};
void drawBitmap(const uchar *bmp)
{ //画图像
signed char x,y;
uchar bmpHeight,bmpWidth,wByte;
uintlpOffset;
uchar dat;
uchar nx=0;
uchar i;
uintnumb;
lpOffset=pgm_read_byte(bmp+BMP_COMPRESSION); //bmp;
if(lpOffset!=0) return; //只处理未压缩的图像
bmpHeight=pgm_read_byte(bmp+BMP_HEIGHT); //bmp;
bmpWidth=pgm_read_byte(bmp+BMP_WIDTH); //bmp;
lpOffset=pgm_read_byte(bmp+BMP_bfOffBits); //bmp 得到图像数据的偏移量
/*
1.BMP图象为到着存放的:即最后一行的数据存放在最前面
2.2色为一个字节表示一行中的8个像素点
*/
if(bmpWidth%8!=0)
{
wByte=bmpWidth/8+1;
}
else
{
wByte=bmpWidth/8;
}
if(wByte%4!=0)
{
wByte=(wByte/4+1)*4; //DWORD对齐
}
numb=0;
for(y=(bmpHeight-1);y>=0;y--)
{
for(nx=0;nx<wByte;nx++)
{
dat=pgm_read_byte(bmp+lpOffset+numb);// bmp;
numb++;
for(i=0;i<8;i++)
{
if(tstbit(dat,(7-i))==0)
{
x=nx*8+i;
if(x<bmpWidth)
{
pixel(x,y);
}
}
}
}
}
}
仿真图:
http://cache.amobbs.com/bbs_upload782111/files_5/armok01104907.jpg
源程序&工具&说明:
点击此处下载armok01104908.rar Proteus中有LCD12864的?以前还没有注意,晚上看看还有没有Nokia的那种48*84的LCD,可以的话先模拟实验。哪位大侠知道的请指点一下,先谢谢啦! Proteusd 在哪儿可以下载下来? 12864有好多种,楼主最好标明型号。
比如我用的SUNSON 12864A在读数据和忙信号时不是下降沿锁定,
而是高电平锁定的。
不同12864时序有细微差别的:) 好,顶一下。 健!顶 不顶不行!!!!! 顶 请教用什么工具制作图案呢?
我试着用Windows自带的画图工具来画,结果发现生成的文件好大,当用你的软件(BMP TO TXT)时,都卡死了! to 12楼 LOVEMCU:
用windows自带的画笔来画图,注意设置图像的属性宽度最大为127,高度最大为63(你不防就设为宽127,高63).然后用黑色绘图(因为最后的图像是2色的,只有白色和黑色所以浅色会被变成白色).画好以后保存的时候选保存类型为"单色位图(*.bmp,*.dib)" 起个名字保存即可.这样的图像是不会很大的. :) 好帖当然要先顶一个!
不过我刚才看到这个论坛里对Proteus有新的规定:
http://www2.ouravr.com/es_avr_serial_software.html
小心被列入黑名单哦,呵呵 顶一下,不必担心黑名单,没人会来论坛一个一个的帖子翻,那搞法律的还不累死? 我个人认为你画的我们的AVR那个图不对
我用Ultraedit取数据的时候
图片的每一行最后一个点是亮的
你可以看取出来的数据,都0xFE.
但是你仿真的上面没有 多谢楼主了!!
图片转换成数组成功!
转换过程:
将图片存为128*64的黑白BMP格式 -> 反色 -> 用UE编辑去掉前62(0x2e)个byte的BMP文件头,另存为BMP格式 -> 用bmp2txt.exe转换TXT格式数组
const unsigned char Bitmap PROGMEM={
有个问题:为什么转换出来1024个数组却只有1020个数值?
能不能调成每行显示16组数据?这样好看点。 是那种型号液晶, 好东西,学习。
顶!!!
-----此内容被wj414于2007-04-22,23:09:35编辑过 留个记号 好帖 做个记号 呵呵 不错,记号个 点解再画竖线时,读回来的值不对。会有空格出现,是不是读函数有时间的限制?
读程序如下(TS12864):
51的:
unsigned char Read_Data(void) //读数据
{
unsigned char adata1=0;
//LCD_busy();
//LCD_DATA=0XFF;
DI=1;
RW=1;
EN=1;
EN=0;
EN=1;
adata1=LCD_DATA;
return adata1;
}
void putpixel(int x,int y)
{
unsigned char py1,py2,adata0=0;
py1=y/8;
py2=y%8;
if(x>=64) Sel_Right(); // left = 0:for right.
else Sel_Left(); // left = 1:for left.
Write_Cmad(py1 | 0xb8);
Write_Cmad(x | 0x40);
adata0=Read_Data();
adata0|=(1<<py2); //这个数据有问题
Write_Cmad(py1 | 0xb8);
Write_Cmad(x | 0x40);
Write_Data(adata0);
}
画横线没什么问题,竖就会空格试了很多次了,有没有朋友遇到这种问题
或者画成功的指导下 Thanks 我用M16+LCM192*64,8M晶振,采取读两次的方式才能保证读LCM数据可靠! 记号 好贴 ding mark and up! 好资料,支持楼主! 谢了 谢谢楼主! mARK! mark mark MARK 很好的资料,谢谢楼上的各位! 支持一下 标记下好好看看;不顶不行了;这么好的帖子! mark 我顶一下 谢谢 jilu 不错,记号 MARK 标记! MARK
标记 先收下了! 好的不行!很多书都是不负责地抄过来抄过去,像这样的细说比什么书都强! 顶下 没办法,只能顶了 雁过留痕 收下,谢谢!! mark,不错 好 mark mark 标记 GOOD 我正好学一下GCC 向前辈学习了 顶 好帖啊!!! mark 帅气 三个点都画到同一行和同一列上试试看 顶顶顶 顶顶顶 顶顶顶 mark1 最近正学12864液晶,仿楼主程序搭了一个电路(M8),发现根本不能工作(没有反应),换了三个不同的12864,查了硬件,学习了12864手册,仍不能显示。但仿真没问题。估计程序还是有问题的。
同样试了网上几个例子,发现许多程序也是仿真没问题,实际硬件使用不行。
不得已换了楼主的bool lcdBusy(void)函数,终于12864有反应了,但显示仍是一团糟。
不知是否有高手试过该程序,请指导一下!!!
多谢了!!!!!! 留个记号。。 mark
Oooo
oooO ( )
( ) ) /
\ ( (_/
\_) mark mark 读数据时有问题,在同一列会出现空格点啊,哪位能指出问题在哪里吗?谢谢了! mark mark! MARK LCM驱动 顶一下,感谢分享 好谢谢分享 mark 不得不顶!谢谢分享 mark,谢谢分享! mark ding! mark mark mAKR mark mark mark 收下,谢谢了!! 学习了 mark 12864 回复【23楼】xiaorenren
-----------------------------------------------------------------------
void draw_dot(uint8 x,uint8 y) //全屏画任意点 x:0~63 y:0~127
{
uint8 dat,page,temp;
page=x>>3; //计算所画的点在第几行(页)
LCD12864_CS1_EN;
if(y>63) //判断是否在右屏
{ LCD12864_CS2_EN; y=y-64;}
LCD12864_wcmd(0xb8+page); //先读屏幕上第x/8行
LCD12864_wcmd(0x40+y); //屏幕上第y列DRAM中的数据
dat=LCD12864_rdata(); //读出所画点的所在行中数据
LCD12864_wcmd(0x40+y); //屏幕上第y列DRAM中的数据
dat=LCD12864_rdata(); //再读一次
temp=1<<(x%8);
dat=dat|temp; //将该行数据中该点的位置与1相或(即写1)
LCD12864_wcmd(0x40+y); //读操作后列值会自动加一为了不产生一列的偏差将地写重新再写一次
LCD12864_wdata(dat);
}
我也出现过这样的问题 横着打点是连续的但是竖着打点是间隔的单步调试时发现下一次读出来的数据是上一次想要的 不知道为什么 然后我就试着连着再读了一次 然后再处理写进去 调试发现打点是连续的如果有人能解释一下为什么 不胜感激 可以 不错不错 初学者过来学习 very good !
ding !!! 多谢分享! mark一下,先留着 MARKMARKMARK
页:
[1]
2