stm32H743 LTDC驱动正点原子RGB屏幕(个人笔记)

本篇内容为个人笔记,可能存在错误,欢迎指正。

屏幕为正点原子7寸屏幕(1024*600)。SDRAM是W9825G6KH-6芯片,容量32MByte。开发板是自己弄的,接口除了背光引脚,其余跟正点的阿波罗核心板原子一样。

DMA2D下期使用,这期先点灯,原理一笔带过,下期补回来。

以下内容参考正点原子的STM32H743 阿波罗开发指南V1.0

一、驱动SDRAM

1、使用外部高速时钟

2、开启ICche和Dcahe

提高 CPU访问指令/数据速度。开启D-Cache 强制透写功能,在初始化代码中添加SCB->CACR |= 1 << 2;

3、配置时钟树

主频选择480M。FMC外设最大速率是200MHz,W9825G6KH-6最大速率是166MHz,这里为了方便使用PPL2R时钟源,配置时钟200MHz。后面再2分频,使SDRAM时钟为100MHz。

4、配置FMC

SDRAM timing in memory clock cycles要根据配置的FMC时钟而定。我这里配置读写大块内存测试没有问题。

1)使用SRRAM1。

        Clock and chip enable  时钟信号使用FMC_SDCKE0,片选信号使用FMC SDME0。

        FMC_BA0和FMC_BA1, 2根bank选择线都使用。

        13位地址线,16位数据线

2)SDRAM control 配置

       

        Bank:使用bank1。

        Number of column address bits:W9825G6KH-6的列地址宽度为9。

        Number of row address bits:W9825G6KH-6的行地址宽度为13。

        CAS Latency:列地址选通延迟(简称 CL)。 在读命令(同时发送列地址) 发送完之后,需要等待几个时钟周期, DQ 数据线上的数据, 才会有效, 这个延迟时间, 就叫 CL, 一般
设置为 2/3 个时钟周期。(正点原子手册原话)。
 

        

         Write protection:写保护,不使能。

        SDRAM common clock:SDRAM通用时钟,2分频。我们的FMC时钟是200Mhz,分频后,SDRAM 时钟就变成100Mhz。

        SDRAM common burst read:SDRAM突发读取,使能。

        SDRAM common read pipe delay:SDRAM读管延时,选择1个HCLK时钟周期。

3)配置SDRAM timing in memory clock cycles

        Load mode register to active dela:负载模式寄存器到活动延时,选择2。Load mode register to active delay 是FMC_SDTRx寄存器的TMRD位,根据手册配置为2;

        Exit self-refresh delay:退出自动刷新延时,72ns。如果是100MHz的时钟,周期就10ns。72ns / 10ns = 7.2 clock cycles。所以选择8。

        Self-refresh time:自我刷新时间:42ns,选择5。

        SDRAM common row cycle delay:60ns,选择6。

        Write recovery time:写入恢复时间,writerrecoverytime必须满足以下约束条件:

                1: WriteRecoveryTime >= SelfRefreshTime - RowToColumnDelay,

                2: WriteRecoveryTime >= RowCycleDelay - RowToColumnDelay -                         RowPrechargeDelay.

                SelfRefreshTime=5,RowToColumnDelay=2,所以这里填3;

        SDRAM common row precharge dela:SDRAM行预充电时间,18ns,选择2。

        Row to column delay:行到列延时,18ns ,选择2。

        

5、开启串口,生成工程。

打开串口

勾选以下,生成.h文件。

6、初始化代码

正点原子也有这个代码,都是差不多的

1)新建一个 "sdram_fmc_drv.h"文件

//以下代码来自网友,作者是 mculover666
/**
 *@file    sdram_fmc_drv.h
 *@brief   使用 FMC 操作 SDRAM
 *@author  mculover666
 *@date    2020-08-27
 *@note    此驱动测试 W9825G6KH SDRAM芯片通过
*/

#ifndef _SDRAM_FMC_DRV_H_
#define _SDRAM_FMC_DRV_H_

#include "fmc.h"

#define SDRAM_MODEREG_BURST_LENGTH_1             ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2             ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4             ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8             ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL      ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED     ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2              ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3              ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD    ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE     ((uint16_t)0x0200)

void SDRAM_Init(void);

#endif /* _SDRAM_FMC_DRV_H_ */

2)新建一个 "sdram_fmc_drv.c"文件

#include "sdram_fmc_drv.h"

//以下代码来自网友,作者是 mculover666

/**
 * @brief       向SDRAM发送命令
 * @param       CommandMode     : 指令(0,正常模式/1,时钟配置使能/2,预充电所有存储区/3,自动刷新/4,加载模式寄存器/5,自刷新/6,掉电)
 * @param       Bank   : 0,向BANK5上面的SDRAM发送指令
 *   @arg                 1,向BANK6上面的SDRAM发送指令 
 * @param       RefreshNum : 自刷新次数
 * @RegVal      模式寄存器的定义
 * @retval      返回值:0,正常;1,失败.

 */
