ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

stm32—LCD外设详解

2020-03-20 22:53:21  阅读:295  来源: 互联网

标签:Pin FSMC stm32 LCD WR GPIO REG 外设


stm32—LCD外设详解(5510)

图像处理,不会用LCD怎么行。本实验基于正点原子战舰开发板重新编写,正点原子的代码写的很好,但奈何本新手看了表示一脸懵逼,因此重新编写,将代码简单化,去除操作系统以及兼容性等干扰项。

写命令函数
//regval:命令
void LCD_WR_REG(u16 regval){   
    LCD->LCD_REG=regval;//写入命令
}
写数据函数
//data:要写入的值
void LCD_WR_DATA(u16 data){  
    LCD->LCD_RAM=data;       
}
读LCD数据函数

这个需要采用volatile防止编译器优化

u16 LCD_RD_DATA(void){
    vu16 ram;//防止被优化,否则会出现读出的值为刚写入的值,这是编译器优化的结果,也可以用volatile
    ram=LCD->LCD_RAM;   
    return ram;  
}           
写寄存器函数
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue){    
    LCD->LCD_REG = LCD_Reg;     //写入命令   
    LCD->LCD_RAM = LCD_RegValue;//写入数据               
}
读寄存器函数
u16 LCD_ReadReg(u16 LCD_Reg){                                          
    LCD_WR_REG(LCD_Reg);        //写入命令
    delay_us(5);          
    return LCD_RD_DATA();       //返回读到的值
}
写GRAM准备函数

可以看到5510的写显存指令为2C00h

image-20200320164130115

//开始写GRAM命令
void LCD_WriteRAM_Prepare(void){
    LCD->LCD_REG=2C00h;  
}
延时函数

提供不是那么精确的延时

void opt_delay(u8 i){
    while(i--);
}
编写设置光标函数

x坐标由列地址设置寄存器控制,通过参考手册可以看到列地址设置寄存器都是低八位有效,而地址为16位数据需要将地址的高八位放入2A00h寄存器的低八位,地址的低八位放入2A01h寄存器的低八位。

y坐标由页地址设置寄存器控制,指令为2B00h,原理参考x坐标设置。

image-20200320154707847

image-20200320155101666

//设置光标坐标
void LCD_SetCursor(u16 Xpos, u16 Ypos){  
    LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(Xpos>>8);         //0X2A00h写入x的高字节数据
    LCD_WR_REG(lcddev.setxcmd+1);LCD_WR_DATA(Xpos&0XFF);     //0X2A01h写入x的低字节数据      
    LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(Ypos>>8);         //0X2B00h写入y的高字节数据
    LCD_WR_REG(lcddev.setycmd+1);LCD_WR_DATA(Ypos&0XFF);     //0X2B01h写入y的低字节数据 
}
开窗函数

XS为x坐标的起始坐标,XE为X坐标的结束坐标

根据手册可以发现,输入0X2A00h指令后输入XS高八位,输入0X2A01h指令后输入XS低八位,输入0X2A02h指令后输入XE高八位,输入0X2A03h指令后输入XE低八位。

Y轴同理,由此可以设置出X轴的起始坐标与结束坐标,Y轴的起始坐标与结束坐标,这样就可以画一个框出来。

image-20200320154707847

void open_windows(u16 x,u16 y,u16 width,u16 heigth){
    LCD_WR_REG(0X2A00);LCD_WR_DATA((x&0xFF00)>>8);
    LCD_WR_REG(0X2A01);LCD_WR_DATA((x&0x00FF));
    LCD_WR_REG(0X2A02);LCD_WR_DATA(((x+width)&0xFF00)>>8);
    LCD_WR_REG(0X2A03);LCD_WR_DATA(((x+width)&0x00FF));
    
    LCD_WR_REG(0X2B00);LCD_WR_DATA((y&0xFF00)>>8);
    LCD_WR_REG(0X2B01);LCD_WR_DATA((y&0x00FF));
    LCD_WR_REG(0X2B02);LCD_WR_DATA(((y+heigth)&0xFF00)>>8);
    LCD_WR_REG(0X2B03);LCD_WR_DATA(((y+heigth)&0x00FF));    
}
画矩形函数

在开窗后,总得验证一下吧,先来一个画矩形函数:0x2C00为写显存指令,我这里是以坐标(1.1)为原点,开了一个长宽为100个像素的正方形窗口,开窗后,使用0x2C00向窗口显存输入数据为0x00,也就是黑色。

