最近用nRF51822写了个天马4线SPI的1.77寸LCD彩屏驱动效果如下

屏幕的规格资料为:http://pan.baidu.com/s/1gdfkr5L

屏幕的驱动资料为:http://pan.baidu.com/s/1dD3AUFB

工程结构为:

main.c是main函数所在,程序入口

core文件夹中是nrf51822的启动文件,不必看

lib中:

nrf_delay.c是一个简单的延时函数(while循环延时,不精准)

font.c存放一种字体,供屏幕显示用(屏幕每种字体都会对应一种字体资源,字体是一个个01点阵)

代码详解:

main.c中只看main函数即可~(上面相关函数是main函数中被注释掉部分调用实现一个稍微复杂的动画用的)

 int main(void)
{
unsigned int x,y,cnt;
int dis;
double my_time; myDis.cen_x=COL/;
myDis.cen_y=ROW-;
myDis.dir=;
myDis.pre_dir=;
myDis.len=;
myDis.dir_change=; 15 GPIO_Init();
16 LCD_Init();
17 DispColor(RED);

x=y=cnt=;
dis=;
my_time=; // while(1<2)
// {
// cnt++;
// myDis.pre_dir=myDis.dir;
// myDis.dir=cnt%4;
// DispInt(cnt,COL/2-FONT_W*2,ROW,BLUE,RED);
// DispStr("X-CASE",x,y,BLACK,RED);
// drawDIS(1); // x+=dis*1;
// y+=dis*2;
// if(x>=COL-FONT_W*6)
// {
// dis=-1;
// }
// if(x<=0)
// {
// dis=1;
// }
// nrf_delay_ms(100);
// }
while(<)
{
my_time+=0.1;
//DrawLine(x,x+30,y,y+30,RED);
x=*cos(my_time)+my_time;
y=*sin(my_time)+; //nrf_delay_ms(10);
if(my_time<)DrawLine(x,x+,y,y+,BLUE);
else if(my_time<)DrawLine(x,x+,y,y+,GREEN);
else if(my_time<)DrawLine(x,x+2,y,y+2,BLACK);
else {
57 DispColor(RED);
my_time=;
}
}
}

nrf_delay.c中是一个简单延时函数:

 #include "compiler_abstraction.h"
#include "nrf.h"
#include "nrf_delay.h" /*lint --e{438} "Variable not used" */
void nrf_delay_ms(uint32_t volatile number_of_ms)
{
while(number_of_ms != )
{
number_of_ms--;
nrf_delay_us();
}
}

下面重点看nrf_lcd部分:

先看.H文件:

 #ifndef _NRF_LCD_H
#define _NRF_LCD_H #include "pca10001.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "font.h"
/*
引脚高低电平宏定义
*/
#define CS_SET nrf_gpio_pin_set(CS)
#define CS_CLEAR nrf_gpio_pin_clear(CS)
#define RS_SET nrf_gpio_pin_set(RS)
#define RS_CLEAR nrf_gpio_pin_clear(RS)
#define RET_SET nrf_gpio_pin_set(RET)
#define RET_CLEAR nrf_gpio_pin_clear(RET)
#define SCL_SET nrf_gpio_pin_set(SCL)
#define SCL_CLEAR nrf_gpio_pin_clear(SCL)
#define SDA_SET nrf_gpio_pin_set(SDA)
#define SDA_CLEAR nrf_gpio_pin_clear(SDA)
/*
宏定义等待函数
*/
#define DELAY_MS(n) nrf_delay_ms(n) //------------------------------------------------------
#define PIC_WIDTH 160 //预备向LCD显示区域填充的图片的大小
#define PIC_HEIGHT 160 #define ROW 160 //显示的行、列数
#define COL 128 #define BLUE 0xF800 //定义颜色常量
#define GREEN 0x07E0
#define RED 0x001F
#define WHITE 0xFFFF
#define BLACK 0x0000
#define GRAY 0xEF5D //0x2410
#define GRAY75 0x39E7
#define GRAY50 0x7BEF
#define GRAY25 0xADB5 void GPIO_Init(void);
void LCD_Init(void);
void DispColor(unsigned int color);
void DispInt(unsigned int i, unsigned int Xstart, unsigned int Ystart, unsigned int TextColor, unsigned int BackColor);
void DispStr(unsigned char *str, unsigned int Xstart, unsigned int Ystart, unsigned int TextColor, unsigned int BackColor);
void DrawLine(unsigned int Xstart, unsigned int Xend, unsigned int Ystart, unsigned int Yend, unsigned int color); #endif

