【STM32H7教程】如何使用QSPI驱动ST77916屏幕

前言

最近要驱动一个qspi的tft手表屏幕,之前在cubemx上能完美驱动nand flash的qspi配置步骤却突然不好使了,感觉这里面有很多问题,特此记录一下。

问题

1.flash_size要手动配置

最近使用qspi被一个问题卡了几天。就是不设置qspi的flash_size的话,qspi发送地址就会出错,比如当flash_size为1时 地址只能为0(不管位宽多少)。可能是发送时要检查地址超出了flash_size没有,所以你的地址有多少位就要设置flash_size为多少,比如24位地址就设置24。设置如下图所示。
在这里插入图片描述

2.逻辑分析仪一定要接mcu地

在观察qspi波形时发现一次传输中间cs引脚短暂拉高了(非常短比一个bit周期短很多),以为是哪里配置有问题,刚开始觉得是sample shifting的配置有问题,结果不是。最后把逻辑分析仪地接上mcu了才解决,看来就算逻辑分析仪和mcu共享usb接口的地也要额外接上地,不然还是有毛刺。
在这里插入图片描述

3.无法发出的命令

如果一个命令无需发数据(len==0),就必须配置cmd_cfg.DataMode = QSPI_DATA_NONE来关闭数据通路,否则整个命令都无法发出。

4.超时问题

发送数据很长的时候,就卡在while (HAL_QSPI_GetState(&hqspi) != HAL_QSPI_STATE_READY);这里是判断上一个qspi数据发送完毕没有的,感觉可能是上一个发送的数据太长超时了,还有很多数据没发完。我代码是这样写的HAL_QSPI_Transmit(&hqspi, (void*)pbuf, 100);于是就改成了(HAL_QSPI_Transmit(&hqspi, (void*)pbuf, HAL_MAX_DELAY);)这样延时很长,卡住的问题马上就好了。

5.四线模式不能用于配置

刚开始我固定了在一个程序里面要么单线要么四线,结果四线双线始终启动不了屏幕,只有单线可以启动。我看到屏幕官方代码的配置代码是单线的,于是只把屏幕刷新做成四线的,LCD_init里面用的单线spi,结果马上就可以了。刚开始以为是线的问题,看来是屏幕的寄存器只能用单线。当然四线qspi的指令和地址还是单线传输的。下面是配置不同线数的代码,在发送cmd前设置。只要配置指令和数据线数就可以了,要使用不同的线数要用不同的指令(02h,A2h,32h)

	if( QSbit==1)
    cmd_cfg.Instruction = 0x02;// 单线写指令字节
	else if(QSbit==2)
    cmd_cfg.Instruction = 0xA2;// 双线写指令字节
	else//QSbit==4
    cmd_cfg.Instruction = 0x32;// 四线写指令字节

   	if(QSbit==1)
   	cmd_cfg.DataMode = QSPI_DATA_1_LINE;//如果有Address要发 datamode千万不能为QSPI_DATA_NONE
   	else if(QSbit==2)
   	cmd_cfg.DataMode = QSPI_DATA_2_LINES;//如果有Address要发 datamode千万不能为QSPI_DATA_NONE
   	else//QSbit==4
   	cmd_cfg.DataMode = QSPI_DATA_4_LINES;//如果有Address要发 datamode千万不能为QSPI_DATA_NONE

问题6.DMA发送不能超过65536字节

在使用HAL_QSPI_Transmit_DMA函数发送数据的时候和HAL_QSPI_Transmit不同,HAL_QSPI_Transmit几乎不限制发送数据量,而HAL_QSPI_Transmit_DMA必须限制在65536字节内。这个问题卡了一天才发现是输入的len大于65536了。问题代码修改后如下所示。

blockRowCnt=90;//90是高度360的因数,保证最后一次发送也完整。同时90*360*2最接近65536的数了
lcd_tx_len = blockRowCnt*360*2;
qspi_lcd_wr_cmd(0x2C, lcd_tx_buf, lcd_tx_len);

