第22章_瑞萨MCU零基础入门系列教程之DMA控制器

news2024/11/25 23:01:30

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id=728461040949

配套资料获取:https://renesas-docs.100ask.net

瑞萨MCU零基础入门系列教程汇总: https://blog.csdn.net/qq_35181236/article/details/132779862


第22章 DMA控制器

本章目标

  • 了解DMA基本概念和RA6M5处理器的DMAC模块;
  • 学会使用RASC配置DMAC在指定内存间搬移数据;

22.1 DMA简介

DMA(Direct Memory Access)直接内存访问,可以大大减轻CPU工作量。CPU执行的众多指令中,有的用于计算、有的用于控制程序、有的用于转移数据等。其中转移数据的指令,尤其是转移大量数据,会占用大量CPU。如果是把外设A的数据,传给外设B,这种情况其实不需要CPU一直参与,只需在A、B之间创建个通道,让它们自己传输即可。这就是DMA设计的目的,在进行大量数据转移时较少CPU的干预,让DMA专注于数据转移,让CPU专注于计算、控制。

DMA主要实现将A处的数据直接搬运到B处,这里的A、B可以是内存(内/外部SRAM等),也可以是外设(UART、I2C、SPI、ADC等),因此所有的场景如下三种:

  • 内存到外设
  • 外设到内存
  • 内存到内存

无论是以上何种方式,都是先设置好DMA的数据源地址、数据目标地址、数据长度。设置好后,启动DMA就可以自动地把数据从源地址依次传输到目标地址。

22.2 RA6M5处理器的DMAC控制器

22.2.1 DMAC的特性

RA6M5包括一个8通道的直接内存访问控制器(DMAC),可以在不需要CPU干预的情况下传输数据。当产生DMA传输请求时,DMAC将存储在传输源地址的数据传输到传输目标地址。其外设模块特性详见下表:

image2

22.2.2 DMAC的系统框图

22.3 DMAC模块配置

22.3.1 配置DMAC模块

  1. 添加DMAC Stack模块

在RASC中配置DMAC时,首先需要去Stacks中添加DMAC的Stack模块,步骤如下图所示:

  1. 配置 DMAC Stack属性

对于DMAC某个通道的Stack属性有许多参数可以配置,在不同的应用场景下需要重点关注的配置项可能有所不同。以软件触发DMAC在内存间传输数据为例,开发者应该关注DMAC在传输过程中每次传输的数据大小是多少位的,内存地址的变化是递增还是递减的,是否需要循环传输,所有的数据都传输完毕后是否需要触发完成中断等等。

软件触发DMAC在内存间传输数据的配置如下图所示:

  • 通道:第0通道;
  • 传输模式;正常模式;
  • 每次传输的数据大小;2bytes-16bits;
  • 地址变化:源地址和目的地址在每次传输完一个数据后都递增;
  • 触发源:无,也就是软件触发;
  • 中断回调函数:dma0_callback;
  • 中断:所有的数据都传输完毕后才触发一次完成中断;

至于其它的参数,例如传输的数据个数,源数据buffer大小等,均可以在程序中根据每次实际的传输情况来实时修改。

22.3.2 DMAC配置信息解读

使用RASC配置了DMAC并生成代码后,会在hal_data.c中生成DMAC的设备对象g_dma0,它是transfer_instance_t类型的结构体。此结构体的成员表明了DMAC设备对象的控制参数、配置信息和操作api等信息:

const transfer_instance_t g_dma0 =
{
    .p_ctrl        = &g_dma0_ctrl,
    .p_cfg         = &g_dma0_cfg,
    .p_api         = &g_transfer_on_dmac
};
  1. p_ctrl:transfer_ctrl_t结构体类型指针成员,实际是void类型,指向dmac_instance_ctrl_t结构体类型的全局变量g_dma0_ctrl。dmac_instance_ctrl_t,明了DMAC设备对象的状态和类型:
typedef struct st_dmac_instance_ctrl
{
    uint32_t open;                     // Driver ID

    transfer_cfg_t const * p_cfg;

    /* Pointer to base register. */
    R_DMAC0_Type * p_reg;
} dmac_instance_ctrl_t;
  1. p_cfg:transfer_cfg_t结构体类型常量指针成员,指向同类型的g_dma0_cfg全局常量,g_dma0_cfg的成员取值来自RASC中对于该DMAC设备对象的配置:
const transfer_cfg_t g_dma0_cfg =
{
    .p_info              = &g_dma0_info,
    .p_extend            = &g_dma0_extend,
};

2.1 g_dma0_info:表明DMAC设备对象的地址变化方式、传输数据大小等信息:

transfer_info_t g_dma0_info =
{
    .transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED,
    .transfer_settings_word_b.repeat_area    = TRANSFER_REPEAT_AREA_SOURCE,
    .transfer_settings_word_b.irq            = TRANSFER_IRQ_END,
    .transfer_settings_word_b.chain_mode     = TRANSFER_CHAIN_MODE_DISABLED,
    .transfer_settings_word_b.src_addr_mode  = TRANSFER_ADDR_MODE_INCREMENTED,
    .transfer_settings_word_b.size           = TRANSFER_SIZE_2_BYTE,
    .transfer_settings_word_b.mode           = TRANSFER_MODE_NORMAL,
    .p_dest                                  = (void *) NULL,
    .p_src                                   = (void const *) NULL,
    .num_blocks                              = 0,
    .length                                  = 1,
};

2.2 g_dma0_extend:表明DMAC设备对象的中断、内存偏移大小、源数据内存大小等信息:

const dmac_extended_cfg_t g_dma0_extend =
{
    .offset              = 1,
    .src_buffer_size     = 1,
#if defined(VECTOR_NUMBER_DMAC0_INT)
    .irq                 = VECTOR_NUMBER_DMAC0_INT,
#else
    .irq                 = FSP_INVALID_VECTOR,
#endif
    .ipl                 = (10),
    .channel             = 0,
    .p_callback          = dma0_callback,
    .p_context           = NULL,
    .activation_source   = ELC_EVENT_NONE,
};
  • p_api:transfer_api_t结构体类型常量指针,指向在r_dmac.c中实现的g_transfer_on_dmac,里面含有各种操作函数;

22.3.3 中断回调函数

在RASC中设置中断回调函数的名字后,会在hal_data.h中声明次函数:

#ifndef dma0_callback
void dma0_callback(dmac_callback_args_t * p_args);
#endif

用户需要实现此函数,例如在软件触发实验中的drv_dma.c中的实现此函数,代码如下:

void dma0_callback(dmac_callback_args_t * p_args)
{
    (void)p_args;
    gDMAXferCplt = true;
}

在所有的数据都传输完毕后,中断被触发,进而调用这个回调函数。它只是设置一个变量表示DMA传输完毕。

22.3.4 DMAC的API及其用法

DMAC设备的接口函数在transfer_api_t结构体中声明,结构体原型如下:

typedef struct st_transfer_api
{
    fsp_err_t (* open)(transfer_ctrl_t * const p_ctrl, 
                       transfer_cfg_t const * const p_cfg);
    fsp_err_t (* reconfigure)(transfer_ctrl_t * const p_ctrl, 
                              transfer_info_t * p_info);
    fsp_err_t (* reset)(transfer_ctrl_t * const p_ctrl, 
                        void const * p_src, void * p_dest,
                        uint16_t const num_transfers);
    fsp_err_t (* enable)(transfer_ctrl_t * const p_ctrl);
    fsp_err_t (* disable)(transfer_ctrl_t * const p_ctrl);
    fsp_err_t (* softwareStart)(transfer_ctrl_t * const p_ctrl, 
                                transfer_start_mode_t mode);
    fsp_err_t (* softwareStop)(transfer_ctrl_t * const p_ctrl);
    fsp_err_t (* infoGet)(transfer_ctrl_t * const p_ctrl, 
                          transfer_properties_t * const p_properties);
    fsp_err_t (* close)(transfer_ctrl_t * const p_ctrl);
} transfer_api_t;

此结构体在r_dmac.c中实现,代码如下:

const transfer_api_t g_transfer_on_dmac =
{
    .open          = R_DMAC_Open,
    .reconfigure   = R_DMAC_Reconfigure,
    .reset         = R_DMAC_Reset,
    .infoGet       = R_DMAC_InfoGet,
    .softwareStart = R_DMAC_SoftwareStart,
    .softwareStop  = R_DMAC_SoftwareStop,
    .enable        = R_DMAC_Enable,
    .disable       = R_DMAC_Disable,
    .close         = R_DMAC_Close,
};

接下来就来了解下这些函数的用法。

  1. 打开DMAC设备对象
fsp_err_t (* open)(transfer_ctrl_t * const p_ctrl, 
                   transfer_cfg_t const * const p_cfg);
  • p_ctrl:用来记录一些状态信息;
  • p_cfg:配置信息,实际上就是指向RASC生成的g_dma0_cfg结构体;

调用open函数之后,DMAC设备就被初始化了,用户可以参考以下代码:

fsp_err_t err = g_dma0.p_api->open(g_dma0.p_ctrl, g_dma0.p_cfg);
if(FSP_SUCCESS != err)
{
    LOG(__FUNCTION__, __LINE__);
    return -1;
}
  1. 关闭DMAC设备对象
fsp_err_t (* close)(transfer_ctrl_t * const p_ctrl);

close函数会将DMAC设备对象的open标志位设置为CLOSED,并且关闭中断。

  1. 复位DMAC传输的地址和数据个数
fsp_err_t (* reset)(transfer_ctrl_t * const p_ctrl, 
                    void const * p_src, 
                    void * p_dest,
                    uint16_t const num_transfers);

如果使用软件触发DMAC开启传输,那么建议在每次开启数据传输前调用此函数,用法如下:

fsp_err_t err = g_dma0.p_api->reset(g_dma0.p_ctrl, ptdev->src, ptdev->dst, ptdev->length);
if(FSP_SUCCESS != err)
{
    LOG(__FUNCTION__, __LINE__);
    return -1;
}
  1. 使能DMAC的数据传输
fsp_err_t (* enable)(transfer_ctrl_t * const p_ctrl);

可能会在其它的API中使能DMAC的数据传输功能,因而此函数并非一定要手动调用,例如reset函数内就会在最后使能DMAC的数据传输。

  1. 失能DMAC的链接触发
fsp_err_t (* disable)(transfer_ctrl_t * const p_ctrl);

恰如其名,此函数和enable的功能是相反的,调用此函数可以关闭DMAC的数据传输功能。

  1. 软件开启DMAC的数据传输
fsp_err_t (* softwareStart)(transfer_ctrl_t * const p_ctrl, 
                            transfer_start_mode_t mode);

mode参数是transfer_start_mode_t枚举类型,可以选择每次软件触发传输时,是只传输一个数据,还是传输多个数据直到所有数据传输完成,此枚举的原型如下:

typedef enum e_transfer_start_mode
{
    TRANSFER_START_MODE_SINGLE = 0,
    TRANSFER_START_MODE_REPEAT = 1
} transfer_start_mode_t;

用户可以参考以下代码利用软件触发DMAC的数据传输:

fsp_err_t err = g_dma0.p_api->softwareStart(g_dma0.p_ctrl, TRANSFER_START_MODE_REPEAT);
if(FSP_SUCCESS != err)
{
    LOG(__FUNCTION__, __LINE__);
    return -1;
}
  1. 软件停止DMAC的数据传输
fsp_err_t (* softwareStop)(transfer_ctrl_t * const p_ctrl);

只有在使用softwareStart(…, TRANSFER_START_MODE_REPEAT)模式的软件触发DMAC时,才能使用softwareStop函数中断DMAC的传输。

22.4 软件触发数据搬移实验

22.4.1设计目的

让用户了解在RASC只如何配置DMAC为软件触发方式,并且了解DMAC的FSP库函数接口,使用这些接口完成数据的传输。

2.4.2驱动程序

1.DMAC设备对象的再抽象