1、其中引脚高低电平宏定义是将控制LCD的5条线的引脚的高低电平用宏定义,方便移植

2、将nrf_delay中的毫秒延时函数也用宏定义为DELAY_MS也是方便移植

3、这里PIC_WIDTH和PIC_HEIGHT没用~是我的大的工程中用的~表示一个图片的大小,不必看

4、ROW和COL表示屏幕的长和宽

5、37~45行是常用颜色定义

6、48~53是对外的函数,要想用屏幕首先要调用GPIO初始化函数,然后调用LCD初始化函数将LCD屏幕显示属性的一些命令传送给屏幕,最后可以调用dispcolor将整个屏幕显示一种颜色,调用dispInt显示一个int的数值在屏幕上,dispstr将一个字符串显示在屏幕上(不要觉得这两个函数不起眼,其实要有字库支持,不像你平时在高级的操作系统上调用print那么简单),这里drawline函数不仅可以绘制直线,还能绘制矩形,直线的宽度变宽就变成矩形了~呵呵

.c中则是上面函数的具体实现:

首先看GPIO初始化函数

 void GPIO_Init()
{
nrf_gpio_cfg_output(CS);
nrf_gpio_cfg_output(RS);
nrf_gpio_cfg_output(RET);
nrf_gpio_cfg_output(SCL);
nrf_gpio_cfg_output(SDA);
}

1、该函数是将我们对屏幕控制的5条线设置成输出模式这个和nRF51822系统相关,要根据nRF限制进行设置,51单片机则不用设置引脚模式,stm则需要设置引脚模式

2、这里的CS\RS等引脚是在pca10001.h中定义的,也就是指定用nRF51的哪些引脚,这里可见用了第10、11、13、14、15五个引脚

接着看模拟的SPI发送一个字节的函数

 void SendDataSPI(unsigned char dat)
{
unsigned char i;
for(i = ; i < ; i++)
{
if( (dat & 0x80) != ) SDA_SET;
else SDA_CLEAR; dat <<= ; SCL_CLEAR;
SCL_SET;
}
}

1、这个是根据屏幕驱动协议ILI9163文档中要求而写的通信底层协议

2、函数中将dat的8位从高到底发送出去,11、12行SCL变化一次将数据发送出去一位

3、接下来会基于这个函数实现向LCD屏幕的写数据和写命令函数,然后又基于写数据和写命令函数封装成绘制图形的函数

5.1、dispint是用一个4位的形式显示一个整数,不足的补零

5.2、dispstr是显示一个字符串

5.3、drawline不仅能绘制直线还能绘制矩形


4.1、putpixel是绘制一个像素点,和底层writeonedot不同就在于先调用blockwrite刷新了该点的区域

4.2、dispcolor是将整个屏用一种颜色刷屏

4.3、disonechar是显示一个字符


3.1、blockwrite相当于擦除,每次要对大范围的区域进行刷新前都要调用这个函数进行“擦除”

    可见更高层的函数都调用了这个函数,因为无论是显示字符还是绘制区域都要先“擦除”

3.2、lcd_init是屏幕初始化函数


2.1、写命令和写数据函数是基于SPI发送一字节函数写的

2.2、writeonedot函数是填充一像素点数据,上面绘制直线和绘制字符都用到了它


1、SPI发送一字节的底层通信函数

