使用DMA+SPI驱动Aliyun Things 上的ST7789H2 LCD屏幕

2023-05-16

目录

  • 前言
  • 硬件
  • CUBEMX
      • 时钟树
      • GPIO
      • SPI
  • 代码部分
      • LCD驱动
      • 中断服务函数
      • 测试代码
      • 现象

前言

1:驱动程序参考自https://blog.csdn.net/BearPi/article/details/104311705.:
2:这是我的一个记录,实现的功能不多,只是将在内存中开辟的一片显存通过DMA的方式搬运到屏幕上。

硬件

在这里插入图片描述
MCU:STM32L496VGT6
屏幕:ST7789H2驱动的LCD屏幕,大小为240*240,色深2B,通过SPI1硬件方式连接。
在这里插入图片描述
查阅开发版=板原理图,相关的硬件连接如下,这里板子只设计了发送引脚,我们不可以从屏幕上读回数据

名称引脚
LCD_RSTPB2
LCD_DCPA6
LCD_PWRPE7
LCD_NSSPA4
SCLKPA5
DOPA7
LEDPB6

CUBEMX

时钟树

这里将主频设置的非常低,目的是观察DMA传输过程。但也不要太低,太低会导致屏幕不工作
在这里插入图片描述

GPIO

初始化一些我们需要控制的引脚
注意:片选引脚LCD_NSS通过硬件控制,不在这里初始化。
在这里插入图片描述

SPI

单向主模式,硬件片选,8bit传输
在这里插入图片描述
打开SPI传输中断。
在这里插入图片描述
添加SPI DMA传输通道
在这里插入图片描述
SPI引脚复用配置,按照原理图配
在这里插入图片描述
到此为止,硬件配置完成。

代码部分

LCD驱动

ST7789.c


static void LCD_reset(void)
{
	/* 复位LCD */
    LCD_RST(0);
    HAL_Delay(100);
    LCD_RST(1);
}
static void LCD_Write_Cmd(uint8_t cmd)
{
    LCD_WR_RS(0);
    HAL_SPI_Transmit(&hspi1,&cmd,1,1000);
}

static void LCD_Write_Data(uint8_t dat)
{
    LCD_WR_RS(1);
     HAL_SPI_Transmit(&hspi1,&dat,1,1000);
}


static void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    /* 指定X方向操作区域 */
    LCD_Write_Cmd(0x2a);
    LCD_Write_Data(x1 >> 8);
    LCD_Write_Data(x1);
    LCD_Write_Data(x2 >> 8);
    LCD_Write_Data(x2);

    /* 指定Y方向操作区域 */
    LCD_Write_Cmd(0x2b);
    LCD_Write_Data(y1 >> 8);
    LCD_Write_Data(y1);
    LCD_Write_Data(y2 >> 8);
    LCD_Write_Data(y2);

    /* 发送该命令,LCD开始等待接收显存数据 */
    LCD_Write_Cmd(0x2C);
}
/**
 * @brief   以一种颜色清空LCD屏
 * @param   color —— 清屏颜色(16bit)
 * @return  none
 */
void LCD_Fill_first_half(uint8_t *data)
{
    /* 指定显存操作地址为全屏*/
    LCD_Address_Set(0, 0, LCD_Width - 1, LCD_Height/2 - 1);
    LCD_WR_RS(1);/* 指定接下来的数据为数据 */
	/* 写前半屏*/

	HAL_SPI_Transmit_DMA(&hspi1,data, LCD_RAM_SIZE/2);
}
void LCD_Fill_last_half(uint8_t *data)
{
    /* 指定显存操作地址为全屏*/
    LCD_Address_Set(0, LCD_Height/2, LCD_Width - 1, LCD_Height - 1);
    LCD_WR_RS(1);/* 指定接下来的数据为数据 */
    /*写后半屏*/
    HAL_SPI_Transmit_DMA(&hspi1,data+LCD_RAM_SIZE/2, LCD_RAM_SIZE/2);
}

