STM32F103软件模拟SPI接口驱动ILI9486液晶屏

  • ILI9486的工作模式
    • ILI9486的SPI总线方式简介
    • ILI9486的3线SPI总线底层驱动配置步骤

ILI9486的工作模式

ILI9486是ILITEC推出的一款LCD驱动器,支持262144种色彩,支持的显示分辨率为320X480,内部GRAM显存为345600Bytres(320X480X18bit)。ILI9486支持8种总线方式,由硬件决定,见下表。硬件设计时,可以给IM0,IM1,IM2这3个引脚都设计上拉至电源、下拉至GND的电阻,这样可以通过选择焊接这6个上下拉电阻来灵活选择芯片的总线方式。
  

ILI9486的SPI总线方式简介

ILI9486的8080并行总线这里不做详述,重点介绍其SPI总线。ILI9486的SPI总线比我们通常用到的SPI总线稍复杂一些。其SPI总线有两种方式:3线方式和4线方式。这两种方式的区别是3线方式中,只有一根数据线,ILITEC官方称为DIN/SDA,这根线既做MOSI,又做MISO;4线方式中,有两根数据线,ILITEC官方称此为DIN/SDA和DOUT,这里SPI的MOSI和MISO是分开的。4线方式用法和我们通常使用的SPI总方式基本一样。3 线方式稍有区别,这里根据本人的应用经验,重点介绍一下3线SPI总线的驱动。

ILI9486的3线SPI总线底层驱动配置步骤

第一步:根据上表,从硬件上将IM[2:0]设置为101,选择3线SPI总线方式

第二步:初始化3个GPIO口:片选(CS),时钟(CLK),数据线(DIN/SDA)。其它线如:复位、数据/命令选择、背光、显示开/关等根据自己的硬件去配置。
注意:虽然数据线在使用中,要根据收、发而改变方向,但在LCD初始化时,MCU需要给ILI9486发送初始化序列,因此初始化过程中,要将其初始化为输出状态!

第三步:写底层SPI发送、接收函数
这里要注意:
(1)发送顺序是MSB在前,LSB在后。
(2)在发送是DIN/SDA要置为输出;接收时DIN/SDA要置为输入。实际代码中,尽量不使用库函数,可直接操作寄存器,以提高程序运行速度。本人在应用中,是用宏定义实现的。如下(这里DIN/SDA线用PB15):

#define SDA_IN   {GPIOB->CRH&=0X0FFFFFFF;GPIOB->CRH|=(u32)8<<28;}  //PB15为输入 CRH的[31:28]为 1 0 0 0
#define SDA_OUT  {GPIOB->CRH&=0X0FFFFFFF;GPIOB->CRH|=(u32)3<<28;}  //PB15为输出 CRH的[31:28]为 0 0 1 1
#define READ_SDA  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)    //读数据,这时PB15是在输入状态。发送时,PB15为输出,接收(读)时PB15为输入

第四步:给ILI9486发送初始化序列
LCD的初始化序列包括屏的像素时钟,同步参数,扫描时间,扫描方向,颜色格式,辉度,工作模式等类初始化设置,其它不做叙述,这里只讲SPI的配置。查阅 芯片手册,做如下配置:

SPILCD_WriteReg(0xB0);   //接口模式设置SPILCD_WriteData8(0x80); //SDA_EN=1,只用DIN;DOUT不用

有兴趣的读者可以仔细查阅芯片手册中与下图相关资料(红色标记是本文所描述的相关配置):

通过上述4步,即可对ILI9486进行正常操作,实现LCD的显示。
这里再补充如下内容:通常,我们在操作LCD时,大部分是给其GRAM里写数据而实现在LCD屏上的显示。但是,在需要实现复杂、精美的显示界面时,我们就需要使用一些图形化界面开发环境,如emWin等。emWin在移植过程中,最基本的工作是把LCD的底层读写函数交给emWin,ILI9486在不同模式下,读GRAM数据的指令都是0x2E,但时序是有区别的,若时序不对,读出的颜色值就是错误的。下图是3线SPI总线,18bit颜色格式。(3线SPI有两种格式:8色RGB111和262K色GB666)

