stm32学习笔记--LCD显示实验

1.硬件连接

在硬件上,TFTLCD 模块与战舰 STM32F103 的 IO 口对应关系如下:

LCD_BL(背光控制)对应 PB0;

LCD_CS 对应 PG12 即 FSMC_NE4;

LCD _RS 对应 PG0 即 FSMC_A10;

LCD _WR 对应 PD5 即 FSMC_NWE;

LCD _RD 对应 PD4 即 FSMC_NOE;

LCD _D[15:0]则直接连接在 FSMC_D15~FSMC_D0;

2.代码 :LCD结构体

//LCD地址结构体
typedef struct
{
        vu16 LCD_REG;
        vu16 LCD_RAM;
} LCD_TypeDef;
//使用NOR/SRAM的 Bank1.sector4,地址位HADDR[27,26]=11 A10作为数据命令区分线 
//注意设置时STM32内部会右移一位对其! 			    
#define LCD_BASE        ((u32)(0x6C000000 | 0x000007FE))
#define LCD             ((LCD_TypeDef *) LCD_BASE)

LCD_BASE,须根据外部电路的连接来确定,如Bank1.sector4就是从地址0X6C000000开 始,而0X000007FE,则是A10的偏移量。以A10为例,7FE换成二进制为:111 1111 1110 ,而16位数据时,地址右移一位对齐,对应到地址引脚,就是:A10:A0=011 1111 1111, 此时A10是0,但是如果16位地址再加1(对应到8位地址是加2,即7FE+0X02),那么: A10:A0=100 0000 0000,此时A10就是1了,即实现了对RS的0和1的控制
我们将这个地址强制转换为LCD_TypeDef结构体地址,那么可以得到LCD->LCD_REG的 地址就是0X6C00,07FE,对应A10的状态为0(即RS=0),而LCD-> LCD_RAM的地址就是 0X6C00,0800(结构体地址自增),对应A10的状态为1(即RS=1),从而实现对RS的控制。

关于vu16,实际上就是u16,但前面加上了volatile 
volatile 影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化。)。
例如:
volatile int i=10;
int j = i;
...
int k = i;
volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。
而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。

3.代码 :_lcd_dev结构体

//LCD重要参数集
typedef struct
{
    u16 width;      //LCD 宽度
    u16 height;     //LCD 高度
    u16 id;         //LCD ID
    u8  dir;        //横屏还是竖屏控制:0,竖屏;1,横屏。
    u16	wramcmd;    //开始写gram指令
    u16 setxcmd;    //设置x坐标指令
    u16  setycmd;   //设置y坐标指令
} _lcd_dev;

//LCD参数
extern _lcd_dev lcddev;     //管理LCD重要参数

lcddev结构体参数的赋值,基本上都是在LCD_Display_Dir函数完成

//设置LCD显示方向
//dir:0,竖屏;1,横屏
void LCD_Display_Dir(u8 dir)
{
    lcddev.dir = dir;       //竖屏/横屏

    if (dir == 0)           //竖屏
    {
        lcddev.width = 240;
        lcddev.height = 320;

        if (lcddev.id == 0x5510)
        {
            lcddev.wramcmd = 0X2C00;
            lcddev.setxcmd = 0X2A00;
            lcddev.setycmd = 0X2B00;
            lcddev.width = 480;
            lcddev.height = 800;
        }
        else if (lcddev.id == 0X1963)
        {
            lcddev.wramcmd = 0X2C;  //设置写入GRAM的指令
            lcddev.setxcmd = 0X2B;  //设置写X坐标指令
            lcddev.setycmd = 0X2A;  //设置写Y坐标指令
            lcddev.width = 480;     //设置宽度480
            lcddev.height = 800;    //设置高度800
        }
        else                        //其他IC, 包括: 9341 / 5310 / 7789等IC
        {
            lcddev.wramcmd = 0X2C;
            lcddev.setxcmd = 0X2A;
            lcddev.setycmd = 0X2B;
        }

        if (lcddev.id == 0X5310)    //如果是5310 则表示是 320*480分辨率
        {
            lcddev.width = 320;
            lcddev.height = 480;
        }
    }
    else     //横屏
    {
        lcddev.width = 320;
        lcddev.height = 240;

        if (lcddev.id == 0x5510)
        {
            lcddev.wramcmd = 0X2C00;
            lcddev.setxcmd = 0X2A00;
            lcddev.setycmd = 0X2B00;
            lcddev.width = 800;
            lcddev.height = 480;
        }
        else if (lcddev.id == 0X1963)
        {
            lcddev.wramcmd = 0X2C;  //设置写入GRAM的指令
            lcddev.setxcmd = 0X2A;  //设置写X坐标指令
            lcddev.setycmd = 0X2B;  //设置写Y坐标指令
            lcddev.width = 800;     //设置宽度800
            lcddev.height = 480;    //设置高度480
        }
        else                        //其他IC, 包括: 9341 / 5310 / 7789等IC
        {
            lcddev.wramcmd = 0X2C;
            lcddev.setxcmd = 0X2A;
            lcddev.setycmd = 0X2B;
        }

        if (lcddev.id == 0X5310)    //如果是5310 则表示是 320*480分辨率
        {
            lcddev.width = 480;
            lcddev.height = 320;
        }
    }

    LCD_Scan_Dir(DFT_SCAN_DIR);     //默认扫描方向
}

 4.底层接口函数

