NXP i.MX8系列平台开发讲解 - 3.11 Linux PCIe设备调试(WIFI模块)

专栏文章目录传送门:返回专栏目录


文章目录

目录

1. WIFI 模块简单介绍

2. 设备驱动原理介绍

3. PCIE WIFI驱动实例分析

3.1 查看设备树

3.2 wifi 设备驱动代码分析

3.3 内核配置选项

4. WIFI驱动调试相关


根据前面对PCIe的讲解,对PCIe的整体都有了一定的认识,具体工作原理有了一定了解,这章将在Linux系统下以PCIe接口的WIFI模块使用能够正常跑起来,环境如下:

  • cpu: i.mx8mq

  • OS:Android 11

  • Kernel version:kernel 5.4

  • WIFI module:AW-CM276MA-PUR模组(WIFI芯片88W8997)


1. WIFI 模块简单介绍

在我们认识WIFI模块,WIFI模块的接口主要有SDIO,USB,PCIe,采用PCIe接口对于其他这些接口来说,就是速度的优势,适用于需要更高性能和带宽的应用。如图AW-CM276MA-PUR模组采用的PCIe接口,采用M.2 2230接口;

查看NXP官方提供的相关AW-CM276MA-PUR

无线模块需要在i.MX 8M Quad主机系统上加载内核驱动程序,并在88W8987/88W8997 SoC上运行固件。MLAN模块会在SDIO/PCIe总线驱动程序检测到模块的SDIO/PCIe接口时,将固件二进制文件下载到SD8987/PCIE8997适配器中。内核驱动程序(SD8987/PCIE8997)会在总线驱动程序和内核中的“cfg80211”子系统之间加载。NXP内核驱动程序包含一组控制和配置,通过以下接口之一与用户空间进行通信

• 输入/输出控制(IOCTL)

• 无线扩展(Wext)

• CFG80211

IOCTL为用户空间应用程序(如iwconfig和iwpriv)提供了一条通路,而cfg80211为不同的用户空间应用程序(如wpa_supplicant、hostapd和iw)提供了另一条通路。图6说明了Wi-Fi层接口。


2. 设备驱动原理介绍

这里项目中将涉及到PCIe的RC和EP两端,目前Soc中的RC端的驱动芯片厂商已经提供,EP端的WIFI模块中的驱动wifi模块也已经是由厂家提供,如果是对于原厂厂商制作具有PCIe功能的产品那就需要实现PCIe驱动代码。本章节重点讲诉EP端相关。

对于EP设备原理以及驱动实现的过程如图

根据上图,EP设备驱动主要包含4个步骤:

  • 初始化注册pci设备,当一个pci设备,首先需要去注册一个strcut pci_driver的结构体

  • 填充struct pci_driver,里面包含实现PCIe的Vendor ID和Device ID,probe 接口,有需要添加pcie 电源管理接口;

  • PCIe设备probe,设备里面做了很多工作,设备的初始化资源,使能设备,申请设备资源,启动总线控制权,设置DMA传输方式,设置Bar空间映射,申请DMA中断;

  • 创建字符设备驱动


3. PCIE WIFI驱动实例分析

3.1 查看设备树

从设备树入手,查看i.MX8MQ端PCIe接口

vim ./arch/arm64/boot/dts/freescale/imx8mq.dtsi

