EBAZ4205 ZYNQ HDMI扩展板 显示Linux桌面播放视频
之前的文章介绍了如何生成EBAZ4205矿板的u-boot和Linux内核,使用的硬件只有改完SD卡启动的EBAZ4205裸板,只能通过串口与板子通信。这次尝试启动桌面并播放视频(由于EBAZ4205没有引出USB,仍然通过串口控制),看看双核A9性能怎样。原板子是没有显示接口的,需要自己做一块HDMI扩展板。
ZYNQ的FPGA部分使用的是Artix-7,网上有不少Artix-7输出HDMI的教程,大多使用Digilent DVI IP,FPGA部分可以通过这个IP直接输出TMDS编码的数据,这样就不用专用的RGB2HDMI的IC了。Digilent DVI IP将RGB888 1080P时序的并口数据编码成10bit一组的串行数据输出到pin上,1080P的像素时钟是148.5Mhz,FPGA内部最高时钟是148.5的5倍,采用双边延输出。Digilent DVI IP不支持将音频编码进TMDS流,想要输出音频必须在FPGA上搭建I2S IP,或者使用PDM的方式输出。
EBAZEXT-V2扩展板
古早之前就画过一板VGA的扩展板,后来没测过。VGA接口太笨重了,索性再画了一板HDMI。板子中DDC/I2C部分不使用,可能有设计上的bug。没有DDC接口,Linux将获取不到显示器分辨率,我们可以设置设备树让VDMA强制输出1920x1080P60的画面。
接口资源:
- 全部采用USB-C供电
- CMOS/GPIO 接ov摄像头或通用IO
- 1.3寸LCD/GPIO 接SPI屏幕(1.3寸 240x240 SPI st7789方案)
- USB转串口
- HDMI-A 1080P60帧
- PDM音频输出
- 按键x2、LEDx2
这次测试中只用到了HDMI接口。
VIVADO 2019.1硬件搭建
为了输出画面,FPGA部分要将图像数据从DDR搬运到DVI IP,该工作由XILINX VDMA完成。XILINX VDMA输入AXI4-MM,输出AXI4-Stream接口数据,需要XILINX AXI4S-VID-OUT IP转换为视频时序。时序由XILINX VTC产生。整个硬件架构参考原子《领航者 ZYNQ 之嵌入式 SDK开发指南 V1.3》中的第20章,硬件搭完后必须先裸机测试下SD卡的图片能不能显示。可以直接复制原子的设计,但要修改内存大小、增加concat_2和中断、增加MII接口和4bit concat。硬件架构下图。图中以太网使用的是GMII接口,125MHz x 8bit。4205板子使用MII的PHY芯片,GMII兼容MII,当GMII工作在25MHz x 4bit时就是MII(高4位不用)。PHY芯片自动侦测链路速率,给FPGA 25MHz或2.5MHz时钟。25MHz x 4bit=100Mbps,考虑到96clk帧间隔和包头,实际速率大约90几。
图中的中断concat_2必须接上,否则petalinux报错。综合编译导出硬件设计后产生以下文件备用:
to_pl.bit
fsbl.elf
xxx.hdf (实际上xxx.hdf文件就足以可以产生fsbl.elf 和to_pl.bit了,petalinux-build之后就有了)
在ubuntu16.04环境编译 u-boot & Linux
以下过程参考自CSDN文章
全部采用18.3版本。也可以用最新版本,但版本必须一致。如果选择petalinux19.1之后版本,建议直接在ubuntu安装 Vitis
下载:
去XILINX官网下载 petalinux-v2018.3-final-installer.run
https://github.com/Xilinx/linux-xlnx/releases/tag/xlnx_rebase_v4.14_2018.3
https://github.com/Xilinx/u-boot-xlnx/releases/tag/xilinx-v2018.3
https://github.com/Digilent/linux-digilent/blob/master/drivers/clk/clk-dglnt-dynclk.c
https://github.com/Digilent/linux-digilent/blob/master/drivers/gpu/drm/xilinx/digilent_encoder.c
执行过程:
# 安装petalinux
petalinux-v2018.3-final-installer.run ./petalinux
unzip u-boot和linux压缩包到当前目录
# 载入petalinux环境
source ./petalinux/settings.sh
# 建立petalinux工程
petalinux-create --type project --template zynq --name proj
# 拷贝vivado export hardware 中生成的 hdf文件到当前目录
cp x.hdf ./
# 配置hdf到petalinux工程
cd proj
petalinux-config --get-hw-description=../
# 出现选择框(我们选择外部linux源码编译工程,因为我们需要在Linux中添加内核模块,两个.c文件)
1. Image Packaging Configuration -> Root filesystem type 选项中选择 SD card
2. Linux Components Selection -> linux-kernel选择ext-local-src。新增的External linux-kernel local source settings选项内填入你的刚解压linux源码完整路径 保存退出。
# 进入之前解压后的xilinx linux目录
cd ../linux-xlnx-xlnx_rebase_v4.14_2018.3
#------------------------------------------------------------------------------------------
# 增加内核驱动
# 打开
vi ./drivers/clk/Kconfig
# 加入以下内容:
config COMMON_CLK_DGLNT_DYNCLKtristate "Digilent axi_dynclk Driver"depends on ARCH_ZYNQ || MICROBLAZEhelp---help---Support for the Digilent AXI Dynamic Clock core for XilinxFPGAs.
# 打开
vi ./drivers/clk/Makefile
# 加入以下内容:
obj-$(CONFIG_COMMON_CLK_DGLNT_DYNCLK) += clk-dglnt-dynclk.o
# 打开
vi ./drivers/gpu/drm/xilinx/Kconfig
# 加入以下内容:
config DRM_DIGILENT_ENCODERtristate "Digilent VGA/HDMI DRM Encoder Driver"depends on DRM_XILINXhelpDRM slave encoder for Video-out on Digilent boards.
# 打开
vi ./drivers/gpu/drm/xilinx/Makefile
# 加入以下内容:
obj-$(CONFIG_DRM_DIGILENT_ENCODER) += digilent_encoder.o# 加入刚下载的.c文件
cp clk-dglnt-dynclk.c ./drivers/clk/
cp digilent_encoder.c ./drivers/gpu/drm/xilinx/cd ../proj
# 配置linux内核
petalinux-config -c kernel
1. 在 Device Drivers -> Graphics support,选择 Digilent VGA/HDMI DRM Encoder Driver 按 y
2. 在 Device Drivers -> Common Clock Framework 选项中选择 Digilent axi_dynclk Driver 按 y
3. 保存退出,不要修改默认的保存文件名
#------------------------------------------------------------------------------------------
以上过程就是在Linux中增加新内核模块/驱动的方法,这个是编译进内核而不是编译成.ko驱动文件。可以参考原子《领航者 ZYNQ 之嵌入式Linux 开发指南 V1.5.2》第四篇驱动开发,讲的很详细。
- 在对应目录加入.c文件
- 修改对应目录内的Makefile、Kconfig。使之出现在make menuconfig菜单中
# 进入petalinux工程编辑设备树
vi project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
【EBAZ4205+EBAZEXTV2】的设备树内容如下(EBAZEXTV2的LED/KEY外设未添加)。这东西在build之前看不到system-conf.dtsi的内容,否则就可以不依赖petalinux了。
笔者不清楚pinctrl0部分的作用,一般而言,在fsbl配置好FPGA后,MIO的多路选择器已经选到正确的引脚,自带的pinctrl驱动可能是多余的 (MIO的多路选择器、FCLK等外设由ARM GPIO子系统控制)。需要注意的是ebaz-4205设备树已经出现在最新Linux内核中,该设备树对IO进行了描述。
以下内容部分参考Linux内核中的设备树。与显示相关的是最后三个端点。其实除了led和key这些节点,其他节点petalinux都已经生成了。可查看system_top.dts,添加它所没有的节点。在文件system_top.dts中,system-user.dtsi被包含在了最后,因此system-user.dtsi权力最大。
/include/ "system-conf.dtsi"
/ {model = "EBAZ4205 board";compatible = "xlnx,zynq-EBAZ4205", "xlnx,zynq-7000";aliases {ethernet0 = &gem0;serial0 = &uart1;mmc0 = &sdhci0;};memory@0 {device_type = "memory";reg = <0x0 0x10000000>;};chosen {bootargs = "";stdout-path = "serial0:115200n8";};ebaz-keys {compatible = "gpio-keys";autorepeat;s2 {label = "s2";gpios = <&gpio0 20 0>;/*KEY_POWER*/linux,code = <116>;wakeup-source;autorepeat;};s3 {label = "s3";gpios = <&gpio0 32 0>;/*KEY_HOME*/linux,code = <102>;wakeup-source;autorepeat;};};ebaz-leds {compatible = "gpio-leds";led-green {label = "green";gpios = <&gpio0 54 1>;default-state = "on";};led-red {label = "red";gpios = <&gpio0 55 1>;default-state = "on";};};
};&clkc {ps-clk-frequency = <33333333>;
};&gem0 {status = "okay";phy-mode = "mii";phy-handle = <&phy>;/* PHY clock */assigned-clocks = <&clkc 18>;assigned-clock-rates = <25000000>;phy: ethernet-phy@0 {reg = <0>;};
};&smcc {status = "okay";
};
/**/
&nand0 {status = "okay";pinctrl-names = "default";pinctrl-0 = <&pinctrl_nand0_default>;partition@0 {label = "nand-fsbl-uboot";reg = <0x0 0x8000000>;};
};&pinctrl0 {pinctrl_nand0_default: nand0-default {mux {groups = "smc0_nand8_grp";function = "smc0_nand";};conf {groups = "smc0_nand8_grp";bias-pull-up;};};pinctrl_uart1_default: uart1-default {mux {groups = "uart1_4_grp";function = "uart1";};conf {groups = "uart1_4_grp";slew-rate = <0>;io-standard = <3>;};};
};&sdhci0 {u-boot,dm-pre-reloc;status = "okay";
};&uart1 {u-boot,dm-pre-reloc;status = "okay";pinctrl-names = "default";pinctrl-0 = <&pinctrl_uart1_default>;
};&amba_pl {hdmi_encoder_0:hdmi_encoder {compatible = "digilent,drm-encoder";//digilent,edid-i2c = <&i2c0>;//我没有使用DDC/I2C接口因此把这段注释,强制使用以下分辨率digilent,hpref = <1920>;digilent,vpref = <1080>;};xilinx_drm {compatible = "xlnx,drm";xlnx,vtc = <&v_tc_0>;xlnx,connector-type = "HDMIA";xlnx,encoder-slave = <&hdmi_encoder_0>;clocks = <&axi_dynclk_0>;dglnt,edid-i2c = <&i2c0>;planes {xlnx,pixel-format = "rgb888";plane0 {dmas = <&axi_vdma_0 0>;dma-names = "dma";};};};
};
&axi_dynclk_0 {compatible = "digilent,axi-dynclk";#clock-cells = <0>;clocks = <&clkc 15>;
};
&v_tc_0 {compatible = "xlnx,v-tc-5.01.a";
};
执行:
petalinux-build
# 由zimage产生uImage:
petalinux-package --image -c kernel --format uImage
# 将设备树名字改为devicetree.dtb
mv xxxx.dtb devicetree.dtb
# 如果更改了设备树dts,不用再petalinux-build ,编译设备树:
petalinux-build -b device-tree
产生uboot、内核(uImage)、根文件系统、设备树(devicetree.dtb)、zynq_fsbl.elf、xx.bit。这里只用设备树(devicetree.dtb)和内核(uImage):
uImage
devicetree.dtb
petalinux-build默认会产生image.ub和zImage镜像。image.ub是由zImage .dtb .cpio.gz 构成。.cpio.gz 是内存盘,一般在调试的时候使用。在使用SD卡时,用bootargs告诉内核rootfs的盘位。
我习惯用uboot的bootm启动uImage和加载设备树,所以将zImage转成uImage。用bootz启动zImage也是可以的。
内存中的存放地址如下(zynq-7000):
- 0x0 devicetree_image
- 0x8000 kernel_image
- 0x1000000 ramdisk_image 如果使用内存盘就放到这个位置,本文使用SD卡上的ext4根目录,所以没用到
进入u-boot源码。参考之前文章(EBAZ4205 ZYNQ 7Z010 u-boot & Linux 生成方法记录)的4、5、6、7、8、9、10步生成 BOOT.bin和devicetree.dtb,注意第10步使用的是新的硬件bit和fsbl.elf(这两个文件可以从vivado&XILINX SDK生成也可以直接用上面petalinux-build产生的)。注意这里u-boot产生的devicetree.dtb是不需要的,用petalinux-build产生的。
参照之前文章第12步编写uEnv.txt。
BOOT.bin
uEnv.txt
将BOOT.bin uEnv.txt uImage devicetree.dtb放入SD卡fat32分区,分区大小设置为100M左右。大部分进入U-BOOT但启动不了Linux的原因是U-BOOT的bootcmd变量设置不对。在运行bootcmd变量之前,U-BOOT会载入uEnv.txt内的环境变量,可以在环境变量内做一些操作跳过自动bootcmd。在uEnv.txt内修改bootcmd或者加入类似于uenvcmd=run bootm…的命令来指定启动过程。
根文件系统
可以直接使用的根文件系统:linaro-precise-ubuntu-desktop-20120723305.tar.gz(https://releases.linaro.org/archive/12.07/ubuntu/precise-images/ubuntu-desktop/)
建议在命令行解压到SD卡ext4分区:
sudo tar --strip-components=3 -C /media/xxx/rootfs -xzpf linaro-precise-ubuntu-desktop-20120723-305.tar.gz binary/boot/filesystem.dir
分区工具用ubuntu自带的图形disk工具或者用 DiskGenius
启动
启动后在ebaz4205的自带串口登录,执行一些必要的修改:
vi /etc/apt/sources.list
# 源改为以下内容
deb http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/ precise main universe
deb-src http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/ precise main universe
deb http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/ precise-security main universe
deb-src http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/ precise-security main universe
deb http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/ precise-updates main universe
deb-src http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/ precise-updates main universe
# 安装
apt-get update
apt-get install openssh-server mplayer fbi jfbterm
该Linux内核已经支持HDMI上的 FrameBuffer 所有对显存的操作都是针对/dev/fb0文件的。FrameBuffer学习参考
http://bbs.chinaunix.net/thread-1932291-1-1.html
https://zhouyuqian.com/2020/05/11/ZYNQ-%E7%A7%BB%E6%A4%8D-Linux-Frame-Buffer/
https://blog.yangl1996.com/post/ba-tu-pian-zhi-jie-xie-ru-framebuffer/
framebuffer的c语言操作一般是这样的:
// 完整的程序参见附录,该程序引用自原子《领航者 ZYNQ 之嵌入式Linux 开发指南 V1.5.2》。fd = open("/dev/fb0", O_RDWR);/* 获取framebuffer设备的参数信息 */ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);screensize = fb_var.yres * fb_fix.line_length;// 用户内存到内核地址的映射// 如果不做映射,就相当于读写文件,读写文件函数会进入内核态运行,将数据拷贝到用户内存。多一次拷贝。base = (unsigned char *)mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);memset(base, 0x00, screensize); // 显存清零
打开桌面:
# lightdm上电初期存在顺序bug,显示不正常,需要先关闭lightdm上电启动。
echo "manual" | sudo tee -a /etc/init/lightdm.override
kill -9 <lightdm的pid>
# 重新启动lightdm桌面
startx &
尝试播放视频
一、图片文件与pdf文件浏览:
fbi -T 1 1.JPG
fbgs -c 1.pdf
二、视频播放,可以用mplayer:
mplayer -lavdopts threads=2 -vo fbdev:/dev/fb0 -nosound /root/Videos/1080p1.mp4
-vo fbdev:/dev/fb0 指定framebuffer文件。
当采用两个线程时,cpu占用率为90%。帧数仍然很低。
三、中文显示(HDMI屏幕显示终端,串口输入按键):
jfbterm
以上备忘
有些地方仍有不足,实际复现时可能会遇到其他困难,如有疑问欢迎讨论学习
部分资料托管在 GitHub,Elrori/EBAZ4205仓库遵守GPL。
附录
/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.文件名 : lcdTest.c作者 : 邓涛版本 : V1.0描述 : LCD应用层测试程序其他 : 无论坛 : www.openedv.com日志 : 初版V1.0 2020/7/23 邓涛创建***************************************************************/#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>static void display_demo_1 (unsigned char *frame, unsigned int width, unsigned int height, unsigned int stride)
{unsigned int xcoi, ycoi;unsigned char wRed, wBlue, wGreen;unsigned int iPixelAddr = 0;for(ycoi = 0; ycoi < height; ycoi++) {for(xcoi = 0; xcoi < (width * 3); xcoi += 3) {if (((xcoi / 4) & 0x20) ^ (ycoi & 0x20)) {wRed = 255;wGreen = 255;wBlue = 255;} else {wRed = 0;wGreen = 0;wBlue = 0;}frame[xcoi + iPixelAddr + 0] = wRed;frame[xcoi + iPixelAddr + 1] = wGreen;frame[xcoi + iPixelAddr + 2] = wBlue;}iPixelAddr += stride;}
}static void display_demo_2 (unsigned char *frame, unsigned int width, unsigned int height, unsigned int stride)
{unsigned int xcoi, ycoi;unsigned int iPixelAddr = 0;unsigned char wRed, wBlue, wGreen;unsigned int xInt;xInt = width * 3 / 8;for(ycoi = 0; ycoi < height; ycoi++) {for(xcoi = 0; xcoi < (width*3); xcoi+=3) {if (xcoi < xInt) { //White colorwRed = 255;wGreen = 255;wBlue = 255;}else if ((xcoi >= xInt) && (xcoi < xInt*2)) { //YELLOW colorwRed = 255;wGreen = 255;wBlue = 0;}else if ((xcoi >= xInt * 2) && (xcoi < xInt * 3)) { //CYAN colorwRed = 0;wGreen = 255;wBlue = 255;}else if ((xcoi >= xInt * 3) && (xcoi < xInt * 4)) { //GREEN colorwRed = 0;wGreen = 255;wBlue = 0;}else if ((xcoi >= xInt * 4) && (xcoi < xInt * 5)) { //MAGENTA colorwRed = 255;wGreen = 0;wBlue = 255;}else if ((xcoi >= xInt * 5) && (xcoi < xInt * 6)) { //RED colorwRed = 255;wGreen = 0;wBlue = 0;}else if ((xcoi >= xInt * 6) && (xcoi < xInt * 7)) { //BLUE colorwRed = 0;wGreen = 0;wBlue = 255;}else { //BLACK colorwRed = 0;wGreen = 0;wBlue = 0;}frame[xcoi+iPixelAddr + 0] = wRed;frame[xcoi+iPixelAddr + 1] = wGreen;frame[xcoi+iPixelAddr + 2] = wBlue;}iPixelAddr += stride;}
}int main (int argc, char **argv)
{struct fb_var_screeninfo fb_var = {0};struct fb_fix_screeninfo fb_fix = {0};unsigned int screensize;unsigned char *base;int fd;/* 打开LCD */fd = open("/dev/fb0", O_RDWR);if (fd < 0) {printf("Error: Failed to open /dev/fb0 device.\n");return fd;}/* 获取framebuffer设备的参数信息 */ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);/* mmap映射 */screensize = fb_var.yres * fb_fix.line_length;base = (unsigned char *)mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if ((unsigned char *)-1 == base) {close(fd);return -1;}memset(base, 0x00, screensize); // 显存清零/* 循环显示不同颜色 */for ( ; ; ) {display_demo_1(base, fb_var.xres, fb_var.yres, fb_fix.line_length);sleep(2);display_demo_2(base, fb_var.xres, fb_var.yres, fb_fix.line_length);sleep(2);}/* 关闭设备 释放内存 */memset(base, 0x00, screensize);munmap(base, screensize);close(fd);return 0;
}
EBAZ4205 ZYNQ HDMI扩展板 显示Linux桌面播放视频相关推荐
- linux gst-launch 播放视频旋转,【视频开发】Gstreamer中一些gst-launch常用命令
GStreamer是著名的开源多媒体框架,功能强大,其命令行程序 gst-launch 可以实现很多常规测试.播放等,作为系统调试等是非常方便的. 1.摄像头测试 gst-launch v4l2src ...
- ViewPager中显示图片和播放视频填坑之旅
一.需求来源与实现思路 1.最近项目需求中有用到需要在ViewPager中播放视频和显示图片的功能,视频是本地视频,最开始的实现思路是ViewPager中根据当前item位置对应的是图片还是视频去初始 ...
- 浏览器显示linux桌面,如何从Web浏览器远程监视Linux服务器和桌面
当你要监视一台Linux机器时,你会有很多的选择.虽然现在有很多产品质量监控方案(比如 Nagios. Zabbix. Zenoss),它们拥有华丽的UI,可扩展监控,易于理解的报告等等,这些方案对于 ...
- linux桌面播放视频播放器,五个最佳桌面媒体播放器
你的硬盘里可能有数百兆的音乐和视频文件,因此你需要一个完美的桌面媒体播放器,不仅仅能够完美博客你所有的多媒体文件,还能帮助你找到你需要的歌曲或电影,这里我们就推荐了五个英文的桌面媒体播放器,相信能帮助 ...
- linux双屏播放视频,Ubuntu Linux下双屏显示解决方案
Ubuntu从起后居然把Windows的设置给记住了.显示ok.我晕.以前倒是也有过这问题,在双系统情况下,外接键盘的灯会继承Windows的 早就有心弄个显示器,把笔记本的外接上,倒不是说非要2个屏 ...
- docker显示linux桌面,怎样在桌面上安装 Docker CE?
按照这些简单的步骤在你的 Linux.Mac 或 Windows 桌面上安装 Docker CE. 在上一篇文章中,我们学习了容器世界的一些基本术语.当我们运行命令并在后续文章中使用其中一些术语时,这 ...
- UE4_UE5播放视频(附工程)
UE4_UE5播放视频的方式: 要播放本地视频文件,先将视频文件存放在路径"Content/Movies"下,Movies文件夹默认是没有的,得自己手动创建. 参考官方教程:htt ...
- ssm项目实现上传视频,在web端播放视频
实现的思路: 1,上传视频,将视频存放在服务器端,数据库中存放相对服务器的相对地址 2,网页端播放视频:使用ckplayer插件:www.ckplayer.com/ 参考:https://blog.c ...
- 计算机 hdmi不显示桌面,请问各位网友,用HDMI线接电脑主机和液晶显示器时。显示器显示没有桌面图标,只有桌面壁纸。怎么回事...
满意答案 nbchj7028960 推荐于 2017.08.14 采纳率:46% 等级:12 已帮助:16066人 笔记本电脑用HDMI连接电视后桌面上没有图标只有桌面壁纸,出现这种情况一般是因 ...
最新文章
- RemoteFX原理简介
- [Swust OJ 404]--最小代价树(动态规划)
- 绿色数据中心将惠及众生
- OpenGL超级宝典笔记——遮挡查询 [转]
- Python算法——二叉树
- [css] 会引起Reflow和Repaint的操作有哪些?
- 如何HTML中输入正确格式,以HTML格式输入样式
- 赛门铁克调研发现越来越多的物联网设备被用于实施DDoS攻击
- static成员函数
- HDU 4539 郑厂长系列故事——排兵布阵 —— 状压DP
- 基于网易云API做的一个扫码登录
- nacos 适配人大金仓数据
- MFC 预处理器相关知识
- allegro不规则焊盘制作
- js如何简单实现汉字转成拼音的功能
- stack、queue、priority_queue
- Java Application 程序
- 企业微信脚手架(第三方)
- SAE J1708协议
- 调光调色LED台灯触摸芯片-DLT8MA12T
热门文章
- AI新动向:多模态+自监督!Meta AI一次搞定语音、视觉和文本三个SOTA
- 会心自选-淘宝店铺装修和转化率的关系
- [转]俞敏洪:我和马云就差了8个字... [来自: news.mbalib.com]
- 《研磨设计模式》 与 《设计模式之禅》对比
- 故宫官网推荐的一日游路线
- 微信小程序开发BUG经验总结
- 9个面向前端开发者的有用VSCode 插件工具
- 适合Mac版的即时翻译软件
- Android仿qq邮箱账号邮件账号输入框交互
- 蓝色巨人IBM(International Business Machines)