从下到上依次为:数据传输实现层、基础数据段传输封装层、高级数据段传输封装层、基础应用层、高级应用层

  • 高级数据段传输封装层
 void WriteComm(unsigned int i)
{
CS_CLEAR;
RS_CLEAR;
SendDataSPI(i);
CS_SET;
}
void WriteData(unsigned int i)
{
CS_CLEAR;
RS_SET;
SendDataSPI(i);
CS_SET;
}
void WriteOneDot(unsigned int color)
{
CS_CLEAR;
RS_SET; SendDataSPI(color >> );
SendDataSPI(color); CS_SET;
}
  • 高级数据段传输封装层
 /*
LCD初始化函数
*/
void LCD_Init(void)
{
DELAY_MS();
RET_SET;
DELAY_MS();
RET_CLEAR;
DELAY_MS();
RET_SET;
DELAY_MS(); //-------------Start Initial Sequence--------//
WriteComm(0x11); //Exit Sleep
DELAY_MS();//
WriteComm(0x26); //Set Default Gamma
WriteData(0x04); WriteComm(0xB1);//Set Frame Rate
WriteData(0x0B);
WriteData(0x14); WriteComm(0xC0); //Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD
WriteData(0x0C);
WriteData(0x05); WriteComm(0xC1); //Set BT[2:0] for AVDD & VCL & VGH & VGL
WriteData(0x02); WriteComm(0xC5); //Set VMH[6:0] & VML[6:0] for VOMH & VCOML
WriteData(0x3F);//
WriteData(0x48); WriteComm(0xC7);// Set VMF
WriteData(0xC2); WriteComm(0x2A); //Set Column Address
WriteData(0x00);
WriteData(0x00);
WriteData(0x00);
WriteData(0x7F);
WriteComm(0x2B); //Set Page Address
WriteData(0x00);
WriteData(0x00);
WriteData(0x00);
WriteData(0x9F); WriteComm(0x3A); //Set Color Format
WriteData(0x55);
WriteComm(0x36);
WriteData(0xC8); WriteComm(0xF2); //Enable Gamma bit
WriteData(0x01);
WriteComm(0xE0);
WriteData(0x3F);//p1
WriteData(0x25);//p2
WriteData(0x21);//p3
WriteData(0x24);//p4
WriteData(0x1D);//p5
WriteData(0x0D);//p6
WriteData(0x4C);//p7
WriteData(0xB8);//p8
WriteData(0x38);//p9
WriteData(0x17);//p10
WriteData(0x0F);//p11
WriteData(0x08);//p12
WriteData(0x04);//p13
WriteData(0x02);//p14
WriteData(0x00);//p15
WriteComm(0xE1);
WriteData(0x00);//p1
WriteData(0x1A);//p2
WriteData(0x1E);//p3
WriteData(0x0B);//p4
WriteData(0x12);//p5
WriteData(0x12);//p6
WriteData(0x33);//p7
WriteData(0x47);//p8
WriteData(0x47);//p9
WriteData(0x08);//p10
WriteData(0x20);//p11
WriteData(0x27);//p12
WriteData(0x3C);//p13
WriteData(0x3D);//p14
WriteData(0x3F);//p15 WriteComm(0x36); //MX, MY, RGB mode
WriteData(0xC0);//c8竖屏 68横屏 WriteComm(0x29); // Display On WriteComm(0x2C);
} /*
LCD块写(大量数据修改,相当于擦除)
*/
void BlockWrite(unsigned int Xstart, unsigned int Xend, unsigned int Ystart, unsigned int Yend)
{
//ILI9163C
WriteComm(0x2A);
WriteData(Xstart >> );
WriteData(Xstart);
WriteData(Xend >> );
WriteData(Xend); WriteComm(0x2B);
WriteData(Ystart >> );
WriteData(Ystart);
WriteData(Yend >> );
WriteData(Yend); WriteComm(0x2c);
}

注:在LCD初始化函数和blockwrite函数中经常会看到writecomm或者部分writedata参数有些是很奇怪的数据,如0x2A,0x2B...其实这些是屏幕驱动芯片所规定的一些寄存器的地址和这些寄存器的配置数值。这里屏幕初始化就是按照屏幕驱动芯片规格来配置的~而块写这个功能的实现也是要严格按照屏幕驱动说明的!!!反正,玩硬件少不了和各种文档打交道!!!

  • 基础应用层
 /*
绘制一个像素点
*/
void PutPixel(unsigned int x, unsigned int y, unsigned int color)
{
BlockWrite(x, x, y, y);
CS_CLEAR;
RS_SET;
SendDataSPI(color >> );
SendDataSPI(color);
CS_SET;
}
/*
LCD显示颜色(颜色已在.h文件中定义)
*/
void DispColor(unsigned int color)
{
unsigned int i, j;
BlockWrite(, COL - , , ROW - ); CS_CLEAR;
RS_SET;
for(i = ; i < ROW; i++)
{
for(j = ; j < COL; j++)
{
SendDataSPI(color >> );
SendDataSPI(color);
}
}
CS_SET;
}
void DispOneChar(unsigned char ord, unsigned int Xstart, unsigned int Ystart, unsigned int TextColor, unsigned int BackColor) // ord:0~95
{
unsigned char i, j;
unsigned char *p;
unsigned char dat;
unsigned int index; BlockWrite(Xstart, Xstart + (FONT_W - ), Ystart, Ystart + (FONT_H - )); index = ord; if(index > ) //95:ASCII CHAR NUM
index = ; index = index * ((FONT_W / ) * FONT_H); p = ascii;
p = p + index; for(i = ; i < (FONT_W / * FONT_H); i++)
{
dat = *p++;
for(j = ; j < ; j++)
{
if((dat << j) & 0x80)
{
WriteOneDot(TextColor);
}
else
{
WriteOneDot(BackColor);
}
}
}
67 }