void LCD_Init(void)
{
	/*关闭显示*/
    LCD_PWR(0);
    /* 初始化和LCD通信的引脚 */
    LCD_reset();
    HAL_Delay(120);
    /* 关闭睡眠模式 */
    LCD_Write_Cmd(0x11);
    HAL_Delay(120);

    /* 开始设置显存扫描模式,数据格式等 */
    LCD_Write_Cmd(0x36);
    LCD_Write_Data(0x00);
    /* RGB 5-6-5-bit格式  */
    LCD_Write_Cmd(0x3A);
    LCD_Write_Data(0x65);
    /* porch 设置 */
    LCD_Write_Cmd(0xB2);
    LCD_Write_Data(0x0C);
    LCD_Write_Data(0x0C);
    LCD_Write_Data(0x00);
    LCD_Write_Data(0x33);
    LCD_Write_Data(0x33);
    /* VGH设置 */
    LCD_Write_Cmd(0xB7);
    LCD_Write_Data(0x72);
    /* VCOM 设置 */
    LCD_Write_Cmd(0xBB);
    LCD_Write_Data(0x3D);
    /* LCM 设置 */
    LCD_Write_Cmd(0xC0);
    LCD_Write_Data(0x2C);
    /* VDV and VRH 设置 */
    LCD_Write_Cmd(0xC2);
    LCD_Write_Data(0x01);
    /* VRH 设置 */
    LCD_Write_Cmd(0xC3);
    LCD_Write_Data(0x19);
    /* VDV 设置 */
    LCD_Write_Cmd(0xC4);
    LCD_Write_Data(0x20);
    /* 普通模式下显存速率设置 60Mhz */
    LCD_Write_Cmd(0xC6);
    LCD_Write_Data(0x0F);
    /* 电源控制 */
    LCD_Write_Cmd(0xD0);
    LCD_Write_Data(0xA4);
    LCD_Write_Data(0xA1);
    /* 电压设置 */
    LCD_Write_Cmd(0xE0);
    LCD_Write_Data(0xD0);
    LCD_Write_Data(0x04);
    LCD_Write_Data(0x0D);
    LCD_Write_Data(0x11);
    LCD_Write_Data(0x13);
    LCD_Write_Data(0x2B);
    LCD_Write_Data(0x3F);
    LCD_Write_Data(0x54);
    LCD_Write_Data(0x4C);
    LCD_Write_Data(0x18);
    LCD_Write_Data(0x0D);
    LCD_Write_Data(0x0B);
    LCD_Write_Data(0x1F);
    LCD_Write_Data(0x23);
    /* 电压设置 */
    LCD_Write_Cmd(0xE1);
    LCD_Write_Data(0xD0);
    LCD_Write_Data(0x04);
    LCD_Write_Data(0x0C);
    LCD_Write_Data(0x11);
    LCD_Write_Data(0x13);
    LCD_Write_Data(0x2C);
    LCD_Write_Data(0x3F);
    LCD_Write_Data(0x44);
    LCD_Write_Data(0x51);
    LCD_Write_Data(0x2F);
    LCD_Write_Data(0x1F);
    LCD_Write_Data(0x1F);
    LCD_Write_Data(0x20);
    LCD_Write_Data(0x23);
    /* 显示开 */
    LCD_Write_Cmd(0x21);
    LCD_Write_Cmd(0x29);

    /*打开显示*/
    LCD_PWR(1);
}


ST7789.h

#ifndef __ST7789_H
#define __ST7789_H
#include "main.h"
#include "spi.h"

#define	LCD_PWR(n)		(n?\
						HAL_GPIO_WritePin(LCD_PWR_GPIO_Port,LCD_PWR_Pin,GPIO_PIN_SET):\
						HAL_GPIO_WritePin(LCD_PWR_GPIO_Port,LCD_PWR_Pin,GPIO_PIN_RESET))
#define	LCD_WR_RS(n)	(n?\
						HAL_GPIO_WritePin(LCD_DCX_GPIO_Port,LCD_DCX_Pin,GPIO_PIN_SET):\
						HAL_GPIO_WritePin(LCD_DCX_GPIO_Port,LCD_DCX_Pin,GPIO_PIN_RESET))