​pcie1: pcie@33c00000 {compatible = "fsl,imx8mq-pcie";reg = <0x33c00000 0x400000>,<0x27f00000 0x80000>;reg-names = "dbi", "config";#address-cells = <3>;#size-cells = <2>;device_type = "pci";ranges =  <0x81000000 0 0x00000000 0x27f80000 0 0x00010000 /* downstream I/O 64KB */0x82000000 0 0x20000000 0x20000000 0 0x07f00000>; /* non-prefetchable memory */num-lanes = <1>;num-viewport = <4>;interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>; /* eDMA */interrupt-names = "msi", "dma";#interrupt-cells = <1>;interrupt-map-mask = <0 0 0 0x7>;interrupt-map = <0 0 0 1 &gic GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,<0 0 0 2 &gic GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>,<0 0 0 3 &gic GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,<0 0 0 4 &gic GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;linux,pci-domain = <1>;fsl,max-link-speed = <2>;power-domains = <&pgc_pcie>;resets = <&src IMX8MQ_RESET_PCIEPHY2>,<&src IMX8MQ_RESET_PCIE2_CTRL_APPS_EN>,<&src IMX8MQ_RESET_PCIE2_CTRL_APPS_CLK_REQ>,<&src IMX8MQ_RESET_PCIE2_CTRL_APPS_TURNOFF>;reset-names = "pciephy", "apps", "clkreq", "turnoff";assigned-clocks = <&clk IMX8MQ_CLK_PCIE2_CTRL>,<&clk IMX8MQ_CLK_PCIE2_PHY>,<&clk IMX8MQ_CLK_PCIE2_AUX>;assigned-clock-parents = <&clk IMX8MQ_SYS2_PLL_250M>,<&clk IMX8MQ_SYS2_PLL_100M>,<&clk IMX8MQ_SYS1_PLL_80M>;assigned-clock-rates = <250000000>, <100000000>,<10000000>;status = "disabled";};

在i.MX8MQ 中含有两个pcie,这里查看pcie1,主要定义了这个PCIE控制器在i.MX8MQ处理器上的配置和属性。列举一些重要的属性:

reg = <0x33c00000 0x400000>, <0x27f00000 0x80000>; // 定义寄存器空间的基地址和大小,用于PCIe控制器的访问;
​
一共定义两个元组 <0x33c00000 0x400000>定义了Device Bus Interface 寄存器空间;另外一个 <0x33c00000 0x400000>指定了配置空间;
​
device_type = "pci";         // 指示设备类型为 PCI
compatible = "pci-host-generic"; // 或其他 PCIe 控制器的兼容性字符串
num-lanes = <1>;            // 指定通道数
fsl,max-link-speed = <2>;       // 定义最大链路速度为Gen2
​
•                        ranges =  <0x81000000 0 0x00000000 0x27f80000 0 0x00010000 /* downstream I/O 64KB */
•                                   0x82000000 0 0x20000000 0x20000000 0 0x07f00000>; /* non-prefetchable memory */
​
rangs 表示PCIe设备的地址范围和映射范围;
​
interrupt-map-mask = <0 0 0 0x7>;  //定义中断映射掩码

以上这个pcie1: pcie@33c00000就是定义的Soc i.MX8MQ其中的一个pcie。在对于wifi设备是做一个ED端,设备树是如何定义,具体查看文件

vim ./arch/arm64/boot/dts/freescale/imx8mq-evk.dts

&pcie1 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_pcie1>;disable-gpio = <&gpio5 10 GPIO_ACTIVE_LOW>;reset-gpio = <&gpio5 12 GPIO_ACTIVE_LOW>;clocks = <&clk IMX8MQ_CLK_PCIE2_ROOT>,<&clk IMX8MQ_CLK_PCIE2_AUX>,<&clk IMX8MQ_CLK_PCIE2_PHY>,<&pcie1_refclk>;clock-names = "pcie", "pcie_aux", "pcie_phy", "pcie_bus";assigned-clocks = <&clk IMX8MQ_CLK_PCIE2_AUX>,<&clk IMX8MQ_CLK_PCIE1_PHY>,<&clk IMX8MQ_CLK_PCIE1_CTRL>;assigned-clock-rates = <10000000>, <100000000>, <250000000>;assigned-clock-parents = <&clk IMX8MQ_SYS2_PLL_50M>,<&clk IMX8MQ_SYS2_PLL_100M>,<&clk IMX8MQ_SYS2_PLL_250M>;vph-supply = <&vgen5_reg>;l1ss-disabled;status = "disabled";
​wifi_wake_host {compatible = "nxp,wifi-wake-host";interrupt-parent = <&gpio5>;interrupts = <11 IRQ_TYPE_LEVEL_LOW>;interrupt-names = "host-wake";};
};

其实对于ED设备,并不需要去怎么定义设备树,这里只是定义了wifi的唤醒相关,当唤醒后将会通过PCIE接口与它通信。pcie1表示一个

Root Complex设备,定义了相关的引脚,时钟,电源,状态,WIFI唤醒等配置。

3.2 wifi 设备驱动代码分析

初始化PCI 设备结构体,

查看pci设备接口体的数据结构

vim ./include/linux/pci.h