鉴于瑞萨RA6M5的DMAC有8个通道,每个通道都可以单独使用,并且操作方法是类似的,所以可以将DMAC设备进行抽象,将DMAC的设备名称、通道值、内存地址、传输数据个数等信息放入一个结构体中:

typedef struct DMADev{
    char            *name;
    unsigned char   channel;
    void            *dst;
    void            *src;
    unsigned short  length;
    int (*Init)(struct DMADev *ptdev);
    int (*Xfer)(struct DMADev *ptdev);
}DMADevTypeDef;

在驱动程序中实现一个DMADev结构体:

static int DMADrvInit(struct DMADev *ptdev);
static int DMADrvXfer(struct DMADev *ptdev);
struct DMADev gDMADev = {
    .name    = "DMA0",
    .channel = 0,
    .dst     = NULL,
    .src     = NULL,
    .length  = 0,
    .Init    = DMADrvInit,
    .Xfer    = DMADrvXfer
};

再向上层调用者提供设备获取接口:

struct DMADev *DMADevGet(void)
{
    return &gDMADev;
}
  1. 初始化DMAC

本次实验使用的是软件触发DMAC,那么只需要打开DMAC设备即可:

static int DMADrvInit(struct DMADev *ptdev)
{
    if(ptdev == NULL)   return -1;
    /* open dma device */
    {
        fsp_err_t err = g_dma0.p_api->open(g_dma0.p_ctrl, g_dma0.p_cfg);
        if(FSP_SUCCESS != err)
        {
            LOG(__FUNCTION__, __LINE__);
            return -1;
        }
    }
    return 0;
}
  1. 中断回调函数和传输完成等待函数

在RASC中设置了DMAC传输完所有的数据后触发完成中断,并且也设置了中断回调函数的名字,那么需要实现这个回调函数,代码如下:

static volatile bool gDMAXferCplt = false;
void dma0_callback(dmac_callback_args_t * p_args)
{
    (void)p_args;
    gDMAXferCplt = true;
}

然后将此标志为封装一个等待函数,当标志被置为true后才会退出此函数:

static void DMADrvWaitXferCplt(void)
{
    while(!gDMAXferCplt);
    gDMAXferCplt = false;
}
  1. DMAC数据传输函数

在开启传输之前,需要重新设置参数(比如源地址、目的地址、长度),然后再softwareStart函数开启传输,代码如下:

static int DMADrvXfer(struct DMADev *ptdev)
{
    if(ptdev == NULL)   return -1;
    
    /* reconfigure dma config from ptdev */
    {
        fsp_err_t err = g_dma0.p_api->reset(g_dma0.p_ctrl, ptdev->src, ptdev->dst, ptdev->length);
        if(FSP_SUCCESS != err)
        {
            LOG(__FUNCTION__, __LINE__);
            return -1;
        }
    }
    
    fsp_err_t err = g_dma0.p_api->softwareStart(g_dma0.p_ctrl, TRANSFER_START_MODE_REPEAT);
    if(FSP_SUCCESS != err)
    {
        LOG(__FUNCTION__, __LINE__);
        return -1;
    }

    DMADrvWaitXferCplt();

    return 0;
}

22.4.3 测试程序

在初始化各个外设和DMAC设备对象后,每隔500ms使用DMA传输一次数据,传输完毕之后将源数据和目的数据一一比较,最后打印比较结果:

static volatile uint16_t uwSrcBuffer[512] = {0};
static volatile uint16_t uwDstBuffer[512] = {0};

