当前位置:   article > 正文

嵌入式Linux使用TFT屏幕:使用树莓派4B的MIPI-DSI接口点亮ST7701S屏幕_st7701s linux

st7701s linux

前言

距上一次写文章有点时间了,今天调通了一块MIPI DSI屏幕, 特写一篇笔记置于此,希望能帮到也想研究这个MIPI DSI的朋友。

正题

博主使用的开发板Raspbery Pi 4B,系统为Raspberry Pi OS 64Bit (full版本)。

关于系统版本

博主测试了一下各个版本的Raspberry Pi OS对于DRM驱动的兼容性,发现驱动只能在Raspberry Pi OS 11(bullseye)版本以上运行,之前的旧版本系统博主测试过了均不能使用。推测原因是因为旧版本系统使用的legacy GL driver不兼容drm,即使在raspi-config中启用了fakekms驱动也不能运行,kernel报出一长串错误。博主使用的系统版本为2022年1月28日发布的Raspberry Pi OS 64Bit正式版,经测试该系统能正常驱动屏幕。
*32位版也可以正常使用该驱动。

屏幕信息

博主使用的屏幕是一块2.8寸的IPS屏幕,MIPI接口,驱动IC是ST7701S,分辨率是480*640。
为避免广告嫌疑,这里就不放链接了。

提取屏幕信息

博主拿到屏幕后,也拿到了商家提供的初始化代码和屏幕信息,我们主要关注这两个部分:

  1. 屏幕接口定义
  2. 驱动IC datasheet(如果没有IC的datasheet,屏幕的也行)

博主这里放几张图:引脚定义

在这里插入图片描述
我们要从这两张图中提取到关键的信息。
首先第一张图中我们可以得出来引脚定义,后面需要根据它来lay板子。
还能从第一张图中得知,我们的屏幕是1 Lane的。
第二张图中则包含了关键的初始化序列,我们需要用它来初始化我们的屏幕。
好了,基本信息都齐了,开干。

开发环境准备

打开终端,安装一下raspberrypi-kernel-headers:

sudo apt install raspberrypi-kernel-headers
  • 1

P.S.如果安装的内核头文件不是您现在使用的内核的版本,那您需要自行下载符合您目前内核版本的内核头文件,或者从(内核)源码编译。如果您无法找到需要的内核头文件(换句话说,必须得从[内核]源码编译了),请您参考博主的上一篇文章 嵌入式Linux使用TFT屏幕:使用TinyDRM点亮ST7789V屏幕 中的从内核源码编译章节。
然后我们建个文件夹,就取名叫w280bf036i:

mkdir w280bf036i
  • 1

然后在那个文件夹中编写我们的驱动源码(panel-wlk-w280bf036i.c):

/*
** Copyright (C) 2022 CNflysky. All rights reserved.
** Kernel DRM driver for W280BF036I LCD Panel in DSI interface.
** Driver IC: ST7701
*/

#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>

struct w280bf036i_panel_desc {
  const struct drm_display_mode *mode;
  unsigned int lanes;
  unsigned long flags;
  enum mipi_dsi_pixel_format format;
};

struct w280bf036i {
  struct drm_panel panel;
  struct mipi_dsi_device *dsi;
  const struct w280bf036i_panel_desc *desc;
  struct gpio_desc *reset;
};

static inline struct w280bf036i *panel_to_w280bf036i(struct drm_panel *panel) {
  return container_of(panel, struct w280bf036i, panel);
}

static inline int w280bf036i_dsi_write(struct w280bf036i *w280bf036i,
                                       const void *seq, size_t len) {
  return mipi_dsi_dcs_write_buffer(w280bf036i->dsi, seq, len);
}

#define w280bf036i_command(w280bf036i, seq...)          \
  {                                                     \
    const uint8_t d[] = {seq};                          \
    w280bf036i_dsi_write(w280bf036i, d, ARRAY_SIZE(d)); \
  }

