【NXP-MCXA153】SPI驱动移植
介绍
SPI总线由摩托罗拉公司开发,是一种全双工同步串行总线,由四个IO口组成:CS、SCLK、MISO、MOSI;通常用于CPU和外设之间进行通信,常见的SPI总线设备有:TFT LCD、QSPI FLASH、时钟模块、IMU等;NXP-MCXA153开发板上集成了两路SPI总线,本次实验将重点介绍RT-Thread系统中SPI BSP驱动的移植过程
移植流程
以SPI0为例
① 在board里边添加相应的外设:配置spi0外设为复位状态、设置GPIO引脚功能
② 添加相应的Kconfig开关,用以指示相应的外设开启与关闭(本质是通过宏定义或者条件编译的方式)
③ 根据SDK_2_14_2_FRDM-MCXA153提供的spi示例工程编写spi总线驱动,需要实现几个关键的函数
- rt_hw_spi_init
 - spi_configure
 - spixfer
 
④ 添加相应的库文件依赖:fsl_lpspi.c、fsl_lpspi_edma.c
引脚对应关系
| 序号 | GPIO | function | 
|---|---|---|
| 1 | P1_3 | CS | 
| 2 | P1_1 | SCLK | 
| 3 | P1_2 | MISO | 
| 4 | P1_0 | MOSI | 
驱动文件
board.c
在rt_hw_board_init函数里加入以下代码
edma_config_t userConfig = {0};
EDMA_GetDefaultConfig(&userConfig);
EDMA_Init(DMA0, &userConfig);
 
pin_mux.c
在BOARD_InitPins函数里加入以下代码
#ifdef BSP_USING_SPI0RESET_ReleasePeripheralReset(kLPSPI0_RST_SHIFT_RSTn);const port_pin_config_t port1_0_pin56_config = {/* Internal pull-up/down resistor is disabled */kPORT_PullDisable,/* Low internal pull resistor value is selected. */kPORT_LowPullResistor,/* Fast slew rate is configured */kPORT_FastSlewRate,/* Passive input filter is disabled */kPORT_PassiveFilterDisable,/* Open drain output is disabled */kPORT_OpenDrainDisable,/* Low drive strength is configured */kPORT_LowDriveStrength,/* Normal drive strength is configured */kPORT_NormalDriveStrength,/* Pin is configured as LPSPI0_SDO */kPORT_MuxAlt2,/* Digital input enabled */kPORT_InputBufferEnable,/* Digital input is not inverted */kPORT_InputNormal,/* Pin Control Register fields [15:0] are not locked */kPORT_UnlockRegister};/* PORT1_0 (pin 56) is configured as LPSPI0_SDO */PORT_SetPinConfig(PORT1, 0U, &port1_0_pin56_config);const port_pin_config_t port1_1_pin57_config = {/* Internal pull-up/down resistor is disabled */kPORT_PullDisable,/* Low internal pull resistor value is selected. */kPORT_LowPullResistor,/* Fast slew rate is configured */kPORT_FastSlewRate,/* Passive input filter is disabled */kPORT_PassiveFilterDisable,/* Open drain output is disabled */kPORT_OpenDrainDisable,/* Low drive strength is configured */kPORT_LowDriveStrength,/* Normal drive strength is configured */kPORT_NormalDriveStrength,/* Pin is configured as LPSPI0_SCK */kPORT_MuxAlt2,/* Digital input enabled */kPORT_InputBufferEnable,/* Digital input is not inverted */kPORT_InputNormal,/* Pin Control Register fields [15:0] are not locked */kPORT_UnlockRegister};/* PORT1_1 (pin 57) is configured as LPSPI0_SCK */PORT_SetPinConfig(PORT1, 1U, &port1_1_pin57_config);const port_pin_config_t port1_2_pin58_config = {/* Internal pull-up/down resistor is disabled */kPORT_PullDisable,/* Low internal pull resistor value is selected. */kPORT_LowPullResistor,/* Fast slew rate is configured */kPORT_FastSlewRate,/* Passive input filter is disabled */kPORT_PassiveFilterDisable,/* Open drain output is disabled */kPORT_OpenDrainDisable,/* Low drive strength is configured */kPORT_LowDriveStrength,/* Normal drive strength is configured */kPORT_NormalDriveStrength,/* Pin is configured as LPSPI0_SDI */kPORT_MuxAlt2,/* Digital input enabled */kPORT_InputBufferEnable,/* Digital input is not inverted */kPORT_InputNormal,/* Pin Control Register fields [15:0] are not locked */kPORT_UnlockRegister};/* PORT1_2 (pin 58) is configured as LPSPI0_SDI */PORT_SetPinConfig(PORT1, 2U, &port1_2_pin58_config);const port_pin_config_t port1_3_pin59_config = {/* Internal pull-up/down resistor is disabled */kPORT_PullDisable,/* Low internal pull resistor value is selected. */kPORT_LowPullResistor,/* Fast slew rate is configured */kPORT_FastSlewRate,/* Passive input filter is disabled */kPORT_PassiveFilterDisable,/* Open drain output is disabled */kPORT_OpenDrainDisable,/* Low drive strength is configured */kPORT_LowDriveStrength,/* Normal drive strength is configured */kPORT_NormalDriveStrength,/* Pin is configured as LPSPI0_PCS0 */kPORT_MuxAlt2,/* Digital input enabled */kPORT_InputBufferEnable,/* Digital input is not inverted */kPORT_InputNormal,/* Pin Control Register fields [15:0] are not locked */kPORT_UnlockRegister};/* PORT1_3 (pin 59) is configured as LPSPI0_PCS0 */PORT_SetPinConfig(PORT1, 3U, &port1_3_pin59_config);
#endif
 