static int SDRAM_SendCommand(uint32_t CommandMode, uint32_t Bank, uint32_t RefreshNum, uint32_t RegVal)
{
    uint32_t CommandTarget;
    FMC_SDRAM_CommandTypeDef Command;
    
    if (Bank == 1) {
        CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
    } else if (Bank == 2) {
        CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
    }
    
    Command.CommandMode = CommandMode;
    Command.CommandTarget = CommandTarget;
    Command.AutoRefreshNumber = RefreshNum;
    Command.ModeRegisterDefinition = RegVal;
    
    if (HAL_SDRAM_SendCommand(&hsdram1, &Command, 0x1000) != HAL_OK) {
        return -1;
    }
    
    return 0;
}


void SDRAM_Init(void)
{
    uint32_t temp;
    
    /* 1. 时钟使能命令 */
    SDRAM_SendCommand(FMC_SDRAM_CMD_CLK_ENABLE, 1, 1, 0);
    
    /* 2. 延时,至少100us */
    HAL_Delay(1);
    
    /* 3. SDRAM全部预充电命令 */
    SDRAM_SendCommand(FMC_SDRAM_CMD_PALL, 1, 1, 0);
    
    /* 4. 自动刷新命令 */
    SDRAM_SendCommand(FMC_SDRAM_CMD_AUTOREFRESH_MODE, 1, 8, 0);
    
    /* 5. 配置SDRAM模式寄存器 */   
    temp = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1            |          //设置突发长度:1
                     SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL     |          //设置突发类型:连续
                     SDRAM_MODEREG_CAS_LATENCY_3             |          //设置CL值:3
                     SDRAM_MODEREG_OPERATING_MODE_STANDARD   |          //设置操作模式:标准
                     SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;              //设置突发写模式:单点访问  
    SDRAM_SendCommand(FMC_SDRAM_CMD_LOAD_MODE, 1, 1, temp);
    
    /* 6. 设置自刷新频率 */
    /**
     * 刷新频率计数器(以SDCLK频率计数),计算方法:
     * COUNT=SDRAM刷新周期/行数-20=SDRAM刷新周期(us)*SDCLK频率(Mhz)/行数
     * 我们使用的SDRAM刷新周期为64ms,SDCLK=200/2=100Mhz,行数为8192(2^13).
     * 所以,COUNT=64*1000*100/8192-20=761.25
     */
    HAL_SDRAM_ProgramRefreshRate(&hsdram1, 761);
}

 HAL_SDRAM_ProgramRefreshRate(&hsdram1, 761);要根据时钟频率计算,如果不想算,可以直接套 refresh = 7.8125*(你的时钟频率)-20,我使用的是100MHz,

所以refresh = 7.8125*100-20=761.25。

3)在main.c中初始化串口和编程测试函数。

在main.c中添加以下代码

//在main.c中添加
#include <stdio.h>
#include "sdram_fmc_drv.h"
#include <string.h>

#define EXT_SDRAM_ADDR  	((uint32_t)0xC0000000+0x200000)
#define EXT_SDRAM_SIZE		(28 * 1024 * 1024)

int fputc(int ch,FILE *f)
{
	HAL_UART_Transmit(&huart1 ,(uint8_t *) &ch,1,100);
	return ch;
}


uint32_t bsp_TestExtSDRAM(void)
{
	uint32_t i;
	uint32_t *pSRAM;
	uint8_t *pBytes;
	uint32_t err;
	const uint8_t ByteBuf[4] = {0x55, 0xA5, 0x5A, 0xAA};

	/* 写SDRAM */
	pSRAM = (uint32_t *)EXT_SDRAM_ADDR;
	for (i = 0; i < EXT_SDRAM_SIZE / 4; i++)
	{
		*pSRAM++ = i;
	}

	/* 读SDRAM */
	err = 0;
	pSRAM = (uint32_t *)EXT_SDRAM_ADDR;
	for (i = 0; i < EXT_SDRAM_SIZE / 4; i++)
	{
		if (*pSRAM++ != i)
		{
			err++;
		}
	}

	if (err >  0)
	{
		return  (4 * err);
	}

	/* 对SDRAM 的数据求反并写入 */
	pSRAM = (uint32_t *)EXT_SDRAM_ADDR;
	for (i = 0; i < EXT_SDRAM_SIZE / 4; i++)
	{
		*pSRAM = ~*pSRAM;
		pSRAM++;
	}

	/* 再次比较SDRAM的数据 */
	err = 0;
	pSRAM = (uint32_t *)EXT_SDRAM_ADDR;
	for (i = 0; i < EXT_SDRAM_SIZE / 4; i++)
	{
		if (*pSRAM++ != (~i))
		{
			err++;
		}
	}

	if (err >  0)
	{
		return (4 * err);
	}

	/* 测试按字节方式访问, 目的是验证 FSMC_NBL0 、 FSMC_NBL1 口线 */
	pBytes = (uint8_t *)EXT_SDRAM_ADDR;
	for (i = 0; i < sizeof(ByteBuf); i++)
	{
		*pBytes++ = ByteBuf[i];
	}

	/* 比较SDRAM的数据 */
	err = 0;
	pBytes = (uint8_t *)EXT_SDRAM_ADDR;
	for (i = 0; i < sizeof(ByteBuf); i++)
	{
		if (*pBytes++ != ByteBuf[i])
		{
			err++;
		}
	}
	if (err >  0)
	{
		return err;
	}
	return 0;
}

