在全志XR806上移植st7789屏幕驱动

news2025/1/29 8:50:42

前言

很高兴有机会参加本次极术社区举办的“「免费试用」搭载安谋科技STAR-MC1的全志XR806 Wi-Fi+BLE 开发板试用活动”。
去年就对全志的mcu芯片感兴趣了,一直没有机会接触,看到本次极术社区提供的全志wifi + BLE开发板试用,就马上参加了。板子拿到手之后,很快就搭建好了环境,由于自己时间安排的问题,一直没有空搞,这两天赶紧搞了一下。

SDK下载和环境搭建
git clone https://sdk.aw-ol.com/git_repo/XR806/xr806_sdk/xr806_sdk.git -b xr806_sdk

如果提示 Username for ‘https://sdk.aw-ol.com’: 请输入 全志在线开发者论坛 的用户名和密码。(注:需要全志在线开发者论坛LV2等级以上用户才有权限拉取 SDK,随便注册个账户,灌灌水就到了)

由于 SDK 普遍较大,拉取可能需要一定的时间。

接下来安装环境依赖(我用Buildroot的docker容器,都装过了,就不需要再搞了)

sudo apt-get install build-essential subversion git libncurses5-dev zlib1g-dev gawk flex bison quilt libssl-dev xsltproc libxml-parser-perl mercurial bzr ecj cvs unzip lsof kconfig-frontends android-tools-mkbootimg python2 libpython3-dev gcc-multilib libc6:i386 libstdc++6:i386 lib32z1

然后配置工具链,直接下载gcc-arm-none-eabi-8-2019-q3-update-linux.tar.bz2 压缩包并解压缩到~/.bin目录下,并修改gcc.mk文件

# ----------------------------------------------------------------------------
# cross compiler
# ----------------------------------------------------------------------------
CC_DIR := /home/vuser/.bin/gcc-arm-8.3/bin/
CC_PREFIX := ccache $(CC_DIR)/arm-none-eabi-

这样就配置好了。看了一下project/example目录下,有个spi工程,适合拿来修改,就是它了。

移植st7789驱动

ST7789是一款高度集成的彩色TFT液晶显示屏控制器芯片,通常用于驱动小到中等尺寸的液晶屏。例如淘宝上常见的1.4寸、1.47寸、1.69寸屏幕等等。
现在就开始吧,首先新建st7789.c和st7789.h文件。然后创建用于初始化st7789芯片的命令序列表。

static lcd_init_cmd_t st7789_init_cmds[] = {
    {0x01, {0}, 0x80, 120},
    /* Sleep Out */
    {0x11, {0}, 0x80, 120},
    /* Memory Data Access Control, MX=MV=1, MY=ML=MH=0, RGB=0 */
    {0x36, {0x00}, 1},
    /* Interface Pixel Format, 16bits/pixel for RGB/MCU interface */
    {0x3A, {0x05}, 1},
#if 0
      {0x30, {0x00,0x50,0x01,0x3F}, 4},
      {0x12, {0x00}, 0},
#endif
    /* Porch Setting */
    {0xB2, {0x0c, 0x0c, 0x00, 0x33, 0x33}, 5},
    /* Gate Control, Vgh=13.65V, Vgl=-10.43V */
    {0xB7, {0x35}, 1},
    /* VCOM Setting, VCOM=1.35V */
    {0xBB, {0x32}, 1},
    // /* LCM Control, XOR: BGR, MX, MH */
    // {0xC0, {0x2C}, 1},
    /* VDV and VRH Command Enable, enable=1 */
    {0xC2, {0x01, 0xFF}, 2},
    /* VRH Set, Vap=4.4+... */
    {0xC3, {0x15}, 1},
    /* VDV Set, VDV=0 */
    {0xC4, {0x20}, 1},
    /* Frame Rate Control, 60Hz, inversion=0 */
    {0xC6, {0x0F}, 1},
    /* Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V */
    {0xD0, {0xA4, 0xA1}, 1},
    /* Positive Voltage Gamma Control */
    {0xE0,
     {0xD0, 0x08, 0x0E, 0x09, 0x09, 0x05, 0x31, 0x33, 0x48, 0x17, 0x14, 0x15,
      0x31, 0x34},
     14},
    /* Negative Voltage Gamma Control */
    {0xE1,
     {0xD0, 0x08, 0x0E, 0x09, 0x09, 0x15, 0x31, 0x33, 0x48, 0x17, 0x14, 0x15,
      0x31, 0x34},
     14},
    /* Display On */
    {0x21, {0}, 0},
    {0x29, {0}, 0},
    {0, {0}, 0xff}};