void DMAAppTest(void)
{
    SystickInit();
    UARTDrvInit();
    
    DMADevTypeDef *ptDmdDev = DMADevGet();
    if(NULL == ptDmdDev)
    {
        printf("Error. Can not find DMA Device.\r\n");
        return;
    }
    /* 初始化DMAC */
    if(0 != ptDmdDev->Init(ptDmdDev))
    {
        printf("Failed to init DMA Device: %s\r\n", ptDmdDev->name);
        return;
    }
    /* 将源数据数组赋值 */
    for(uint16_t i=0; i<ptDmdDev->length; i++)
    {
        uwSrcBuffer[i] = i+1;
    }
    uint16_t count = 5;
    while(count)
    {
        /* 指定源数据地址和目的数据地址以及数据个数 */
        ptDmdDev->src = (uint16_t*)uwSrcBuffer;
        ptDmdDev->dst = (uint16_t*)uwDstBuffer;
        ptDmdDev->length = 512;
        /* 开始使用DMAC传输数据 */
        if(0 != ptDmdDev->Xfer(ptDmdDev))
        {
            printf("Failed to transmit data by %s\r\n", ptDmdDev->name);
            continue;
        }
        uint16_t uwErrCount = 0;
        /* 数据比较 */
        for(uint16_t i=0; i<ptDmdDev->length; i++)
        {
            if(uwDstBuffer[i] != uwSrcBuffer[i])
            {
                uwErrCount++;
            }
        }
        /* 打印结果 */
        printf("\r\n%d --> Transmit %s\r\n", count--, (uwErrCount==0)?"Success":"Failed");
        HAL_Delay(500);
    }
}

22.4.4 测试结果

在hal_entry()函数中调用测试函数,然后将编译出来的二进制可执行文件烧录到板子上运行,可以观察到下图这样的效果:

22.5 定时器触发数据搬移实验

此实验会使用到printf功能和滴答定时器,请读者将前文的关于串口和滴答定时器的配置以及驱动文件移植到本实验中。请参考上一小节添加DMAC的Stack模块和GPT的Stack模块。

22.5.1 设计目的

让用户学会在RASC中配置定时器外设,让它触发DMAC的数据传输,并且了解如何在代码中使用API来让定时器触发DMAC的数据传输。

22.5.2 模块配置

  1. GPT的模块配置

本次实验只需要配置GPT为普通定时计数即可。

  1. MAC的模块配置

static int DMADrvInit(struct DMADev *ptdev)
{
    if(ptdev == NULL)   return -1;
    /* open dma device */
    {
        fsp_err_t err = g_dma0.p_api->open(g_dma0.p_ctrl, g_dma0.p_cfg);
        if(FSP_SUCCESS != err)
        {
            LOG(__FUNCTION__, __LINE__);
            return -1;
        }
    }
    /* open timer0 */
    {
        fsp_err_t err = g_timer0.p_api->open(g_timer0.p_ctrl, g_timer0.p_cfg);
        if(FSP_SUCCESS != err)
        {
            LOG(__FUNCTION__, __LINE__);
            return -1;
        }
    }
    
    return 0;
}
  • 第15行:打开GPT设备完成对定时器的初始化;
  1. 中断回调函数和传输完成等待函数

参考《22.4.2驱动程序》。

4.数据传输函数

下述代码中,复位DMAC后并没有立刻启动DAM传输,而是开启定时器——通过定时器触发DMA传输,然后等待传输完成后。当数据传输完成后,就关闭定时器。代码如下:

static int DMADrvXfer(struct DMADev *ptdev)
{
    if(ptdev == NULL)   return -1;
    
    /* reconfigure dma config from ptdev */
    {
        fsp_err_t err = g_dma0.p_api->reset(g_dma0.p_ctrl, ptdev->src, ptdev->dst, ptdev->length);
        if(FSP_SUCCESS != err)
        {
            LOG(__FUNCTION__, __LINE__);
            return -1;
        }
    }
    /* start timer to triger DMA xfer data */
    {
        fsp_err_t err = g_timer0.p_api->start(g_timer0.p_ctrl);
        if(FSP_SUCCESS != err)
        {
            LOG(__FUNCTION__, __LINE__);
            return -1;
        }
        
    }
    /* wait for dma xfer cplt */
    DMADrvWaitXferCplt();
    /* stop timer */
    {
        fsp_err_t err = g_timer0.p_api->stop(g_timer0.p_ctrl);
        if(FSP_SUCCESS != err)
        {
            LOG(__FUNCTION__, __LINE__);
            return -1;
        }
        
    }

    return 0;
}
  • 第16行:开启定时器触发DMAC开始传输数据;
  • 第25行:等待数据传输完成;
  • 第28行:传输完毕后关闭定时器停止DMAC传输;

22.5.3 测试程序