在main函数中添加代码

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* Enable the CPU Cache */

  /* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();

  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
	SCB->CACR |= 1 << 2; /* 强制 D-Cache 透写,如不开启透写,实际使用中可能遇到各种问题 */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_FMC_Init();
  MX_USART1_UART_Init();
  MX_DMA2D_Init();
  MX_LTDC_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
printf("STM32H743 SDRAM Test\r\n");

SDRAM_Init();

printf("SDRAM Init complete\r\n");

if (bsp_TestExtSDRAM() == 0) {
    printf("SDRAM Test success\r\n");
} else {
    printf("SDRAM Test fail\r\n");
}


  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
			
  }
  /* USER CODE END 3 */
}

7、观察串口输出结果

        SDRAM Test success 代表成功了。

如果下载失败,则是SDRAM配置有问题,程序可能跑飞了,按住复位引脚点下载,再松开即可。

二、驱动RGB屏幕

1、配置LTDC

   1)Display Type

        选择RGB565颜色格式。其实565和888色彩区别不是很大,为了节省一个字节就选择565。

    2)Parameter Settings

        Parameter Settings的配置比较简单,就不说明了,直接套用。

3)Layer Settings配置

先配置Number of Layers :Number of Layers 这里选择1 layer,LTDC是有前景层和背景层的,后面再出一期讲解。

Windows Position:第0层的窗口的开始和结束,就根据屏幕像素配置,我的是1024x600 pixel的屏幕。

Pixel Parameters:该图层选择RGB565格式。

Blending:图层颜色混合,通透度选择255,前景色不透明,剩下两个图层通透度系数默认选项。

Layer Default Color:图层默认颜色,全是0就行了。

Frame Buffe:

        1)Layer 0 - Color Frame Buffer Start Adress:第0层-彩色帧缓冲区起始地址选择0xC0000000,就是我们SDRAM的起始地址。以也可偏移32M字节内。因为我们使用RGB565格式,1024x600 pixel,要预留2x1024x600个字节空间。

        2)Layer 0 - Color Frame Buffer Line Length :1024;

        3)Layer 0 - Color Frame Buffer Number of Lines :600

4)配置RGB引脚

LTDC的引脚的Maximum output speed全部配置为High或者Very High,不然工程会失败。

5)打开背光灯

        背光灯使用推挽上拉输出,默认高电平就行了。

6、配置LTCD时钟

        像素时钟就是 RGB LCD 的时钟信号,以 ATK7016 这款屏幕为例,显示一帧图像所需要的
时钟数就是:
= (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
= (3 + 20 + 600 + 12) * (20 + 140 + 1024 + 160)
= 635 * 1344
= 853440。
显示一帧图像需要 853440个时钟数,那么显示 60帧就是:853440 * 60 = 51206400≈51.2M,
所以像素时钟就是 51.2MHz。

2、打开DMA2D

        这里打开就行了,不用作任何配置,因为用HAL库操作DMA2D非常浪费资源,频繁进栈和出栈没有达到使用DMA2D的初衷,所以我们直接对寄存器操作。

3、编写测试代码

        代码先写一个简单的测试,不使用DMA2D,后面出下一章节再讲。

__ attribute __( at(绝对地址) )的作用是定位到 Flash或 RAM。绝对定位不能在函数中定义,局部变量是定义在栈区,栈区是自动分配、释放,不能定义为绝对地址,只能于函数外定义。

uint16_t LCD_SDRAM_BUFF[1024][600] __attribute__((at(0XC0000000)));


//在main函数的死循环里中添加

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		

		memset(LCD_SDRAM_BUFF,0x001F,sizeof(LCD_SDRAM_BUFF));
        HAL_Delay(2000);
        memset(LCD_SDRAM_BUFF,0xF800,sizeof(LCD_SDRAM_BUFF));
        HAL_Delay(2000);
		memset(LCD_SDRAM_BUFF,0x07E0,sizeof(LCD_SDRAM_BUFF));
        HAL_Delay(2000);
		
  }

点灯带师,启动。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值