这个序列表使用的是这样的数据结构

/*The LCD needs a bunch of command/argument values to be initialized. They are
 * stored in this struct. */
typedef struct {
  # 指令
  uint8_t cmd;
  # 数据
  uint8_t data[16];
  # 数据长度和类型,一般初始化数据不会很长,使用部分做其他用。
  # 例如 0x80代表需要延时,延时时间由delaytime指定,0xFF代表结束
  uint8_t databytes; // No of data in data; bit 7 = delay after set; 0xFF =
                     // end of cmds.
  uint8_t delaytime; // delaytime
} lcd_init_cmd_t;

然后编写初始化函数,这里把gpio和spi的初始化都放在里面了。

  printf("ST7789 initialization.\n");

  int ret = dirver_spi_init();
  if (ret != 0) {
    printf("SPI dev init fail!\n");
  }

  gpio_init(disp_pin_dc);
  gpio_init(disp_pin_rst);
  gpio_init(disp_pin_bckl);

  HAL_SPI_CS(DEMO_SPI_PORT, 1);
  // Reset the displayc
  gpio_set_level(disp_pin_rst, 1);
  OS_MSleep(1);
  gpio_set_level(disp_pin_rst, 0);
  OS_MSleep(100);
  gpio_set_level(disp_pin_rst, 1);
  OS_MSleep(100);

  st7789_enable_backlight(true);
  OS_MSleep(100);

  // Send all the commands
  uint16_t cmd = 0;
  while (st7789_init_cmds[cmd].databytes != 0xff) {
    printf("Send command 0x%02x\n", st7789_init_cmds[cmd].cmd);
    st7789_send_cmd(st7789_init_cmds[cmd].cmd);
    if ((st7789_init_cmds[cmd].databytes & 0x1F) != 0) {
      st7789_send_data(st7789_init_cmds[cmd].data,
                       st7789_init_cmds[cmd].databytes & 0x1F);
    }
    if (st7789_init_cmds[cmd].databytes & 0x80) {
      OS_MSleep(st7789_init_cmds[cmd].delaytime);
    }
    cmd++;
  }
  printf("init finish.\n");
  st7789_set_orientation(DISPLAY_ORIENTATION);

硬件连接如图所示
在这里插入图片描述

屏幕开发板
BLKB14
CSB06
DCB03
RESVCC
SDAB04
SCLB07

为什么RES引脚直接接的VCC,因为不知道是这个芯片的问题还是什么问题。RES引脚接到推挽输出的IO引脚后,屏幕也能点亮,但是亮度莫名其妙很低。手上几个屏都测试了一下,都这样。

然后就是编写一下写命令和写数据的函数,写命令时需要设置一下DC引脚,然后写完立即将DC引脚切换回高电平。

static void st7789_send_cmd(uint8_t cmd) {
  gpio_set_level(disp_pin_dc, 0);
  dirver_spi_send_data(&cmd, 1);
  gpio_set_level(disp_pin_dc, 1);
}
static void st7789_send_data(void *data, uint16_t length) {
  dirver_spi_send_data(data, length);
}

然后就是编写屏幕翻转配置函数

static void st7789_set_orientation(uint8_t orientation) {
  // ESP_ASSERT(orientation < 4);

  const char *orientation_str[] = {"PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE",
                                   "LANDSCAPE_INVERTED"};

  printf("Display orientation: %s\n", orientation_str[orientation]);

  uint8_t data[] = {0xC0, 0x00, 0x60, 0xA0};

  printf("0x36 command value: 0x%02X\n", data[orientation]);

  st7789_send_cmd(ST7789_MADCTL);
  st7789_send_data((void *)&data[orientation], 1);
}