注:1、可见基础应用层都在绘制前先调用了块写,并且下面准备用多大区域就用块写写对应多大区域

注:2、绘制一像素点的升级版是填充整个屏幕,不同是不必for循环调用绘制一像素来实现刷屏,而是直接先块写然后刷屏

注:3、显示一个字符需要调用字库,字库格式如下:

  • 高级应用层
 void DispInt(unsigned int i, unsigned int Xstart, unsigned int Ystart, unsigned int TextColor, unsigned int BackColor)
{
if(Xstart > ((COL - ) - FONT_W * ))
{
Xstart = (COL - ) - FONT_W * ;
}
if(Ystart > ((ROW - ) - FONT_H))
{
Ystart = (Ystart - ) - FONT_H;
} DispOneChar((i >> ) % , Xstart, Ystart, TextColor, BackColor); //ID value
DispOneChar((i >> ) % , Xstart + FONT_W, Ystart, TextColor, BackColor);
DispOneChar((i >> ) % , Xstart + FONT_W * , Ystart, TextColor, BackColor);
DispOneChar(i % , Xstart + FONT_W * , Ystart, TextColor, BackColor); BlockWrite(, COL - , , ROW - );
}
void DispStr(unsigned char *str, unsigned int Xstart, unsigned int Ystart, unsigned int TextColor, unsigned int BackColor)
{
while(!(*str == '\0'))
{
DispOneChar(ToOrd(*str++), Xstart, Ystart, TextColor, BackColor); if(Xstart > ((COL - ) - FONT_W))
{
Xstart = ;
Ystart = Ystart + FONT_H;
}
else
{
Xstart = Xstart + FONT_W;
} if(Ystart > ((ROW - ) - FONT_H))
{
Ystart = ;
}
}
BlockWrite(, COL - , , ROW - );
}
/*
绘制一片区域(名字为线,其实可以刷一个面)
*/
void DrawLine(unsigned int Xstart, unsigned int Xend, unsigned int Ystart, unsigned int Yend, unsigned int color)
{
unsigned int i, j; BlockWrite(Xstart, Xend, Ystart, Yend); for(i = Ystart; i < Yend + ; i++)
{
for(j = Xstart; j < Xend + ; j++)
{
WriteOneDot(color);
}
}
}

注:1、无论是写一个字符还是写字符串或是整数,最麻烦的不过是计算所需要的像素区域的值位于字库的哪里,所以里面多了很多计算~

注:2、绘制直线和区域填充类似,只是区域填充直接调用底层SPI数据传输函数,这里调用了writeonedot函数。我觉得也可以直接调用底层,也许会加快绘制速度!

小结

上面一个小小的驱动函数组织不算完美,我只是用厂家给的demo移植到nRF上,所以函数调用有点乱~

用nRF51822四线SPI驱动1.77寸(128X160像素,每个像素需要16位数据)刷屏的速度人是可以感知的

因此,如果想利用它来做复杂的动画就有点难度了~

而我目前正遇到这个难题~攻克中!!!

该nRF51822晶振是16M的,刷一张图时间大概0.8s左右,接下来我将用stm32,72M的试试~

上述工程代码:http://pan.baidu.com/s/1bnHmi55

@beautifulzzzz

  2015-11-25 持续更新中~