从此时序图可以看出,每次读取的GRAM颜色数据是24bit,RGB各6bit,即18bit颜色(颜色格式是初始化序列中,用0x3A指令设置的,值为0x66)。那么,是不是我们发送了0x2E指令后就直接读取24bit,即3个字节数据就可以了呢。答案是否的!见下图:

MCU给ILI9486发送0x2E指令(此指令不要参数)后,ILI9486返回24bit颜色数据前,有9个时钟的哑数据,因此必须在发送指令后,要连续读取32bit,而不是24bit。这样才能读取到正确的GRAM数据。否则读取的值就是错误的。

##以下是我的源代码,供大家参考(MCU是STM32F103C8T6)

#include “ili9486.h”
#include “delay.h”

u16 BACK_COLOR; //背景色

/----------------------------------------------------------------------------
屏的控制接口初始化
-----------------------------------------------------------------------------
/
static void ILI9486_GpioInit()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PORTA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能PORTB时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz; //100MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOA, &GPIO_InitStructure);//SPI_CS2

SPI_CS2_H;
SPI_CS1_H;        //初始时SPI都不选中
LCD_RST_L;
delay_ms(200);
LCD_RST_H;       //复位
delay_ms(200);
LCD_BLK_H;       //背光开
delay_ms(200);

}

/----------------------------------------------------------------------------
SPI总线向屏发送1个字节
-----------------------------------------------------------------------------
/
static void SPILCD_WriteByte(u8 dat)
{
u8 i;
SDA_OUT;
SPI_CS1_L; //选择LCD
for(i=0;i<8;i++)
{
SPI_SCK=0; //时钟拉低
if(dat&0x80)
SPI_MOSI_H; //发送1
else
SPI_MOSI_L; //发送0
SPI_SCK=1; //时钟拉高,屏上升沿接收
dat<<=1; //准备下一位
}
SPI_CS1_H; //禁止LCD
}
/----------------------------------------------------------------------------
SPI总线从屏读取1个字节
-----------------------------------------------------------------------------
/
u8 SPILCD_ReadByte(void)
{
u8 i,dat;
SDA_IN;
LCD_DC_H;
SPI_CS1_L; //选择LCD
SPI_SCK_L;//先将时钟拉低,
for(i=0;i<8;i++)
{
SPI_SCK_H; //时钟拉高,屏会在这个上升沿输出数据
dat=dat<<1;
if(READ_SDA)
{
dat=dat+1;
}
else
{
dat=dat;
}
SPI_SCK_L; //时钟拉低,准备下一次上升沿
}
SPI_CS1=1; //禁止LCD
return (dat);
}

/----------------------------------------------------------------------------
SPI总线从屏读取4个字节
-----------------------------------------------------------------------------
/
u32 SPILCD_ReadData(void)
{
u8 i;
u32 dat;
SDA_IN;
LCD_DC_H;
SPI_CS1_L; //选择LCD
SPI_SCK_L;//先将时钟拉低,
for(i=0;i<32;i++)
{
SPI_SCK_H; //时钟拉高,屏会在这个上升沿输出数据
dat=dat<<1;
if(READ_SDA)
{
dat=dat+1;
}
else
{
dat=dat;
}
SPI_SCK_L; //时钟拉低,准备下一次上升沿
}
SPI_CS1=1; //禁止LCD
return (dat);
}

/----------------------------------------------------------------------------
通过SPI总线向屏发送1个字节数据
-----------------------------------------------------------------------------
/
void SPILCD_WriteData8(u8 dat)
{
LCD_DC_H;//写数据
SPILCD_WriteByte(dat);
}