struct pci_driver {struct list_head        node;const char              *name;const struct pci_device_id *id_table;   /* Must be non-NULL for probe to be called */int  (*probe)(struct pci_dev *dev, const struct pci_device_id *id);     /* New device inserted */void (*remove)(struct pci_dev *dev);    /* Device removed (NULL if not a hot-plug capable driver) */int  (*suspend)(struct pci_dev *dev, pm_message_t state);       /* Device suspended */int  (*resume)(struct pci_dev *dev);    /* Device woken up */void (*shutdown)(struct pci_dev *dev);int  (*sriov_configure)(struct pci_dev *dev, int num_vfs); /* On PF */const struct pci_error_handlers *err_handler;const struct attribute_group **groups;struct device_driver    driver;struct pci_dynids       dynids;
​ANDROID_KABI_RESERVE(1);ANDROID_KABI_RESERVE(2);ANDROID_KABI_RESERVE(3);ANDROID_KABI_RESERVE(4);
};

作为一个PCIe设备就需要创建一个pci_driver,重点函数在

    const struct pci_device_id *id_table;   /* Must be non-NULL for probe to be called */ //EP设备的ID相关int  (*probe)(struct pci_dev *dev, const struct pci_device_id *id);     /* New device inserted */ //EP probevoid (*remove)(struct pci_dev *dev);    /* Device removed (NULL if not a hot-plug capable driver) */ // EP设备removeint  (*suspend)(struct pci_dev *dev, pm_message_t state);       /* Device suspended */int  (*resume)(struct pci_dev *dev);    /* Device woken up */

具体查看PCIe wifi 设备驱动(注:这里的驱动程序放在Android路径下,不在内核路径下)

./vendor/nxp-opensource/nxp-mwifiex/mxm_wifiex/wlan_src/mlinux/moal_pcie.c

/* PCI Device Driver */
static struct pci_driver REFDATA wlan_pcie = {.name = "wlan_pcie",.id_table = wlan_ids,.probe = woal_pcie_probe,.remove = woal_pcie_remove,
#ifdef CONFIG_PM/* Power Management Hooks */.suspend = woal_pcie_suspend,.resume = woal_pcie_resume,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0).err_handler = woal_pcie_err_handler,
#endif
};

支持的wlan_ids,这里将定义了vendor_id, Device_id

