i.MX 6ULL 驱动开发 二十三:UART
创始人
2024-04-10 23:47:15
0

一、UART 协议

UART详解_sternlycore的博客-CSDN博客

二、UART 和 TTY 关系

基于Linux的tty架构及UART驱动详解 - 一口Linux - 博客园 (cnblogs.com)

三、Linux UART 驱动框架中重要对象

1、UART 驱动

struct uart_driver {struct module		*owner;const char		*driver_name;const char		*dev_name;int			 major;int			 minor;int			 nr;struct console		*cons;/** these are private; the low level driver should not* touch these; they should be initialised to NULL*/struct uart_state	*state;struct tty_driver	*tty_driver;
};

每个串口驱动都需要定义一个 uart_driver,加载驱动时向系统注册这个 uart_driver,注销驱动时注销掉注册的 uart_driver

2、UART 控制器

struct uart_port {spinlock_t		lock;			/* port lock */unsigned long		iobase;			/* in/out[bwl] */unsigned char __iomem	*membase;		/* read/write[bwl] */unsigned int		(*serial_in)(struct uart_port *, int);void			(*serial_out)(struct uart_port *, int, int);void			(*set_termios)(struct uart_port *,struct ktermios *new,struct ktermios *old);void			(*set_mctrl)(struct uart_port *, unsigned int);int			(*startup)(struct uart_port *port);void			(*shutdown)(struct uart_port *port);void			(*throttle)(struct uart_port *port);void			(*unthrottle)(struct uart_port *port);int			(*handle_irq)(struct uart_port *);void			(*pm)(struct uart_port *, unsigned int state,unsigned int old);void			(*handle_break)(struct uart_port *);int			(*rs485_config)(struct uart_port *,struct serial_rs485 *rs485);unsigned int		irq;			/* irq number */unsigned long		irqflags;		/* irq flags  */unsigned int		uartclk;		/* base uart clock */unsigned int		fifosize;		/* tx fifo size */unsigned char		x_char;			/* xon/xoff char */unsigned char		regshift;		/* reg offset shift */unsigned char		iotype;			/* io access style */unsigned char		unused1;#define UPIO_PORT		(SERIAL_IO_PORT)	/* 8b I/O port access */
#define UPIO_HUB6		(SERIAL_IO_HUB6)	/* Hub6 ISA card */
#define UPIO_MEM		(SERIAL_IO_MEM)		/* 8b MMIO access */
#define UPIO_MEM32		(SERIAL_IO_MEM32)	/* 32b little endian */
#define UPIO_AU			(SERIAL_IO_AU)		/* Au1x00 and RT288x type IO */
#define UPIO_TSI		(SERIAL_IO_TSI)		/* Tsi108/109 type IO */
#define UPIO_MEM32BE		(SERIAL_IO_MEM32BE)	/* 32b big endian */unsigned int		read_status_mask;	/* driver specific */unsigned int		ignore_status_mask;	/* driver specific */struct uart_state	*state;			/* pointer to parent state */struct uart_icount	icount;			/* statistics */struct console		*cons;			/* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)unsigned long		sysrq;			/* sysrq timeout */
#endif/* flags must be updated while holding port mutex */upf_t			flags;/** These flags must be equivalent to the flags defined in* include/uapi/linux/tty_flags.h which are the userspace definitions* assigned from the serial_struct flags in uart_set_info()* [for bit definitions in the UPF_CHANGE_MASK]** Bits [0..UPF_LAST_USER] are userspace defined/visible/changeable* except bit 15 (UPF_NO_TXEN_TEST) which is masked off.* The remaining bits are serial-core specific and not modifiable by* userspace.*/
#define UPF_FOURPORT		((__force upf_t) ASYNC_FOURPORT       /* 1  */ )
#define UPF_SAK			((__force upf_t) ASYNC_SAK            /* 2  */ )
#define UPF_SPD_HI		((__force upf_t) ASYNC_SPD_HI         /* 4  */ )
#define UPF_SPD_VHI		((__force upf_t) ASYNC_SPD_VHI        /* 5  */ )
#define UPF_SPD_CUST		((__force upf_t) ASYNC_SPD_CUST   /* 0x0030 */ )
#define UPF_SPD_WARP		((__force upf_t) ASYNC_SPD_WARP   /* 0x1010 */ )
#define UPF_SPD_MASK		((__force upf_t) ASYNC_SPD_MASK   /* 0x1030 */ )
#define UPF_SKIP_TEST		((__force upf_t) ASYNC_SKIP_TEST      /* 6  */ )
#define UPF_AUTO_IRQ		((__force upf_t) ASYNC_AUTO_IRQ       /* 7  */ )
#define UPF_HARDPPS_CD		((__force upf_t) ASYNC_HARDPPS_CD     /* 11 */ )
#define UPF_SPD_SHI		((__force upf_t) ASYNC_SPD_SHI        /* 12 */ )
#define UPF_LOW_LATENCY		((__force upf_t) ASYNC_LOW_LATENCY    /* 13 */ )
#define UPF_BUGGY_UART		((__force upf_t) ASYNC_BUGGY_UART     /* 14 */ )
#define UPF_NO_TXEN_TEST	((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER	((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )/* Port has hardware-assisted h/w flow control */
#define UPF_AUTO_CTS		((__force upf_t) (1 << 20))
#define UPF_AUTO_RTS		((__force upf_t) (1 << 21))
#define UPF_HARD_FLOW		((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW		((__force upf_t) (1 << 22))
#define UPF_CONS_FLOW		((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ		((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR		((__force upf_t) (1 << 25))
#define UPF_BUG_THRE		((__force upf_t) (1 << 26))
/* The exact UART type is known and should not be probed.  */
#define UPF_FIXED_TYPE		((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF	((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT		((__force upf_t) (1 << 29))
#define UPF_DEAD		((__force upf_t) (1 << 30))
#define UPF_IOREMAP		((__force upf_t) (1 << 31))#define __UPF_CHANGE_MASK	0x17fff
#define UPF_CHANGE_MASK		((__force upf_t) __UPF_CHANGE_MASK)
#define UPF_USR_MASK		((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))#if __UPF_CHANGE_MASK > ASYNC_FLAGS
#error Change mask not equivalent to userspace-visible bit defines
#endif/** Must hold termios_rwsem, port mutex and port lock to change;* can hold any one lock to read.*/upstat_t		status;#define UPSTAT_CTS_ENABLE	((__force upstat_t) (1 << 0))
#define UPSTAT_DCD_ENABLE	((__force upstat_t) (1 << 1))
#define UPSTAT_AUTORTS		((__force upstat_t) (1 << 2))
#define UPSTAT_AUTOCTS		((__force upstat_t) (1 << 3))
#define UPSTAT_AUTOXOFF		((__force upstat_t) (1 << 4))int			hw_stopped;		/* sw-assisted CTS flow state */unsigned int		mctrl;			/* current modem ctrl settings */unsigned int		timeout;		/* character-based timeout */unsigned int		type;			/* port type */const struct uart_ops	*ops;unsigned int		custom_divisor;unsigned int		line;			/* port index */unsigned int		minor;resource_size_t		mapbase;		/* for ioremap */resource_size_t		mapsize;struct device		*dev;			/* parent device */unsigned char		hub6;			/* this should be in the 8250 driver */unsigned char		suspended;unsigned char		irq_wake;unsigned char		unused[2];struct attribute_group	*attr_group;		/* port specific attributes */const struct attribute_group **tty_groups;	/* all attributes (serial core use only) */struct serial_rs485     rs485;void			*private_data;		/* generic platform data pointer */
};