/----------------------------------------------------------------------------
通过SPI总线向屏发送3个字节数据(发送dat的低24位【23:0】)
-----------------------------------------------------------------------------
/
void SPILCD_WriteData32(u32 dat)
{
LCD_DC_H;//写数据
SPILCD_WriteByte(dat>>16);
SPILCD_WriteByte(dat>>8);
SPILCD_WriteByte(dat);
}

/----------------------------------------------------------------------------
通过SPI总线向屏发送1个字节命令
-----------------------------------------------------------------------------
/
void SPILCD_WriteReg(u8 dat)
{
LCD_DC_L;//写命令
SPILCD_WriteByte(dat);
}

///----------------------------------------------------------------------------
//LCD初始化函数
//-----------------------------------------------------------------------------
/
void SPILCD_Init(void)
{
ILI9486_GpioInit();
SPILCD_WriteReg(0x11); //Exit Sleep
delay_ms(60);

SPILCD_WriteReg(0XF2);
SPILCD_WriteData8(0x18);
SPILCD_WriteData8(0xA3);
SPILCD_WriteData8(0x12);
SPILCD_WriteData8(0x02);
SPILCD_WriteData8(0XB2);
SPILCD_WriteData8(0x12);
SPILCD_WriteData8(0xFF);
SPILCD_WriteData8(0x10);
SPILCD_WriteData8(0x00);
SPILCD_WriteData8(0XF8);
SPILCD_WriteData8(0x21);
SPILCD_WriteData8(0x04);SPILCD_WriteReg(0X13);

SPILCD_WriteReg(0x36); // Memory Access Control
SPILCD_WriteData8(0x78);

SPILCD_WriteReg(0xB4);
SPILCD_WriteData8(0x02);SPILCD_WriteReg(0xB6);
SPILCD_WriteData8(0x02);
SPILCD_WriteData8(0x22);SPILCD_WriteReg(0xC1);
SPILCD_WriteData8(0x41);SPILCD_WriteReg(0xC5);
SPILCD_WriteData8(0x00);
SPILCD_WriteData8(0x18);SPILCD_WriteReg(0x3a);   //像素格式:18bits/pixel
SPILCD_WriteData8(0x66);
delay_ms(50);SPILCD_WriteReg(0xB0);   //接口模式设置
SPILCD_WriteData8(0x80); //SDA_EN=1,只用DIN;DOUT不用SPILCD_WriteReg(0xE0);
SPILCD_WriteData8(0x0F);
SPILCD_WriteData8(0x1F);
SPILCD_WriteData8(0x1C);
SPILCD_WriteData8(0x0C);
SPILCD_WriteData8(0x0F);
SPILCD_WriteData8(0x08);
SPILCD_WriteData8(0x48);
SPILCD_WriteData8(0x98);
SPILCD_WriteData8(0x37);
SPILCD_WriteData8(0x0A);
SPILCD_WriteData8(0x13);
SPILCD_WriteData8(0x04);
SPILCD_WriteData8(0x11);
SPILCD_WriteData8(0x0D);
SPILCD_WriteData8(0x00);
SPILCD_WriteReg(0xE1);
SPILCD_WriteData8(0x0F);
SPILCD_WriteData8(0x32);
SPILCD_WriteData8(0x2E);
SPILCD_WriteData8(0x0B);
SPILCD_WriteData8(0x0D);
SPILCD_WriteData8(0x05);
SPILCD_WriteData8(0x47);
SPILCD_WriteData8(0x75);
SPILCD_WriteData8(0x37);
SPILCD_WriteData8(0x06);
SPILCD_WriteData8(0x10);
SPILCD_WriteData8(0x03);
SPILCD_WriteData8(0x24);
SPILCD_WriteData8(0x20);
SPILCD_WriteData8(0x00);SPILCD_WriteReg(0x11);
delay_ms(120);
SPILCD_WriteReg(0x29);
SPILCD_WriteReg(0x2C);
}