测试程序和上一小节的一模一样,参考《22.4.3测试程序》。

22.5.4 测试结果


本章完

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

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

相关文章

nginx配置vue前端代理

背景&#xff1a;做一个前后端分离的项目&#xff0c;我这里是vue3 view ts创建的前端项目&#xff0c;在前端配置跨域请求。 一、开发阶段 在vue.config.js中配置devserver的proxy进行代理请求配置&#xff0c;然后将所有请求改为/api开头的即可。但是这样配置只在开发阶段…

数仓主题域和数据域、雪花模型,星型模型和星座模型

数仓模型和领域划分 一、主题域和数据域的差别二、雪花模型&#xff0c;星座模型和星型模型 一、主题域和数据域的差别 明确数据域作为数仓搭建的重要一环&#xff0c;能够让数仓的数据便于管理和应用。 数据域和主题域都是数据仓库中的重要概念&#xff0c;但含义略有不同&am…

【计算机视觉 | 图像模型】常见的计算机视觉 image model(CNNs Transformers) 的介绍合集(四)

文章目录 一、ResNeSt二、ShuffleNet v2三、FBNet四、Inception-v4五、ResNet-D六、MetaFormer七、PyramidNet八、RevNet九、Convolutional Vision Transformer&#xff08;CVT&#xff09;十、Tokens-To-Token Vision Transformer十一、Self-Attention Network十二、MixNet十三…

高速电路设计笔记----第二章

本章主要讲解的是电阻、电容、电感的选型。 一、电阻&#xff1a;关键还是限流。 1、通常在电源滤波时除了LC外&#xff0c;还会串接一个R。目的是为了降低信号的Q值&#xff0c;防止信号失真。常用于失真电源滤波。&#xff08;例如时钟电源滤波&#xff09; 2、选型的电阻的…

眺望数据应用新态势|第八届腾讯云Techo TVP开发者峰会圆满落幕

引言 在数据驱动的时代&#xff0c;如何有效地利用大数据已经成为了各个行业的重要课题。而随着云计算、人工智能等新兴技术的蓬勃发展&#xff0c;数据技术也随之不断生长并呈现出新的趋势与特点&#xff0c;企业该如何把握数据技术的新脉络&#xff0c;从而洞察数据背后的价…

【动态规划刷题 14】最长递增子序列 摆动序列

673. 最长递增子序列的个数 链接: 673. 最长递增子序列的个数 给定一个未排序的整数数组 nums &#xff0c; 返回最长递增子序列的个数 。 注意 这个数列必须是 严格 递增的。 示例 1: 输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列&#xff0c;分别是 [1, 3, 4,…

【校招VIP】产品工作难点之如何平衡团队协作

考点介绍&#xff1a; 对于简历上有实习经验的同学&#xff0c;团队配合和项目推进是一个非常常见的提问点。产品经理经常会面临项目延期&#xff0c;无法上线的情况。基于此&#xff0c;产品经理应该做些什么来保障项目按时上线呢? 产品工作难点之如何平衡团队协作-相关题目…

Linux下创建普通用户遇到的问题及解决办法

在Linux下只有root一个超级用户&#xff0c;但是可以创建多个普通用户的&#xff0c;具体的创建方法如下。 先切换到root用户&#xff0c;使用下面的命令创建用户名为user1(本文均以此用户名为例&#xff0c;注意后续键入指令时不要弄错了)的普通用户。 su root useradd user1 …

interview4-集合篇

一、算法复杂度分析 为什么要进行复杂度分析&#xff1f;因为可以指导你编写出性能更优的代码和评判别人写的代码的好坏。 &#xff08;1&#xff09;时间复杂度分析 时间复杂度是用来评估代码的执行耗时的。 1.假如每行代码的执行耗时一样&#xff1a;1ms 2.分析这段代码总…

跟随算网超人,深度解析算力网络!

随着数字时代全面开启 算力网络已成为当下热点议题 作为信息社会两大基石 算力、网络为何如此重要&#xff1f; 又将如何影响社会发展脉动&#xff1f; 为帮助大家深入了解算力网络 我们特别推出“算网超人”系列科普 下面&#xff0c;请跟随算网超人的步伐 来到该系列的…

