STC15W4K56S4学习笔记——LCD12864滚动显示
最近在准备学校的单片机校赛,重新回顾了一遍51单片机,上手了STC15W4K56S4试验箱,写一些笔记来记录一下,水平有限,有错误的希望各位大佬指出!
一、硬件
STC15W4K56S4核心板+12864液晶屏
引脚分配:
数据端(D0-D7) <–> P0
RS:命令/数据寄存器选择端 <–> P20
RW:读写控制端 <–> P21
EN:使能端 <–> P22
原理图连接如下:
12864液晶是一种具有4位/8位并行、2线或3线串行多种接口方式的点阵图形液晶显示模块,本实验使用8位并行总线驱动。
对12864的所有操作概括起来有4种:
(1)读忙状态(同时读出指针地址内容):初始化之后每次对12864的读写均要进行忙检测。
(2)写命令:所有的命令可以查看指令表,后续讲解指令的详细用法,写地址也是写指令。
(3)写数据:操作对象有DDRAM(Data Display Ram)、CGRAM(Character Generation Ram )、GDRAM(Graphic Display RAM)。
(4)读数据:操作对象也是DDRAM、CGRAM、GDRAM。
二、软件
lcd12864.c文件
#include "lcd12864.h"
/*******************************************************************************
* 函 数 名 : LCD12864_Delay1ms
* 函数功能 : 延时1MS
* 输 入 : c
* 输 出 : 无
*******************************************************************************/
void LCD12864_Delay1ms(uint c)
{
uchar a,b;
for(; c>0; c--)
{
for(b=199; b>0; b--)
{
for(a=1; a>0; a--);
}
}
}
/*******************************************************************************
* 函 数 名 : LCD12864_Busy
* 函数功能 : 检测LCD是否忙
* 输 入 : 无
* 输 出 : 1或0(1表示不忙,0表示忙)
*******************************************************************************/
//uchar LCD12864_Busy(void)
//{
// uchar i = 0;
// LCD12864_RS = 0; //选择命令
// LCD12864_RW = 1; //选择读取
// LCD12864_EN = 1;
// LCD12864_Delay1ms(1);
//
// LCD12864_Delay1ms(10);
// LCD12864_EN = 0;
// return 1;
//}
/*******************************************************************************
* 函 数 名 : LCD12864_WriteCmd
* 函数功能 : 写命令
* 输 入 : cmd
* 输 出 : 无
*******************************************************************************/
void LCD12864_WriteCmd(uchar cmd)
{
uchar i;
i = 0;
LCD12864_Delay1ms(20);
LCD12864_RS = 0; //选择命令
LCD12864_RW = 0; //选择写入
LCD12864_EN = 0; //初始化使能端
LCD12864_DATAPORT = cmd; //放置数据
LCD12864_EN = 1; //写时序
LCD12864_Delay1ms(5);
LCD12864_EN = 0;
}
/*******************************************************************************
* 函 数 名 : LCD12864_WriteData
* 函数功能 : 写数据
* 输 入 : dat
* 输 出 : 无
*******************************************************************************/
void LCD12864_WriteData(uchar dat)
{
uchar i;
i = 0;
LCD12864_Delay1ms(20);
LCD12864_RS = 1; //选择数据
LCD12864_RW = 0; //选择写入
LCD12864_EN = 0; //初始化使能端
LCD12864_DATAPORT = dat; //放置数据
LCD12864_EN = 1; //写时序
LCD12864_Delay1ms(5);
LCD12864_EN = 0;
}
/*******************************************************************************
* 函 数 名 : LCD12864_ReadData
* 函数功能 : 读取数据
* 输 入 : 无
* 输 出 : 读取到的8位数据
*******************************************************************************/
#ifdef LCD12864_PICTURE
uchar LCD12864_ReadData(void)
{
uchar i, readValue;
i = 0;
LCD12864_Delay1ms(20);
LCD12864_RS = 1; //选择命令
LCD12864_RW = 1;
LCD12864_EN = 0;
LCD12864_Delay1ms(1); //等待
LCD12864_EN = 1;
LCD12864_Delay1ms(1);
readValue = LCD12864_DATAPORT;
LCD12864_EN = 0;
return readValue;
}
#endif
/*******************************************************************************
* 函 数 名 : LCD12864_Init
* 函数功能 : 初始化LCD12864
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void LCD12864_Init()
{
LCD12864_WriteCmd(0x30); //选择基本指令操作
LCD12864_WriteCmd(0x0c); //显示开,关光标
LCD12864_WriteCmd(0x01); //清除LCD12864的显示内容
}
/*******************************************************************************
* 函 数 名 : LCD12864_ClearScreen
* 函数功能 : 在画图模式下,LCD12864的01H命令不能清屏,所以要自己写一个清
* * 屏函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
#ifdef LCD12864_PICTURE
void LCD12864_ClearScreen(void)
{
uchar i,j;
LCD12864_WriteCmd(0x34); //开启拓展指令集
for(i=0;i<32;i++) //因为LCD有纵坐标32格所以写三十二次
{
LCD12864_WriteCmd(0x80+i); //先写入纵坐标Y的值
LCD12864_WriteCmd(0x80); //再写入横坐标X的值
for(j=0;j<32;j++) //横坐标有16位,每位写入两个字节的的数据,也
{ //就写入32次以为当写入两个字节之后横坐标会自
LCD12864_WriteData(0xFF); //动加1,所以就不用再次写入地址了。
}
}
LCD12864_WriteCmd(0x36); //0x36扩展指令里面打开绘图显示
LCD12864_WriteCmd(0x30); //恢复基本指令集
}
#endif
/*******************************************************************************
* 函 数 名 : LCD12864_SetWindow
* 函数功能 : 设置在基本指令模式下设置显示坐标。注意:x是设置行,y是设置列
* 输 入 : x, y
* 输 出 : 无
*******************************************************************************/
void LCD12864_SetWindow(uchar x, uchar y)
{
uchar pos;
if(x == 0) // 第一行的地址是80H
{
x = 0x80;
}
else if(x == 1) //第二行的地址是90H
{
x = 0x90;
}
else if(x == 2) //第三行的地址是88H
{
x = 0x88;
}
else if(x == 3)
{
x = 0x98;
}
pos = x + y;
LCD12864_WriteCmd(pos);
}
lcd12864.h文件
#ifndef __LCD12864_H
#define __LCD12864_H
//---包含头文件---//
#include "stc15f2k60s2.h"
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
//---定义使用的IO口---//
#define LCD12864_DATAPORT P0 //数据IO口
sbit LCD12864_RS = P2^0; //(数据命令)寄存器选择输入 TFT_D10 A8
sbit LCD12864_RW = P2^1; //液晶读/写控制 TFT_D8 A9
sbit LCD12864_EN = P2^2; //液晶使能控制 TFT_D9 A10
//---声明全局函数---//
void LCD12864_Delay1ms(uint c);
//uchar LCD12864_Busy(void);
void LCD12864_WriteCmd(uchar cmd);
void LCD12864_WriteData(uchar dat);
void LCD12864_Init();
void LCD12864_ClearScreen(void);
void LCD12864_SetWindow(uchar x, uchar y);
#endif
main.c文件
#include "stc15f2k60s2.h"
#include <intrins.h>
#include "lcd12864.h"
/***************
12864液晶实验
从下到上滚动显示字符串
***************/
unsigned char code CharCode1[]="2020";
unsigned char code CharCode2[]="1010";
//针对STC15W4K56S4系列IO口初始化
//io口初始化 P0~7 为准双向IO口
void IO_init(void)
{
P0M0 = 0X00; P0M1 = 0X00;
P1M0 = 0X00; P1M1 = 0X00;
P2M0 = 0X00; P2M1 = 0X00;
P3M0 = 0X00; P3M1 = 0X00;
P4M0 = 0X00; P4M1 = 0X00;
P5M0 = 0X00; P5M1 = 0X00;
P6M0 = 0X00; P6M1 = 0X00;
P7M0 = 0X00; P7M1 = 0X00;
}
/*****
12864显示字符串函数
字符串从下往上依次间隔500ms滚动显示
********/
void display()
{
uchar i,j,k;
LCD12864_Init();
for(k=5;k>=1;k--)
{
i=0;
j=0;
LCD12864_WriteCmd(0x01); //清除LCD12864的显示内容
if(k!=5) //令k不等于5,因为lcd的行只有0.1.2.3,k=5时,
{ //k-1=4,会出现错误,所以当k=5时将不会执行以下函数。
LCD12864_SetWindow(k-1, 0); //设置在基本指令模式下设置显示的坐标,k-1取3.2.1.0
while(CharCode1[i]!='\0') //当不是字符串结束符时,执行循环
{
LCD12864_WriteData(CharCode1[i]); //向12864写数据
i++; //每次只能写一个,所以要++,依次写进去
}
}
if(k!=1) //k不等于1时执行以下函数
{
LCD12864_SetWindow(k-2, 0); //k-2依次为3.2.1.0,即从最下面往上显示
while(CharCode2[j]!='\0') //当不是字符串结束符时,执行循环
{
LCD12864_WriteData(CharCode2[j]); //向12864写数据
j++; //自加依次写数据
}
}
LCD12864_Delay1ms(5000); //延时500ms
}
}
void main()
{
IO_init(); //针对STC15W4K56S4 IO口初始化
//设置STC单片机的P20 P21 22为推挽输出
P2M1 &= ~(1<<2),P2M0 |= (1<<2);
P2M1 &= ~(1<<1),P2M0 |= (1<<1);
P2M1 &= ~(1<<0),P2M0 |= (1<<0);
LCD12864_Init();
while(1)
{
display();
}
}
(1)STC15W4K56S4系列IO口初始化解释:
STC单片机有四种工作模式:
准双向口,推挽输出,高阻输入,开漏输出
如果要设置为后面3种,则需要配置寄存器来设置,通过M0,M1来设置IO口模式
M0 M1
0 0 准双向口
0 1 高阻输入
1 0 推挽输出
1 1 开漏输出
P2M1 &= ~(1<<0),P2M0 |= (1<<0); //设置STC单片机的P20为推挽输出
P2M1 &= ~(1<<0):1左移0位,再非,与之后赋值给P2的M1,即把P20的M1设置为0
同理P2M0 |= (1<<0),把P20的M0设置为1,所P20口为推挽输出
(2)lcd12864滚动显示:
首先初始化12864函数后,进入一个for循环,此循环执行5次,每次进去都会进行一次清屏,显示一次清除一次,每次开始的位置不一样,从而达到滚动的效果。
LCD12864_SetWindow(x, y)函数设置显示的位置,x为行(取0.1.2.3四个值),y为列。
此函数为显示两行字符滚动,所以设置了两个if条件,因为在最上面或者最下面的时候,总会有一行没能显示,所以要判断能显示那串字符串的条件,不然x的值设定会不存在,显示的时候会出错。之前写的时候就是没有主要到这个,没有进行判断,还困惑了好久。
我这个是第一串字符从最后一行往上显示,第二串紧跟着,最后是第二串在最上面(第一行)显示,想要其他效果改变显示的坐标就可以。
for(k=5;k>=1;k--)
{
i=0;
j=0;
LCD12864_WriteCmd(0x01); //清除LCD12864的显示内容
if(k!=5) //令k不等于5,因为lcd的行只有0.1.2.3,k=5时,
{ //k-1=4,会出现错误,所以当k=5时将不会执行以下函数。
LCD12864_SetWindow(k-1, 0); //设置在基本指令模式下设置显示的坐标,k-1取3.2.1.0
while(CharCode1[i]!='\0') //当不是字符串结束符时,执行循环
{
LCD12864_WriteData(CharCode1[i]); //向12864写数据
i++; //每次只能写一个,所以要++,依次写进去
}
}
if(k!=1) //k不等于1时执行以下函数
{
LCD12864_SetWindow(k-2, 0); //k-2依次为3.2.1.0,即从最下面往上显示
while(CharCode2[j]!='\0') //当不是字符串结束符时,执行循环
{
LCD12864_WriteData(CharCode2[j]); //向12864写数据
j++; //自加依次写数据
}
}
LCD12864_Delay1ms(5000); //延时500ms
}
}
(3)延时函数
LCD12864_Delay1ms(5000); //延时500ms
这个延时是500ms,因为单片机设置为24MHz的晶振,12T模式,所以一个机器周期为(1s/24MHz)*12=0.5us
void LCD12864_Delay1ms(uint c)
{
uchar a,b;
for(; c>0; c--)
{
for(b=199; b>0; b--)
{
for(a=1; a>0; a--);
}
}
}
所以 LCD12864_Delay1ms()的延时就是:0.5*200=100us,
LCD12864_Delay1ms(5000) 就是5000*100us=500ms
三、实验效果
欢迎大家指出错误的地方哦,第一次写博客,写得不是很好,参考参考就行,希望以后能坚持下去,在CSDN记录自己的学习笔记。