#define	LCD_RST(n)		(n?\
						HAL_GPIO_WritePin(LCD_RST_GPIO_Port,LCD_RST_Pin,GPIO_PIN_SET):\
						HAL_GPIO_WritePin(LCD_RST_GPIO_Port,LCD_RST_Pin,GPIO_PIN_RESET))


//LCD屏幕分辨率定义
#define LCD_Width   240
#define LCD_Height  240
#define LCD_RAM_SIZE LCD_Width*LCD_Height*2		//长240 宽240 色深2bit
#define Pixel_NUM (LCD_RAM_SIZE/2)

void LCD_Init(void);
void LCD_Fill_first_half(uint8_t *data);
void LCD_Fill_last_half(uint8_t *data);

#endif

大家能看到我这里将一帧图像分成了两半传输,分别使用 LCD_Fill_first_half 和 LCD_Fill_last_half 分开传输。这样做的原因是hal库函数里,HAL_SPI_Transmit_DMA的Size参数是使用uint16_t 定义的,最大支持6万5左右。而LCD_RAM_SIZE = LCD_WidthLCD_Height2 = 240*24-*2=115200,11万多,超出范围了。所以,掰两半传输就可以解决了。

HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;
  /* Check tx dma handle */
  assert_param(IS_SPI_DMA_HANDLE(hspi->hdmatx));
  /* Check Direction parameter */
  assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));
  /* Process Locked */
  __HAL_LOCK(hspi);
  .........
}

中断服务函数

我们已经开启了DMA传输,spi传输完成的时候触发一次中断,通知CPU传输完成。这里我们再spi.c的末尾重写一下HAL_SPI_TxCpltCallback。one_frame_done 是一个全局变量,标识传输完成。

volatile uint8_t one_frame_done;
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
	one_frame_done = 1;
}

测试代码

main.c中,首先定义两个色块,引用one_frame_done

/* USER CODE BEGIN PV */
volatile uint8_t data1[LCD_RAM_SIZE];
volatile uint8_t data2[LCD_RAM_SIZE];
extern volatile uint8_t one_frame_done;
/* USER CODE END PV */

然后编写测试程序

int main(void)
{
	uint16_t color = 0;
	HAL_Init();
	SystemClock_Config();
	MX_GPIO_Init();
	MX_DMA_Init();
	MX_SPI1_Init();
	LCD_Init();
	//初始化两个色块
	
	for(uint16_t j = 0; j < Pixel_NUM; j++)
    {//黄色
        data1[j * 2] = (uint8_t)(0x3333 >> 8);
        data1[j * 2 + 1] =  (uint8_t)(0x3333);
    }
	for(uint16_t j = 0; j < Pixel_NUM; j++)
    {//蓝色
        data2[j * 2] = (uint8_t)(0xeeee >> 8);
        data2[j * 2 + 1] =  (uint8_t)(0xeeee);
    }
	while (1)
	{
		/*显示第一帧*/
		one_frame_done = 0;
		LCD_Fill_first_half((uint8_t *)data1);
		while(!one_frame_done){/*release cpu and doing something else*/}
		one_frame_done = 0;
		LCD_Fill_last_half((uint8_t *)data1);
		while(!one_frame_done){/*release cpu and doing something else*/}	
		
		/*显示第二帧*/
		one_frame_done = 0;
		LCD_Fill_first_half((uint8_t *)data2);
		while(!one_frame_done){/*release cpu and doing something else*/}
		one_frame_done = 0;
		LCD_Fill_last_half((uint8_t *)data2);
		while(!one_frame_done){/*release cpu and doing something else*/}	
		
		HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
	}
}