board/Kconfig
加入SPI0相关配置
menuconfig BSP_USING_SPIconfig BSP_USING_SPIbool "Enable SPI"select RT_USING_SPIdefault yif BSP_USING_SPIconfig BSP_USING_SPI0bool "Enable SPI0"default endif
 
drv_spi.c
spi驱动层修改如下
/** Copyright (c) 2006-2024, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date           Author       Notes* 2024-08-3      hywing       The first version for MCXA*/
#include "rtdevice.h"
#include "drv_spi.h"
#include "fsl_common.h"
#include "fsl_lpspi.h"
#include "fsl_lpspi_edma.h"#define DMA_MAX_TRANSFER_COUNT (32767)enum
{
#ifdef BSP_USING_SPI0SPI1_INDEX,
#endif
};struct lpc_spi
{struct rt_spi_bus           parent;LPSPI_Type                  *LPSPIx;clock_attach_id_t           clock_attach_id;clock_div_name_t            clock_div_name;clock_name_t                clock_name;DMA_Type                    *DMAx;uint8_t                     tx_dma_chl;uint8_t                     rx_dma_chl;edma_handle_t               dma_tx_handle;edma_handle_t               dma_rx_handle;dma_request_source_t        tx_dma_request;dma_request_source_t        rx_dma_request;lpspi_master_edma_handle_t  spi_dma_handle;rt_sem_t                    sem;char                        *name;
};static struct lpc_spi lpc_obj[] =
{
#ifdef BSP_USING_SPI0{.LPSPIx = LPSPI0,.clock_attach_id = kFRO12M_to_LPSPI0,.clock_div_name = kCLOCK_DivLPSPI0,.clock_name = kCLOCK_Fro12M,.tx_dma_request = kDma0RequestLPSPI0Tx,.rx_dma_request = kDma0RequestLPSPI0Rx,.DMAx = DMA0,.tx_dma_chl = 0,.rx_dma_chl = 1,.name = "spi0",},
#endif
};struct lpc_sw_spi_cs
{rt_uint32_t pin;
};rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_uint32_t pin)
{rt_err_t ret = RT_EOK;struct rt_spi_device *spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));struct lpc_sw_spi_cs *cs_pin = (struct lpc_sw_spi_cs *)rt_malloc(sizeof(struct lpc_sw_spi_cs));cs_pin->pin = pin;rt_pin_mode(pin, PIN_MODE_OUTPUT);rt_pin_write(pin, PIN_HIGH);ret = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);return ret;
}static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
{rt_err_t ret = RT_EOK;
//    struct lpc_spi *spi = RT_NULL;
//    spi = (struct lpc_spi *)(device->bus->parent.user_data);
//    ret = lpc_spi_init(spi->SPIx, cfg);return ret;
}static void LPSPI_MasterUserCallback(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, status_t status, void *userData)
{struct lpc_spi *spi = (struct lpc_spi *)userData;rt_sem_release(spi->sem);}static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{int i;lpspi_transfer_t transfer = {0};RT_ASSERT(device != RT_NULL);RT_ASSERT(device->bus != RT_NULL);RT_ASSERT(device->bus->parent.user_data != RT_NULL);struct lpc_spi *spi = (struct lpc_spi *)(device->bus->parent.user_data);struct lpc_sw_spi_cs *cs = device->parent.user_data;if (message->cs_take){rt_pin_write(cs->pin, PIN_LOW);}transfer.dataSize = message->length;transfer.rxData   = (uint8_t *)(message->recv_buf);transfer.txData   = (uint8_t *)(message->send_buf);//  if(message->length < MAX_DMA_TRANSFER_SIZE)if (0){LPSPI_MasterTransferBlocking(spi->LPSPIx, &transfer);}else{uint32_t block, remain;block = message->length / DMA_MAX_TRANSFER_COUNT;remain = message->length % DMA_MAX_TRANSFER_COUNT;for (i = 0; i < block; i++){transfer.dataSize = DMA_MAX_TRANSFER_COUNT;if (message->recv_buf) transfer.rxData   = (uint8_t *)(message->recv_buf + i * DMA_MAX_TRANSFER_COUNT);if (message->send_buf) transfer.txData   = (uint8_t *)(message->send_buf + i * DMA_MAX_TRANSFER_COUNT);LPSPI_MasterTransferEDMA(spi->LPSPIx, &spi->spi_dma_handle, &transfer);rt_sem_take(spi->sem, RT_WAITING_FOREVER);}if (remain){transfer.dataSize = remain;if (message->recv_buf) transfer.rxData   = (uint8_t *)(message->recv_buf + i * DMA_MAX_TRANSFER_COUNT);if (message->send_buf) transfer.txData   = (uint8_t *)(message->send_buf + i * DMA_MAX_TRANSFER_COUNT);LPSPI_MasterTransferEDMA(spi->LPSPIx, &spi->spi_dma_handle, &transfer);rt_sem_take(spi->sem, RT_WAITING_FOREVER);}}if (message->cs_release){rt_pin_write(cs->pin, PIN_HIGH);}return message->length;
}static struct rt_spi_ops lpc_spi_ops =
{.configure = spi_configure,.xfer      = spixfer
};int rt_hw_spi_init(void)
{int i;for (i = 0; i < ARRAY_SIZE(lpc_obj); i++){CLOCK_SetClockDiv(lpc_obj[i].clock_div_name, 1u);CLOCK_AttachClk(lpc_obj[i].clock_attach_id);lpc_obj[i].parent.parent.user_data = &lpc_obj[i];lpc_obj[i].sem = rt_sem_create("sem_spi", 0, RT_IPC_FLAG_FIFO);lpspi_master_config_t masterConfig;LPSPI_MasterGetDefaultConfig(&masterConfig);masterConfig.baudRate = 12 * 1000 * 1000;masterConfig.pcsToSckDelayInNanoSec        = 1000000000U / masterConfig.baudRate * 1U;masterConfig.lastSckToPcsDelayInNanoSec    = 1000000000U / masterConfig.baudRate * 1U;masterConfig.betweenTransferDelayInNanoSec = 1000000000U / masterConfig.baudRate * 1U;LPSPI_MasterInit(lpc_obj[i].LPSPIx, &masterConfig, CLOCK_GetFreq(lpc_obj[i].clock_name));EDMA_CreateHandle(&lpc_obj[i].dma_tx_handle, lpc_obj[i].DMAx, lpc_obj[i].tx_dma_chl);EDMA_CreateHandle(&lpc_obj[i].dma_rx_handle, lpc_obj[i].DMAx, lpc_obj[i].rx_dma_chl);EDMA_SetChannelMux(lpc_obj[i].DMAx, lpc_obj[i].tx_dma_chl, lpc_obj[i].tx_dma_request);EDMA_SetChannelMux(lpc_obj[i].DMAx, lpc_obj[i].rx_dma_chl, lpc_obj[i].rx_dma_request);LPSPI_MasterTransferCreateHandleEDMA(lpc_obj[i].LPSPIx, &lpc_obj[i].spi_dma_handle, LPSPI_MasterUserCallback, &lpc_obj[i], &lpc_obj[i].dma_rx_handle, &lpc_obj[i].dma_tx_handle);rt_spi_bus_register(&lpc_obj[i].parent, lpc_obj[i].name, &lpc_spi_ops);}return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_spi_init);
 