static const struct pci_device_id wlan_ids[] = {
#ifdef PCIE8897{PCIE_VENDOR_ID_NXP,PCIE_DEVICE_ID_NXP_88W8897P,PCI_ANY_ID,PCI_ANY_ID,0,0,},
#endif
#ifdef PCIE8997{PCIE_VENDOR_ID_NXP,PCIE_DEVICE_ID_NXP_88W8997P,PCI_ANY_ID,PCI_ANY_ID,0,0,},{PCIE_VENDOR_ID_V2_NXP,PCIE_DEVICE_ID_NXP_88W8997P,PCI_ANY_ID,PCI_ANY_ID,0,0,},
#endif
#ifdef PCIE9097{PCIE_VENDOR_ID_V2_NXP,PCIE_DEVICE_ID_NXP_88W9097,PCI_ANY_ID,PCI_ANY_ID,0,0,},
#endif
#ifdef PCIE9098{PCIE_VENDOR_ID_V2_NXP,PCIE_DEVICE_ID_NXP_88W9098P_FN0,PCI_ANY_ID,PCI_ANY_ID,0,0,},{PCIE_VENDOR_ID_V2_NXP,PCIE_DEVICE_ID_NXP_88W9098P_FN1,PCI_ANY_ID,PCI_ANY_ID,0,0,},{},}

这些设备的pci_device_id包含了什么信息,主要包含当前EP设备的Vendosr id, Device id 这些信息都是设备厂商提供的,都是需要去组织里面申请的。

注册PCI设备到内核pci_register_driver

/***  @brief This function registers the IF module in bus driver**  @return         MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE*/
mlan_status woal_pcie_bus_register(void)
{mlan_status ret = MLAN_STATUS_SUCCESS;ENTER();
​/* API registers the NXP PCIE driver */if (pci_register_driver(&wlan_pcie)) {PRINTM(MFATAL, "PCIE Driver Registration Failed \n");ret = MLAN_STATUS_FAILURE;}
​LEAVE();return ret;
}

当加载这个wifi时候,匹配上设备树后 ,查看probe主要做了什么;

/***  @brief This function handles PCIE driver probe**  @param pdev     A pointer to pci_dev structure*  @param id       A pointer to pci_device_id structure**  @return         error code*/
static int woal_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{pcie_service_card *card = NULL;t_u16 card_type = 0;int ret = 0;
​ENTER();
​PRINTM(MINFO, "vendor=0x%4.04X device=0x%4.04X rev=%d\n", pdev->vendor,pdev->device, pdev->revision);
​/* Preinit PCIE device so allocate PCIE memory can be successful */if (woal_pcie_preinit(pdev)) {PRINTM(MFATAL, "MOAL PCIE preinit failed\n");LEAVE();return -EFAULT;}
​card = kzalloc(sizeof(pcie_service_card), GFP_KERNEL);if (!card) {PRINTM(MERROR, "%s: failed to alloc memory\n", __func__);ret = -ENOMEM;goto err;}
​card->dev = pdev;
​card_type = woal_update_card_type(card);if (!card_type) {PRINTM(MERROR, "pcie probe: woal_update_card_type() failed\n");ret = MLAN_STATUS_FAILURE;goto err;}woal_pcie_init(card);
​if (woal_add_card(card, &card->dev->dev, &pcie_ops, card_type) ==NULL) {woal_pcie_cleanup(card);PRINTM(MERROR, "%s: failed\n", __func__);ret = -EFAULT;goto err;}
​
#ifdef IMX_SUPPORTwoal_regist_oob_wakeup_irq(card->handle);
#endif /* IMX_SUPPORT */
​LEAVE();return ret;
err:kfree(card);if (pci_is_enabled(pdev))pci_disable_device(pdev);
​LEAVE();return ret;
}
​

probe 中主要对设备进行一个初始化,最初开始调用woal_pcie_preinit进行使能PCI设备,设置一些DMA地址掩码相关

/***  @brief This function pre-initializes the PCI-E host*  memory space, etc.**  @param handle   A pointer to moal_handle structure**  @return         MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE*/
static mlan_status woal_pcie_preinit(struct pci_dev *pdev)
{int ret;
​if (pdev->multifunction)device_disable_async_suspend(&pdev->dev);
​ret = pci_enable_device(pdev); //使能PCI设备
​if (ret)goto err_enable_dev;
​pci_set_master(pdev); //设置成总线主模式DMA模式, EP可以发起memory 方式请求
​PRINTM(MINFO, "Try set_consistent_dma_mask(32)\n");ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); //设置设备的DMA地址掩码32位,决定了使用物理内存范围if (ret) {PRINTM(MERROR, "set_dma_mask(32) failed\n");goto err_set_dma_mask;}
​ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); // 设置设备的一致性 DMA 地址掩码为 32 位if (ret) {PRINTM(MERROR, "set_consistent_dma_mask(64) failed\n");goto err_set_dma_mask;}return MLAN_STATUS_SUCCESS;
​
err_set_dma_mask:pci_disable_device(pdev);
err_enable_dev:return MLAN_STATUS_FAILURE;
}

申请PCIe内存映射到系统内存空间,这里重点查看woal_pcie_init,截取

​ret = pci_request_region(pdev, 0, DRV_NAME); //请求分配第一个区域的内存资源if (ret) {PRINTM(MERROR, "req_reg(0) error\n");goto err_req_region0;}card->pci_mmap = pci_iomap(pdev, 0, 0); //将第一个区域进行内存映射 BAR, 将其映射到驱动程序的虚拟内存地址空间中if (!card->pci_mmap) {PRINTM(MERROR, "iomap(0) error\n");goto err_iomap0;}ret = pci_request_region(pdev, 2, DRV_NAME); //请求第二个区域的内存资源if (ret) {PRINTM(MERROR, "req_reg(2) error\n");goto err_req_region2;}card->pci_mmap1 = pci_iomap(pdev, 2, 0); //将第二个区域进行内存映射 BARif (!card->pci_mmap1) {PRINTM(MERROR, "iomap(2) error\n");goto err_iomap2;}
​PRINTM(MINFO,"PCI memory map Virt0: %p PCI memory map Virt2: ""%p\n",card->pci_mmap, card->pci_mmap1);
​return MLAN_STATUS_SUCCESS;
 