问题7

如果用cubemx配置的其他外设占用了qspi的引脚比如cs引脚可能导致 qspi配置复位,使得flash_size复位为一并且DMA配置消失,进而导致出现硬件错误。进入如下错误处理函数。

void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */
  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

问题8.如果DMA从QSPI发不出去

那可能就是qspi global interrupt没勾选,如下图勾选就可以了
在这里插入图片描述

代码

发送和接收代码

要启动一个qspi传输首先要新建一个命令类型cmd_cfg,设置好参数后就用HAL_QSPI_Command发送命令出去,如果是写数据后续还要用HAL_QSPI_Transmit发送数据出去,如果是读数据还要用HAL_QSPI_Receive接收。无论发送还是接收数据长度都是cmd_cfg.NbData定义的。

void qspi_lcd_wr_cmd(uint8_t cmd, uint8_t *pbuf, uint32_t len)
{
    QSPI_CommandTypeDef cmd_cfg = {0};
    while (HAL_QSPI_GetState(&hqspi) != HAL_QSPI_STATE_READY);//确定上个传输完成

    cmd_cfg.Instruction = 0x02;// 指令字节
    cmd_cfg.InstructionMode = QSPI_INSTRUCTION_1_LINE;//指令用了几根线

    cmd_cfg.Address = cmd<<8;    // 0x00前缀 = 写寄存器 address不为0可能卡住
    cmd_cfg.AddressSize = QSPI_ADDRESS_24_BITS;//地址位数
    cmd_cfg.AddressMode = QSPI_ADDRESS_1_LINE;//地址用了几根线
    cmd_cfg.DummyCycles = 0;//空闲位数
    cmd_cfg.NbData = len;//后续数据长度
    if(len!=0)
    cmd_cfg.DataMode = QSPI_DATA_1_LINE;//如果有Address要发 datamode千万不能为QSPI_DATA_NONE
    else
    cmd_cfg.DataMode = QSPI_DATA_NONE;//如果NbData为0 datamode千万不能为QSPI_DATA_NONE	否则这次传输无法启动
    cmd_cfg.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;//每次调用HAL_QSPI_Command都要发指令


    HAL_QSPI_Command(&hqspi, &cmd_cfg, 100);//发送指令和地址
    HAL_QSPI_Transmit(&hqspi, (void*)pbuf, 100);//发送数据 长度在cmd_cfg定义了
}
void qspi_lcd_rd_reg(uint8_t cmd, uint8_t *pbuf, uint32_t len)
{
    QSPI_CommandTypeDef cmd_cfg = {0};
    while (HAL_QSPI_GetState(&hqspi) != HAL_QSPI_STATE_READY);//确定上个传输完成

    cmd_cfg.Instruction = 0x0b;// 指令字节
    cmd_cfg.InstructionMode = QSPI_INSTRUCTION_1_LINE;//指令用了几根线

    cmd_cfg.Address = cmd<<8;    // 0x00前缀 = 写寄存器 address不为0可能卡住
    cmd_cfg.AddressSize = QSPI_ADDRESS_24_BITS;//地址位数
    cmd_cfg.AddressMode = QSPI_ADDRESS_1_LINE;//地址用了几根线
    cmd_cfg.DummyCycles = 0;//空闲位数
    cmd_cfg.NbData = len;//后续数据长度
    if(len!=0)
    cmd_cfg.DataMode = QSPI_DATA_1_LINE;//如果有Address要发 datamode千万不能为QSPI_DATA_NONE
    else
    cmd_cfg.DataMode = QSPI_DATA_NONE;//如果NbData为0 datamode千万不能为QSPI_DATA_NONE	否则这次传输无法启动
    cmd_cfg.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;//每次调用HAL_QSPI_Command都要发指令

    HAL_QSPI_Command(&hqspi, &cmd_cfg, 100);//发送指令和地址
    HAL_QSPI_Receive(&hqspi, (void*)pbuf, 100);//发送数据 长度在cmd_cfg定义了
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