现象

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用DMA+SPI驱动Aliyun Things 上的ST7789H2 LCD屏幕 的相关文章

  • day3. -2 NX的SPI操作

    1 NVIDIA在线文档 https docs nvidia com jetson l4t index html page Tegra 20Linux 20Driver 20Package 20Development 20Guide hw
  • STM32 DMA正常模式等待传输完成和开始下一次传输

    选择DMA的正常模式 xff0c 即DMA只传输一次 如果当传输完一次后 xff0c 还想再传输一次 xff0c 就需要重启DMA xff1a DMA Cmd DMA1 Channel6 DISABLE 重新设置源地址 重新设置目的地址 重
  • SPI通讯协议介绍

    来到SPI通讯协议了 废话两句 xff0c SPI很重要 xff0c 这是我在学校时候听那些单片机开发工程师说的 出来实习 xff0c 到后来工作 xff0c 确实如此 xff0c SPI的使用很常见 xff0c 那么自然重要咯 SPI S
  • AXI DMA总结、内核axidmatest.c测试程序分析、SG mode

    AXI DMA 概述 xff1a XILINX提供的AXI DMA支持Scatter Gather mode和Direct Register mode 数据位宽支持32 64 128 256 512 1024bits xff0c strea
  • rk3399下spi驱动

    SPI 使用 Note xff1a 本文从firefly wiki截取 SPI是一种高速的 xff0c 全双工 xff0c 同步串行通信接口 xff0c 用于连接微控制器 传感器 存储设备等 Firefly RK3399 开发板提供了 SP
  • 串口通信协议 UART+I2C+SPI

    UART 异步 串行 全双工 I2C SPI 不同通信协议比较 UART UART协议详解 UART通信 xff0c 接收与发送 xff08 详细版 xff0c 附代码 xff09 UART串行通信详解 待整理 UART是Universal
  • 轮询、中断和DMA三种方式的原理和联系

    CPU控制外部设备的方式 xff1a 中断 轮询 DMA 轮询中断DMA 由于外部设备的速度差异 xff0c CPU可以使用不同的方式控制外部设备的访问 常见的方式轮询 中断和DMA 轮询 轮询最简单 xff0c CPU通过不断地查询某个外
  • UART,SPI,IIC,RS232通信时序和规则

    一 UART 1 串口通信方式 2 串口通信步骤 注意 xff1a 串口协议规定 xff0c 闲置时必须是高电平 校验位 xff1a 是使用奇偶校验 停止位必须高电平 一个0和多个0区分是靠掐时间 异步通信 xff1a 时钟各不一样 二 I
  • 16. GD32F103C8T6入门教程-adc 使用教程2-dma+连续扫描方式采集数据

    adc 使用教程2 dma 连续扫描方式采集数据 adc 的扫描模式就是把配置了规则或注入通道按照配置的顺序采集一轮 adc 的连续转换模式就是把配置了规则或注入通道按照配置的顺序采集N轮 注意 dma使用时存在一个外设映射到一个dam外设
  • Jlink使用技巧之烧写SPI Flash存储芯片

    文章目录 前言 准备 硬件连接 1 打开 2 连接SPI Flash芯片 3 打开程序文件 4 下载 5 程序文件的读取 6 程序文件的保存 7 命令行工具的使用 支持的芯片列表 速度说明 参考资料 JLink软件的下载 前言 大多数玩单片
  • 沁恒CH32V307使用记录:SPI基础使用

    文章目录 目的 基础说明 使用演示 其它补充 总结 目的 SPI是单片机中比较常用的一个功能 这篇文章将对CH32V307中相关内容进行说明 本文使用沁恒官方的开发板 CH32V307 EVT R1沁恒RISC V模块MCU赤兔评估板 进行
  • SPI技术-JDK实现

    SPI是什么 SPI全称Service Provider Interface 是Java提供的一套用来被第三方实现或者扩展的API 它可以用来启用框架扩展和替换组件 Java SPI 实际上是 基于接口的编程 策略模式 配置文件 组合实现的
  • STM32 进阶教程 15 - 串口DMA收发

    前言 串口操作相信大家一定很熟悉 如果你已经会串口的收发数据 并可以灵活使用轮询及中断方式对串口进行数据收发 那么恭喜你 学完本节内容后 也将可以学会串口的更高级操作方式 DMA方式 DMA操作串口可以大大减轻MCU的负担 同时也可以加快数
  • STM32F1应用DMA——串口收发不定长数据

    STM32F1应用DMA 串口收发不定长数据 使用STM32自带DMA传输数据 可以减轻CPU负担 只需设置一些参数即可发送想要发送的数据 以下是STM32F1系列芯片测试过的部分代码 可实现DMA串口收发数据 下图来自STM32官网的手册
  • FPGA驱动0.96oled显示屏 (4线 SPI) verilog语言

    之前也陆陆续续看了很多博客 也都能在自己的屏幕上显示出来 但是问题就是不知道怎么修改代码显示自己希望显示的东西 而且由于没注释原因看不太懂 最终的实现效果最终实现效果视频 b站视频链接1 评论区有人给了源码的百度网盘链接 csdn博客链接1
  • SpringBoot系列--自定义Starter

    提到Spring Boot时 很多人想到的是它的自动化装配特性 当我们项目需要Redis MongoDB时 只需要引入相应的 spring boot starter data redis spring boot starter data m
  • 各种通信接口的简单对比

    对比表 同步方式与异步方式的主要区别在于 是否传输时钟信号 只要是通訊前雙方需要設定相同波特率的 都是異步傳輸方式 异步传输 Asynchronous Transmission 每次异步传输的信息都以一个起始位开头 它通知接收方数据已经到达
  • Linux SPI 总线 和设备驱动架构之三:SPI控制器驱动

    通过第一篇文章 我们已经知道 整个SPI驱动架构可以分为协议驱动 通用接口层和控制器驱动三大部分 其中 控制器驱动负责最底层的数据收发工作 为了完成数据收发工作 控制器驱动需要完成以下这些功能 1 申请必要的硬件资源 例如中断 DMA通道
  • Arm 板上有两个以上的 SPI 设备,但只支持两个?

    我们其中一块板上的 Arm 处理器有一个 spi 端口 带有两条片选线 该处理器的数据表中提到它最多可以控制两个 spi 设备 是否可以使用 GPIO 作为附加 spi 设备的从选择 如何修改现有的库 设备驱动程序以支持此更改 到目前为止
  • Python - 如何使用 ioctl 或 spidev 从设备读取输入?

    我有一个提供它的供应商提供了一些 C 代码来监听按钮按下 该代码使用ioctl 该设备具有 SSD1289 控制器 按钮不需要额外的引脚 可以通过 SPI 读取其状态 这就是我想要的 读取按下了哪个按钮 我试图在 Python 中为我自己的