SConscript
在Libraries/MCXA153/SConscript文件里边加上以下代码
if GetDepend('BSP_USING_SPI'):src += ['MCXA153/drivers/fsl_lpspi.c']src += ['MCXA153/drivers/fsl_lpspi_edma.c']
 
测试用例
打开menuconfig使能spi0驱动

短接MISO和MOSI引脚(P1_0和P1_2)进行自发自收测试

测试程序
#include <rtthread.h>  
#include "rtdevice.h" 
#include "drv_spi.h"#define SPI_BUS_NAME "spi0" 
#define SPI_DEV_NAME "spi00" static struct rt_spi_device *spi_device;static void spi_sample(void)  
{      rt_err_t result;      struct rt_spi_configuration cfg;    uint8_t tx_buf[] = "Hello RT-Thread!";      uint8_t rx_buf[sizeof(tx_buf)];  rt_base_t cs = 1*32+3;    rt_hw_spi_device_attach(SPI_BUS_NAME, SPI_DEV_NAME, cs);/* »ñÈ¡SPIÉ豸 */spi_device = (struct rt_spi_device *)rt_device_find(SPI_DEV_NAME);if (!spi_device){rt_kprintf("can't find %s device!\n", SPI_BUS_NAME);}/* ÅäÖÃSPIÉ豸 */cfg.data_width = 8;cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;cfg.max_hz = 12* 1000 * 1000;/* ÉèÖÃSPIÉ豸 */rt_spi_configure(spi_device, &cfg);    result = rt_spi_transfer(spi_device, tx_buf, rx_buf, sizeof(tx_buf));      if (result == sizeof(tx_buf))      {          rt_kprintf("Send: %s\n", tx_buf);          rt_kprintf("Received: %s\n", rx_buf);      }      else      {          rt_kprintf("spi transfer failed! error code: %d\n", result);      }       
}  int main(void)  
{      spi_sample();      return 0;  
}
 