/----------------------------------------------------------------------------
设置起始和结束地址(设置坐标)
-----------------------------------------------------------------------------
/
void SPILCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2)
{
SPILCD_WriteReg(0x2a); //列地址设置
SPILCD_WriteData8(x1>>8);
SPILCD_WriteData8(x1);
SPILCD_WriteData8(x2>>8);
SPILCD_WriteData8(x2);
SPILCD_WriteReg(0x2b); //行地址设置
SPILCD_WriteData8(y1>>8);
SPILCD_WriteData8(y1);
SPILCD_WriteData8(y2>>8);
SPILCD_WriteData8(y2);
SPILCD_WriteReg(0x2c); //储存器写
}

/----------------------------------------------------------------------------
LCD清屏函数
-----------------------------------------------------------------------------
/
void SPILCD_Clear(u32 color)
{
u16 i,j;
SPILCD_Address_Set(0,0,SPILCD_W-1,SPILCD_H-1);
for(i=0;i<SPILCD_W;i++)
{
for (j=0;j<SPILCD_H;j++)
{
SPILCD_WriteData32(color);
}
}
}

/----------------------------------------------------------------------------
LCD画点函数
-----------------------------------------------------------------------------
/
void SPILCD_DrawPoint(u16 x,u16 y,u32 color)
{
SPILCD_Address_Set(x,y,x,y);//设置光标位置
SPILCD_WriteData32(color);
}

/----------------------------------------------------------------------------
LCD读点函数
-----------------------------------------------------------------------------
/
u32 SPILCD_ReadPoint(u16 x,u16 y)
{
u32 data;
SPILCD_Address_Set(x,y,x,y);//设置光标位置
SPILCD_WriteReg(0x2E);
data=SPILCD_ReadData();//读24bit颜色数据
return data;
}

/----------------------------------------------------------------------------
LCD在指定区域填充颜色
-----------------------------------------------------------------------------
/
void SPILCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u32 color)
{
u16 i,j;
SPILCD_Address_Set(xsta,ysta,xend,yend); //设置光标位置
for(i=ysta;i<=yend;i++)
{
for(j=xsta;j<=xend;j++)SPILCD_WriteData32(color);//设置光标位置
}
}