随机推荐

  • 记录复现“VIBE”遇见的BUG

    docker配置 xff1a ubuntu18 01 xff0c cuda10 1 输入视频测试demo时 xff0c 依次遇见下列问题 第一个问题 39 EGL span class token punctuation span cann
  • 软件专业大学生常用的刷题、找答案、找资源的网站

    接下来介绍的主要是结合个人和身边同学经常使用的几个网站 xff0c 肯定有很多不全 xff0c 欢迎大佬们查漏补缺 刷题 一 牛客网 xff08 https www nowcoder com xff09 牛客网 是一个专注于程序员的学习和成
  • Linux设备驱动

    Linux设备驱动 介绍 Linux系列文章记录自己学习Linux开发的历程 最近终于把迅为4412开发环境搞定了 xff0c 先吐槽一波迅为 xff0c 版本太 的多了 xff0c 安卓Linux搞在一起 xff0c 还有那个硬件原理图
  • Linux 磁盘坏块修复处理(错误:read error: Input/output error)

    当磁盘出现坏块时 xff0c 你对所关联的文件进行读取时 xff0c 一般会出现 read error Input output error 这样的错误 反过来讲 xff0c 当你看到 read error Input output err
  • docker给运行中的容器添加端口映射

    问题描述 docker上面运行容器 xff0c run p 的时候只映射了一个端口 xff0c 后面对该nginx做扩展 xff0c 需要开放其他端口 当然重新再启一个容器在docker启动参数里多配置几个 p端口映射是能解决的 xff0c
  • Git常用命令符

    1 强制推送 xff08 慎用 xff0c 除非你认为其他冲突等可以丢弃 或者不是很重要 xff09 git push force 2 创建文件等小命令 touch a 创建一个a文件 echo 1234 gt gt a 把1234这个内容
  • 全网最全的 LeetCode 国人大神刷题指南,全部 Go 语言实现

    大家好 xff0c 我是欧盆索思 xff08 opensource xff09 xff0c 每天为你带来优秀的开源项目 xff01 说到 LeetCode xff0c 作为一个程序员来说 xff0c 应该不陌生 xff0c 近几年参加面试都
  • ROS实现串口通信

    虚拟串口的搭建 参考Linux下添加虚拟串口 xff0c 接收和发送数据 com py文件代码如下 xff1a span class token comment usr bin env python span span class toke
  • 基于Linux的UART驱动框架源码分析笔记

    文章目录 前言一 I MX6ULL串口接收和发送方式1 非DMA方式1 1 接收方式1 2 发送方式 2 DMA方式2 1 接收方式2 2 发送方式 二 UART驱动注册1 uart register driver 函数解析2 serial
  • cmake install 命令

    install指令用于定义安装规则 xff0c 安装的内容可以包括目标二进制 动态库 静态库以及文件 目录 脚本等 需要引入一个新的cmake指令和一个非常有用的变量 cmake install prefix 法一 xff1a cmake
  • 基于全景相机的视觉里程计算法研究

    一 视觉里程计 视觉里程计技术首先建立相机的成像模型 xff0c 接着通过标定算法计算相机参数 xff0c 最后建立相邻图像的关联并估计相机运动轨迹 1 1相机在空间中运动的描述 描述相机在三维空间中的运动状态 xff0c 即求解相机在空间
  • 多旋翼无人机组成(小白上路)

    1 无人机组成 1 1 机架 四旋翼最常见的两种机身布局如下图 xff1a 机架指无人机的承载平台 xff0c 通常用轴距衡量机架的大小 xff0c 轴距是指对角线两个螺旋桨的距离 xff0c 一般以mm为单位 xff0c 如F330表示轴
  • freertos创建任务后进入prvStartFirstTask发生HardFault_Handler中断

    在stm32f103zet6环境中移植成功freertos之后 xff0c 创建第一个任务之后 xff0c 会进入硬件中断 xff0c 经过排查发现死在了prvStartFirstTask 排查原因 xff1a 发现是启动文件startup
  • 解决无法对docker容器进行端口映射的问题

    初学docker的时候 xff0c 不知道为啥 xff0c 按着教程里打的代码 xff0c 最后却出现了映射失败的情况 即 xff1a 在docker内部设置的映射端口 xff0c 外部却没有办法访问 想了想 xff0c 不外乎两个原因 x
  • K8s手工创建kubeconfig

    我们通过 kubectl 命令行连接 k8s apiserver 时需要依赖 kubeconfig 文件 kubeconfig 文件通常包含了 context xff08 上下文 xff09 列表 xff0c 每个 context 又会引用
  • grep命令总结

    grep命令总结 1 关于 nbsp ps ef grep php grep v grep wc l grep v grep 代表在查询的最终结果中去掉grep命令本身 wc l 标示统计查询到的结果数量 grep常用命令 1 grep n
  • Ubuntu 16.04安装realsense D435i SDK以及realsense-ros

    先直接上一个报错信息 xff0c 折腾了半天才解决 在使用catkin make编译realsense ros时 xff0c 报错 traversing 4 packages in topological order realsense c
  • 关于视觉SLAM的一些常识(纯小白学习笔记)

    本文只是小白对于视觉slam的一个非常泛的介绍 xff0c 对于视觉slam中的数学运算均没有提及 xff0c 适合于对没有接触过视觉slam的新人进行一个简单的科普 作者即小白 xff0c 文章如有错误 xff0c 非常非常非常欢迎指正
  • 使用CubeMX快速搭建FREERTOS

    如何使用STM32快速搭建FREERTOS 小编之前一直使用正点原子家的产品 xff0c 最近准备学习学习TOUCHGFX 要用到HAL 43 RTOS 原子家的使用起来不方便 于是琢磨着使用STM32CUBEMX直接生成FREERTOS
  • 使用DMA+SPI驱动Aliyun Things 上的ST7789H2 LCD屏幕

    目录 前言硬件CUBEMX时钟树GPIOSPI 代码部分LCD驱动中断服务函数测试代码现象 前言 1 xff1a 驱动程序参考自https blog csdn net BearPi article details 104311705 2 x