从代码看到请求了两个区域的资源,也就是前几个章节提到的Configuration SpaceConfiguration Space,第一个用于配置,第二个用于数据的通道传输;

关于PCIe设备模式,这里采用PCIE_INT_MODE_MSI 中断方式

#ifdef PCIE
/* Enable/disable Message Signaled Interrupt (MSI) */
int pcie_int_mode = PCIE_INT_MODE_MSI;
static int ring_size;
#endif /* PCIE */

3.3 内核配置选项

CONFIG_WLAN_VENDOR_NXP=yCONFIG_MXMWIFIEX=mCONFIG_PM=y....

还有一些必要的并未举例;


4. WIFI驱动调试相关

按照上面分析,将WIFI驱动模块编译放入设备运行,成功可以看到以下打印信息:

打印信息看到关键信息:

Link up

看到这个,表示与设备已经建立了通信链路,可以进行链路的交换;剩下很多pci 0001:01:那些信息都是WIFI设备相关的一些配置信息打印。

启动成功后,通过命令lspci可以看到WIFI PCIe的信息:

01:00.0 Class 0200: 1b4b:2b42

  • 01:00.0:PCI设备的域号、总线号和设备号。这个地址表示总线1上的设备0。

  • Class 0200:设备的类别码,表示这是一个以太网控制器设备。

  • 1b4b:2b42:设备的厂商ID和设备ID。

调试PCIe出现的问题:

  1. 设备不能Link up上:

        

  1. 对硬件电路的排查,速率的确定监视信号是否正常;

注意:对于Android 相关调试并未列出,后续整理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/7703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

练习题(2024/5/7)

1验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 …

c++ cpp 在类中执行线程 进行恒定计算

在编程中&#xff0c;顺序执行是常见的模式&#xff0c;但是对cpu的利用率不是很高&#xff0c;采用线程池&#xff0c;又太麻烦了&#xff0c;原因是还得不断地把任务拆分&#xff0c;扫描返回值。 如果 初始化n个类的时候&#xff0c;传递数据自身即可异步计算&#xff0c;那…

JavaWEB 框架安全:Spring 漏洞序列.(CVE-2022-22965)

什么叫 Spring 框架. Spring 框架是一个用于构建企业级应用程序的开源框架。它提供了一种全面的编程和配置模型&#xff0c;可以简化应用程序的开发过程。Spring 框架的核心特性包括依赖注入&#xff08;Dependency Injection&#xff09;、面向切面编程&#xff08;Aspect-Or…

AIGC绘画辅助网站

Midjourney风格样式 Midjourney Style Classifier | Andrei Kovalevs Midlibrary

124.反转链表(力扣)

题目描述 代码解决&#xff08;思路1&#xff1a;双指针&#xff09; class Solution { public:ListNode* reverseList(ListNode* head) {ListNode*temp;//保存cur下一个节点ListNode*curhead;ListNode*preNULL;while(cur){tempcur->next;// 保存一下 cur的下一个节点&#…

接口自动化测试的最佳实践和常见错误!

引言&#xff1a; 随着软件开发的不断演进&#xff0c;接口自动化测试在软件质量保障的过程中扮演着越来越重要的角色。良好的接口自动化测试可以提升测试效率、减少人力成本&#xff0c;但同时也存在着一些常见错误。本文将从零到一详细介绍接口自动化测试的最佳实践和常见错…

突破传统 重新定义:3D医学影像PACS系统源码(包含RIS放射信息)实现三维重建与还原

突破传统&#xff0c;重新定义PACS/RIS服务,洞察用户需求&#xff0c;关注应用场景&#xff0c;新一代PACS/RIS系统&#xff0c;系统顶层设计采用集中分布式架构&#xff0c;满足医院影像全流程业务运行,同时各模块均可独立部署&#xff0c;满足医院未来影像信息化扩展新需求、…

Ecovadis认证是什么?

Ecovadis认证是一种企业社会责任&#xff08;CSR&#xff09;评估和评级的认证&#xff0c;旨在衡量企业在环境、劳工和人权、道德以及可持续采购四个方面的可持续发展表现。该认证已成为全球范围内许多公司和组织的评估标准之一&#xff0c;有助于提高企业的会声誉和可持续发展…