Linux 中使用 struct uart_port 描述硬件信息。

3、UART 操作集

/** This structure describes all the operations that can be done on the* physical hardware.  See Documentation/serial/driver for details.*/
struct uart_ops {unsigned int	(*tx_empty)(struct uart_port *);void		(*set_mctrl)(struct uart_port *, unsigned int mctrl);unsigned int	(*get_mctrl)(struct uart_port *);void		(*stop_tx)(struct uart_port *);void		(*start_tx)(struct uart_port *);void		(*throttle)(struct uart_port *);void		(*unthrottle)(struct uart_port *);void		(*send_xchar)(struct uart_port *, char ch);void		(*stop_rx)(struct uart_port *);void		(*enable_ms)(struct uart_port *);void		(*break_ctl)(struct uart_port *, int ctl);int		(*startup)(struct uart_port *);void		(*shutdown)(struct uart_port *);void		(*flush_buffer)(struct uart_port *);void		(*set_termios)(struct uart_port *, struct ktermios *new,struct ktermios *old);void		(*set_ldisc)(struct uart_port *, struct ktermios *);void		(*pm)(struct uart_port *, unsigned int state,unsigned int oldstate);/** Return a string describing the type of the port*/const char	*(*type)(struct uart_port *);/** Release IO and memory resources used by the port.* This includes iounmap if necessary.*/void		(*release_port)(struct uart_port *);/** Request IO and memory resources used by the port.* This includes iomapping the port if necessary.*/int		(*request_port)(struct uart_port *);void		(*config_port)(struct uart_port *, int);int		(*verify_port)(struct uart_port *, struct serial_struct *);int		(*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLLint		(*poll_init)(struct uart_port *);void		(*poll_put_char)(struct uart_port *, unsigned char);int		(*poll_get_char)(struct uart_port *);
#endif
};

四、UART 驱动编写思路

LinuxUART 一般都编写好,根据设备树找到相关驱动分析即可。

五、imx6ull 下 UART 驱动分析

文档路径:drivers\tty\serial\imx.c

1、驱动框架

static struct uart_driver imx_reg = {.owner          = THIS_MODULE,.driver_name    = DRIVER_NAME,.dev_name       = DEV_NAME,.major          = SERIAL_IMX_MAJOR,.minor          = MINOR_START,.nr             = ARRAY_SIZE(imx_ports),	// imx_ports 封装 struct uart_port.cons           = IMX_CONSOLE,
};static struct platform_driver serial_imx_driver = {.probe		= serial_imx_probe,.remove		= serial_imx_remove,.suspend	= serial_imx_suspend,.resume		= serial_imx_resume,.id_table	= imx_uart_devtype,.driver		= {.name	= "imx-uart",.of_match_table = imx_uart_dt_ids,},
};static int __init imx_serial_init(void)
{int ret = uart_register_driver(&imx_reg);if (ret)return ret;ret = platform_driver_register(&serial_imx_driver);if (ret != 0)uart_unregister_driver(&imx_reg);return ret;
}static void __exit imx_serial_exit(void)
{platform_driver_unregister(&serial_imx_driver);uart_unregister_driver(&imx_reg);
}

1、UART 驱动使用 platform 驱动框架。

2、在加载驱动时注册 UART 驱动。

2、初始化

serial_imx_probe 函数。

3、注销

serial_imx_remove 函数。

六、添加设备树

1、UART 设备树相关说明

见文档:Documentation\devicetree\bindings\serial\fsl-imx-uart.txt

2、确定使用 UART

通过原理图可以确定,使用接口为 uart3

3、添加 pinctrl 子系统相关配置

pinctrl_uart3: uart3grp {fsl,pins = ;
};

4、在 uart3 下追加配置

&uart3 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_uart3>;status = "okay";
};