最后再写一下写屏函数即可,这里为了快速刷屏,设置了比较大的缓存区。目前还不会使用XR806的DMA,学会了可以减少缓存RAM的大小。

/* The ST7789 display controller can drive 320*240 displays, when using a
 * 240*240 display there's a gap of 80px, we need to edit the coordinates to
 * take into account that gap, this is not necessary in all orientations. */
void st7789_flush(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2,
                  uint16_t color) {
  uint8_t data[4] = {0};

  uint16_t offsetx1 = x1;
  uint16_t offsetx2 = x2;
  uint16_t offsety1 = y1;
  uint16_t offsety2 = y2;

#if (TFT_DISPLAY_OFFSETS)
  offsetx1 += TFT_DISPLAY_X_OFFSET;
  offsetx2 += TFT_DISPLAY_X_OFFSET;
  offsety1 += TFT_DISPLAY_Y_OFFSET;
  offsety2 += TFT_DISPLAY_Y_OFFSET;

#elif (LV_HOR_RES_MAX == 320) && (LV_VER_RES_MAX == 320)
#if (DISPLAY_ORIENTATION_PORTRAIT)
  offsetx1 += 80;
  offsetx2 += 80;
#elif (DISPLAY_ORIENTATION_LANDSCAPE_INVERTED)
  offsety1 += 80;
  offsety2 += 80;
#endif
#endif

  /*Column addresses*/
  st7789_send_cmd(ST7789_CASET);
  data[0] = (offsetx1 >> 8) & 0xFF;
  data[1] = offsetx1 & 0xFF;
  data[2] = (offsetx2 >> 8) & 0xFF;
  data[3] = offsetx2 & 0xFF;
  st7789_send_data(data, 4);

  /*Page addresses*/
  st7789_send_cmd(ST7789_RASET);
  data[0] = (offsety1 >> 8) & 0xFF;
  data[1] = offsety1 & 0xFF;
  data[2] = (offsety2 >> 8) & 0xFF;
  data[3] = offsety2 & 0xFF;
  st7789_send_data(data, 4);

  /*Display On*/
  st7789_send_cmd(ST7789_DISPON);
  /*Memory write*/
  st7789_send_cmd(ST7789_RAMWR);

  uint32_t size = (y2 - y1) * (x2 - x1);

  uint32_t buffsize = (x2 - x1) * 80;
  unsigned char *burst_buffer = (unsigned char *)malloc(buffsize * 2);

  for (uint32_t i = 0; i < size + buffsize; i += buffsize) {
    for (uint32_t j = 0; j < buffsize; j++) {
      burst_buffer[2 * j] = color >> 8;
      burst_buffer[2 * j + 1] = color;
    }
    st7789_send_data(burst_buffer, buffsize * 2);
  }

  free(burst_buffer);
}

还需要添加一个刷屏函数作为测试,现在补一下。
由于1.69寸屏幕不需要设置屏幕窗口偏移量,就直接按满屏来刷了。

void lcd_clear(uint16_t color) { st7789_flush(0, 240, 0, 320, color); }

然后在main.c里调用屏幕初始化和刷屏函数就可以啦。

#include "common/framework/platform_init.h"
#include "kernel/os/os.h"
#include <stdio.h>

extern void st7789_init();
extern void st7789_flush(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2,
                         uint16_t color);
extern void lcd_clear(uint16_t color);

int main(void) {

  platform_init();

  printf("gpio demo started.\n");

  st7789_init();
  printf("flush color.\n");
  // st7789_flush(0, 240, 0, 280, 0xFFFF);

  while (1) {
    lcd_clear(0x0000);
    OS_MSleep(1000);
    lcd_clear(0xFFFF);
    OS_MSleep(1000);
    lcd_clear(0xEF5D);
    OS_MSleep(1000);
    lcd_clear(0xF800);
    OS_MSleep(1000);
    lcd_clear(0x07E0);
    OS_MSleep(1000);
    lcd_clear(0x001F);
    OS_MSleep(1000);
  }

  printf("never run here.\n");
  return 0;
}