Linux专栏10:Linux权限详解(上)

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Linux权限详解&#xff08;上&#xff09; 编号&#xff1a;10 文章…

商务分析方法与工具(四):Python的趣味快捷-简单函数你真的会用吗?

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

SolidWorks进行热力学有限元分析一、模型建立

1.话不多说按照我的操作来 2.这一步鼠标移到中心点直接拉就行 3.这里选单位&#xff0c;继续按照操作来 4.选中这个边&#xff0c;直接拉&#xff0c;输入尺寸后确定&#xff0c;其他边同理 5.鼠标右键设置厚度 6.右键零件&#xff0c;然后编辑材料&#xff0c;给他赋予你需要的…

游戏辅助 -- 实战找人物对象基址

本节课在线学习视频&#xff1a; https://pan.quark.cn/s/3e83f4568031 一、打开CE工具&#xff0c;加载游戏进程 二、搜索人物血量144&#xff0c;选择首次扫描 三、进入游戏&#xff0c;让人物血量发生变化&#xff0c;搜索减少的数值 四、发现绿色的数值&#xff0c;一般绿…

Stable Diffusion:AI绘画的新纪元

摘要&#xff1a; Stable Diffusion&#xff08;SD&#xff09;作为AI绘画领域的新星&#xff0c;以其开源免费、强大的生成能力和高度的自定义性&#xff0c;正在引领一场艺术与技术的革命。本文旨在为读者提供Stable Diffusion的全面介绍&#xff0c;包括其原理、核心组件、安…

linux的信号量的使用

1.信号量 在多线程情况下&#xff0c;线程要进入关键代码就得获取信号量&#xff08;钥匙&#xff09;{sem_init(&sem, 0, 0);}&#xff0c;没有信号量的情况下就一直等待sem_wait(&sem)&#xff0c;只到别人把钥匙&#xff08;sem_post(&sem)&#xff09;给你。 …

分析错误ValueError: could not determine the shape of object type ‘Series‘

这个错误提示 ValueError: could not determine the shape of object type Series 通常发生在尝试将 pandas 的 Series 直接转换为 PyTorch 的 tensor 时&#xff0c;尤其是当 Series 的数据类型不明确或者包含非数值类型的数据时。为了修正这个问题&#xff0c;确保在转换之前…

如何做好一个活动策划?

活动策划的关键要素是什么&#xff1f; 首先&#xff0c;要明确一个概念:做活动就是走钢丝&#xff0c;没有保险的高空走钢丝!因为&#xff0c;活动没有“彩排”&#xff0c;只有现场"直播”! 无论什么类型的活动&#xff0c;人数是50人还是2000人&#xff0c;也不论预算…

预定类小程序源码搭建包含各行业+源码开源可二开+详细图文搭建部署教程

在数字化浪潮席卷的今天&#xff0c;各行各业都急需找到与顾客连接的新方式。为了满足这一需求&#xff0c;很多店铺和企业都推出了预定类小程序&#xff0c;分享一款开源版预订类小程序源码&#xff0c;一站式解决方案&#xff0c;覆盖餐饮、旅游、美容、医疗、教育等多个行业…

java--GUI

目录 GUI&#xff08;Graphical User Interface&#xff09; swing 窗口(JFrame) 面板(JPanel) 流水布局(FlowLayout) 边界布局(BorderLayout) ​编辑 网格布局(GridLayout) 按钮(JButton) 标签组件&#xff0c;用来设置文字(JLabel) 单行文本框组件(JTextField) 多…

使用Java编写的简单彩票中奖概率计算器

前言 在当今社会&#xff0c;彩票已经成为许多人追逐梦想和改变生活的一种方式。然而&#xff0c;中奖的概率却是一个让人犹豫和兴奋的话题。在这篇文章中&#xff0c;我们将探讨如何使用Java编程语言实现一个简单的彩票中奖概率计算器。通过这个计算器&#xff0c;我们可以根…

【星海随笔】windows 上跑MySQL

step one 使用 WSL 在 Windows 上安装 Linux wsl官方文档 在管理员模式下打开 PowerShell windows上安装wsl wsl --install查看哪些是可用的 wsl --list --onlineC:\Windows\System32\drivers\hosts docker-desktop下载官网&#xff1a;Install Docker Desktop on Windows …