5、编译设备树

onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ make dtbsCHK     include/config/kernel.releaseCHK     include/generated/uapi/linux/version.hCHK     include/generated/utsrelease.h
make[1]: 'include/generated/mach-types.h' is up to date.CHK     include/generated/bounds.hCHK     include/generated/asm-offsets.hCALL    scripts/checksyscalls.shDTC     arch/arm/boot/dts/imx6ull-alientek-emmc.dtbDTC     arch/arm/boot/dts/imx6ull-alientek-nand.dtb
onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ 

6、测试

# pwd
/proc/device-tree/soc/aips-bus@02100000
# ls
#address-cells       lcdif@021c8000       serial@021f0000
#size-cells          mmdc@021b0000        serial@021f4000
adc@02198000         name                 serial@021fc000
compatible           ocotp-ctrl@021bc000  usb@02184000
csi@021c4000         pxp@021cc000         usb@02184200
csu@021c0000         qspi@021e0000        usbmisc@02184800
ethernet@02188000    ranges               usdhc@02190000
i2c@021a0000         reg                  usdhc@02194000
i2c@021a4000         romcp@021ac000       weim@021b8000
i2c@021a8000         serial@021e8000
i2c@021f8000         serial@021ec000
# cd serial@021ec000/
# ls
clock-names    dma-names      name           reg
clocks         dmas           pinctrl-0      status
compatible     interrupts     pinctrl-names
# cat compatible
fsl,imx6ul-uartfsl,imx6q-uartfsl,imx21-uart#
#