static void w280bf036i_init_sequence(struct w280bf036i *w280bf036i) {
  // Command2 BK3 Selection: Enable the BK function of Command2
  w280bf036i_command(w280bf036i, 0xFF, 0x77, 0x01, 0x00, 0x00, 0x13);
  // Unknown
  w280bf036i_command(w280bf036i, 0xEF, 0x08);
  // Command2 BK0 Selection: Disable the BK function of Command2
  w280bf036i_command(w280bf036i, 0xFF, 0x77, 0x01, 0x00, 0x00, 0x10);
  // Display Line Setting
  w280bf036i_command(w280bf036i, 0xC0, 0x4f, 0x00);
  // Porch Control
  w280bf036i_command(w280bf036i, 0xC1, 0x10, 0x0c);
  // Inversion selection & Frame Rate Control
  w280bf036i_command(w280bf036i, 0xC2, 0x07, 0x14);
  // Unknown
  w280bf036i_command(w280bf036i, 0xCC, 0x10);
  // Positive Voltage Gamma Control
  w280bf036i_command(w280bf036i, 0xB0, 0x0a, 0x18, 0x1e, 0x12, 0x16, 0x0c, 0x0e,
                     0x0d, 0x0c, 0x29, 0x06, 0x14, 0x13, 0x29, 0x33, 0x1c);
  // Negative Voltage Gamma Control
  w280bf036i_command(w280bf036i, 0xB1, 0x0a, 0x19, 0x21, 0x0a, 0x0c, 0x00, 0x0c,
                     0x03, 0x03, 0x23, 0x01, 0x0e, 0x0c, 0x27, 0x2b, 0x1c);

  // Command2 BK1 Selection: Enable the BK function of Command2
  w280bf036i_command(w280bf036i, 0xFF, 0x77, 0x01, 0x00, 0x00, 0x11);
  // Vop Amplitude setting
  w280bf036i_command(w280bf036i, 0xB0, 0x5d);
  // VCOM amplitude setting
  w280bf036i_command(w280bf036i, 0xB1, 0x61);
  // VGH Voltage setting
  w280bf036i_command(w280bf036i, 0xB2, 0x84);
  // TEST Command Setting
  w280bf036i_command(w280bf036i, 0xB3, 0x80);
  // VGL Voltage setting
  w280bf036i_command(w280bf036i, 0xB5, 0x4d);
  // Power Control 1
  w280bf036i_command(w280bf036i, 0xB7, 0x85);
  // Power Control 2
  w280bf036i_command(w280bf036i, 0xB8, 0x20);
  // Source pre_drive timing set1
  w280bf036i_command(w280bf036i, 0xC1, 0x78);
  // Source EQ2 Setting
  w280bf036i_command(w280bf036i, 0xC2, 0x78);
  // MIPI Setting 1
  w280bf036i_command(w280bf036i, 0xD0, 0x88);
  // Sunlight Readable Enhancement
  w280bf036i_command(w280bf036i, 0xE0, 0x00, 0x00, 0x02);
  // Noise Reduce Control
  w280bf036i_command(w280bf036i, 0xE1, 0x06, 0xa0, 0x08, 0xa0, 0x05, 0xa0, 0x07,
                     0xa0, 0x00, 0x44, 0x44);
  // Sharpness Control
  w280bf036i_command(w280bf036i, 0xE2, 0x20, 0x20, 0x44, 0x44, 0x96, 0xa0, 0x00,
                     0x00, 0x96, 0xa0, 0x00, 0x00);
  // Color Calibration Control
  w280bf036i_command(w280bf036i, 0xE3, 0x00, 0x00, 0x22, 0x22);
  // Skin Tone Preservation Control
  w280bf036i_command(w280bf036i, 0xE4, 0x44, 0x44);

  w280bf036i_command(w280bf036i, 0xE5, 0x0d, 0x91, 0xa0, 0xa0, 0x0f, 0x93, 0xa0,
                     0xa0, 0x09, 0x8d, 0xa0, 0xa0, 0x0b, 0x8f, 0xa0, 0xa0);
  w280bf036i_command(w280bf036i, 0xE6, 0x00, 0x00, 0x22, 0x22);
  w280bf036i_command(w280bf036i, 0xE7, 0x44, 0x44);
  w280bf036i_command(w280bf036i, 0xE8, 0x0c, 0x90, 0xa0, 0xa0, 0x0e, 0x92, 0xa0,
                     0xa0, 0x08, 0x8c, 0xa0, 0xa0, 0x0a, 0x8e, 0xa0, 0xa0);
  w280bf036i_command(w280bf036i, 0xE9, 0x36, 0x00);
  w280bf036i_command(w280bf036i, 0xEB, 0x00, 0x01, 0xe4, 0xe4, 0x44, 0x88,
                     0x40);
  w280bf036i_command(w280bf036i, 0xED, 0xff, 0x45, 0x67, 0xfa, 0x01, 0x2b, 0xcf,
                     0xff, 0xff, 0xfc, 0xb2, 0x10, 0xaf, 0x76, 0x54, 0xff);
  w280bf036i_command(w280bf036i, 0xEF, 0x10, 0x0d, 0x04, 0x08, 0x3f, 0x1f);
  /* disable Command2 */
  // w280bf036i_command(w280bf036i, 0xFF, 0x77, 0x01, 0x00, 0x00, 0x00);
}

