74595的使用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
单片机与74LS595(8位输出锁存移位寄存器)的使用方法
2009—04—12 13:55
74595的数据端:
QA-—QH: 八位并行输出端,可以直接控制数码管的8个段.
QH':级联输出端。
我将它接下一个595的SI端.
SI: 串行数据输入端。
74595的控制端说明:
/SCLR(10脚): 低点平时将移位寄存器的数据清零。
通常我将它接Vcc。
SCK(11脚):上升沿时数据寄存器的数据移位。
QA—->QB--〉QC-—>...——>QH;下降沿移位寄存器数据不变.(脉冲宽度:5V时,大于几十纳秒就行了。
我通常都选微秒级)
RCK(12脚):上升沿时移位寄存器的数据进入数据存储寄存器,下降沿时存储寄存器数据不变.通常我将RCK置为低点平,当移位结束后,在RCK端产生一个正脉冲(5V时,大于几十纳秒就行了。
我通常都选微秒级),更新显示数据。
/G(13脚): 高电平时禁止输出(高阻态).如果单片机的引脚不紧张,用一个引脚控制它,可以方便地产生闪烁和熄灭效果。
比通过数据端移位控制要省时省力。
注:
1)74164和74595功能相仿,都是8位串行输入转并行输出移位寄存器。
74164的驱动电流(25mA)比74595(35mA)的要小,14脚封装,体积也小一些。
2)74595的主要优点是具有数据存储寄存器,在移位的过程中,输出端的数据可以保持不变。
这在串行速度慢的场合很有用处,数码管没有闪烁感.
3)595是串入并出带有锁存功能移位寄存器,它的使用方法很简单,在正常使用时SCLR为高电平,G为低电平。
从SER每输入一位数据,串行输595是串入并出带有锁存功能移位寄存器,它的使用方法很简单,如下面的真值表,在正常使用时SCLR为高电平,G为低电平。
从SER每输入一位数据,串行输入时钟SCK上升沿有效一次,直到八位数据输入完毕,输出时钟上升沿有效一次,此时,输入的数据就被送到了输出端.入时钟SCK上升沿有效一次,直到八位数据输入完毕,输出时钟上升沿有效一次,此时,输入的数据就被送到了输出端。
其实,看了这么多595的资料,觉得没什么难的,关键是看懂其时序图,说到底,就是下面三步(引用):
第一步:目的:将要准备输入的位数据移入74HC595数据输入端上。
方法:送位数据到P1。
0。
第二步:目的:将位数据逐位移入74HC595,即数据串入
方法:P1.2产生一上升沿,将P1。
0上的数据移入74HC595中。
从低到高.
第三步:目的:并行输出数据.即数据并出
方法:P1.1产生一上升沿,将由P1.0上已移入数据寄存器中的数据送入到输出锁存器。
说明:从上可分析:从P1.2产生一上升沿(移入数据)和P1。
1产生一上升沿
(输出数据)是二个独立过程,实际应用时互不干扰。
即可输出数据的
同时移入数据。
而具体编程方法为
如:R0中存放3FH,LED数码管显示“0”
;*****接口定义:
DS_595 EQU P1.0 ;串行数据输入(595-14)
CH_595 EQU P1.2 ;移位时钟脉冲(595—11)
CT_595 EQU P1。
1 ;输出锁存器控制脉冲(595—12)
;*****将移位寄存器内的数据锁存到输出寄存器并显示
OUT_595:
CALL WR_595 ;调用移位寄存器接收一个字节数据子程序
CLR CT_595 ;拉低锁存器控制脉冲
NOP
NOP
SETB CT_595 ;上升沿将数据送到输出锁存器,LED数码管显示“0"
NOP
NOP
CLR CT_595
RET
;*****移位寄存器接收一个字节(如3FH)数据子程序
WR_595:
MOV R4,#08H ;一个字节数据(8位)
MOV A,R0 ;R0中存放要送入的数据3FH
LOOP:
;第一步:准备移入74HC595数据
RLC A ;数据移位
MOV DS_595,C ;送数据到串行数据输入端上(P1。
0)
;第二步:产生一上升沿将数据移入74HC595
CLR CH_595 ;拉低移位时钟
NOP
NOP
setb CH_595 ;上升沿发生移位(移入一数据)
DJNZ R4,LOOP ;一个字节数据没移完继续
RET
而其级联的应用
74HC595主要应用于点阵屏,以16*16点阵为例:传送一行共二个字节(16位)
如:发送的是06H和3FH。
其方法是:
1.先送数据3FH,后送06H.
2。
通过级联串行输入后,3FH在IC2内,06H在IC1内。
应用如图二
3。
接着送锁存时钟,数据被锁存并出现在IC1和IC2的并行输出口上显示。
编程方法:
数据在30H和31H中
;MOV 30H,#3FH
;MOV 31H,#06H
;*****接口定义:
DS_595 EQU P1。
0 ;串行数据输入(595—14)
CH_595 EQU P1。
2 ;移位时钟脉冲(595—11)
CT_595 EQU P1.1 ;输出锁存器控制脉冲(595-12)
;*****串行输入16位数据
MOV R0,30H
CALL WR_595 ;串行输入3FH
nop
NOP
MOV R0,31H
CALL WR_595 ;串行输入06H
NOP
NOP
SETB CT_595 ;上升沿将数据送到输出锁存器,显示
NOP
NOP
CLR CT_595
RET
利用74HC595实现多位LED显示的新方法
摘要:本文介绍了应用移位寄存器芯片74HC595实现LED动、静态显示的基本原理.提出了一种用74HC595实现多位LED显示的新方法。
同时对该系统的硬件组成和软件实现作了详细说明。
实际应用表明,此方法连线简单方便,成本低廉,可用于24位LED或更多位LED显示。
关键词:LED 74HC595 动态显示静态显示
1 引言
单片机应用系统中使用的显示器主要有LED和LCD两种。
近年来也有用C RT显示的。
前者价格低廉,配置灵活,与单片机接口方便;后者可进行图形显示,但接口较复杂,成本也较高。
LED(Ling Emiting Diode)是发光二极管的缩写。
实际应用非常普遍的是八段LED显示器。
LED显示器在大型报时屏幕,银行利率显示,城市霓虹灯建设中,得到广泛应用。
在这些需要多位LED显示的场合,怎样实现系统稳定,价格低廉的显示,成为决定其成本的关键所在。
2 74HC595实现LED静、动态显示基本原理
74HC595是美国国家半导体公司生产的通用移位寄存器芯片。
并行输出端具有输出锁存功能。
与单片机连接简单方便,只须三个I/O口即可.而且通过芯片的Q7引脚和SER引脚,可以级联。
而且价格低廉,每片单价为1.5元左右。
2。
1 静态显示
每位LED显示器段选线和74HC595的并行输出端相连,每一位可以独立显示(见图1)。
在同一时间里,每一位显示的字符可以各不相同(每一位由一个74H C595的并行输出口控制段选码).
N位LED显示要求N个74HC595芯片及N+3条I/O口线,占用资源较多,而且成本较高。
这对于多位LED显示很不利。
2.2 动态显示
在多位LED显示时,为了简化电路,降低成本,节省系统资源,将所有的N位段选码并联在一起,由一片74HC595控制(见图2)。
由于所有LED的段选码皆由一个74HC595并行输出口控制,因此,在每一瞬间,N位LED会显示相同的字符。
想要每位显示不同的字符,就必须采用扫描的方法,即在每一瞬间只使用一位显示字符.在此瞬间,74HC595并行输出口输出相应字符段选码,而位选则控制I/O口在该显示位送入选通电平,以保证该位显示相应字符。
如此轮流,使每位分时显示该位应显示字符.由于74HC595具有锁存功能,而且串行输入段选码需要一定时间,因此,不需要延时,即可形成视觉暂留效果.
N位LED显示时,只需要一片74HC595即可完成,成本最低。
但是,此种方法的最大弱点就是当LED的位数大于12位时,出现闪烁现象,这是所有动态LED 显示方式共同的弱点.
3 多位LED显示方法的实现
图3 多位LED动态显示驱动电路连线图
为实现24位或更多位LED显示,本文提出了一种全新的方法。
此方法结合了动态和静态显示的优点,可以说是两者的结合。
连线图如图3所示。
段选码由三片74HC595控制,段选数据由74HC595的SER引脚串行输入,由于输出使能时钟RCLK并接在一起,因此,三片74HC595并行输出端同时输出。
而三个L ED位选信号也并接在一起,因此,一次可以同时点亮三位LED.此过程类似于静态显示。
每片74HC595并行输出端并接8位LED,用于扫描输出,此过程类似于动态扫描过程。
此方法运用3片74HC595,n条位选信号,即可实现3n位L ED显示。
成本低廉,而且节省资源。
此种方法实现多位LED显示程序框图为图4所示,MCU为89S52。
示例程序如下(24位LED显示):
4 结论
实践证明,此多位LED显示方法性能稳定,如再级联一片74HC595,在不需要增加I/O口线的情况下,即可实现32位LED显示。
笔者做过48位LED显示,应稳定可靠。
其中,部分注释是本人注的,大家讨论一下这个程序吧.
/*
KS0108 128*64 LCD C语言驱动
参考ICCAVR资料和网上的资料改写
LCD引脚定义
1—--GND
2-—-VCC
3-——VLCD
4-——D/I
5—--R/W
6--—E
7到14 D0-D7
15--CS1
16——CS2
17—-RESET
18--VEE
19--SW
20——NC
*/
#include 〈iom16v。
h〉
#include 〈macros。
h>
/* 当前行、列存储,行高16点,列宽8点 */ unsigned char CurOffset,CurRow,CurPage,CurCol;
/* 常量定义*/
#define LCD_STATUS_BUSY 0x80
#define START_LINE0 0xc0
#define DISPLAY_ON 0x3f
#define DISPLAY_OFF 0x3e
#define PARA1 0x40
//PORTA-——数据口 PORTB----控制口
#define LCD_DIR_PORT DDRA
#define LCD_IP_PORT PINA
#define LCD_OP_PORT PORTA
#define LCD_EN_PORT PORTC
#define LCD_CS2_PORT PORTC
#define LCD_CS1_PORT PORTC
#define LCD_RW_PORT PORTC
#define LCD_DI_PORT PORTC
#define LCD_DI_BIT BIT(7)//0x80
#define LCD_RW_BIT BIT(6)//0x40
#define LCD_EN_BIT BIT(5)//0x20
#define LCD_CS1_BIT BIT(4)//0x10
#define LCD_CS2_BIT BIT(3)//0x08
#define SET_LCD_E LCD_EN_PORT |= LCD_EN_BIT //LCD使能
#define CLEAR_LCD_E LCD_EN_PORT &= ~LCD_EN_BIT //LCD禁止
//以下可能出错,数据指令接口
#define SET_LCD_DATA LCD_DI_PORT |= LCD_DI_BIT //选择数据端口
#define SET_LCD_CMD LCD_DI_PORT &= ~LCD_DI_BIT //选择指令端口
#define SET_LCD_READ LCD_RW_PORT |= LCD_RW_BIT //读模式
#define SET_LCD_WRITE LCD_RW_PORT &= ~LCD_RW_BIT //写模式
#define SET_LCD_CS2 LCD_CS2_PORT |= LCD_CS2_BIT //右屏选择禁止
#define CLEAR_LCD_CS2 LCD_CS2_PORT &= ~LCD_CS2_BIT //右屏选择使能
#define SET_LCD_CS1 LCD_CS1_PORT |= LCD_CS1_BIT //左屏选择禁止
#define CLEAR_LCD_CS1 LCD_CS1_PORT &= ~LCD_CS1_BIT //左屏选择使能
#define LEFT 0
#define RIGHT 1
#define CMD 0
#define DATA 1
void LCD_BUSY(unsigned char lr) //判断忙标志。
{
unsigned char status;
CLI();
if(lr==LEFT)
{
//选择左半屏.。
CLEAR_LCD_CS2; //cs2=0
SET_LCD_CS1; //cs1=1
}
else
{
//选择右半屏。
SET_LCD_CS2; //cs2=1
CLEAR_LCD_CS1; //cs1=0
}
SET_LCD_CMD;//选择指令端口.。
LCD_DIR_PORT = 0x00;//数据口方向设置。
LCD_OP_PORT = 0xff;//数据口输出高电平。
SET_LCD_READ;//读模式.。
SET_LCD_E;//LCD使能。
asm("nop"); asm(”nop”);
asm("nop”); asm(”nop”);
while((LCD_IP_PORT)& LCD_STATUS_BUSY)//判断LCD是否忙。
{
CLEAR_LCD_E;//LCD禁止。
.
asm(”nop”); asm("nop”);
asm(”nop”); asm("nop”);
SET_LCD_E;//LCD使能..
asm(”nop”);asm(”nop”);
asm(”nop"); asm(”nop”);
}
CLEAR_LCD_E;
SET_LCD_WRITE;//写模式。
LCD_OP_PORT = 0xff;//写入显示RAM
SEI();
}
void write_LCD(unsigned char lr,unsigned char cd,unsigned char data) /*写入指令或数据*/
{
CLI();
LCD_BUSY(lr);
if(cd==CMD) SET_LCD_CMD;
else SET_LCD_DATA;
SET_LCD_WRITE;
SET_LCD_E;
LCD_DIR_PORT = 0xff;
LCD_OP_PORT = data;
asm(”nop"); asm("nop");
asm("nop"); asm("nop");
CLEAR_LCD_E;
LCD_OP_PORT = 0xff;
SEI();
}
unsigned char read_LCD(unsigned char lr) /*读显示数据*/
{
unsigned char data;
CLI();
LCD_BUSY(lr);
SET_LCD_DATA;
LCD_DIR_PORT = 0x00;
LCD_OP_PORT = 0xff;
SET_LCD_READ;
SET_LCD_E;
asm(”nop"); asm("nop");
asm(”nop"); asm("nop");
data=LCD_IP_PORT;
CLEAR_LCD_E;
SET_LCD_WRITE;
LCD_BUSY(lr);
SET_LCD_DATA;
LCD_DIR_PORT = 0x00;
LCD_OP_PORT = 0xff;
SET_LCD_READ;
SET_LCD_E;
asm("nop”); asm(”nop");
asm("nop”); asm(”nop”);
data=LCD_IP_PORT;
CLEAR_LCD_E;
SET_LCD_WRITE;
SEI();
return data;
}
void set_start_line_L(unsigned char line) /*设置显示起始行*/ //0—63
{
write_LCD(LEFT,CMD,0xc0|line);
}
void set_start_line_R(unsigned char line) /*设置显示起始行*/ //0—63 {
write_LCD(RIGHT,CMD,0xc0|line);
}
void set_page_L(unsigned char page) /*设置X地址设置页*/ //0—7 {
write_LCD(LEFT,CMD,0xb8|page);
}
void set_page_R(unsigned char page) /*设置X地址设置页*/ //0—7 {
write_LCD(RIGHT,CMD,0xb8|page);
}
void set_col_addr_L(unsigned char col) /*设置Y地址*/ //0—63
{
write_LCD(LEFT,CMD,0x40|col);
}
void set_col_addr_R(unsigned char col) /*设置Y地址*/ //0—63
{
write_LCD(RIGHT,CMD,0x40|col);
}
void init_lcd(void) /*初始化函数*/
{
set_start_line_L(0); /*显示起始行为0*/
set_start_line_R(0); /*显示起始行为0*/
write_LCD(LEFT,CMD,DISPLAY_ON); /*开显示 */
write_LCD(RIGHT,CMD,DISPLAY_ON); /* 开显示 */
}
void clr_lcd(void) /*清屏函数*/
{
unsigned char pages,i;
for(pages=0;pages〈8;pages++)
{
set_page_L(pages); /* X 页地址 */
set_page_R(pages); /* X 页地址*/
for(i=0;i〈64;i++)
{
set_col_addr_L(i);//Y
set_col_addr_R(i);//Y
write_LCD(LEFT,DATA,0x0);
write_LCD(RIGHT,DATA,0x0);
}
}
}
/*************************************/
/*绘点函数*/
/*************************************/
void pixel(unsigned char xx,unsigned char yy,unsigned char flag){
unsigned int y,ch;
ch=yy%8; //余数
y=1;
for(;ch!=0;)
{
y=y*2;
ch--;
}
if(xx〈64)
{
set_page_L(yy/8);
set_col_addr_L(xx);
ch=read_LCD(LEFT);
set_col_addr_L(xx);
if(flag)
write_LCD(LEFT,DATA,ch|y);
else
{
y=~y;
ch&=y;
write_LCD(LEFT,DATA,ch|y);
}
}
else
{
set_page_R(yy/8);
set_col_addr_R(xx—64);
ch=read_LCD(RIGHT);
set_col_addr_R(xx—64);
if(flag)
write_LCD(RIGHT,DATA,ch|y);
else
{
y=~y;
ch&=y;
write_LCD(RIGHT,DATA,ch|y);
}
}
}
//ASCII 字模宽度及高度
#define ASC_CHR_WIDTH 8
#define ASC_CHR_HEIGHT 12
typedef struct typFNT_ASC16 /*汉字字模显示数据结构*/
{
char Index[1];
char Msk[16];
};
struct typFNT_ASC16 const ASC_16[] = { /*显示为8*16 Curier 10 常规*/
"1",0x00,0x00,0x08,0x08,0xFC,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x07,0x04,0x04,0x00,
”2”,0x00,0x00,0x08,0x04,0x84,0x44,0x38,0x00,0x00,0x00,0x06,0x05,0x04,0x04,0x04,0x00,
"3",0x00,0x00,0x08,0x04,0x44,0x44,0xB8,0x00,0x00,0x00,0x02,0x04,0x04,0x04,0x03,0x00,
”4”,0x00,0x00,0x80,0x60,0x18,0xFC,0x00,0x00,0x00,0x00,0x01,0x01,0x05,0x07,0x05,0x00,
"5",0x00,0x00,0x7C,0x44,0x44,0x44,0x84,0x00,0x00,0x00,0x02,0x04,0x04,0x04,0x03,0x00,
"6”,0x00,0x00,0xF0,0x48,0x44,0x44,0x80,0x00,0x00,0x00,0x03,0x04,0x04,0x04,0x03,0x00,
"7",0x00,0x00,0x0C,0x04,0x84,0x64,0x1C,0x00,0x00,0x00,0x00,0x06,0x01,0x00,0x00,0x00,
”8",0x00,0x00,0xB8,0x44,0x44,0x44,0xB8,0x00,0x00,0x00,0x03,0x04,0x04,0x04,0x03,0x00,
"9",0x00,0x00,0x38,0x44,0x44,0x44,0xF8,0x00,0x00,0x00,0x00,0x04,0x04,0x02,0x01,0x00,
”0",0x00,0x00,0xF8,0x04,0x04,0x04,0xF8,0x00,0x00,0x00,0x03,0x04,0x04,0x04,0x03,0x00,
”.",0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,
":",0x00,0x00,0x00,0x0C,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,
””,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
"(”,0x00,0x00,0x00,0xE0,0x18,0x04,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x10,0x00,0x00,
")",0x00,0x00,0x00,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x00,0x10,0x0C,0x03,0x00,0x00,
};
#define ASC_HZ_WIDTH 12
//#define ASC_HZ_HEIGHT 12
typedef struct typFNT_GB16 /*12*16 汉字字模显示数据结构 */
{
char Index[2];
char Msk[24];
};
struct typFNT_GB16 const GB_16[] = { /*宋体 9小五显示为12*16 */
”液",0x19,0xE2,0x14,0x42,0xF2,0x2E,0x72,0x8F,0xAA,0x7A,0x02,0x00,0x01,0x07,0x00,0x00,0x07,0x04,0x04,0x02,0x01,0x02,0x04,0x00,
"晶”,0x00,0xC0,0x40,0x5F,0xD5,0x15,0xD5,0x55,0x5F,0x40,0xC0,0x00,0x00,0x07,0x05,0x05,0x07,0x00,0x07,0x05,0x05,0x05,0x07,0x00,
”显”,0x00,0x40,0x9F,0x15,0xD5,0x15,0xD5,0x15,0x1F,0xC0,0x00,0x00,0x04,0x04,0x05,0x04,0x07,0x04,0x07,0x06,0x05,0x04,0x04,0x00,
”示",0x10,0x12,0x92,0x52,0x12,0xF2,0x12,0x12,0x53,0x92,0x10,0x00,0x02,0x01,0x00,0x04,0x04,0x07,0x00,0x00,0x00,0x00,0x03,0x00,
”的”,0xFC,0x44,0x46,0x45,0xFC,0x10,0x2C,0xC7,0x04,0x04,0xFC,0x00,0x07,0x02,0x02,0x02,0x07,0x00,0x00,0x04,0x04,0x04,0x03,0x00,
”第”,0x04,0xEA,0xAB,0xAE,0xAA,0xFC,0xAA,0xAB,0xAE,0xBA,0x82,0x00,0x04,0x04,0x02,0x01,0x00,0x07,0x00,0x02,0x02,0x02,0x01,0x00,
"一”,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
”行”,0x48,0x24,0xF3,0x08,0x09,0x09,0x09,0x09,0xF9,0x09,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x04,0x04,0x04,0x07,0x00,0x00,0x00,
”二",0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x06,0x04,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,
”三",0x00,0x02,0x22,0x22,0x22,0x22,0x22,0x22,0x23,0x02,0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x06,0x04,0x00,
"四",0x00,0xFF,0x81,0x41,0x3F,0x01,0x01,0xFF,0x81,0x81,0xFF,0x00,0x00,0x07,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x07,0x00,
"五",0x00,0x11,0x11,0x91,0x7F,0x11,0x11,0x11,0xF1,0x01,0x00,0x00,0x04,0x04,0x04,0x07,0x04,0x04,0x04,0x04,0x07,0x04,0x04,0x00,
};
unsigned char GetPage(void) /*得到当前页*/
{
return CurPage;
}
unsigned char GetCol(void) /*得到当前列*/
{
return CurCol;
}
void SetPageCol(unsigned char upage, unsigned char ucol) /* 设置液晶的页和列*/
CurPage = upage;
CurCol = ucol;
if(ucol〈64)
{
set_page_L(upage);
set_col_addr_L(ucol);
}
else
{
set_page_R(upage);
set_col_addr_R(ucol—64);
}
}
/* 设置当前显示的页和列*/
void SetRowCol(unsigned char urow, unsigned char ucol) {
unsigned char page;
CurRow = urow;
CurCol = ucol;
switch(urow)
{
case 1:
page=0;
CurOffset=1;
break;
case 2:
page=1;
CurOffset=2;
break;
case 3:
page=3;
CurOffset=1;
break;
case 4:
page=4;
CurOffset=2;
break;
case 5:
page=6;
CurOffset=1;
break;
SetPageCol(page,ucol);
}
void disp_char(unsigned char c)
{
unsigned char k,j,uPage,uCol,ch_r,ch_w;
unsigned char width;
unsigned char len;
uPage = GetPage();
uCol = GetCol();
len=sizeof(ASC_16)/sizeof(ASC_16[0]);
for(k=0;k<len;k++)
{
if(c == ASC_16[k]。
Index[0] ) break;
}
if(k〈len)
{
if(c==’-’||c==':')
width=ASC_CHR_WIDTH—2;
else if(c==’|’)
width=ASC_HZ_WIDTH—ASC_CHR_WIDTH;
else
width=ASC_CHR_WIDTH;
if(CurOffset==1) //下半部是写半个字节
{
for(j=0;j<width;j++)
{
SetPageCol(uPage,uCol+j);
ch_w=ASC_16[k]。
Msk[j];
if(uCol+j<64) write_LCD(LEFT,DATA,ch_w);
else write_LCD(RIGHT,DATA,ch_w);
}
SetPageCol(uPage+1,uCol);
for(j=0;j〈width;j++)
{
SetPageCol(uPage+1,uCol+j);
if(uCol+j〈64) ch_r=read_LCD(LEFT);
else ch_r=read_LCD(RIGHT);
ch_r&=0xf0;
ch_w=ASC_16[k]。
Msk[ASC_CHR_WIDTH+j]&0x0f;
ch_w|=ch_r;
SetPageCol(uPage+1,uCol+j);
if(uCol+j〈64) write_LCD(LEFT,DATA,ch_w); else write_LCD(RIGHT,DATA,ch_w);
}
}
else //上半部是写半个字节
{
for(j=0;j〈width;j++)
{
SetPageCol(uPage,uCol+j);
if(uCol+j<64) ch_r=read_LCD(LEFT);
else ch_r=read_LCD(RIGHT);
ch_r&=0x0f;
ch_w=ASC_16[k].Msk[j];
ch_w=ch_w〈<4;
ch_w|=ch_r;
SetPageCol(uPage,uCol+j);
if(uCol+j〈64) write_LCD(LEFT,DATA,ch_w);
else write_LCD(RIGHT,DATA,ch_w);
}
SetPageCol(uPage+1,uCol);
for(j=0;j〈width;j++)
{
SetPageCol(uPage+1,uCol+j);
ch_r=ASC_16[k].Msk[j];
ch_w=ASC_16[k].Msk[ASC_CHR_WIDTH+j];
ch_r=ch_r〉>4;
ch_w=ch_w〈<4;
ch_w|=ch_r;
SetPageCol(uPage+1,uCol+j);
if(uCol+j<64) write_LCD(LEFT,DATA,ch_w); else write_LCD(RIGHT,DATA,ch_w);
}
}
}
SetPageCol(uPage,uCol+width);
}
void disp_hz(unsigned char const *hz)
{
unsigned char k,j,uPage,uCol,ch_r,ch_w;
uPage = GetPage();
uCol = GetCol();
for(k=0;k<sizeof(GB_16)/sizeof(GB_16[0]);k++)
{
if(hz[0] == GB_16[k]。
Index[0] && hz[1] == GB_16[k]。
Index[1]) break;
}
if(CurOffset==1)
{
for(j=0;j〈ASC_HZ_WIDTH;j++)
{
SetPageCol(uPage,uCol+j);
ch_w=GB_16[k].Msk[j];
if(uCol+j<64) write_LCD(LEFT,DATA,ch_w);
else write_LCD(RIGHT,DATA,ch_w);
}
SetPageCol(uPage+1,uCol);
for(j=0;j〈ASC_HZ_WIDTH;j++)
{
SetPageCol(uPage+1,uCol+j);
if(uCol+j<64) ch_r=read_LCD(LEFT);
else ch_r=read_LCD(RIGHT);
ch_r&=0xf0;
ch_w=GB_16[k]。
Msk[ASC_HZ_WIDTH+j]&0x0f;
ch_w|=ch_r;
SetPageCol(uPage+1,uCol+j);
if(uCol+j<64) write_LCD(LEFT,DATA,ch_w);
else write_LCD(RIGHT,DATA,ch_w);
}
SetPageCol(uPage,uCol+ASC_HZ_WIDTH);
}
else //汉字上半部是写半个字节
{
for(j=0;j〈ASC_HZ_WIDTH;j++)
{
SetPageCol(uPage,uCol+j);
if(uCol+j<64) ch_r=read_LCD(LEFT);
else ch_r=read_LCD(RIGHT);
ch_r&=0x0f;
ch_w=GB_16[k]。
Msk[j];
ch_w=ch_w〈〈4;
ch_w|=ch_r;
SetPageCol(uPage,uCol+j);
if(uCol+j〈64) write_LCD(LEFT,DATA,ch_w); else write_LCD(RIGHT,DATA,ch_w);
}
SetPageCol(uPage+1,uCol);
for(j=0;j〈ASC_HZ_WIDTH;j++)
{
SetPageCol(uPage+1,uCol+j);
ch_r=GB_16[k].Msk[j];
ch_w=GB_16[k].Msk[ASC_HZ_WIDTH+j];
ch_r=ch_r〉〉4;
ch_w=ch_w<<4;
ch_w|=ch_r;
SetPageCol(uPage+1,uCol+j);
if(uCol+j〈64) write_LCD(LEFT,DATA,ch_w); else write_LCD(RIGHT,DATA,ch_w);
}
SetPageCol(uPage,uCol+ASC_HZ_WIDTH);
}
}
void disp_str(unsigned char const *p)
{
unsigned char i=0;
while(p[i]>0)
{
if(p[i] < 128)
{ /* ASCII */
disp_char(p[i]);
}
else
{ /* 中文 */
disp_hz(&p[i]);
i++;
}
i++;
}
}
void main()
{
unsigned char i;
DDRC=0xff;
init_lcd();
clr_lcd();
SetRowCol(1,0);
disp_str(”液晶显示的第一行1234");
SetRowCol(2,0);
disp_str(”液晶显示的第二行2345”);
SetRowCol(3,0);
disp_str("液晶显示的第三行3456");
SetRowCol(4,0);
disp_str(”液晶显示的第四行5678”);
SetRowCol(5,0);
disp_str("液晶显示的第五行6789”);
for(i=0;i<64;i++) pixel(127,i,1);
for(i=0;i<64;i++) pixel(0,i,1);
for(i=0;i〈128;i++) pixel(i,0,1);
for(i=0;i<128;i++) pixel(i,63,1);
while(1);
}
-—-——此内容被mcuhost于2006-11-10,09:31:24编辑过
if(uCol+j〈64) ch_r=read_LCD(LEFT);
else ch_r=read_LCD(RIGHT);
ch_r=~ch_r;
再write就可以了
这里就可以
感觉写汉字和写字母的不应该分开,反正一个扫八列,一个扫十六
列
用一个标志来做汉字和字母的判断,
那样就可以用一个函数写出来.
我把GUI贴上来
/*****************************************************************************
***********
*文件名:GUI.C
* 功能:GUI基本绘图函数。
进行基本绘图运算,并调用相应的刷
新程序更新LCD显示。
* 作者:黄绍斌
* 修改:冯建辉
*日期:2006。
09.13
* 备注:图形操作层,进行各种图形运算操作。
********************************************************************************
********/
#include ”gui.h”
#include <math。
h>
/****************************************************************************
* 名称:GUI_HLine()
* 功能:画水平线.
* 入口参数:x0 水平线起点所在列的位置
* y0 水平线起点所在行的位置
* x1 水平线终点所在列的位置
* color 显示颜色(对于黑白色LCM,为0时灭,为1时
显示)
* 出口参数:无
*说明:操作失败原因是指定地址超出缓冲区范围。
****************************************************************************/
void GUI_HLine(unsigned int x0, unsigned char y0, unsigned i
nt x1, TCOLOR color)
{
unsigned char bak;
if(x0>x1) // 对x0、x1大小进行排列,以便画图
{
bak = x1;
x1 = x0;
x0 = bak;
}
do
{
GUI_Point(x0, y0, color); // 逐点显示,描出垂直线
x0++;
}
while(x1〉=x0);
}
/***********************************************************************
*名称:GUI_RLine()
*功能:画竖直线。
根据硬件特点,实现加速。
* 入口参数:x0 垂直线起点所在列的位置
* y0 垂直线起点所在行的位置
* y1 垂直线终点所在行的位置
* color 显示颜色(对于黑白色LCM,为0时灭,为1时显
示)
* 出口参数:无
*说明:操作失败原因是指定地址超出缓冲区范围。
***********************************************************************/ void GUI_RLine(unsigned int x0, unsigned char y0, unsigne
d char y1, TCOLOR color)
{
unsigned char bak;
if(y0〉y1) // 对y0、y1大小进行排列,以
便画图
{
bak = y1;
y1 = y0;
y0 = bak;
}
do
{
GUI_Point(x0, y0, color); // 逐点显示,描出垂直线
y0++;
}
while(y1>=y0);
}
/****************************************************************************
*名称:GUI_Rectangle()
* 功能:画矩形。
*入口参数:x0 矩形左上角的x坐标值
* y0 矩形左上角的y坐标值
* x1 矩形右下角的x坐标值
* y1 矩形右下角的y坐标值
* color 显示颜色
* 出口参数:无
*说明:操作失败原因是指定地址超出有效范围。
***************************************************************************
*/
void GUI_Rectangle(uint16 x0, uint8 y0, uint16 x1, uint8 y
1, TCOLOR color)
{
GUI_HLine(x0, y0, x1, color);
GUI_HLine(x0, y1, x1, color);
GUI_RLine(x0, y0, y1, color);
GUI_RLine(x1, y0, y1, color);
}
/***************************************************************************
*
*名称:GUI_RectangleFill()
* 功能:填充矩形。
画一个填充的矩形,填充色与边框色一样。
*入口参数:x0 矩形左上角的x坐标值
* y0 矩形左上角的y坐标值
* x1 矩形右下角的x坐标值
* y1 矩形右下角的y坐标值
* color 填充颜色
*出口参数:无
*说明:操作失败原因是指定地址超出有效范围。
****************************************************************************/ void GUI_RectangleFill(uint16 x0, uint8 y0, uint16 x1, uint8 y
1, TCOLOR color)
{
uint32 i;
/*先找出矩形左上角与右下角的两个点,保存在(x0,y0),(x
1,y1)*/
if(x0〉x1) // 若x0>x1,则x0与x1交换
{
i = x0;
x0 = x1;
x1 = i;
}
if(y0〉y1) // 若y0〉y1,则y0与y1交换
{
i = y0;
y0 = y1;
y1 = i;
}
/* 判断是否只是直线*/
if(y0==y1)
{
GUI_HLine(x0, y0, x1, color);
return;
}
if(x0==x1)
{
GUI_RLine(x0, y0, y1, color);
return;
}
while(y0<=y1)
{
GUI_HLine(x0, y0, x1, color); // 当前画水
平线
y0++; // 下一行
}
}
/****************************************************************************
* 名称:GUI_Square()
*功能:画正方形。
* 入口参数:x0 正方形左上角的x坐标值
* y0 正方形左上角的y坐标值
* with 正方形的边长
* color 显示颜色
* 出口参数:无
*说明:操作失败原因是指定地址超出有效范围。
***************************************************************************
*/
void GUI_Square(uint32 x0, uint32 y0, uint32 with, TCOL
OR color)
{
if(with==0)
return;
if( (x0+with) > GUI_LCM_XMAX )
return;
if( (y0+with)〉 GUI_LCM_YMAX )
return;
GUI_Rectangle(x0, y0, x0+with, y0+with, color);
}
/***************************************************************************
*
*名称:GUI_Line()
* 功能:画任意两点之间的直线。
*入口参数:x0 直线起点的x坐标值
* y0 直线起点的y坐标值
* x1 直线终点的x坐标值
* y1 直线终点的y坐标值
* color 显示颜色(对于黑白色LCM,为0时灭,为1时
显示)
* 出口参数:无
* 说明:操作失败原因是指定地址超出有效范围。
******************************************* *********************************/
void GUI_Line(uint32 x0, uint32 y0, uint32 x1, uint32 y1, T
COLOR color)
{
int32 dx; // 直线x轴差值变量 int32 dy; // 直线y轴差值变量
int8 dx_sym; // x轴增长方向,为-1时减值方向,为
1时增值方向
int8 dy_sym; // y轴增长方向,为-1时减值方向,为
1时增值方向
int32 dx_x2; // dx*2值变量,用于加快运算速度 int32 dy_x2; // dy*2值变量,用于加快运算速度 int32 di; // 决策变量
dx = x1-x0; // 求取两点之间的差值
dy = y1—y0;
/* 判断增长方向,或是否为水平线、垂直线、点 */
if(dx>0) // 判断x轴方向
{
dx_sym = 1; // dx>0,设置dx_sym=1
}
else
{
if(dx〈0)
{
dx_sym = -1; // dx<0,设置dx_sym=—1
}
else
{ // dx==0,画垂直线,或一点
GUI_RLine(x0, y0, y1, color);
return;
}
}
if(dy〉0) // 判断y轴方向
{
dy_sym = 1; // dy>0,设置dy_sym=1
}
else
{
if(dy〈0)
{
dy_sym = —1; // dy<0,设置dy_sym=-1
}
else
{ // dy==0,画水平线,或一点
GUI_HLine(x0, y0, x1, color);
return;
}
}
/* 将dx、dy取绝对值 */
dx = dx_sym * dx;
dy = dy_sym * dy;
/* 计算2倍的dx及dy值 */
dx_x2 = dx*2;
dy_x2 = dy*2;
/* 使用Bresenham法进行画直线*/
if(dx>=dy) // 对于dx〉=dy,则使用x轴为基准
{
di = dy_x2 - dx;
while(x0!=x1)
{
GUI_Point(x0, y0, color);
x0 += dx_sym;
if(di<0)
{
di += dy_x2; // 计算出下一步的决策值
}
else
{
di += dy_x2 — dx_x2;
y0 += dy_sym;
}
}
GUI_Point(x0, y0, color); // 显示最后一点
}
else // 对于dx〈dy,则使用y轴为基准
{
di = dx_x2 — dy;
while(y0!=y1)
{
GUI_Point(x0, y0, color);
y0 += dy_sym;
if(di〈0)
{
di += dx_x2;
}
else
{
di += dx_x2 — dy_x2;
x0 += dx_sym;
}
}
GUI_Point(x0, y0, color); // 显示最后一点
}
}
#if GUI_LineWith_EN==1
/****************************************************************************
* 名称:GUI_LineWith()
*功能:画任意两点之间的直线,并且可设置线的宽度.
* 入口参数:x0 直线起点的x坐标值
* y0 直线起点的y坐标值
* x1 直线终点的x坐标值
* y1 直线终点的y坐标值
* with 线宽(0—50)
* color 显示颜色
* 出口参数:无
*说明:操作失败原因是指定地址超出有效范围。
****************************************************************************/ void GUI_LineWith(uint32 x0, uint32 y0, uint32 x1, uint32 y
1, uint8 with, TCOLOR color)
{
int32 dx; // 直线x轴差值变量
int32 dy; // 直线y轴差值变量
int8 dx_sym; // x轴增长方向,为—1时减值方向,为1
时增值方向
int8 dy_sym; // y轴增长方向,为—1时减值方向,
为1时增值方向
int32 dx_x2; // dx*2值变量,用于加快运算速度
int32 dy_x2; // dy*2值变量,用于加快运算速度
int32 di; // 决策变量
int32 wx, wy; // 线宽变量
int32 draw_a, draw_b;
/* 参数过滤*/
if(with==0)
return;
if(with>50)
with = 50;
dx = x1-x0; // 求取两点之间的差值
dy = y1—y0;
wx = with/2;
wy = with-wx—1;
/* 判断增长方向,或是否为水平线、垂直线、点 */
if(dx>0) // 判断x轴方向
{
dx_sym = 1; // dx〉0,设置dx_sym=1
}
else
{
if(dx<0)
{
dx_sym = —1; // dx<0,设置dx_sym=—1
}
else
{ /* dx==0,画垂直线,或一点*/
wx = x0-wx;
if(wx<0)
wx = 0;
wy = x0+wy;
while(1)
{
x0 = wx;
GUI_RLine(x0, y0, y1, color);
if(wx>=wy)
break;
wx++;
}
return;
}
}
if(dy〉0) // 判断y轴方向
{
dy_sym = 1; // dy>0,设置dy_sym=1
}
else
{
if(dy<0)
{
dy_sym = -1; // dy<0,设置dy_sym=-1
}
else
{ /* dy==0,画水平线,或一点*/
wx = y0—wx;
if(wx〈0) wx = 0;
wy = y0+wy;
while(1)
{
y0 = wx;
GUI_HLine(x0, y0, x1, color);
if(wx〉=wy)
break;
wx++;
}
return;
}
}
/* 将dx、dy取绝对值*/
dx = dx_sym * dx;
dy = dy_sym * dy;
/*计算2倍的dx及dy值*/
dx_x2 = dx*2;
dy_x2 = dy*2;
/* 使用Bresenham法进行画直线*/
if(dx>=dy) // 对于dx>=dy,则使用x轴为基准
{
di = dy_x2 — dx;
while(x0!=x1)
{ /* x轴向增长,则宽度在y方向,即画垂直线 */
draw_a = y0—wx;
if(draw_a〈0) draw_a = 0;
draw_b = y0+wy;
GUI_RLine(x0, draw_a, draw_b, color);
x0 += dx_sym;
if(di〈0)
{
di += dy_x2; // 计算出下一步的决策值
}
else
{
di += dy_x2 — dx_x2;
y0 += dy_sym;
}
}
draw_a = y0—wx;
if(draw_a〈0)
draw_a = 0;
draw_b = y0+wy;
GUI_RLine(x0, draw_a, draw_b, color);
}
else // 对于dx<dy,则使用y轴为基准
{
di = dx_x2 — dy;
while(y0!=y1)
{ /* y轴向增长,则宽度在x方向,即画水平线 */
draw_a = x0-wx;
if(draw_a〈0)
draw_a = 0;
draw_b = x0+wy;
GUI_HLine(draw_a, y0, draw_b, color);
y0 += dy_sym;
if(di<0)
{
di += dx_x2;
}
else
{
di += dx_x2 — dy_x2;
x0 += dx_sym;
}
}
draw_a = x0—wx;
if(draw_a〈0)
draw_a = 0;
draw_b = x0+wy;
GUI_HLine(draw_a, y0, draw_b, color);
}
}
#endif
/**************************************************************************
**
*名称:GUI_LineS()
* 功能:多个点之间的连续连线。
从第一点连到第二点,再连到第
三点。
.。
* 入口参数:points 多个点坐标数据的指针,数据排列为(x0,y0)、(x1,y1)、(x2,y2)。
.
* no 点数目,至少要大于1
* color 显示颜色
* 出口参数:无
*说明:操作失败原因是指定地址超出有效范围.
****************************************************************************/ void GUI_LineS(uint32 const *points, uint8 no, TCOLOR c
olor)
{
uint32 x0, y0;
uint32 x1, y1;
uint8 i;
/* 入口参数过滤 */
if(0==no)
return;
if(1==no) // 单点
{
x0 = *points++;
y0 = *points;
GUI_Point(x0, y0, color);
}
/* 画多条线条 */
x0 = *points++; // 取出第一点坐标值,作为原起点
坐标值
y0 = *points++;
for(i=1; i<no; i++)
{
x1 = *points++; // 取出下一点坐标值
y1 = *points++;
GUI_Line(x0, y0, x1, y1, color);
x0 = x1; // 更新原起点坐标
y0 = y1;
}
}
#if GUI_CircleX_EN==1
/**************************************************************************
**
*名称:GUI_Circle()
*功能:指定圆心位置及半径,画圆。
*入口参数:x0 圆心的x坐标值
* y0 圆心的y坐标值
* r 圆的半径
* color 显示颜色
* 出口参数:无
*说明:操作失败原因是指定地址超出有效范围.
****************************************************************************/ void GUI_Circle(uint32 x0, uint32 y0, uint32 r, TCOLOR c
olor)
{
int32 draw_x0, draw_y0; // 刽图点坐标变量
int32 draw_x1, draw_y1;
int32 draw_x2, draw_y2;
int32 draw_x3, draw_y3;
int32 draw_x4, draw_y4;
int32 draw_x5, draw_y5;
int32 draw_x6, draw_y6;
int32 draw_x7, draw_y7;
int32 xx, yy; // 画圆控制变量
int32 di; // 决策变量
/* 参数过滤*/
if(0==r)
return;
/* 计算出8个特殊点(0、45、90、135、180、225、270度),
进行显示 */
draw_x0 = draw_x1 = x0;
draw_y0 = draw_y1 = y0 + r;
if(draw_y0〈GUI_LCM_YMAX)
GUI_Point(draw_x0, draw_y0, color); // 90度
draw_x2 = draw_x3 = x0;
draw_y2 = draw_y3 = y0 - r;
if(draw_y2>=0)
GUI_Point(draw_x2, draw_y2, color); // 270度
draw_x4 = draw_x6 = x0 + r;
draw_y4 = draw_y6 = y0;
if(draw_x4<GUI_LCM_XMAX)
GUI_Point(draw_x4, draw_y4, color); // 0度
draw_x5 = draw_x7 = x0 - r;
draw_y5 = draw_y7 = y0;
if(draw_x5〉=0)
GUI_Point(draw_x5, draw_y5, color); // 180度
if(1==r)
return; // 若半径为1,则已圆画完
/*使用Bresenham法进行画圆*/
di = 3 — 2*r; // 初始化决策变量。