# 清除错误用
void main_cmd_exec(char *cmd) {}

刷屏效果如图

在这里插入图片描述

经过测试,手上的1.47寸屏幕和1.69寸st7789屏幕都可以正常驱动。
就是偏移值和屏幕分辨率设置需要再优化一下代码,过几天再说吧。
详细的代码在文章末尾下载,放到example目录应该就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1191946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WebGL智慧城市软件项目

WebGL开发智慧城市项目时&#xff0c;需要考虑多个方面&#xff0c;包括技术、隐私、安全和可持续性。以下是一些需要注意的关键问题&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.隐私和数据安全…

6.3二叉树的层序遍历(LC102,LC107-M)

二叉树的层序遍历&#xff08;LC102&#xff09;&#xff1a; 算法&#xff08;长度法&#xff09;&#xff1a; 需要借用一个辅助数据结构即队列来实现&#xff0c;队列先进先出&#xff0c;符合一层一层遍历的逻辑&#xff0c;而用栈先进后出适合模拟深度优先遍历也就是递归…

【外部服务对接】对接Firebase支持谷歌、Facebook、苹果等第三方平台用户注册登录

【外部服务对接】对接Firebase支持谷歌、Facebook、苹果等第三方平台登录 背景 因主要做国外尼日的市场&#xff0c;相关的应用的全是国外用户使用&#xff0c;为了方便用户的注册和登录&#xff0c;接入国外的统一平台Firebase,集成使用很方便。 主要步骤 1.注册登录Fireb…

如何提高小红书笔记的互动率

相信有很多新手在运营小红书的时候&#xff0c;可能都会遇到过以下这样的情况&#xff1a; 笔记点赞、收藏数据明明还可以&#xff0c;但评论区却没有人留言&#xff1f;为何大家只给点赞、收藏&#xff0c;却不关注账号&#xff1f; 其实&#xff0c;这背后有很多运营技巧&a…

​做好研发管理的三个条件​

1.制造鼓励创新的环境 要做好研发管理&#xff0c;首先要制造一个鼓励创新、适合研发的环境&#xff0c;必须采取弹性而目标化的管理&#xff0c;不以死板的制度限制员工的创意&#xff0c;必须要求实质的成果。 2.融入行销观念 将行销的观念融入研发中&#xff1a;为使有限的…