7个底层接口函数:

1,写寄存器值函数 :void LCD_WR_REG(u16 regval)
2,写数据函数:void LCD_WR_DATA(u16 data)
3,读数据函数:u16 LCD_RD_DATA(void)
4,写寄存器内容函数: void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue)
5,读寄存器内容函数: u16 LCD_ReadReg(u16 LCD_Reg)
6,开始写GRAM函数: void LCD_WriteRAM_Prepare(void)
7,写GRAM函数: void LCD_WriteRAM(u16 RGB_Code)

 1.写寄存器值

写入地址之后FSMC会硬件去实现

//写寄存器函数
//regval:寄存器值
void LCD_WR_REG(u16 regval)
{
    LCD->LCD_REG=regval;        //写入要写的寄存器序号
}

2.写LCD数据

//写LCD数据
//data:要写入的值
void LCD_WR_DATA(u16 data)
{
    LCD->LCD_RAM=data;
}

3. 

//读LCD数据
//返回值:读到的值
u16 LCD_RD_DATA(void)
{
    vu16 ram;                   //防止被优化
    ram=LCD->LCD_RAM;
    return ram;	 
}

4.

//写寄存器
//LCD_Reg:寄存器地址
//LCD_RegValue:要写入的数据
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue)
{
    LCD->LCD_REG = LCD_Reg;     //写入要写的寄存器序号
    LCD->LCD_RAM = LCD_RegValue;//写入数据
}

5.

//读寄存器
//LCD_Reg:寄存器地址
//返回值:读到的数据
u16 LCD_ReadReg(u16 LCD_Reg)
{
    LCD_WR_REG(LCD_Reg);        //写入要读的寄存器序号
    delay_us(5);
    return LCD_RD_DATA();       //返回读到的值
}

6.

//开始写GRAM
void LCD_WriteRAM_Prepare(void)
{
    LCD->LCD_REG = lcddev.wramcmd;
}

7.

//LCD写GRAM
//RGB_Code:颜色值
void LCD_WriteRAM(u16 RGB_Code)
{
    LCD->LCD_RAM = RGB_Code;    //写十六位GRAM
}

5. 第二层--LCD函数

1.初始化

LCD初始化函数伪代码:

//LCD初始化
void LCD_Init(void)
{
    初始化GPIO;
    初始化FSMC;		             	//Mini板不需要
    读取LCD ID;	
    printf(“LCD ID:%x\r\n”,lcddev.id);//打印LCD ID,用到了串口1
                                        //所以必须初始化串口1,否则黑屏	
    根据不同的ID执行LCD初始化代码;
    LCD_Display_Dir(0);		 	//默认为竖屏
    LCD_LED=1;			 	//点亮背光
    LCD_Clear(WHITE);			//清屏
}

2.

//设置光标位置
//Xpos:横坐标
//Ypos:纵坐标
void LCD_SetCursor(u16 Xpos, u16 Ypos)
{	 
     if(lcddev.id==0X9341||lcddev.id==0X5310)
     {		    
          LCD_WR_REG(lcddev.setxcmd); 
          LCD_WR_DATA(Xpos>>8);
          LCD_WR_DATA(Xpos&0XFF);  
          LCD_WR_REG(lcddev.setycmd); 
	 LCD_WR_DATA(Ypos>>8);
	 LCD_WR_DATA(Ypos&0XFF); 
     }else if(lcddev.id==XXXX)	//根据不同的LCD型号,执行不同的代码
     { 
	 ……//省略部分代码
     }
}
		 

3.

//画点
//x,y:坐标
//POINT_COLOR:此点的颜色
void LCD_DrawPoint(u16 x,u16 y)
{
    LCD_SetCursor(x,y);		//设置光标位置 
    LCD_WriteRAM_Prepare();	//开始写入GRAM
    LCD->LCD_RAM=POINT_COLOR; 	//非Mini板的操作方式   
}