[nRF51822] 1、一个简单的nRF51822驱动的天马4线SPI-1.77寸LCD彩屏DEMO的更多相关文章

  1. [stm32] 一个简单的stm32vet6驱动的天马4线SPI-1.77寸LCD彩屏DEMO

    书接上文<1.一个简单的nRF51822驱动的天马4线SPI-1.77寸LCD彩屏DEMO> 我们发现用16MHz晶振的nRF51822驱动1.77寸的spi速度达不到要求 本节主要采用7 ...

  2. [stm32] 一个简单的stm32vet6驱动2.4寸240X320的8位并口tft屏DEMO

    书接上文: 最近在研究用低速.低RAM的单片机来驱动小LCD或TFT彩屏实现动画效果 首先我用一个16MHz晶振的m0内核的8位单片机nRF51822尝试驱动一个1.77寸的4线SPI屏(128X16 ...

  3. 如何编写一个简单的Linux驱动(二)——设备操作集file_operations

    前期知识 如何编写一个简单的Linux驱动(一)--驱动的基本框架 前言 在上一篇文章中,我们学习了驱动的基本框架.这一章,我们会在上一章代码的基础上,继续对驱动的框架进行完善.要下载上一篇文章的全部 ...

  4. 如何编写一个简单的Linux驱动(二)——完善设备驱动

    前期知识 1.如何编写一个简单的Linux驱动(一)——驱动的基本框架 2.如何编写一个简单的Linux驱动(二)——设备操作集file_operations 前言 在上一篇文章中,我们编写设备驱动遇 ...

  5. 如何编写一个简单的Linux驱动(一)

    前言 最近在学习Linux驱动,记录下自己学习的历程. 驱动的基本框架 Linux驱动的基本框架包含两部分,“模块入口.出口的注册”和“模块入口.出口函数的实现”,如下方代码. static int ...

  6. Linux下GPIO驱动(一) ----一个简单的LED驱动

    /******************************* * *杂项设备驱动:miscdevice *majior=10; * * *****************************/ ...

  7. linux设备驱动归纳总结(五):4.写个简单的LED驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-84693.html linux设备驱动归纳总结(五):4.写个简单的LED驱动 xxxxxxxxxxx ...

  8. 【Linux开发】linux设备驱动归纳总结(五):4.写个简单的LED驱动

    linux设备驱动归纳总结(五):4.写个简单的LED驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. Shiro —— 从一个简单的例子开始

    一.Shiro是用来做权限的. 二.权限 1.基本概念: (1)安全实体:要保护的数据. (2)权限:是否有能力去操作(查看.修改.删除 )保护的数据. 2.权限的两个特性 (1)权限的继承性:A 包 ...

随机推荐

  1. HttpWebRequest类

    HttpWebRequest类与HttpRequest类的区别. HttpRequest类的对象用于服务器端,获取客户端传来的请求的信息,包括HTTP报文传送过来的所有信息.而HttpWebReque ...

  2. 我是一只IT小小鸟——读后感

    读到书名我便知道,我们是一类人,都是现在在学IT的学生或者打算以后从事IT行业的人,或者现在正在从事IT行业的人,不同区域的人,不同性格的人,不同家庭背景的人,不同,很多的不同,但是我们都有相同的迷惘 ...

  3. 其它数据类型和Json的转化

    1.ResultSet→Json public static String resultSetToJson(ResultSet rs) throws SQLException,JSONExceptio ...

  4. Runloop 深入理解(转)

    RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...

  5. Windows如何修改MySQL用户root密码

    听语音 浏览:16925 | 更新:2015-06-12 14:49 | 标签:windows 1 2 3 4 5 6 分步阅读 MySQL是一个关系型数据库管理系统,在 WEB 应用方面 MySQL ...

  6. RSpec shared examples with template methods

    It’s pretty common to have multiple tests that are nearly the same. In one app, we support bidding o ...

  7. 1996: [Hnoi2010]chorus 合唱队

    链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1996 Description Input Output Sample Input 4 1701 ...

  8. Django提交POST表单“CSRF verification failed. Request aborted”问题的解决

    1.环境 python 3.4 Django 1.7 Visual Studio 2015 PTVS 2.问题 提交表单,出现以下错误: CSRF verification failed. Reque ...

  9. css3 自定义动画(2)位置的移动

    <style> /*涉及到位置的必须给元素进行相对或绝对定位*/ @-webkit-keyframes move{ %{top:0px;left:0px;} %{top:0px;left: ...

  10. UVaLive 7143 Room Assignment (组合数+DP)

    题意:有 n 个客人,m个房间,每个房间可住ci个人,这 n 个人中有 t 对双胞胎,sum{ci}  = n 问你有多少种住房方法. 析:计数DP,dp[i][j] 表示前 i 个房间,还剩下 j ...