页次: 1
然后staging driver 开启7789的驱动。
我是这样写的,没毛病:
&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>;
};
}; 将SPI频率和帧率调低点
yyyyppppp wrote:
a06041114 wrote:
楼主,从128M改成256M,那个分区配置文件需要改不,不改的话会不会容量只用了128M,剩余128M没使用起来
请问你解决了吗 改为256MB
N04的应该这么写:
struct aw_spinand_phy_info winbond[] = {
{
.Model = "W25N01GVZEIG",
.NandID = {0xef, 0xaa, 0x21, 0xff, 0xff, 0xff, 0xff, 0xff},
.DieCntPerChip = 1,
.SectCntPerPage = 4,
.PageCntPerBlk = 64,
.BlkCntPerDie = 1024,
.OobSizePerPage = 64,
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
SPINAND_DUAL_READ,
.MaxEraseTimes = 65000,
.EccType = BIT2_LIMIT1_ERR2,
.EccProtectedType = SIZE16_OFF4_LEN4_OFF8,
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
},
{
.Model = "W25N04KVZEIR",
.NandID = {0xef, 0xaa, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff},
.DieCntPerChip = 1,
.SectCntPerPage = 4,
.PageCntPerBlk = 64,
.BlkCntPerDie = 4096,
.OobSizePerPage = 128,
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
SPINAND_DUAL_READ,
.MaxEraseTimes = 65000,
.EccType = BIT2_ERR2_LIMIT3,
.EccProtectedType = SIZE16_OFF4_LEN12,
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
},
};
已经搞定,设备树如下:
&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>;
};
};
内核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");
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
不知道问题在哪?
lovelessing wrote:
搞好了,调整了一些参数,多谢大佬们帮助
/files/members/5589/IMG_20240530_171004.jpg
改了哪些东西调整好的
一定要用指定的sdk么,能否在现有sdk上移植应用呢?
我的s3是 uart3 能用么
请问 , 我的板子sdk默认开启了并口屏作为显示,那么现在要改成SPI屏幕显示,原先的那个屏幕要注释掉么?
还有 ,chosen {
/delete-node/ framebuffer@0;
};
这句话的作用是什么呢 ?
@上邪
请问W25N04 怎么改呢?
我把W25N04这个芯片焊上去,总是识别成W25N01, 有点莫名其妙:
try emmc fail
[08.600]sunxi-spinand: AW SPINand MTD Layer Version: 1.8 20220106
[08.605]sunxi-spinand-phy: AW SPINand Phy Layer Version: 1.11 20211217
[08.613]sunxi-spinand-phy: request spi0 gpio ok
[08.617]sunxi-spinand-phy: request general tx dma channel ok!
[08.623]sunxi-spinand-phy: request general rx dma channel ok!
[08.628]sunxi-spinand-phy: set spic0 clk to 20 Mhz
[08.633]sunxi-spinand-phy: init spic0 clk ok
[08.637]sunxi-spinand-phy: detect munufacture from id table: Winbond
[08.643]sunxi-spinand-phy: detect spinand id: ff21aaef ffffffff
[08.648]sunxi-spinand-phy: ========== arch info ==========
[08.654]sunxi-spinand-phy: Model: W25N01GVZEIG
[08.659]sunxi-spinand-phy: Munufacture: Winbond
[08.664]sunxi-spinand-phy: DieCntPerChip: 1
[08.668]sunxi-spinand-phy: BlkCntPerDie: 1024
[08.673]sunxi-spinand-phy: PageCntPerBlk: 64
[08.678]sunxi-spinand-phy: SectCntPerPage: 4
[08.682]sunxi-spinand-phy: OobSizePerPage: 64
[08.686]sunxi-spinand-phy: BadBlockFlag: 0x0
[08.691]sunxi-spinand-phy: OperationOpt: 0x7
[08.696]sunxi-spinand-phy: MaxEraseTimes: 65000
[08.700]sunxi-spinand-phy: EccFlag: 0x0
[08.705]sunxi-spinand-phy: EccType: 2
[08.709]sunxi-spinand-phy: EccProtectedType: 3
页次: 1