运行结果

总结
- 另外,你也可以安装
MCUXpresso Config Tools v16,通过图形方式配置时钟树、GPIO复用 - 完整的BSP驱动代码可以在RT-Thread的仓库找到
 
相关文章:
【NXP-MCXA153】SPI驱动移植
介绍 SPI总线由摩托罗拉公司开发,是一种全双工同步串行总线,由四个IO口组成:CS、SCLK、MISO、MOSI;通常用于CPU和外设之间进行通信,常见的SPI总线设备有:TFT LCD、QSPI FLASH、时钟模块、IMU等;…...
Python if 编程题|Python一对一辅导教学
你好,我是悦创。 以下为 if 编程练习题: 1. 奇数乘积问题 题目描述: 编写一个程序,判断给定的两个整数是否都是奇数,如果是,返回它们的乘积;如果不是,返回它们的和。输入: num1, num2输出: n…...
机器学习——第十一章 特征选择与稀疏学习
11.1 子集搜索与评价 对一个学习任务来说,给定属性集,其中有些属性可能很关键、很有用,另一些属性则可能没什么用.我们将属性称为"特征" (feature) ,对当前学习任务有用的属性称为"相关特征" (relevant featu…...
花式表演无人机技术详解
花式表演无人机作为现代科技与艺术融合的典范,以其独特的飞行姿态、绚烂的灯光效果及精准的控制能力,在各类庆典、体育赛事、音乐会等合中展现出非凡的魅力。本文将从以下几个方面对花式表演无人机技术进行详细解析。 1. 三维建模与编程 在花式表演无人…...
服务器那点事--防火墙
Linux服务器那点事--防火墙 Ⅰ、开启关闭Ⅱ、放开端口 Ⅰ、开启关闭 禁止防火墙开机自启systemctl disable firewalld 关闭防火墙systemctl stop firewalld 查看防火墙状态systemctl status firewalldⅡ、放开端口 例如:放开3306端口 设置放开3306端口 [rootbpm2…...
C:每日一题:单身狗
 一、题目: 在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。 整型数组 int arr[ ] {1,1,2,2,3,4,4} 二、思路分析: 1.,明确目标,选择…...
SQL之使用存储过程循环插入数据
1、已经创建了任务日志表 CREATE TABLE t_task_log (id bigint NOT NULL AUTO_INCREMENT,task_id bigint NOT NULL COMMENT 任务ID,read_time bigint NOT NULL COMMENT 单位秒,读取耗时,write_time bigint NOT NULL COMMENT 单位秒,写入耗时,read_size …...
智慧楼宇公厕系统小程序,提高卫生间管理使用效率
在当今的智慧楼宇中,公厕系统的管理和使用效率成为了衡量楼宇品质的重要指标之一。智慧楼宇公厕系统小程序的出现,为解决这一问题带来了全新的思路和方法。 一、检查公厕环境数据 智慧公厕系统不仅关注如厕的基本需求,还注重提升如厕环境的质…...
深度剖析:云数据库与传统数据库的显著差异
【若您对以下内容感兴趣,欢迎关注或联系我们】 在当今数字化时代,数据库技术不断演进,云数据库和传统数据库作为两种主要的数据库类型,在多个方面存在明显区别。下面我们将深入探讨这些差异。 一、部署方式 云数据库࿱…...
# 利刃出鞘_Tomcat 核心原理解析(六)
利刃出鞘_Tomcat 核心原理解析(六) 一、Tomcat专题 - 内容 1、Web 应用配置 2、Tomcat 管理配置 3、JVM 配置 4、Tomcat 集群 5、Tomcat 安全 6、Tomcat 性能调优 7、Tomcat 附加功能。 二、Tomcat专题 - Web应用配置介绍 1、Web.xml 配置文件…...
双亲委派模型
优质博文:IT-BLOG-CN 虚拟机设计团队把类加载阶段中的 “通过一个类的全限定名来获取描述此类的二进制字节流” 这个动作放到 Java虚拟机外部去实现,以便应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。 从Java虚拟机…...
Linux下ETCD安装、配置、命令
目录 1. ETCD简介 2. ETCD的安装 2.1 准备环境 2.2 下载ETCD 2.3 解压和移动文件 2.4 验证安装 3. ETCD的配置 3.1 基本配置 3.2 配置文件 3.3 集群配置 4. ETCD的常用命令 4.1 插入键值对 4.2 读取键值对 4.3 删除键值对 4.4 监视键的变化 4.5 列出所有键值 …...
【QT】静态库与动态库
文章目录 开始之前一、静态库(static Library)定义使用场景特点程序示例 二、动态库(dynamic Library)定义使用场景特点。程序示例第二种调用 开始之前 测试环境:Qt 5.15.2 Based on Qt 6.4.3(MSVC 2019, x86_64) 操作系统:Windows11 专业版 编程语言&am…...
R的行和列命名和类型的转换
下面内容摘录自: 4章8节:用R做数据重塑,行列命名和数据类型转换-CSDN博客 欢迎订阅我们专栏 一、行和列命名 在数据科学和统计分析中,命名是组织和管理数据的一个重要部分。尤其是在处理复杂的多维数据集时,为行和列命…...
某通用系统0day审计过程
前言 代码审计篇章都是自己跟几个师傅们一起审计的1day或者0day(当然都是小公司较为简单),禁止未经允许进行转载,发布到博客的用意主要是想跟师傅们能够交流下审计的思路,毕竟审计的思路也是有说法的,或者是相互源码共享也OK&…...
windows C++-高级并发和异步(一)
并发和异步的由来已经很久了,对于从xp开始编程的人来说,这个概念并不陌生,但问题在于,在早期,这两个技术被认为是操作系统提供的服务,而非编程语言的概念。 事情发生变化的原因,和C标准不断变迁…...
Java FX 学习
声明:参考视频 一. Stage与Scene 舞台与场景:JavaFX应用程序将Ul容器定义为舞台(Stage)与场景(Scene)Stage类是顶级容器,它对应于窗体,其内容由Scene决定。Scene类是所有可视化内容…...
【走迷宫】
题目 DFS代码 #include<bits/stdc.h> using namespace std; const int N 110; int matrix[N][N]; int n, m; int dx[4] {-1, 0, 1, 0}, dy[4] {0, 1, 0, -1}; int dis[N][N]; void dfs(int x, int y, int cnt) {if(cnt > dis[n-1][m-1]) return;if(x n-1 &&a…...
linux(debian)迁移var数据到已分配逻辑卷的物理盘
文章目录 0 背景1 查看当前情况1.1 查看磁盘空间1.2 列出所有可用块设备的信息,而且还能显示他们之间的依赖关系1.3 查看可用磁盘1.4 查看卷组 2 卷组中创建逻辑卷3 创建文件系统4 创建临时文件夹并挂载,然后备份源文件5 修改开机挂载配置5.1 查看原配置…...
【产品那些事】什么是应用程序安全态势管理(ASPM)?
文章目录 前言当前应用安全(AppSec)推进遇到的问题关于ASPM的定义 为什么需要ASPM:B端客户核心需求ASPM产品关键策略理想状态下的ASPMASPM与CSPM的区别国内外产品参考 前言 随着现代软件开发实践的快速演变,特别是在敏捷开发和 DevOps 的推动下…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...
