linux 内核版本5.4
设备树:
&spi1 {
clock-frequency = <5000000>;
pinctrl-0 = <&spi1_pins_a>;
pinctrl-names = "default", "sleep";
spi_slave_mode = <0>;
cs-gpios = <&pio 3 10 GPIO_ACTIVE_LOW>; // 可选:指定 CS 脚,如 PD10
status = "okay";
display@0{
compatible = "sitronix,st7789v";
reg = <0>;
status = "okay";
spi-max-frequency = <96000000>;
spi-cpol;
spi-cpha;
rotate = <270>;
width = <320>;
height = <240>;
fps = <10>;
buswidth = <8>;
dc-gpios = <&pio 3 14 GPIO_ACTIVE_HIGH>; // Pd14 dc
reset-gpios = <&pio 3 15 GPIO_ACTIVE_HIGH>; // Pd15
//led-gpios = <&pio 0 0 GPIO_ACTIVE_LOW>; // PA0
debug = <0x0>;
};
};
应用程序:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#define FB_DEV "/dev/fb0"
// 将 8 位的 RGB 颜色转换为 16 位的 RGB565 格式
unsigned short rgb_to_rgb565(int r, int g, int b) {
// RGB565: R 5 bits, G 6 bits, B 5 bits
return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}
// 填充屏幕颜色
void fill_screen(unsigned short *framebuffer, int width, int height, unsigned short color) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
framebuffer[y * width + x] = color;
}
}
}
int main(void) {
int fb = open(FB_DEV, O_RDWR);
if (fb == -1) {
perror("打开 framebuffer 设备失败");
return -1;
}
// 获取 framebuffer 的信息
struct fb_var_screeninfo vinfo;
if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo)) {
perror("获取屏幕信息失败");
close(fb);
return -1;
}
// 获取屏幕的宽度、高度以及颜色深度
int width = vinfo.xres;
int height = vinfo.yres;
int bpp = vinfo.bits_per_pixel;
printf("屏幕分辨率: %dx%d, 位深: %d\n", width, height, bpp);
// 映射 framebuffer 到进程的内存
unsigned short framebuffer = (unsigned short )mmap(NULL, width height (bpp / 8), PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
if (framebuffer == MAP_FAILED) {
perror("映射 framebuffer 失败");
close(fb);
return -1;
}
// 循环测试:每隔一秒切换颜色
while (1) {
// 填充红色 (RGB: 255, 0, 0 -> RGB565: 0xF800)
fill_screen(framebuffer, width, height, rgb_to_rgb565(255, 0, 0));
usleep(1000000); // 延时 1 秒
// 填充绿色 (RGB: 0, 255, 0 -> RGB565: 0x07E0)
fill_screen(framebuffer, width, height, rgb_to_rgb565(0, 255, 0));
usleep(1000000); // 延时 1 秒
// 填充蓝色 (RGB: 0, 0, 255 -> RGB565: 0x001F)
fill_screen(framebuffer, width, height, rgb_to_rgb565(0, 0, 255));
usleep(1000000); // 延时 1 秒
}
// 解除内存映射
munmap(framebuffer, width height (bpp / 8));
close(fb);
return 0;
}
效果:
【t113s3 驱动spi st7789显示屏,花屏-哔哩哔哩】 https://b23.tv/cqHvRyK
不知道问题在哪?
离线
内核7789源码:
// SPDX-License-Identifier: GPL-2.0+
/*
FB driver for the ST7789V LCD Controller
*
Copyright (C) 2015 Dennis Menschel
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <video/mipi_display.h>
#include "fbtft.h"
#define DRVNAME "fb_st7789v"
#define DEFAULT_GAMMA \
"70 2C 2E 15 10 09 48 33 53 0B 19 18 20 25\n" \
"70 2C 2E 15 10 09 48 33 53 0B 19 18 20 25"
/**
enum st7789v_command - ST7789V display controller commands
*
@PORCTRL: porch setting
@GCTRL: gate control
@VCOMS: VCOM setting
@VDVVRHEN: VDV and VRH command enable
@VRHS: VRH set
@VDVS: VDV set
@VCMOFSET: VCOM offset set
@PWCTRL1: power control 1
@PVGAMCTRL: positive voltage gamma control
@NVGAMCTRL: negative voltage gamma control
*
The command names are the same as those found in the datasheet to ease
looking up their semantics and usage.
*
Note that the ST7789V display controller offers quite a few more commands
which have been omitted from this list as they are not used at the moment.
Furthermore, commands that are compliant with the MIPI DCS have been left
out as well to avoid duplicate entries.
*/
enum st7789v_command {
PORCTRL = 0xB2,
GCTRL = 0xB7,
VCOMS = 0xBB,
VDVVRHEN = 0xC2,
VRHS = 0xC3,
VDVS = 0xC4,
VCMOFSET = 0xC5,
PWCTRL1 = 0xD0,
PVGAMCTRL = 0xE0,
NVGAMCTRL = 0xE1,
};
#define MADCTL_BGR BIT(3) / bitmask for RGB/BGR order /
#define MADCTL_MV BIT(5) / bitmask for page/column order /
#define MADCTL_MX BIT(6) / bitmask for column address order /
#define MADCTL_MY BIT(7) / bitmask for page address order /
/**
init_display() - initialize the display controller
*
@par: FBTFT parameter object
*
Most of the commands in this init function set their parameters to the
same default values which are already in place after the display has been
powered up. (The main exception to this rule is the pixel format which
would default to 18 instead of 16 bit per pixel.)
Nonetheless, this sequence can be used as a template for concrete
displays which usually need some adjustments.
*
Return: 0 on success, < 0 if error occurred.
*/
static int init_display(struct fbtft_par *par)
{
/ turn off sleep mode /
write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
mdelay(120);
/ set pixel format to RGB-565 /
write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
write_reg(par, PORCTRL, 0x08, 0x08, 0x00, 0x22, 0x22);
/*
VGH = 13.26V
VGL = -10.43V
*/
write_reg(par, GCTRL, 0x35);
/*
VDV and VRH register values come from command write
(instead of NVM)
*/
write_reg(par, VDVVRHEN, 0x01, 0xFF);
/*
VAP = 4.1V + (VCOM + VCOM offset + 0.5 * VDV)
VAN = -4.1V + (VCOM + VCOM offset + 0.5 * VDV)
*/
write_reg(par, VRHS, 0x0B);
/ VDV = 0V /
write_reg(par, VDVS, 0x20);
/ VCOM = 0.9V /
write_reg(par, VCOMS, 0x20);
/ VCOM offset = 0V /
write_reg(par, VCMOFSET, 0x20);
/*
AVDD = 6.8V
AVCL = -4.8V
VDS = 2.3V
*/
write_reg(par, PWCTRL1, 0xA4, 0xA1);
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
return 0;
}
/**
set_var() - apply LCD properties like rotation and BGR mode
*
@par: FBTFT parameter object
*
Return: 0 on success, < 0 if error occurred.
*/
static int set_var(struct fbtft_par *par)
{
u8 madctl_par = 0;
if (par->bgr)
madctl_par |= MADCTL_BGR;
switch (par->info->var.rotate) {
case 0:
break;
case 90:
madctl_par |= (MADCTL_MV | MADCTL_MY);
break;
case 180:
madctl_par |= (MADCTL_MX | MADCTL_MY);
break;
case 270:
madctl_par |= (MADCTL_MV | MADCTL_MX);
break;
default:
return -EINVAL;
}
write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, madctl_par);
return 0;
}
/**
set_gamma() - set gamma curves
*
@par: FBTFT parameter object
@curves: gamma curves
*
Before the gamma curves are applied, they are preprocessed with a bitmask
to ensure syntactically correct input for the display controller.
This implies that the curves input parameter might be changed by this
function and that illegal gamma values are auto-corrected and not
reported as errors.
*
Return: 0 on success, < 0 if error occurred.
*/
static int set_gamma(struct fbtft_par par, u32 curves)
{
int i;
int j;
int c; / curve index offset /
/*
Bitmasks for gamma curve command parameters.
The masks are the same for both positive and negative voltage
gamma curves.
*/
static const u8 gamma_par_mask[] = {
0xFF, / V63[3:0], V0[3:0]/
0x3F, / V1[5:0] /
0x3F, / V2[5:0] /
0x1F, / V4[4:0] /
0x1F, / V6[4:0] /
0x3F, / J0[1:0], V13[3:0] /
0x7F, / V20[6:0] /
0x77, / V36[2:0], V27[2:0] /
0x7F, / V43[6:0] /
0x3F, / J1[1:0], V50[3:0] /
0x1F, / V57[4:0] /
0x1F, / V59[4:0] /
0x3F, / V61[5:0] /
0x3F, / V62[5:0] /
};
for (i = 0; i < par->gamma.num_curves; i++) {
c = i * par->gamma.num_values;
for (j = 0; j < par->gamma.num_values; j++)
curves[c + j] &= gamma_par_mask[j];
write_reg(par, PVGAMCTRL + i,
curves[c + 0], curves[c + 1], curves[c + 2],
curves[c + 3], curves[c + 4], curves[c + 5],
curves[c + 6], curves[c + 7], curves[c + 8],
curves[c + 9], curves[c + 10], curves[c + 11],
curves[c + 12], curves[c + 13]);
}
return 0;
}
/**
blank() - blank the display
*
@par: FBTFT parameter object
@on: whether to enable or disable blanking the display
*
Return: 0 on success, < 0 if error occurred.
*/
static int blank(struct fbtft_par *par, bool on)
{
if (on)
write_reg(par, MIPI_DCS_SET_DISPLAY_OFF);
else
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = 240,
.height = 320,
.gamma_num = 2,
.gamma_len = 14,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_var = set_var,
.set_gamma = set_gamma,
.blank = blank,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7789v", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:st7789v");
MODULE_ALIAS("platform:st7789v");
MODULE_DESCRIPTION("FB driver for the ST7789V LCD Controller");
MODULE_AUTHOR("Dennis Menschel");
MODULE_LICENSE("GPL");
离线
已经搞定,设备树如下:
&spi1 {
clock-frequency = <15000000>;
pinctrl-0 = <&spi1_pins_a>;
pinctrl-names = "default", "sleep";
spi_slave_mode = <0>;
cs-gpios = <&pio 3 10 GPIO_ACTIVE_LOW>; // 可选:指定 CS 脚,如 PD10
status = "okay";
bias-pull-up;
display@0{
compatible = "sitronix,st7789v";
reg = <0>;
status = "okay";
spi-max-frequency = <15000000>;//96000000
spi-cpol;
spi-cpha;
rotate = <180>;
// width = <320>;
// height = <240>;
fps = <10>;
buswidth = <8>;
dc-gpios = <&pio 3 14 GPIO_ACTIVE_HIGH>; // Pd14 dc
reset-gpios = <&pio 3 15 GPIO_ACTIVE_HIGH>; // Pd15
//led-gpios = <&pio 0 0 GPIO_ACTIVE_LOW>; // PA0
debug = <0x0>;
};
};
离线