uni-app H5使用 tabbars切换,echartst图表变小 宽度只有100px问题解决

问题&#xff1a; 跳转到别tabbars页面之后&#xff0c;再回来&#xff0c;echarts图显示缩小小团子。 原因分析&#xff1a; 在tabs切换中有echarts的话&#xff0c;我们会发现初始化的那个echarts是有宽度的&#xff0c;当点击tabs切换之后&#xff0c;切换过来的echarts只…

Python+requests编写的自动化测试项目

框架产生目的&#xff1a;公司走的是敏捷开发模式&#xff0c;编写这种框架是为了能够满足当前这种发展模式&#xff0c;用于前后端联调之前&#xff08;后端开发完接口&#xff0c;前端还没有将业务处理完毕的时候&#xff09;以及日后回归阶段&#xff0c;方便为自己腾出学(m…

Biome-BGC生态系统模型与Python融合技术:揭秘未来生态预测新趋势

Biome-BGC是利用站点描述数据、气象数据和植被生理生态参数&#xff0c;模拟日尺度碳、水和氮通量的有效模型&#xff0c;其研究的空间尺度可以从点尺度扩展到陆地生态系统。 在Biome-BGC模型中&#xff0c;对于碳的生物量积累&#xff0c;采用光合酶促反应机理模型计算出每天…

手机提词器有哪些?简单介绍这一款

手机提词器有哪些&#xff1f;手机提词器在现代社会中越来越受欢迎&#xff0c;原因是它可以帮助人们提高演讲和朗读的效果。使用手机提词器可以让人们更加自信地面对演讲和朗读&#xff0c;不至于出现口误或读错字的情况。此外&#xff0c;手机提词器还可以帮助人们节省时间和…

了解稀疏数组

稀疏数组&#xff08;一种数据结构&#xff09; package com.mypackage.array;public class Demo08 {public static void main(String[] args) {//1.创建一个二维数组 11*11// 0&#xff1a;没有棋子 1&#xff1a;黑棋 2&#xff1a;白棋int[][] array1 new int[11][11];…

OpenCV(四十一):图像分割-分水岭法

1.分水岭方法介绍 OpenCV 提供了分水岭算法&#xff08;Watershed Algorithm&#xff09;的实现&#xff0c; 使用分水岭算法对图像进行分割&#xff0c;将图像的不同区域分割成互不干扰的区域。分水岭算法模拟了水在图像中的扩散和聚集过程&#xff0c;将标记的边界被看作是阻…

Android Shadow 插件化原理演示

工程目录图 请点击下面工程名称&#xff0c;跳转到代码的仓库页面&#xff0c;将工程 下载下来 Demo Code 里有详细的注释 代码&#xff1a;LearnShadow

【Linux指令】Centos7 touch修改Access/Modify/Change 时间与恢复系统时间

文章目录 前言正文1. 查看文件状态2.只更新Access Time2.只更新Modify Time3. 修改Acess Time 与Modify Time为指定时间4. 修改Change时间5. 恢复系统时间 总结 前言 本篇主要讲解touch与时间相关的操作&#xff0c;关于touch创建文件&#xff0c;就不再赘述。 正文 1. 查看…

IP地址定位基础数据采集

在互联网时代&#xff0c;IP地址定位技术已经成为了广泛应用的一项重要技术。无论是用于网络安全、广告投放、市场调研还是用户体验优化&#xff0c;IP地址定位技术都发挥着关键作用。 什么是IP地址定位&#xff1f; IP地址定位是一种技术&#xff0c;它通过IP地址来确定设备…

行云管家全面适配信创国产化平台 助力政企信创环境下数字化转型与安全运维

近日&#xff0c;作为云计算管理及信息安全领域优秀的产品服务提供商&#xff0c;深圳市行云绽放科技有限公司宣布旗下行云管家系列产品已全面适配信创国产化平台&#xff0c;包括CPU、服务器、数据库、浏览器等&#xff0c;为政企客户提供符合信创环境要求的云计算管理与信息安…