七、驱动编写

uart 相关驱动 Linux 内核已添加,不需要我们编写。
在这里插入图片描述

# ls /dev/ttymxc* -l
crw-------    1 root     root      207,  16 Jan  1 05:59 /dev/ttymxc0
crw-rw----    1 root     root      207,  18 Jan  1 00:00 /dev/ttymxc2
#

通过以上消息,uart3 驱动加载成功,uart3 在应用层映射为 ttymxc2

八、应用编写

1、概述

LinuxUART 驱动和 tty 关系密切,在应用层使用 UART 是需特别注意,否则一些特殊字符传输可能有问题。

2、程序

/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.文件名 : uart_test.c作者 : 邓涛版本 : V1.0描述 : 串口在原始模式下进行数据传输--应用程序示例代码其他 : 无论坛 : www.openedv.com日志 : 初版 V1.0 2021/7/20 邓涛创建***************************************************************/#define _GNU_SOURCE     //在源文件开头定义_GNU_SOURCE宏
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include typedef struct uart_hardware_cfg {unsigned int baudrate;      /* 波特率 */unsigned char dbit;         /* 数据位 */char parity;                /* 奇偶校验 */unsigned char sbit;         /* 停止位 */
} uart_cfg_t;static struct termios old_cfg;  //用于保存终端的配置参数
static int fd;      //串口终端对应的文件描述符/**** 串口初始化操作** 参数device表示串口终端的设备节点**/
static int uart_init(const char *device)
{/* 打开串口终端 */fd = open(device, O_RDWR | O_NOCTTY);if (0 > fd) {fprintf(stderr, "open error: %s: %s\n", device, strerror(errno));return -1;}/* 获取串口当前的配置参数 */if (0 > tcgetattr(fd, &old_cfg)) {fprintf(stderr, "tcgetattr error: %s\n", strerror(errno));close(fd);return -1;}return 0;
}/**** 串口配置** 参数cfg指向一个uart_cfg_t结构体对象**/
static int uart_cfg(const uart_cfg_t *cfg)
{struct termios new_cfg = {0};   //将new_cfg对象清零speed_t speed;/* 设置为原始模式 */cfmakeraw(&new_cfg);/* 使能接收 */new_cfg.c_cflag |= CREAD;/* 设置波特率 */switch (cfg->baudrate) {case 1200: speed = B1200;break;case 1800: speed = B1800;break;case 2400: speed = B2400;break;case 4800: speed = B4800;break;case 9600: speed = B9600;break;case 19200: speed = B19200;break;case 38400: speed = B38400;break;case 57600: speed = B57600;break;case 115200: speed = B115200;break;case 230400: speed = B230400;break;case 460800: speed = B460800;break;case 500000: speed = B500000;break;default:    //默认配置为115200speed = B115200;printf("default baud rate: 115200\n");break;}if (0 > cfsetspeed(&new_cfg, speed)) {fprintf(stderr, "cfsetspeed error: %s\n", strerror(errno));return -1;}/* 设置数据位大小 */new_cfg.c_cflag &= ~CSIZE;  //将数据位相关的比特位清零switch (cfg->dbit) {case 5:new_cfg.c_cflag |= CS5;break;case 6:new_cfg.c_cflag |= CS6;break;case 7:new_cfg.c_cflag |= CS7;break;case 8:new_cfg.c_cflag |= CS8;break;default:    //默认数据位大小为8new_cfg.c_cflag |= CS8;printf("default data bit size: 8\n");break;}/* 设置奇偶校验 */switch (cfg->parity) {case 'N':       //无校验new_cfg.c_cflag &= ~PARENB;new_cfg.c_iflag &= ~INPCK;break;case 'O':       //奇校验new_cfg.c_cflag |= (PARODD | PARENB);new_cfg.c_iflag |= INPCK;break;case 'E':       //偶校验new_cfg.c_cflag |= PARENB;new_cfg.c_cflag &= ~PARODD; /* 清除PARODD标志,配置为偶校验 */new_cfg.c_iflag |= INPCK;break;default:    //默认配置为无校验new_cfg.c_cflag &= ~PARENB;new_cfg.c_iflag &= ~INPCK;printf("default parity: N\n");break;}/* 设置停止位 */switch (cfg->sbit) {case 1:     //1个停止位new_cfg.c_cflag &= ~CSTOPB;break;case 2:     //2个停止位new_cfg.c_cflag |= CSTOPB;break;default:    //默认配置为1个停止位new_cfg.c_cflag &= ~CSTOPB;printf("default stop bit size: 1\n");break;}/* 将MIN和TIME设置为0 */new_cfg.c_cc[VTIME] = 0;new_cfg.c_cc[VMIN] = 0;/* 清空缓冲区 */if (0 > tcflush(fd, TCIOFLUSH)) {fprintf(stderr, "tcflush error: %s\n", strerror(errno));return -1;}/* 写入配置、使配置生效 */if (0 > tcsetattr(fd, TCSANOW, &new_cfg)) {fprintf(stderr, "tcsetattr error: %s\n", strerror(errno));return -1;}/* 配置OK 退出 */return 0;
}/***
--dev=/dev/ttymxc2
--brate=115200
--dbit=8
--parity=N
--sbit=1
--type=read
***/
/**** 打印帮助信息**/
static void show_help(const char *app)
{printf("Usage: %s [选项]\n""\n必选选项:\n""  --dev=DEVICE     指定串口终端设备名称, 譬如--dev=/dev/ttymxc2\n""  --type=TYPE      指定操作类型, 读串口还是写串口, 譬如--type=read(read表示读、write表示写、其它值无效)\n""\n可选选项:\n""  --brate=SPEED    指定串口波特率, 譬如--brate=115200\n""  --dbit=SIZE      指定串口数据位个数, 譬如--dbit=8(可取值为: 5/6/7/8)\n""  --parity=PARITY  指定串口奇偶校验方式, 譬如--parity=N(N表示无校验、O表示奇校验、E表示偶校验)\n""  --sbit=SIZE      指定串口停止位个数, 譬如--sbit=1(可取值为: 1/2)\n""  --help           查看本程序使用帮助信息\n\n", app);
}/**** 信号处理函数,当串口有数据可读时,会跳转到该函数执行**/
static void io_handler(int sig, siginfo_t *info, void *context)
{unsigned char buf[10] = {0};int ret;int n;if(SIGRTMIN != sig)return;/* 判断串口是否有数据可读 */if (POLL_IN == info->si_code) {ret = read(fd, buf, 8);     //一次最多读8个字节数据printf("[ ");for (n = 0; n < ret; n++)printf("0x%hhx ", buf[n]);printf("]\n");}
}/**** 异步I/O初始化函数**/
static void async_io_init(void)
{struct sigaction sigatn;int flag;/* 使能异步I/O */flag = fcntl(fd, F_GETFL);  //使能串口的异步I/O功能flag |= O_ASYNC;fcntl(fd, F_SETFL, flag);/* 设置异步I/O的所有者 */fcntl(fd, F_SETOWN, getpid());/* 指定实时信号SIGRTMIN作为异步I/O通知信号 */fcntl(fd, F_SETSIG, SIGRTMIN);/* 为实时信号SIGRTMIN注册信号处理函数 */sigatn.sa_sigaction = io_handler;   //当串口有数据可读时,会跳转到io_handler函数sigatn.sa_flags = SA_SIGINFO;sigemptyset(&sigatn.sa_mask);sigaction(SIGRTMIN, &sigatn, NULL);
}int main(int argc, char *argv[])
{uart_cfg_t cfg = {0};char *device = NULL;int rw_flag = -1;unsigned char w_buf[10] = {0x11, 0x22, 0x33, 0x44,0x55, 0x66, 0x77, 0x88};    //通过串口发送出去的数据int n;/* 解析出参数 */for (n = 1; n < argc; n++) {if (!strncmp("--dev=", argv[n], 6))device = &argv[n][6];else if (!strncmp("--brate=", argv[n], 8))cfg.baudrate = atoi(&argv[n][8]);else if (!strncmp("--dbit=", argv[n], 7))cfg.dbit = atoi(&argv[n][7]);else if (!strncmp("--parity=", argv[n], 9))cfg.parity = argv[n][9];else if (!strncmp("--sbit=", argv[n], 7))cfg.sbit = atoi(&argv[n][7]);else if (!strncmp("--type=", argv[n], 7)) {if (!strcmp("read", &argv[n][7]))rw_flag = 0;        //读else if (!strcmp("write", &argv[n][7]))rw_flag = 1;        //写}else if (!strcmp("--help", argv[n])) {show_help(argv[0]); //打印帮助信息exit(EXIT_SUCCESS);}}if (NULL == device || -1 == rw_flag) {fprintf(stderr, "Error: the device and read|write type must be set!\n");show_help(argv[0]);exit(EXIT_FAILURE);}/* 串口初始化 */if (uart_init(device))exit(EXIT_FAILURE);/* 串口配置 */if (uart_cfg(&cfg)) {tcsetattr(fd, TCSANOW, &old_cfg);   //恢复到之前的配置close(fd);exit(EXIT_FAILURE);}/* 读|写串口 */switch (rw_flag) {case 0:  //读串口数据async_io_init();	//我们使用异步I/O方式读取串口的数据,调用该函数去初始化串口的异步I/Ofor ( ; ; )sleep(1);   	//进入休眠、等待有数据可读,有数据可读之后就会跳转到io_handler()函数break;case 1:   //向串口写入数据for ( ; ; ) {   		//循环向串口写入数据write(fd, w_buf, 8); 	//一次向串口写入8个字节sleep(1);       		//间隔1秒钟}break;}/* 退出 */tcsetattr(fd, TCSANOW, &old_cfg);   //恢复到之前的配置close(fd);exit(EXIT_SUCCESS);
}

程序使用正点原子提供。

九、测试

1、数据接收

# ./uart_app --dev=/dev/ttymxc2 --type=read
default baud rate: 115200
default data bit size: 8
default parity: N
default stop bit size: 1[ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
[ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
[ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
[ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
[ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
[ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
[ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]
[ 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 ]#

2、数据发送

在这里插入图片描述

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...