4.LCD读点函数:u16 LCD_ReadPoint(u16 x,u16 y) 1,非Mini板的读点函数代码(FSMC方式,适合战舰、精英、探索者F4板) 2,Mini板的读点函数代码(GPIO方式,适合Mini板)

//读取个某点的颜色值
//x,y:坐标
//返回值:此点的颜色
u16 LCD_ReadPoint(u16 x, u16 y)
{
    u16 r, g, b;

    if (x >= lcddev.width || y >= lcddev.height)return 0;   //超过了范围,直接返回

    LCD_SetCursor(x, y);

    if (lcddev.id == 0X5510)    //5510 发送读GRAM指令
    {
        LCD_WR_REG(0X2E00);
    }
    else                        //其他IC(9341/5310/1963/7789)发送读GRAM指令
    {
        LCD_WR_REG(0X2E);
    }

    r = LCD_RD_DATA();          //假读

    if (lcddev.id == 0X1963)    //对1963来说,是真读
    {
        return r;               //1963直接读就可以
    }

    r = LCD_RD_DATA();          //实际坐标颜色

    //9341/5310/5510/7789 要分2次读出
    b = LCD_RD_DATA();
    g = r & 0XFF;               //对于 9341/5310/5510/7789, 第一次读取的是RG的值,R在前,G在后,各占8位
    g <<= 8;
    return (((r >> 11) << 11) | ((g >> 10) << 5) | (b >> 11));  // 9341/5310/5510/7789 需要公式转换一下
}

5.叠加方式会覆盖颜色,非叠加方式不会

//在指定位置显示一个字符
//x,y:起始坐标
//num:要显示的字符:" "--->"~"
//size:字体大小 12/16/24
//mode:叠加方式(1)还是非叠加方式(0)
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode)
{  							  
       u8 temp,t1,t;
       u16 y0=y;
       u8 csize=(size/8+((size%8)?1:0))*(size/2);	//得到字体一个字符对应点阵集所占的字节数	
       num=num-' ';	//得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)
       for(t=0;t<csize;t++)
       {  
if(size==12)temp=asc2_1206[num][t]; 		//调用1206字体
             else if(size==16)temp=asc2_1608[num][t];	//调用1608字体
             else if(size==24)temp=asc2_2412[num][t];	//调用2412字体
             else return;			//没有的字库
             for(t1=0;t1<8;t1++)
             {			    
      if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);
      else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);
                    temp<<=1;
                    y++;
                    if(y>=lcddev.height)return;		//超区域了
                    if((y-y0)==size)
                    {
	y=y0;
	x++;
	if(x>=lcddev.width)return;		//超区域了
	break;
                     }
              }  	 
        }  	    	   	 	  
} 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32是一款非常流行的嵌入式微控制器系列,它具有强大的性能和丰富的外设资源。在学习STM32时,掌握如何进行Flash读写是非常重要的。 Flash是一种非易失性存储器,可以用来存储程序代码和数据。在STM32中,Flash存储器通常用来存储应用程序代码。下面是一个简单的Flash读写程序的示例: 1.首先,我们需要包含适用于所使用的STM32型号的头文件。例如,对于STM32F4系列,我们需要包含"stm32f4xx.h"。 2.然后,我们需要定义一个指向Flash存储器的指针变量。例如,可以使用如下代码:`uint32_t* flash_address = (uint32_t*)0x08000000;`其中0x08000000是Flash存储器的起始地址。 3.要读取Flash存储器中的数据,我们可以通过以下代码实现:`data = *flash_address;`其中data是一个变量,用于存储读取到的数据。 4.要写入数据到Flash存储器中,我们可以通过以下代码实现:`*flash_address = data;`其中data是要写入的数据。 需要注意的是,STM32的Flash存储器是有写保护机制的,因此在写入数据之前,我们需要禁用写保护。可以使用以下代码禁用写保护:`FLASH->KEYR = 0x45670123; FLASH->KEYR = 0xCDEF89AB;`然后才能进行数据写入。 另外,为了确保数据的完整性,我们可以使用CRC校验来验证Flash存储器中的程序代码的正确性。可以使用库函数来计算校验和,然后将其与预期的校验和进行比较以进行验证。 综上所述,掌握STM32的Flash读写操作对于嵌入式系统的开发非常重要。上述示例代码可以帮助我们快速进行Flash读写操作,同时注意写保护和数据校验可以提高数据的安全性和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值