75-并口LCD128x64显示图形文字-ST7920-用DMA-M6800接口自动刷新,AI8051U实验箱演示程
75-并口LCD128x64显示图形文字-ST7920-用DMA-M6800接口自动刷新,AI8051U实验箱演示程序主程序main.C代码如下(汇编版本在附件中):
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序 */
/*---------------------------------------------------------------------*/
/*************功能说明 **************
本例程基于AI8051U为主控芯片的实验箱V1.1版本进行编写测试。
使用Keil C51编译器,为了达到比较高的效率,一般建议选择"Small"模式。
当编译器出现 "error C249: 'DATA': SEGMENT TOO LARGE" 错误时,则需要手动将部分比较大的数组通过"xdata" 强制分配到 XDATA 区域(例如: char xdata buffer ;)
黑白LCD12864的显示程序,LCM使用M6800接口,通过DMA将1024字节的显示内容送到LCD12864,传送时不占用CPU时间。
显示图形,汉字,英文,数字
其中图形显示发送命令和数据均使用DMA操作,本例运行于24MHz, 每次LCM DMA中断处理占用CPU时间为2.3us, 传输总时间84ms.
将要显示的内容放在1024字节的显存中,启动DMA传输即可。
下载时, 选择时钟 24MHz (用户可自行修改频率后重新编译即可).
******************************************/
// #define MAIN_Fosc 12000000UL
#define MAIN_Fosc 24000000UL
// #define MAIN_Fosc 40000000UL
#include "../comm/AI8051U.h"
#include <stdio.h>
#include <intrins.h>
#include "picture1.h"
#include "picture2.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
#define P3n_standard(bitn) P3M1 &= ~(bitn), P3M0 &= ~(bitn)
#define P3n_push_pull(bitn) P3M1 &= ~(bitn), P3M0 |=(bitn)
#define P3n_pure_input(bitn) P3M1 |=(bitn), P3M0 &= ~(bitn)
#define P3n_open_drain(bitn) P3M1 |=(bitn), P3M0 |=(bitn)
#define Pin6 0x40 //IO引脚 Px.6
#define Pin7 0x80 //IO引脚 Px.7
//=============================================================================================================
#define PullUpEnable(port, pu) port |=(pu) /* 允许端口内部上拉电阻 PxPU, 要设置的端口对应位为1 */
#define PullUpDisable(port, pu) port &= ~(pu) /* 禁止端口内部上拉电阻(默认) PxPU, 要设置的端口对应位为1 */
#define PullDownEnable(port, pu) port |=(pu) /* 允许端口内部下拉电阻 PxPD, 要设置的端口对应位为1 */
#define PullDownDisable(port, pu) port &= ~(pu) /* 禁止端口内部下拉电阻(默认) PxPD, 要设置的端口对应位为1 */
#define SchmitEnable(port, smt) port &= ~(smt) /* 允许端口施密特触发功能(默认) PxNCS, 要设置的端口对应位为1 */
#define SchmitDisable(port, smt) port |=(smt) /* 允许端口施密特触发功能 PxNCS, 要设置的端口对应位为1 */
#define SlewRateNormal(port, sr) port |=(sr) /* 端口输出设置为普通速度(默认) PxSR, 要设置的端口对应位为1 */
#define SlewRateHigh(port, sr) port &= ~(sr) /* 端口输出设置为高速模式 PxSR, 要设置的端口对应位为1 */
#define CurrentNormal(port, cu) port |=(cu) /* 端口输出电流为普通模式(默认) PxDR, 要设置的端口对应位为1 */
#define CurrentHigt(port, cu) port &= ~(cu) /* 端口输出电流为大电流模式 PxDR, 要设置的端口对应位为1 */
#define InputEnable(port, ie) port |=(ie) /* 允许端口数字输入(默认) PxIE, 要设置的端口对应位为1 */
#define InputDisable(port, ie) port &= ~(ie) /* 禁止端口数字输入 PxIE, 要设置的端口对应位为1 */
#define BypassEnable(port, bp) port &= ~(bp) /* 允许外设自动控制端口 PxBP, 要设置的端口对应位为1 */
#define BypassDisable(port, bp) port |=(bp) /* 禁止外设自动控制端口(默认) PxBP, 要设置的端口对应位为1 */
//=============================================================================================================
/****************************** IO定义 ***********************************/
sbit LCD_RS = P4^5;
sbit LCD_RW = P3^6;
sbit LCD_E = P3^7;
sbit LCD_RST = P4^7;
#define LCD_Data P2
/*****************************************************************************/
/*************本地常量声明 **************/
const u8uctech[] = {"LCD12864图文显示"};
const u8net[] = {"驱\xfd动芯片:ST7920"};
const u8mcu[] = {"内带汉字字库"};
const u8qq[] = {" AI8051U LQFP48 "};
/*************本地变量声明 **************/
u16 LCM_TxCnt; //LCM DMA触发发送次数, 一次16字节, 一共64次
bit B_LCM_DMA_busy; //LCM DMA忙标志, 1标志LCM-DMA忙,LCM DMA中断中清除此标志,使用LCM DMA前要确认此标志为0
u16 LCM_TxAddr; //LCM DMA要发送数据的首地址
bit B_TxCmd; //已发送命令标志
u8 xdata CmdTmp; //命令缓冲
u8 xdata DisTmp _at_ 0x0000; //显示缓冲,将要显示的内容放在显存里,启动DMA即可. 由于LCM DMA有4字节对齐问题,所以这里定位对地址为4的倍数
/*************本地函数声明 **************/
void delay_ms(u16 ms);
void WriteDataLCD(u8 WDLCD);
void WriteCommandLCD(u8 WCLCD);
void ReadStatusLCD(void);
void LCDInit(void);
void LCDClear(void);
void DisplayListChar(u8 X, u8 Y, const u8 *DData);
void DisplayImage (u8 xdata *DData);
void LCM_Config(void);
void DMA_Config(u8 xdata *pic);
/********************* 主函数 *************************/
void main(void)
{
u16 i;
EAXSFR(); //扩展寄存器(XFR)访问使能
WTST= 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
P3n_push_pull(Pin7+Pin6); //P3.7 P3.6设置为推挽输出,因为跟双串口IC连接了
PullUpEnable(P2PU, 0xff); // 允许端口内部上拉电阻 PxPU, 要设置的端口对应位为1
PullUpEnable(P3PU, 0xc0); // 允许端口内部上拉电阻 PxPU, 要设置的端口对应位为1
PullUpEnable(P4PU, 0xa0); // 允许端口内部上拉电阻 PxPU, 要设置的端口对应位为1
delay_ms(100); //启动等待,等LCD讲入工作状态
LCM_Config();
delay_ms(10);
LCDInit(); //LCM初始化
delay_ms(10); //延时片刻
EA = 1;
while(1)
{
for(i=0; i<1024; i++) DisTmp = gImage_picture1; //将图片装载到显存
LCDClear();
DMA_Config(DisTmp); //启动DMA,显示图形
delay_ms(3000);
for(i=0; i<1024; i++) DisTmp = gImage_picture2; //将图片装载到显存
LCDClear();
DMA_Config(DisTmp); //启动DMA,显示图形
delay_ms(3000);
LCDClear();
DisplayListChar(0,1,uctech); //显示字库中的中文数字
DisplayListChar(0,2,net); //显示字库中的中文数字
DisplayListChar(0,3,mcu); //显示字库中的中文
DisplayListChar(0,4,qq); //显示字库中的中文数字
delay_ms(3000);
}
}
//========================================================================
// 函数: void delay_ms(u8 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2013-4-1
// 备注:
//========================================================================
void delay_ms(u16 ms)
{
u16 i;
do
{
i = MAIN_Fosc / 6000;
while(--i);
}while(--ms);
}
//========================================================================
// 函数: void LCM_Config(void)
// 描述: LCM配置函数。ST7920 @540KHz,清屏指令典型时间为1.6ms,读忙信号0us, 写入、读出数据、命令72us. 本例子DMA刷新时间84ms @24MHz.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2024-8-17
// 备注:
//========================================================================
//LCMIFCFG(7EFE50H) TFT彩屏接口配置寄存器
#define LCMIFIE (0<<7) //TFT彩屏接口中断使能控制位(bit7), 1->允许中断,0->禁止
#define LCMIFIP (0<<4) //TFT彩屏接口中断优先级控制位(bit5~bit4), (最低)0~3(最高).
#define LCMIFDPS (0<<2) //TFT彩屏接口数据脚选择位(bit3~bit2), D18_D8=0: 0或2->8位数据在P2, D18_D8=1: 0: P2-高字节, P0低字节, 2: P2-高字节,低字节P0 P4.7 P4.6 P4.3 P4.1.
#define LCM_D16_D8 (0<<1) //TFT彩屏接口数据宽度控制位(bit1), 0: 8位数据,1:16位数据
#define LCM_M68_I80 1 //TFT彩屏接口数据模式选择位(bit0), 0: I8080模式,1:M6800模式
//LCMIFCFG2(7EFE51H) TFT彩屏接口配置寄存器2
#define LCMIFCPS (1<<5) //TFT彩屏接口控制脚选择位(bit6~bit5), RS RD(E) WR(RW), 0->P4.5 P4.4 P4.2,1->P4.5 P3.7 P3.6, 2->P4.0 P4.4 P4.2, 3->P4.0 P3.7 P3.6
#define LCMSETUPT (4<<2) //TFT彩屏接口数据建立时间控制位(bit4~bit2), 0~7, 对应1~8个LCMIF时钟, 对于6800模式就是E高电平时间, 至少20us.
#define LCMHOLDT 3 //TFT彩屏接口数据保持时间控制位(bit1~bit0), 0~3, 对应1~4个LCMIF时钟
//LCMIFCR(7EFE52H) TFT彩屏接口控制寄存器
#define ENLCMIF (1<<7) //TFT彩屏接口使能控制位(bit7), 1->允许TFT彩屏接口功能, 0->禁止
#define LCM_WRCMD 4 //TFT彩屏接口触发命令(bit2~bit0), 4->写命令
#define LCM_WRDAT 5 //TFT彩屏接口触发命令(bit2~bit0), 5->写数据
#define LCM_RDCMD 6 //TFT彩屏接口触发命令(bit2~bit0), 6->读命令/状态
#define LCM_RDDAT 7 //TFT彩屏接口触发命令(bit2~bit0), 7->读数据
void LCM_Config(void)
{
LCMIFCFG= LCMIFIE| LCMIFIP | LCMIFDPS | LCM_D16_D8 | LCM_M68_I80;
LCMIFCFG2 = LCMIFCPS | LCMSETUPT | LCMHOLDT; //RS:P45,E:P37,RW:P36; Setup Time,HOLD Time
LCMIFSTA= 0x00; //TFT彩屏接口中断请求标志,软件清0
LCMIFPSCR = (u8)(MAIN_Fosc/250000UL-1); //TFT彩屏接口时钟预分频0~255, LCMIF时钟频率 = SYSclk/(LCMIFPSCR+1), LCD12864速度慢,最快250KHz
LCMIFCR = ENLCMIF;
}
//========================================================================
// 函数: void DMA_Config(u8 xdata *pic)
// 描述: LCM DMA配置函数。
// 参数: *pic: 数据首地址.
// 返回: none.
// 版本: V1.0, 2024-8-17
// 备注:
//========================================================================
//DMA_LCM_CR (7EfA71H) LCM_DMA控制寄存器
#define DMA_ENLCM (1<<7) // LCM DMA功能使能控制位, bit7, 0:禁止LCM DMA功能,1:允许LCM DMA功能。
#define LCM_TRIGWC (1<<6) // LCM DMA触发写命令,bit6, 0:写0无效, 1:写1开始LCM DMA开始写命令。
#define LCM_TRIGWD (1<<5) // LCM DMA触发写数据,bit5, 0:写0无效, 1:写1开始LCM DMA开始写数据。
#define LCM_TRIGRC (1<<4) // LCM DMA触发读命令,bit4, 0:写0无效, 1:写1开始LCM DMA开始读命令。
#define LCM_TRIGRD (1<<3) // LCM DMA触发读数据,bit3, 0:写0无效, 1:写1开始LCM DMA开始读数据。
#define LCM_CLRFIFO 0 // 清除LCM DMA接收FIFO控制位,bit0, 0:写0无效,1:写1复位FIFO指针。
//DMA_LCM_CFG (7EfA70H) LCM_DMA配置寄存器
#define DMA_LCMIE (1<<7) // LCM DMA中断使能控制位,bit7, 0:禁止SPI DMA中断, 1:允许中断。
#define DMA_LCMIP (0<<2) // LCM DMA中断优先级控制位,bit3~bit2, (最低)0~3(最高).
#define DMA_LCMPTY 0 // LCM DMA数据总线访问优先级控制位,bit1~bit0, (最低)0~3(最高).
//DMA_LCM_STA(7EfA72) LCM_DMA状态寄存器
#define LCM_TXOVW (1<<1) // LCM DMA数据覆盖标志位,bit1, 软件清0.
#define DMA_LCMIF 1 // LCM DMA中断请求标志位,bit0, 软件清0.
void DMA_Config(u8 xdata *pic)
{
WriteCommandLCD(0x36); //选择扩充指令集, 显示图形
LCM_TxAddr = (u16)pic; //要发送数据的首地址
DMA_LCM_STA= 0x00;
DMA_LCM_CFG= DMA_LCMIE | DMA_LCMIP | DMA_LCMPTY;;
DMA_LCM_ITVH = 0; //设置传输间隔时间(高8位),对应N+1个LCMIF时钟(1~65536个LCMIF时钟)
DMA_LCM_ITVL = 9-1; //设置传输间隔时间(低8位)
B_TxCmd = 0; //已发送命令标志
LCM_TxCnt = 0; //LCM DMA触发发送次数, 一次16字节, 一共64次
B_LCM_DMA_busy = 1; //标志LCM-DMA忙,LCM DMA中断中清除此标志,使用LCM DMA前要确认此标志为0
DMA_LCM_STA = DMA_LCMIF; //软件触发LCM DMA中断,启动发送
}
//========================================================================
// 函数: void LCMIF_DMA_ISR(void) interrupt USER_VECTOR//DMA_LCM_VECTOR
// 描述: LCM DMA中断函数。中断处理时间:2.3us @24MHz. 刷新时间:84ms @24MHz.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2024-8-17
// 备注:
//========================================================================
void LCMIF_DMA_ISR(void) interrupt USER_VECTOR//DMA_LCM_VECTOR
{
if(DMA_LCM_STA & DMA_LCMIF)
{
if(LCM_TxCnt >= 64) //判断发送是否完毕
{
DMA_LCM_CR = 0;
B_LCM_DMA_busy = 0; //清除LCM-DMA忙标志,LCM DMA中断中清除此标志,使用LCM DMA前要确认此标志为0
}
else //仍有数据要发送
{
if(!B_TxCmd) //还没有发设置地址命令,则先发设置地址命令
{
B_TxCmd = 1; //指示已发地址命令
if(LCM_TxCnt <32) //上半屏
{
CmdTmp = (u8)(0x80+LCM_TxCnt); //列地址
CmdTmp = 0x80; //行地址,上半屏地址0X80
}
else //下半屏
{
CmdTmp = (u8)(0x80+LCM_TxCnt-32); //列地址
CmdTmp = 0x88; //行地址,下半屏地址0X88
}
DMA_LCM_TXAH = (u8)((u16)CmdTmp >> 8); //LCM DMA发送命令首地址
DMA_LCM_TXAL = (u8)CmdTmp;
DMA_LCM_AMTH = 0; //设置传输总字节数(高8位), 设置传输总字节数 = N+1
DMA_LCM_AMT= 2-1; //设置传输总字节数(低8位).
DMA_LCM_CR = DMA_ENLCM | LCM_TRIGWC; //启动LCM DMA发送命令
}
else
{
B_TxCmd = 0; //清除已发地址命令
DMA_LCM_TXAH = (u8)(LCM_TxAddr >> 8); //LCM DMA发送数据首地址
DMA_LCM_TXAL = (u8)LCM_TxAddr;
DMA_LCM_AMTH = 0; //设置传输总字节数(高8位), 设置传输总字节数 = N+1
DMA_LCM_AMT= 16-1; //设置传输总字节数(低8位).
DMA_LCM_CR = DMA_ENLCM | LCM_TRIGWD; //启动LCM DMA发送数据
LCM_TxAddr+= 16; //要发送数据的首地址, 一次DMA传输16字节
LCM_TxCnt++; //发送次数+1
}
}
}
DMA_LCM_STA = 0;
}
//读状态
void ReadStatusLCD(void)
{
// LCD_Data = 0xFF;
LCMIFSTA = 0; //清除完成标志
do
{
LCMIFCR = ENLCMIF | LCM_RDCMD; //读状态
while((LCMIFSTA & 1) == 0) ; //等待读完成
LCMIFSTA = 0; //清除完成标志
LCD_E = 0;
}while(LCMIFDATL & 0x80); //bit7==0则空闲
}
//写数据
void WriteDataLCD(u8 WDLCD)
{
ReadStatusLCD(); //检测忙
LCMIFDATL = WDLCD;
LCMIFCR = ENLCMIF | LCM_WRDAT; //写数据
while((LCMIFSTA & 1) == 0) ; //等待写完成
LCMIFSTA = 0; //清除完成标志
}
//写指令
void WriteCommandLCD(u8 WCLCD)
{
ReadStatusLCD(); //检测忙
LCMIFDATL = WCLCD;
LCMIFCR = ENLCMIF | LCM_WRCMD; //写命令
while((LCMIFSTA & 1) == 0) ; //等待写完成
LCMIFSTA = 0; //清除完成标志
}
void LCDInit(void) //LCM初始化
{
delay_ms(10);
LCD_RST = 0;
delay_ms(10);
LCD_RST = 1;
delay_ms(100); //至少40ms
WriteCommandLCD(0x30); //显示模式设置,开始要求每次检测忙信号
WriteCommandLCD(0x01); //显示清屏
WriteCommandLCD(0x04); //反白显示设置,不反白
WriteCommandLCD(0x0C); //不睡眠
}
void LCDClear(void) //清屏
{
WriteCommandLCD(0x30); //选择基本指令集
WriteCommandLCD(0x01); //显示清屏, 忙信号长达1.2ms
}
//按指定位置显示一串字符
void DisplayListChar(u8 X, u8 Y, const u8 *DData)
{
u8 ListLength,X2;
X2 = X;
if(Y < 1) Y=1;
if(Y > 4) Y=4;
X &= 0x0F; //限制X不能大于16,Y在1-4之内
WriteCommandLCD(0x30); //选择基本指令集
switch(Y)
{
case 1: X2 |= 0X80; break;//根据行数来选择相应地址
case 2: X2 |= 0X90; break;
case 3: X2 |= 0X88; break;
case 4: X2 |= 0X98; break;
}
WriteCommandLCD(X2); //发送地址码
ListLength = 0;
while (DData >= 0x20) //若到达字串尾则退出
{
if (X <= 0x0F) //X坐标应小于0xF
{
WriteDataLCD(DData); //
ListLength++;
X++;
}
}
}
页:
[1]