/----------------------------------------------------------------------------
画线
-----------------------------------------------------------------------------
/
void SPILCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color)
{
u16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;//画线起点坐标
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if (delta_x0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if (delta_y0)incy=0;//水平线
else {incy=-1;delta_y=-delta_x;}
if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0;t<distance+1;t++)
{
SPILCD_DrawPoint(uRow,uCol,color);//画点
xerr+=delta_x;
yerr+=delta_y;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}

//---------------End of this file-------------------------------

STM32F103软件模拟SPI接口驱动ILI9486液晶屏相关推荐

  1. 单片机软件模拟SPI接口—加深理解SPI总线协议

    单片机软件模拟SPI接口-加深理解SPI总线协议   SPI(Serial Peripheral Interfacer 串行外设接口)是摩托罗拉公司推出的一种同步串行通讯接口,用于微处理器臌控制器和外 ...

  2. java如何编写spi接口_软件模拟SPI接口程序代码(4种模式)

    软件模拟SPI接口程序代码(4种模式) SPI协议简介 SPI的通信原理很简单,一般主从方式工作,这种模式通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(数据输入,针对主机来说 ...

  3. 软件模拟SPI接口程序代码(4种模式)

    软件模拟SPI接口程序代码(4种模式) SPI协议简介 SPI的通信原理很简单,一般主从方式工作,这种模式通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(数据输入,针对主机来说 ...

  4. 软件模拟SPI接口程序代码

    目录 SPI协议简介 SPI接口介绍 SPI接口连接图 SPI数据传输方向 SPI传输模式 模拟SPI程序 SPI协议简介 SPI的通信原理很简单,一般主从方式工作,这种模式通常有一个主设备和一个或者 ...

  5. ESP32驱动LCD液晶屏选型、262K什么意思?SPI写LCD的GRAM时序、MCU液晶屏驱动IC的寄存器功能

    最近转战ESP32,ESP32-D0WDQ6 型号的GPIO只有那么20个左右,且还有几个GPIO只能做输入,非常捉襟见肘.所以如果要驱动LCD液晶屏,绝大多数都会选择SPI接口的MCU屏. 为了编写 ...

  6. 用GPIO模拟SPI接口读取传感器数据

    本文基于平头哥开发板RVB2601,简要介绍了用GPIO模拟SPI时序逻辑,实现SPI协议,按照特定温度传感器的时序,读取其数据,及示例程序 一.概述 SPI(Serial Peripheral In ...

  7. 【STM32】0.96寸OLED显示屏(7针SPI协议)软件模拟SPI

    Author:AXYZdong 自动化专业 工科男 有一点思考,有一点想法,有一点理性 [自制展示]2020鼠年大吉 文章目录 概述 3.1硬件设计 3.2软件设计 3.2.1编程要点 3.2.2代码 ...

  8. 软件模拟SPI时序实现25Q64读写操作

    软件模拟SPI时序实现25Q64读写操作 单片机采用SPI/IIC通讯协议访问外围电子模块如:显示屏.EEPROM.FLASH.各种电子传感器等等越来越多,掌握SPI/IIC通讯协议访问外设非常必要. ...

  9. 【Esp32】Esp32+sx1268 Spi接口驱动SX1268模块

    [Esp32]Spi接口驱动SX1268模块 0. 实验结果 1.原理解析 2.硬件接线 3. SX1268 模块的 SPI 时序分析 4. 基于 ESP32 实现函数封装 4.1 读寄存器 4.2 ...

最新文章

  1. oc35--自定义构造方法
  2. SCSM 2012Orchestrator 2012 虚拟机自动交付测试
  3. 集合框架源码学习之HashMap(JDK1.8)
  4. 勒索病毒“WannaCry”复现
  5. iOS GPUImage之视频采集GPUImageVideoCamera
  6. dotConnect for Oracle控件免费下载及使用方法
  7. Python笔记-uiautomator2环境搭建(安卓模拟器测试环境+windows开发环境)
  8. 【.NET】XML文件的创建,修改,删除
  9. FPGA系列之一:Cyclone V中的时钟资源
  10. 自建机房与IDC机房对比
  11. 字蛛(font-spider)教学——ttf/otf字体文件压缩
  12. cass等距离等分线段的命令键_cad直线均分的命令(CAD等分线段快捷键?)
  13. 【OpenGL】FBO渲染到纹理案例
  14. 头歌 MongoDB 文档的高级查询操作(全部关卡)
  15. 关于字符串旋转问题的一些心得
  16. 详谈软件架构设计(一)之软件架构的概念以及风格-上
  17. python制作一线城市地铁运行动态图!赶地铁不怕做错车了!
  18. C语言及程序设计 实践参考——定期存款利息计算器
  19. 计算机网络原理自用笔记_2_计算机网络定义和分类
  20. 操作系统作业 -期末考试选择题

热门文章

  1. 详细介绍Spark安装[内含测试小案例]
  2. Vscode配置python(小白上手教程)
  3. Java方法的基本用法
  4. 为什么-128的补码是1000 0000?
  5. 在线教育直播平台哪个比较好
  6. 锁定计算机的密码在哪改,电脑屏幕锁定怎么设置_电脑屏幕锁定设置在哪里-win7之家...
  7. ubuntu18.04 虚拟机网络配置
  8. 秋式广告杀手:广告拦截原理与杀手组织
  9. AD域密码过期邮件提醒
  10. Halcon clolor_pieces.hedv:分类器_颜色识别