xlua游戏热更新(lua访问C#)

CS.UnityEngine静态方法访问unity虚拟机 创建游戏物体 CS.UnityEngine.GameObject(new by lua);静态属性 CS.UnityEngine.GameObject(new by lua); -- 创建 local camera CS.UnityEngine.GameObject.Find(Main Camera); --查找 camera.name Renamed by Lua;访问组件 loca…

通配符匹配

题目链接 通配符匹配 题目描述 注意点 s 仅由小写英文字母组成p 仅由小写英文字母、‘?’ 或 ‘*’ 组成‘?’ 可以匹配任何单个字符‘*’ 可以匹配任意字符序列&#xff08;包括空字符序列&#xff09; 解答思路 最初想到的是dfs 剪枝&#xff0c;但是用例超时了参照题…

小型企业如何数字化转型?ZohoCRM助力小企业转型

小型企业数字化之路倍加艰难&#xff0c;其组织规模有限、资源有限&#xff0c;数字化布局或转型&#xff0c;也存在与数字平台匹配度的问题。其实小型企业可以通过CRM客户管理系统实现高效的客户关系管理&#xff0c;进一步提高市场竞争力。 建立高效易用的客户关系管理系统 …

OpenAI重磅推出GPTs,无需编码人人可以自定ChatGPT!

原创 | 文 BFT机器人 在11月7日深夜2点&#xff08;北京时间&#xff09;&#xff0c;美国旧金山举办了首届开发者大会&#xff0c;该活动由AI领域的知名公司OpenAI主办。尽管这是该公司的首届大会&#xff0c;但其盛大的规模和影响力已将其誉为“AI春晚”。在会议上&#xff…

关于echarts封装组件以及多次更新数据信息加载问题

项目中经常使用到echarts插件&#xff0c;使用时会遇到封装组件的问题&#xff0c;一个组件到底怎么封装才是完善的&#xff1f;仁者见仁智者见智思路不同封装的方式就是不同的。废话不多直接上封装的代码&#xff1a; <template><div :id"id" :style"…

OpenGL_Learn08(坐标系统与3D空间)

目录 1. 概述 2. 局部空间 3. 世界空间 4. 观察空间 5. 剪裁空间 6. 初入3D 7. 3D旋转 8. 多个正方体 9. 观察视角 1. 概述 OpenGL希望在每次顶点着色器运行后&#xff0c;我们可见的所有顶点都为标准化设备坐标(Normalized Device Coordinate, NDC)。也就是说&#x…

两台Linux服务器之间传送文件

两台Linux服务器之间传送文件 将U盘上的数据传送到服务器上 本地U盘 远程服务器地址&#xff1a; 192.168.30.125 传送到data文件夹的下面 scp -r coco2017 s192.168.30.125:/data传送 两台Linux服务器之间传送文件的4种方法

pytorch搭建squeezenet网络的整套工程(升级版)

上一篇当中&#xff0c;使用pytorch搭建了一个squeezenet&#xff0c;效果还行。但是偶然间发现了一个稍微改动的版本&#xff0c;拿来测试一下发现效果会更好&#xff0c;大概网络结构还是没有变&#xff0c;还是如下的第二个版本&#xff1a; 具体看网络结构代码&#xff1a…

进程控制——进程的程序替换

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析&#xff08;3&#xff09; 目录 &#x1f449;&#x1f3fb;进程的程序替换概念&#x…

R语言和jsonlite库编写代码示例

R语言和jsonlite库来下载的程序。 r # 导入jsonlite库 library(jsonlite) # 设置代理主机和端口 proxy_host <- "" proxy_port <- # 使用httr库创建一个对象 proxy <- create_proxy(proxy_host, proxy_port) # 使用httr库的GET方法下载网页内容 url <…

输入网址到网页显示,期间发生了什么?(收藏篇)

解析url 首先浏览器做的第一步工作就是要对 URL 进行解析&#xff0c;从而生成发送给 Web 服务器的请求信息。对 URL 进行解析之后&#xff0c;浏览器确定了 Web 服务器和文件名&#xff0c;接下来就是根据这些信息来生成 HTTP 请求消息了。 DNS解析 通过浏览器解析 URL 并…

学生用什么台灯对眼睛最好?双十一优质好用护眼台灯推荐

护眼台灯作为时下火爆的学生清单单品&#xff0c;深受众多学生以及家长的认可&#xff0c;市面上的护眼台灯品牌众多&#xff0c;但很多品牌存在不合格、劣质、虚假宣传的问题&#xff0c;为了帮大家避坑&#xff0c;我这个资深测评师反复测评了三十多款&#xff0c;今天给大家…

【Linux】进程程序替换

文章目录 替换原理站在进程的角度站在程序的角度初体验及理解原理 替换函数函数解释命名理解exec系列函数与main函数之间的关系在一个程序中调用我们自己写的程序 替换原理 创建子进程的目的是什么&#xff1f; ->想让子进程执行父进程代码的一部分 执行父进程对应的磁盘代码…

ROS源码安装应用,VSCode

ROS源码安装应用 安装一下VSCode 前置文章 到安装程序的目录中: 完成克隆 编译 catkin_make打开ros核心 roscore打开应用程序 rosrun turtlesim turtlesim_node安装一下VSCode deb下载地址 sudo dpkg -i code_1.84.1-1699275408_amd64.deb添加项目工程到工作空间&#xff…

什么是伺服电机?Parker派克伺服电机盘点

一、什么是伺服电机&#xff1f; 要准确地定义伺服电机&#xff0c;我们首先需理解其核心特性&#xff1a;反馈与闭环控制。伺服电机凭借这些特性&#xff0c;能精确控制扭矩、速度或位置&#xff0c;即使在零速度下&#xff0c;也能保持足够的扭矩以锁定负载。 伺服电机与其…