void draw_rectangle(void){
    u16 i;
    open_windows(1,1,100,100);
    LCD_WR_REG((0X2C00);
    for(i=0;i<100*100;i++){
        LCD_WR_DATA(0x00);
    }

显示结果:

image-20200320173412734

存储器数据访问控制

想要让显示屏亮起来,还需要规定像素点如何去刷新,也就是是先打印行还是先打印列。这个由命令0x3600指令控制。在输入指令后还需要输入一个8字节的数据,来规定扫描方向。

image-20200320182642742

DO:上下翻转显示图像。

D1:左右翻转显示图像。

MH:水平方向控制。

image-20200320213836268

RGB:“0”=RGB颜色序列,“1”=BGR颜色序列。

ML:垂直方向控制。

image-20200320213935188

MV:行列变换,交换X、Y轴

MX:列控制。

MY:行控制。

LCD初始化函数

LCD初始化函数刚开始就是一段厂商提供的初始化代码,下面是代码的一部分,这个不需要管,稍作修改复制粘贴进来即可:

image-20200320214719049

void LCD_Init(void){
    /*此处应该有厂方提供的函数,已省略*/
    LCD_Display();
    LCD_LED=1;              //点亮背光
    LCD_Clear(RED);
}   

先编写LCD_Display()函数,这个应该很清晰了,开窗,这个开窗是针对整个显示屏:

    LCD_WR_REG(0X3600);//存储器数据访问控制指令,主要控制方向
    LCD_WR_DATA(0x0000);
    LCD_WR_REG(0x2A00);LCD_WR_DATA(0);  //2A00h XS[15:8]
    LCD_WR_REG(0x2A01);LCD_WR_DATA(0);//2A01h  XS[7:0] 
    LCD_WR_REG(0x2A02);LCD_WR_DATA((u16)(480-1)>>8);        //2A02h XE[15:8]           
    LCD_WR_REG(0x2A03);LCD_WR_DATA((u16)(480-1)&0XFF);   //2A03h XE[7:0]
    LCD_WR_REG(0x2B00);LCD_WR_DATA(0);  //2B00h YS[15:8]
    LCD_WR_REG(0x2B01);LCD_WR_DATA(0);//2B01h   YS[7:0] 
    LCD_WR_REG(0x2B02);LCD_WR_DATA((u16)(800-1)>>8);    //2B02h YE[15:8]
    LCD_WR_REG(0x2B03);LCD_WR_DATA((u16)(800-1)&0XFF);  

设置好显示格式开窗以后,将背光线拉高,设置好第一个点的位置后,开始打点:

void LCD_Clear(u16 color){
    u32 index=0;      
    u32 totalpoint=480*800;//得到总点数  
    LCD_SetCursor(0x00,0x00);   //设置光标开始位置与(0,0) ,这个函数在上面已经写过了
    LCD->LCD_REG=0x2C00;            //开始写入GRAM命令          
    for(index=0;index<totalpoint;index++){
        LCD->LCD_RAM=color; 
    }
}

LCD相关函数设置完以后,设置好GPIO初始化函数:

void GPIO_LCD_Init(void){
    GPIO_InitTypeDef GPIO_InitStructure;
    
                       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOG,ENABLE);//使能PORTB,D,E,G以及AFIO复用功能时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;                //PB0 推挽输出 背光
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    //PORTD复用推挽输出  
    GPIO_InitStructure.GPIO_Pin =     GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15;                 // //PORTD复用推挽输出  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure); 
     
    //PORTE复用推挽输出  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;              // //PORTD复用推挽输出  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStructure);                                                           
    //  //PORTG12复用推挽输出 A0  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_12;    // //PORTD复用推挽输出  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOG, &GPIO_InitStructure); 
}

以及FSMC初始化函数:

void FSMC_LCD_Init(void){
    FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
  FSMC_NORSRAMTimingInitTypeDef  readWriteTiming; 
    FSMC_NORSRAMTimingInitTypeDef  writeTiming;
    
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE); //使能FSMC时钟
    
    readWriteTiming.FSMC_AddressSetupTime = 0x01;    //地址建立时间(ADDSET)为2个HCLK 1/36M=27ns
  readWriteTiming.FSMC_AddressHoldTime = 0x00;   //地址保持时间(ADDHLD)模式A未用到 
  readWriteTiming.FSMC_DataSetupTime = 0x0f;         // 数据保存时间为16个HCLK,因为液晶驱动IC的读数据的时候,速度不能太快,尤其对1289这个IC。
  readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
  readWriteTiming.FSMC_CLKDivision = 0x00;
  readWriteTiming.FSMC_DataLatency = 0x00;
  readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;   //模式A 
    

    writeTiming.FSMC_AddressSetupTime = 0x00;    //地址建立时间(ADDSET)为1个HCLK  
  writeTiming.FSMC_AddressHoldTime = 0x00;   //地址保持时间(A     
  writeTiming.FSMC_DataSetupTime = 0x03;         ////数据保存时间为4个HCLK  
  writeTiming.FSMC_BusTurnAroundDuration = 0x00;
  writeTiming.FSMC_CLKDivision = 0x00;
  writeTiming.FSMC_DataLatency = 0x00;
  writeTiming.FSMC_AccessMode = FSMC_AccessMode_A;   //模式A 

 
  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;//  这里我们使用NE4 ,也就对应BTCR[6],[7]。
  FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; // 不复用数据地址
  FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM;  //SRAM   
  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit   
  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable; 
  FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
  FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable; 
  FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;   
  FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;  
  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;   //  存储器写使能
  FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;   
  FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; // 读写使用不同的时序
  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; 
  FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming; //读写时序
  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming;  //写时序

  FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);  //初始化FSMC配置

  FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);  // 使能BANK1 
}

至此LCD就可以开始工作了,还可以使用矩形函数 draw_rectangle( )在LCD上画一个矩阵:

image-20200320224320662

这就是最基础的LCD显示了

标签:Pin,FSMC,stm32,LCD,WR,GPIO,REG,外设
来源: https://www.cnblogs.com/roscangjie/p/12535526.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有