完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
最近在调试RK系列的网口,出现网口丢包很严重,或者获取不到IP(手动获取ip也无法ping通外网和内网)等。出现这类问题很大可能是MAC或PHY的延时出现问题,造成收发数据丢包。这时手动调整PHY芯片寄存器收发延时值或者MAC延时值(设备树节点里TX/RX值)很麻烦。我在测试过程中出现由于PCB的原因造成每块板子的延时值不同,需要按照每块板子进行网口延时调整,这样工作量就很大。
为了解决这类问题,RK代码添加回环测试进行手动测试RX/TX延时值,并且集成自动扫描延时值的功能,大大方便调试。(原理:固定PHY芯片的延时(可以将收发延时关掉),调整MAC延时)接下来讲解下相关代码以及使用方法。 手动测试RX TX的延时值 代码实现部分都在 drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c 文件 Kernel-4.4 和 Kernel-3.10版本,测试RX TX 补丁 Kernel-4.19和之后的版本本身已经包含这部分代码 注:代码需要根据实际的phy进行调整(不通用),不通用的地方代码里有解析 节点确认 新固件(内核)会生成几个sysfs节点,在/sys/devices/platform/fe010000.ethernet/目录下生成几个节点: 使用方法 测试前需要拔掉网线 扫描delayline窗口效果图 代码解析 设备树配置 gmac0: ethernet@fe2a0000 { compatible = “rockchip,rk3568-gmac”, “snps,dwmac-4.20a”; reg = 《0x0 0xfe2a0000 0x0 0x10000》; interrupts = 《GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH》, 《GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH》; interrupt-names = “macirq”, “eth_wake_irq”; rockchip,grf = 《&grf》; clocks = 《&cru SCLK_GMAC0》, 《&cru SCLK_GMAC0_RX_TX》, 《&cru SCLK_GMAC0_RX_TX》, 《&cru CLK_MAC0_REFOUT》, 《&cru ACLK_GMAC0》, 《&cru PCLK_GMAC0》, 《&cru SCLK_GMAC0_RX_TX》, 《&cru CLK_GMAC0_PTP_REF》, 《&cru PCLK_XPCS》; clock-names = “stmmaceth”, “mac_clk_rx”, “mac_clk_tx”, “clk_mac_refout”, “aclk_mac”, “pclk_mac”, “clk_mac_speed”, “ptp_ref”, “pclk_xpcs”; resets = 《&cru SRST_A_GMAC0》; reset-names = “stmmaceth”; snps,mixed-burst; snps,tso; snps,axi-config = 《&gmac0_stmmac_axi_setup》; snps,mtl-rx-config = 《&gmac0_mtl_rx_setup》; snps,mtl-tx-config = 《&gmac0_mtl_tx_setup》; status = “disabled”; mdio0: mdio { compatible = “snps,dwmac-mdio”; #address-cells = 《0x1》; #size-cells = 《0x0》; }; gmac0_stmmac_axi_setup: stmmac-axi-config { snps,wr_osr_lmt = 《4》; snps,rd_osr_lmt = 《8》; snps,blen = 《0 0 0 0 16 8 4》; }; gmac0_mtl_rx_setup: rx-queues-config { snps,rx-queues-to-use = 《1》; queue0 {}; }; gmac0_mtl_tx_setup: tx-queues-config { snps,tx-queues-to-use = 《1》; queue0 {}; }; }; &gmac0 { phy-mode = “rgmii”; clock_in_out = “output”; snps,reset-gpio = 《&gpio2 RK_PC5 GPIO_ACTIVE_LOW》; //RK_PB1 RK_PC5 snps,reset-active-low; snps,reset-delays-us = 《0 30000 150000》; assigned-clocks = 《&cru SCLK_GMAC0_RX_TX》, 《&cru SCLK_GMAC0》; assigned-clock-parents = 《&cru SCLK_GMAC0_RGMII_SPEED》, 《&cru CLK_MAC0_2TOP》; assigned-clock-rates = 《0》, 《125000000》; pinctrl-names = “default”; pinctrl-0 = 《&gmac0_miim &gmac0_tx_bus2 &gmac0_rx_bus2 &gmac0_rgmii_clk &gmac0_rgmii_bus》; tx_delay = 《0x00》; // TX 发送延时,便于测试 rx_delay = 《0x00》; // RX 接受延时,便于测试 phy-handle = 《&rgmii_phy0》; status = “disabled”; }; 相关结构体 路径:drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c // lb私有数据 struct dwmac_rk_lb_priv { struct dma_desc *dma_tx; dma_addr_t dma_tx_phy; struct sk_buff *tx_skbuff; dma_addr_t tx_skbuff_dma; unsigned int tx_skbuff_dma_len; struct dma_desc *dma_rx ____cacheline_aligned_in_smp; dma_addr_t dma_rx_phy; struct sk_buff *rx_skbuff; dma_addr_t rx_skbuff_dma; u32 rx_tail_addr; u32 tx_tail_addr; unsigned int dma_buf_sz; // dma mtu大小 unsigned int buf_sz; // RX 接受FIFO大小 int type; // 回环测试类型(LOOPBACK_TYPE_GMAC LOOPBACK_TYPE_PHY) drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c phy_lb_scan_store int speed; // 测试速度(10 100 1000) drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c phy_lb_scan_store struct dwmac_rk_packet_attrs *packet; unsigned int actual_size; int scan; // 扫描标志位 drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c phy_lb_scan_store int sysfs; // 系统文件标志位 drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c phy_lb_scan_store u32 id; int tx; int rx; int final_tx; int final_rx; }; // 接口模式设置 根据设备树phy-mode = “rgmii”定义 typedef enum { PHY_INTERFACE_MODE_NA, PHY_INTERFACE_MODE_INTERNAL, PHY_INTERFACE_MODE_MII, PHY_INTERFACE_MODE_GMII, PHY_INTERFACE_MODE_SGMII, PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_REVMII, PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_RGMII_ID, PHY_INTERFACE_MODE_RGMII_RXID, PHY_INTERFACE_MODE_RGMII_TXID, PHY_INTERFACE_MODE_RTBI, PHY_INTERFACE_MODE_SMII, PHY_INTERFACE_MODE_XGMII, PHY_INTERFACE_MODE_MOCA, PHY_INTERFACE_MODE_QSGMII, PHY_INTERFACE_MODE_TRGMII, PHY_INTERFACE_MODE_1000BASEX, PHY_INTERFACE_MODE_2500BASEX, PHY_INTERFACE_MODE_RXAUI, PHY_INTERFACE_MODE_XAUI, /* 10GBASE-KR, XFI, SFI - single lane 10G Serdes */ PHY_INTERFACE_MODE_10GKR, PHY_INTERFACE_MODE_MAX, } phy_interface_t; 路径:drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c static const struct of_device_id rk_gmac_dwmac_match[] = { { .compatible = “rockchip,px30-gmac”, .data = &px30_ops }, { .compatible = “rockchip,rk1808-gmac”, .data = &rk1808_ops }, { .compatible = “rockchip,rk3128-gmac”, .data = &rk3128_ops }, { .compatible = “rockchip,rk3228-gmac”, .data = &rk3228_ops }, { .compatible = “rockchip,rk3288-gmac”, .data = &rk3288_ops }, { .compatible = “rockchip,rk3308-mac”, .data = &rk3308_ops }, { .compatible = “rockchip,rk3328-gmac”, .data = &rk3328_ops }, { .compatible = “rockchip,rk3366-gmac”, .data = &rk3366_ops }, { .compatible = “rockchip,rk3368-gmac”, .data = &rk3368_ops }, { .compatible = “rockchip,rk3399-gmac”, .data = &rk3399_ops }, { .compatible = “rockchip,rk3568-gmac”, .data = &rk3568_ops }, { .compatible = “rockchip,rv1108-gmac”, .data = &rv1108_ops }, { .compatible = “rockchip,rv1126-gmac”, .data = &rv1126_ops }, { } }; MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); static struct platform_driver rk_gmac_dwmac_driver = { .probe = rk_gmac_probe, // 匹配后调用 .remove = rk_gmac_remove, .driver = { .name = “rk_gmac-dwmac”, .pm = &rk_gmac_pm_ops, .of_match_table = rk_gmac_dwmac_match, }, }; static const struct net_device_ops stmmac_netdev_ops = { .ndo_open = stmmac_open, .ndo_start_xmit = stmmac_xmit, .ndo_stop = stmmac_release, .ndo_change_mtu = stmmac_change_mtu, .ndo_fix_features = stmmac_fix_features, .ndo_set_features = stmmac_set_features, .ndo_set_rx_mode = stmmac_set_rx_mode, .ndo_tx_timeout = stmmac_tx_timeout, .ndo_do_ioctl = stmmac_ioctl, .ndo_setup_tc = stmmac_setup_tc, .ndo_select_queue = stmmac_select_queue, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = stmmac_poll_controller, #endif .ndo_set_mac_address = stmmac_set_mac_address, }; // include/linux/device.h #define DEVICE_ATTR_RW(_name) struct device_attribute dev_attr_##_name = __ATTR_RW(_name) #define DEVICE_ATTR_RO(_name) struct device_attribute dev_attr_##_name = __ATTR_RO(_name) #define DEVICE_ATTR_WO(_name) struct device_attribute dev_attr_##_name = __ATTR_WO(_name) // include/linux/sysfs.h #define __ATTR_RO(_name) { .attr = { .name = __stringify(_name), .mode = 0444 }, .show = _name##_show, } #define __ATTR_WO(_name) { .attr = { .name = __stringify(_name), .mode = 0200 }, .store = _name##_store, } #define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store) // drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c rk_gmac_probe -》 stmmac_dvr_probe -》 ndev-》netdev_ops = &stmmac_netdev_ops; // 网络设备的操作函数 -》 dwmac_rk_create_loopback_sysfs // 初始化系统下的文件创建 -》 device_create_file(device, &dev_attr_rgmii_delayline); // 创建rgmii_delayline -》 static DEVICE_ATTR_RW(rgmii_delayline); -》 device_create_file(device, &dev_attr_mac_lb); // 创建mac_lb -》 static DEVICE_ATTR_WO(mac_lb); -》 device_create_file(device, &dev_attr_phy_lb); // 创建phy_lb -》 static DEVICE_ATTR_WO(phy_lb); -》 device_create_file(device, &dev_attr_phy_lb_scan); // 创建phy_lb_scan -》 static DEVICE_ATTR_WO(phy_lb_scan); // drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c // echo 1000 》 phy_lb_scan phy_lb_scan_store -》 struct net_device *ndev = dev_get_drvdata(dev); // 网络设备 -》 struct dwmac_rk_lb_priv *lb_priv; // lb私有数据 -》 lb_priv = kzalloc(sizeof(*lb_priv), GFP_KERNEL); // 申请dwmac_rk_lb_priv结构体大小的空间 -》 ret = kstrtoint(buf, 0, &speed); // echo 1000 》 phy_lb_scan (1000赋值speed) -》 lb_priv-》sysfs = 1; // 系统文件标志位为1 -》 lb_priv-》type = LOOPBACK_TYPE_PHY; // 类型回环PHY -》 lb_priv-》speed = speed; // 速度设置1000 -》 lb_priv-》scan = 1; // 扫描 -》 dwmac_rk_loopback_run(priv, lb_priv); -》 phy_iface = dwmac_rk_get_phy_interface(priv); // 获取phy接口 设备树phy-mode = “rgmii” phy_iface=8 “PHY_INTERFACE_MODE_RGMII” -》 ndev_up = ndev-》flags & IFF_UP; // 查看网络设备是否正常 -》 ndev-》netdev_ops-》ndo_stop(ndev); // stmmac_release 关闭驱动的入口节点 -》 priv-》plat-》stmmac_rst -》 priv-》mii-》reset -》 priv-》plat-》stmmac_rst -》 usleep_range(100000, 200000); // 等待phy和控制器准备 -》 dwmac_rk_init // 设置DMA FIFO -》 lb_priv-》dma_buf_sz = 1536; // mtu大小 1500 -》 lb_priv-》buf_sz // rx fifo大小 -》 dwmac_rk_alloc_dma_desc_resources // dma描述符申请 -》 dwmac_rk_init_dma_desc_rings // 初始化dma描述符 -》 dwmac_rk_init_dma_engine // dma配置、TX通道配置、RX通道配置 -》 dwmac_rk_set_loopback // 设置回环参数以及使能 -》 dwmac_rk_set_phy_loopback // type = LOOPBACK_TYPE_PHY -》 dwmac_rk_enable_phy_loopback // enable = 1 根据传输速度写mac寄存器 -》 val = mdiobus_read(priv-》mii, 2, MII_BMCR); // 读phy地址2的寄存器0 所有寄存器存在mii.h里,注意:phy的硬件地址一定要对 -》 mdiobus_write(priv-》mii, 2, MII_BMCR, val); // 设置phy寄存器0为回环模式,写入,这部分每块phy芯片回环值不同需看手册确定是否写入正确 -》 dwmac_rk_loopback_delayline_scan(priv, lb_priv); // lb_priv-》scan=1 -》 dwmac_rk_delayline_scan // delayline窗口扫描 -》 循环发送数据 -》 kfree(lb_priv); // 释放结构体 循环发送解析 static int dwmac_rk_loopback_delayline_scan(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv) { if (lb_priv-》sysfs) return dwmac_rk_delayline_scan(priv, lb_priv); else return dwmac_rk_delayline_scan_cross(priv, lb_priv); } // lb_priv-》sysfs = 1 #define MAX_DELAYLINE 0x7f static int dwmac_rk_delayline_scan(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv) { int tx, rx, tx_sum, rx_sum, count; int tx_mid, rx_mid; int ret = -ENXIO; tx_sum = 0; rx_sum = 0; count = 0; // MAX_DELAYLINE = 0x7f;窗口扫描打印,具体扫描dwmac_rk_loopback_with_identify // 在循环过程中tx范围0x00-0x7f,rx范围0x00-0x7f,组合循环测试 // mac发数据给phy芯片,phy芯片回复回来的值进行检测对比 // 根据延时发送接收数据无误成功,则返回0 // 具体过程会再开篇分析整个循环过程 for (rx = 0x0; rx 《= MAX_DELAYLINE; rx++) { printk(KERN_CONT “RX(0x%02x):”, rx); for (tx = 0x0; tx 《= MAX_DELAYLINE; tx++) { if (!dwmac_rk_loopback_with_identify(priv, lb_priv, tx, rx)) { tx_sum += tx; rx_sum += rx; count++; printk(KERN_CONT “O”); } else { printk(KERN_CONT “ ”); } } printk(KERN_CONT “n”); } // 将所有回环测试成功的延时值相加取平均值,作为最后确定的延时值 if (tx_sum && rx_sum && count) { tx_mid = tx_sum / count; rx_mid = rx_sum / count; if (dwmac_rk_delayline_is_valid(tx_mid, rx_mid)) { lb_priv-》final_tx = tx_mid; lb_priv-》final_rx = rx_mid; ret = 0; } } if (ret) pr_err(“nCan‘t find suitable delaylinen”); else pr_info(“nFind suitable tx_delay = 0x%02x, rx_delay = 0x%02xn”, lb_priv-》final_tx, lb_priv-》final_rx); return ret; } 自动扫描延时 补丁(通用,在kernel:4.19.194测试okay ) 注:该补丁重点针对内核版本为4.19和4.19之后的内核,没有CONFIG_DWMAC_RK_AUTO_DELAYLINE自动扫描配置项。可参考此补丁。 开启自动扫描CONFIG_DWMAC_RK_AUTO_DELAYLINE=y 开辟两个空间用于存放TX/RX延时 驱动代码解析 // arch/arm64/configs/rockchip_linux_defconfig CONFIG_DWMAC_RK_AUTO_DELAYLINE=y // drivers/net/ethernet/stmicro/stmmac/stmmac_main.c // 烧写的时候判断值有没有效,无效扫描,有效跳过扫描 #ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE if (!priv-》delayline_scanned) { if (dwmac_rk_get_rgmii_delayline_from_vendor(priv)){ priv-》delayline_scanned = true; schedule_delayed_work(&priv-》scan_dwork, msecs_to_jiffies(8000)); }else{ // TX/RX值有效 priv-》delayline_scanned = true; } } #endif // 任务 #ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE INIT_DELAYED_WORK(&priv-》scan_dwork, stmmac_scan_delayline_dwork); #endif // 扫描delayline窗口得到延时值 #ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE static void stmmac_scan_delayline_dwork(struct work_struct *work) { struct stmmac_priv *priv = container_of(work, struct stmmac_priv, scan_dwork.work); dwmac_rk_search_rgmii_delayline(priv); }; #endif // include/linux/soc/rockchip/rk_vendor_storage.h // 用RKDevInfoWriteTool工具新开两个空间 #define LAN_RGMII_DL_ID 16 // eth0 mac地址 #define EINK_VCOM_ID 17 // eth1 mac地址 #define ETH0_DELAY_ID 18 // 用于存放eth0 TX/RX延时值 #define ETH1_DELAY_ID 19 // 用于存放eth1 TX/RX延时值 #ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE // 从vendor区获取延时值设置到对应的rgmii中 int dwmac_rk_get_rgmii_delayline_from_vendor(struct stmmac_priv *priv) { int phy_iface = dwmac_rk_get_phy_interface(priv); // 获取phy接口类型 unsigned char delayline[2]; int ret; int vendor_id; if(!strcmp(priv-》dev-》name , “eth0”)) // 获取传进来的phy是哪一个,赋值对应的vendor_id vendor_id = ETH0_DELAY_ID; else if(!strcmp(priv-》dev-》name , “eth1”)) vendor_id = ETH1_DELAY_ID; if (phy_iface != PHY_INTERFACE_MODE_RGMII && phy_iface != PHY_INTERFACE_MODE_RGMII_ID) return 0; memset(delayline, 0x0, sizeof(delayline)); // 为delayline窗口申请空间 ret = rk_vendor_read(vendor_id, delayline, 2); // 读vendor_id 里delayline[0], delayline[1]值 if (ret == 2 && dwmac_rk_delayline_is_valid(delayline[0], delayline[1])) { // 判断TX/RX值有效,有效范围00-7f pr_info(“damac rk: read rgmii dl from vendor tx = 0x%02x, rx = 0x%02xn”, delayline[0], delayline[1]); dwmac_rk_set_rgmii_delayline(priv, delayline[0], delayline[1]); // 写入对应的phy的延时值 bsp_priv-》tx_delay = tx_delay; bsp_priv-》rx_delay = rx_delay; return 0; } return -ERANGE; } int dwmac_rk_search_rgmii_delayline(struct stmmac_priv *priv) { struct dwmac_rk_lb_priv *lb_priv; unsigned char delayline[2]; int ret; int vendor_id; lb_priv = kzalloc(sizeof(*lb_priv), GFP_KERNEL); if (!lb_priv) return -ENOMEM; if(!strcmp(priv-》dev-》name , “eth0”)) vendor_id = ETH0_DELAY_ID; else if(!strcmp(priv-》dev-》name , “eth1”)) vendor_id = ETH1_DELAY_ID; lb_priv-》sysfs = 0; // 不以文件系统形式扫描 lb_priv-》type = LOOPBACK_TYPE_PHY; lb_priv-》speed = LOOPBACK_SPEED1000; lb_priv-》scan = 1; ret = dwmac_rk_loopback_run(priv, lb_priv); // 循环测试 if (!ret) { delayline[0] = lb_priv-》final_tx; delayline[1] = lb_priv-》final_rx; if (rk_vendor_write(vendor_id, delayline, 2)) // 将测试后的值写入vendor区,保存 pr_err(“damac rk: write rgmii delayline to vendor failedn”); /* write tx/rx delayline back if loopback okay */ dwmac_rk_set_rgmii_delayline(priv, lb_priv-》final_tx, // 回环ok后写入phy延时 bsp_priv-》tx_delay = tx_delay; bsp_priv-》rx_delay = rx_delay; lb_priv-》final_rx); } kfree(lb_priv); // 释放lb_priv结构体 return ret; } #endif 扫描函数解析 static int dwmac_rk_loopback_delayline_scan(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv) { if (lb_priv-》sysfs) return dwmac_rk_delayline_scan(priv, lb_priv); else return dwmac_rk_delayline_scan_cross(priv, lb_priv); } // lb_priv-》sysfs =0 #define SCAN_STEP 0x1 // 扫描步长为1 #define MAX_DELAYLINE 0x7f #define SCAN_VALID_RANGE 0xA static int dwmac_rk_delayline_scan_cross(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv) { int tx_left, tx_right, rx_up, rx_down; int i, j, tx_index, rx_index; int tx_mid, rx_mid; /* initiation */ tx_index = SCAN_STEP; rx_index = SCAN_STEP; re_scan: /* start from rx based on the experience */ for (i = rx_index; i 《= (MAX_DELAYLINE - SCAN_STEP); i += SCAN_STEP) { tx_left = 0; tx_right = 0; tx_mid = 0; for (j = tx_index; j 《= (MAX_DELAYLINE - SCAN_STEP); j += SCAN_STEP) { if (!dwmac_rk_loopback_with_identify(priv, lb_priv, j, i)) { if (!tx_left) tx_left = j; tx_right = j; } } /* look for tx_mid */ if ((tx_right - tx_left) 》 SCAN_VALID_RANGE) { tx_mid = (tx_right + tx_left) / 2; break; } } /* Worst case: reach the end */ if (i 》= (MAX_DELAYLINE - SCAN_STEP)) goto end; rx_up = 0; rx_down = 0; /* look for rx_mid base on the tx_mid */ for (i = SCAN_STEP; i 《= (MAX_DELAYLINE - SCAN_STEP); i += SCAN_STEP) { if (!dwmac_rk_loopback_with_identify(priv, lb_priv, tx_mid, i)) { if (!rx_up) rx_up = i; rx_down = i; } } if ((rx_down - rx_up) 》 SCAN_VALID_RANGE) { /* Now get the rx_mid */ rx_mid = (rx_up + rx_down) / 2; } else { rx_index += SCAN_STEP; rx_mid = 0; goto re_scan; } if (dwmac_rk_delayline_is_valid(tx_mid, rx_mid)) { lb_priv-》final_tx = tx_mid; lb_priv-》final_rx = rx_mid; pr_info(“Find suitable tx_delay = 0x%02x, rx_delay = 0x%02xn”, lb_priv-》final_tx, lb_priv-》final_rx); return 0; } end: pr_err(“Can’t find suitable delaylinen”); return -ENXIO; } 注: 1、遇到手动扫描可以使用(网络性能好),但是自动扫描失败,报没有合适的延时值匹配,可以调整步长#define SCAN_STEP 0x5 2、该自动扫描也可以不用RK工具去烧写,只要在vendor区ID分配没有用的区间即可 3、自动扫描只能在emmc全部擦除之后才能用,在使用的过程中不能插网线。(RK一般是将emmc全部擦除,第一次加载固件时不能插网线,后续可以插网线烧写单个镜像) 4、关于vendor分区,为什么要讲扫描出来的值保存在这里?后续文章会更新。 |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
基于米尔瑞芯微RK3576核心板/开发板的人脸疲劳检测应用方案
626 浏览 0 评论
887 浏览 1 评论
784 浏览 1 评论
1997 浏览 1 评论
3242 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 02:44 , Processed in 0.696932 second(s), Total 70, Slave 54 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号