static int w280bf036i_prepare(struct drm_panel *panel) {
  struct w280bf036i *w280bf036i = panel_to_w280bf036i(panel);
  gpiod_set_value(w280bf036i->reset, 0);

  msleep(50);
  gpiod_set_value(w280bf036i->reset, 1);
  msleep(150);
  mipi_dsi_dcs_soft_reset(w280bf036i->dsi);

  msleep(5);

  w280bf036i_init_sequence(w280bf036i);

  mipi_dsi_dcs_set_tear_on(w280bf036i->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
  mipi_dsi_dcs_exit_sleep_mode(w280bf036i->dsi);
  return 0;
}

static int w280bf036i_enable(struct drm_panel *panel) {
  return mipi_dsi_dcs_set_display_on(panel_to_w280bf036i(panel)->dsi);
}

static int w280bf036i_disable(struct drm_panel *panel) {
  return mipi_dsi_dcs_set_display_off(panel_to_w280bf036i(panel)->dsi);
}

static int w280bf036i_unprepare(struct drm_panel *panel) {
  struct w280bf036i *w280bf036i = panel_to_w280bf036i(panel);

  mipi_dsi_dcs_enter_sleep_mode(w280bf036i->dsi);

  gpiod_set_value(w280bf036i->reset, 0);

  return 0;
}

static int w280bf036i_get_modes(struct drm_panel *panel,
                                struct drm_connector *connector) {
  struct w280bf036i *w280bf036i = panel_to_w280bf036i(panel);
  const struct drm_display_mode *desc_mode = w280bf036i->desc->mode;
  struct drm_display_mode *mode;

  mode = drm_mode_duplicate(connector->dev, desc_mode);
  if (!mode) {
    dev_err(&w280bf036i->dsi->dev, "failed to add mode %ux%u@%u\n",
            desc_mode->hdisplay, desc_mode->vdisplay,
            drm_mode_vrefresh(desc_mode));
    return -ENOMEM;
  }

  drm_mode_set_name(mode);
  drm_mode_probed_add(connector, mode);

  connector->display_info.width_mm = desc_mode->width_mm;
  connector->display_info.height_mm = desc_mode->height_mm;

  return 1;
}

static const struct drm_panel_funcs w280bf036i_funcs = {
    .disable = w280bf036i_disable,
    .unprepare = w280bf036i_unprepare,
    .prepare = w280bf036i_prepare,
    .enable = w280bf036i_enable,
    .get_modes = w280bf036i_get_modes,
};

static const struct drm_display_mode w280bf036i_mode = {
    .clock = 25000,

    .hdisplay = 480,
    .hsync_start = 480 + /* HFP */ 10,
    .hsync_end = 480 + 10 + /* HSync */ 4,
    .htotal = 480 + 10 + 4 + /* HBP */ 20,

    .vdisplay = 640,
    .vsync_start = 640 + /* VFP */ 8,
    .vsync_end = 640 + 8 + /* VSync */ 4,
    .vtotal = 640 + 8 + 4 + /* VBP */ 14,

    .width_mm = 43,
    .height_mm = 57,

    .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};

static const struct w280bf036i_panel_desc w280bf036i_desc = {
    .mode = &w280bf036i_mode,
    .lanes = 1,
    .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
    .format = MIPI_DSI_FMT_RGB888,
};

static int w280bf036i_dsi_probe(struct mipi_dsi_device *dsi) {
  struct w280bf036i *w280bf036i =
      devm_kzalloc(&dsi->dev, sizeof(*w280bf036i), GFP_KERNEL);
  if (!w280bf036i) return -ENOMEM;

  const struct w280bf036i_panel_desc *desc =
      of_device_get_match_data(&dsi->dev);
  dsi->mode_flags = desc->flags;
  dsi->format = desc->format;
  dsi->lanes = desc->lanes;

  w280bf036i->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
  if (IS_ERR(w280bf036i->reset)) {
    dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");
    return PTR_ERR(w280bf036i->reset);
  }

  drm_panel_init(&w280bf036i->panel, &dsi->dev, &w280bf036i_funcs,
                 DRM_MODE_CONNECTOR_DSI);

  int ret = drm_panel_of_backlight(&w280bf036i->panel);
  if (ret) return ret;

  drm_panel_add(&w280bf036i->panel);

  mipi_dsi_set_drvdata(dsi, w280bf036i);
  w280bf036i->dsi = dsi;
  w280bf036i->desc = desc;

  return mipi_dsi_attach(dsi);
}

static int w280bf036i_dsi_remove(struct mipi_dsi_device *dsi) {
  struct w280bf036i *w280bf036i = mipi_dsi_get_drvdata(dsi);

  mipi_dsi_detach(dsi);
  drm_panel_remove(&w280bf036i->panel);

  return 0;
}

static const struct of_device_id w280bf036i_of_match[] = {
    {.compatible = "wlk,w280bf036i", .data = &w280bf036i_desc}, {}};
MODULE_DEVICE_TABLE(of, w280bf036i_of_match);

static struct mipi_dsi_driver w280bf036i_dsi_driver = {
    .probe = w280bf036i_dsi_probe,
    .remove = w280bf036i_dsi_remove,
    .driver =
        {
            .name = "w280bf036i",
            .of_match_table = w280bf036i_of_match,
        },
};
module_mipi_dsi_driver(w280bf036i_dsi_driver);

MODULE_AUTHOR("CNflysky@qq.com");
MODULE_DESCRIPTION("WLK w280bf036i LCD Panel Driver");
MODULE_LICENSE("GPL");

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272

好了,驱动代码编写完成,我们来解析一下这个代码:
首先在static void w280bf036i_init_sequence函数里面,我们填入了厂家所给的初始化代码:

static void w280bf036i_init_sequence(struct w280bf036i *w280bf036i) {
  w280bf036i_command(w280bf036i, MIPI_DCS_SOFT_RESET, 0x00);

  /* We need to wait 5ms before sending new commands */
  msleep(5);

  w280bf036i_command(w280bf036i, MIPI_DCS_EXIT_SLEEP_MODE, 0x00);

  // Command2 BK3 Selection: Enable the BK function of Command2
  w280bf036i_command(w280bf036i, 0xFF, 0x77, 0x01, 0x00, 0x00, 0x13);
  // Your initial code here...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

然后在w280bf036i_mode这个结构体中,我们填上屏幕的Porch参数:

static const struct drm_display_mode w280bf036i_mode = {
    .clock = 25000,

    .hdisplay = 480,
    .hsync_start = 480 + /* HFP */ 10,
    .hsync_end = 480 + 10 + /* HSync */ 4,
    .htotal = 480 + 10 + 4 + /* HBP */ 20,

    .vdisplay = 640,
    .vsync_start = 640 + /* VFP */ 8,
    .vsync_end = 640 + 8 + /* VSync */ 4,
    .vtotal = 640 + 8 + 4 + /* VBP */ 14,

    .width_mm = 43,
    .height_mm = 57,

    .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

最后,在w280bf036i_desc这个结构体中,我们填上屏幕的lane数量:

static const struct w280bf036i_panel_desc w280bf036i_desc = {
    .mode = &w280bf036i_mode,
    .lanes = 1,
    .flags = MIPI_DSI_MODE_VIDEO,
    .format = MIPI_DSI_FMT_RGB888,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后在当前目录下,编写Makefile:

obj-m += panel-wlk-w280bf036i.o
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • 1
  • 2
  • 3
  • 4
  • 5

最后,我们执行make命令,编译模块:

make
  • 1

编译完成后,目录下会出现多个文件,确保有panel-wlk-w280bf036i.ko即可。

编辑设备树

驱动编写完毕,我们还需要设备树用来探测(Probe)设备,编写设备树代码(vc4-kms-dsi-w280bf036i.dts)如下:

// compile: dtc -@ -I dts -O dtb -o vc4-kms-dsi-w280bf036i.dtbo vc4-kms-dsi-w280bf036i.dts

/dts-v1/;
/plugin/;

/ {
	compatible = "brcm,bcm2835";  

	fragment@0 {
		target = <&dsi1>;
		__overlay__{
			status = "okay";
			#address-cells = <1>;
			#size-cells = <0>;
			port {
				dsi_out_port:endpoint {
					remote-endpoint = <&panel_dsi_port>;
				};
			};

			w280bf036i:w280bf036i@0 {
				compatible    = "wlk,w280bf036i";
				status        = "okay";
				reg           = <0>;
				reset-gpios   = <&gpio 47 1>;   // Dummy GPIO , Unused
				port {
					panel_dsi_port: endpoint {
						remote-endpoint = <&dsi_out_port>;
					};
				};
			};
		};
	};

	fragment@1 {
		target = <&gpio>;
		__overlay__ {
			w280bf036i_pins: w280bf036i_pins {
				brcm,pins = <47>;
				brcm,function = <1>; // out
				brcm,pull = <0>; // off
			};
		};

	};
	fragment@2 {
		target = <&i2c_csi_dsi>;
		__overlay__ {
			#address-cells = <1>;
			#size-cells = <0>;
			status = "okay";
		};
	};
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

我的转接板设计为DSI的I2C SCL脚用于连接Reset,故使用45号引脚作为Reset PIN.
P.S. 该设计有问题,不应该将I2C引脚作为GPIO使用,应加一颗I2C转GPIO芯片(如PCA9536)。
P.P.S. 因为转接板虽然打好了,但是TMD元件还没到,所以先把I2C接口启用了让44和45都高电平阻止屏幕复位,等元件到了会更新的…

应用

dtc -@ -I dts -O dtb -o vc4-kms-dsi-w280bf036i.dtbo vc4-kms-dsi-w280bf036i.dts
sudo cp vc4-kms-dsi-w280bf036i.dtbo /boot/overlays/
sudo cp panel-wlk-w280bf036i.ko /lib/modules/`uname -r`/kernel/drivers/gpu/drm/panel
sudo depmod
echo "ignore_lcd=1" >> /boot/config.txt
echo "dtoverlay=vc4-kms-dsi-w280bf036i" >> /boot/config.txt
sudo reboot
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

执行完上面的命令,板子重启之后,屏幕上就能显示树莓派桌面了。

展示

由于初始化代码或者是博主的layout有些问题,屏幕的显示效果不太正常,待再次调整。
桌面,颜色有点诡异
不过,这个屏幕跑UFOTest居然能跑到80帧,还是挺令人意外的
在这里插入图片描述

链接

本文所用的所有代码都能在这里找到: GitHub链接

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/330284
推荐阅读
相关标签
  

闽ICP备14008679号