普冉(PUYA)单片机开发笔记(11): I2C通信-配置主从收发

news2024/12/24 16:51:04

概述

在上一篇《普冉(PUYA)单片机开发笔记(10): I2C通信-配置从机-CSDN博客》配置了 PY32F003 的 I2C 从机一侧,今天配置主机,并实现主-从机之间的报文收发。

为了完成这个实验,需要两块 PY32F003F18P 的开发板,连接线路如下表所示。注意主从机必须共地,要专门拉一根杜邦线把两块开发板的 GND 连接好。

I2C 主从通信的管脚连接
开发板主机从机备注
3V3连接连接同一个电源
GND连接连接主从机必须共地
UART2_TXPA1PA1接到上位机 USB-TTL 模块
UART2_RXPA0PA0接到上位机 USB-TTL 模块
I2C_SCLPA3PA3主从用杜邦线连接
I2C_SDAPA2PA2主从用杜邦线连接

连接好以后,在上位机开两个 XCOM 的窗口,各自连接主机和从机的串口,以便观察运行结果。就像附后“实验结果”中的截图,上位机的 COM3 接从机的串口,COM32 接主机的串口。

代码实现步骤

老套路,main.h 中定义函数原型,app_i2c.c 中实现这些函数,py32_f0xx_hal_msp.c 中对I2C 用到的管脚进行初始化,对用到的中断和 DMA 进行初始化;py32_f0xx_it.c 中重定向 I2C_IRQHandler。上干货 ;)

1. 在 main.h 中定义四个函数原型

#define I2C_COMM_TEST   1
#define I2C_SLAVE       0

...
...

#if(I2C_COMM_TEST)
/** ----------------------------------------------------------------------------
* @name   : HAL_StatusTypeDef app_i2c_init(void);
* @brief  : i2c 初始化
* @param  : 
* @retval : HAL_OK: 写入成功; 其它: 错误
* @remark : 上级函数必须检查操作返回值, 只有 HAL_OK 时才可继续操作
*** ----------------------------------------------------------------------------
*/
HAL_StatusTypeDef app_i2c_init(void);

/** ----------------------------------------------------------------------------
* @brief  : i2c 测试使用的三个函数, 接收/发送/等待
* @param  : 
* @retval : 
* @remark : 
*** ----------------------------------------------------------------------------
*/
#if(I2C_SLAVE)
HAL_StatusTypeDef app_i2c_slave_receive(void);
HAL_StatusTypeDef app_i2c_slave_transmit(void);
#else
HAL_StatusTypeDef app_i2c_master_receive(void);
HAL_StatusTypeDef app_i2c_master_transmit(void);
#endif

void app_i2c_wait(void);

#endif

其中,为了在同一个工程中容纳主从两部分的代码,预定了 I2C_COMM_TEST 和 I2C_SLAVE,如果 I2C_SLAVE 为 1 表示定义的是从机的函数,如果 I2C_SLAVE 为 0 表示定义的是主机的函数。

在 PY32F003 上,主机和从机的初始化函数都取一样的函数名;动作函数 app_i2c_wait() 函数也使用一样的函数名。需要区分的是 app_i2c_slave/master_tx/rx 函数,因此分主机和从机定义了两组。

2. 在 app_i2c.c 中实现这些代码

/**
 ******************************************************************************
 * @file    app_i2c.c
 * @brief   I2C functions.
 ******************************************************************************
 * @copyright
 *
 * Copyright (c) 2023 CuteModem Intelligence.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
 
#include "main.h"

#define EXDATA_LEN    15                 // 数据长度
#define I2C_ADDRESS   0xA0               // 本机地址0xA0
#define I2C_SPEEDBPS  100000             // 通讯速度100K
#define I2C_DUTYCYCLE I2C_DUTYCYCLE_16_9 // 占空比

I2C_HandleTypeDef I2cHandle;
#if(I2C_SLAVE)
uint8_t mI2cTxBuf[EXDATA_LEN] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
#else
uint8_t mI2cTxBuf[EXDATA_LEN] = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
#endif
uint8_t mI2cRxBuf[EXDATA_LEN] = {0, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0};

HAL_StatusTypeDef app_i2c_init(void)
{
    HAL_StatusTypeDef cfg_res = HAL_OK;
    
    I2cHandle.Instance             = I2C;                     // I2C
    I2cHandle.Init.ClockSpeed      = I2C_SPEEDBPS;            // I2C 通讯速度
    I2cHandle.Init.DutyCycle       = I2C_DUTYCYCLE;           // I2C 占空比
    I2cHandle.Init.OwnAddress1     = I2C_ADDRESS;             // I2C 地址
    I2cHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 禁止广播呼叫
    I2cHandle.Init.NoStretchMode   = I2C_NOSTRETCH_DISABLE;   // 允许时钟延长
    cfg_res = HAL_I2C_Init(&I2cHandle);                       // I2C初始化
    if (cfg_res != HAL_OK) return cfg_res;
    return cfg_res;
}

void app_i2c_wait(void)
{
    /* 判断当前I2C状态, 等待I2C状态就绪 */
    while (HAL_I2C_GetState(&I2cHandle) != HAL_I2C_STATE_READY){}
}

#if(I2C_SLAVE)
HAL_StatusTypeDef app_i2c_slave_receive(void)
{
    /*I2C从机中断方式接收*/
    while (HAL_I2C_Slave_Receive_IT(&I2cHandle, (uint8_t *)mI2cRxBuf, EXDATA_LEN) != HAL_OK)
        Error_Handler();
   
    return HAL_OK;
}
    
HAL_StatusTypeDef app_i2c_slave_transmit(void)
{
    /*I2C从机中断方式发送*/
    while (HAL_I2C_Slave_Transmit_IT(&I2cHandle, (uint8_t *)mI2cTxBuf, EXDATA_LEN) != HAL_OK)
        Error_Handler();

    return HAL_OK;
}

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    uint8_t i = 0;
    
    if(hi2c->Instance != I2C) return;

    printf("Slave received via I2C: %d bytes\r\n", EXDATA_LEN);
    for(i = 0; i < EXDATA_LEN; i++) printf("%02d ", mI2cRxBuf[i]);
    printf("\r\n");
    
    return;
}

void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    uint8_t i = 0;
    
    if(hi2c->Instance != I2C) return;

    printf("Slave data sent via I2C: %d bytes\r\n", EXDATA_LEN);
    for(i = 0; i < EXDATA_LEN; i++) printf("%02d ", mI2cTxBuf[i]);
    printf("\r\n");
    
    return;
}

#else

HAL_StatusTypeDef app_i2c_master_receive(void)
{
    /*I2C 主机以中断方式接收*/
    while (HAL_I2C_Master_Receive_IT(&I2cHandle, I2C_ADDRESS, (uint8_t *)mI2cRxBuf, EXDATA_LEN) != HAL_OK)
        Error_Handler();
    app_i2c_wait();
    return HAL_OK;
}
    
HAL_StatusTypeDef app_i2c_master_transmit(void)
{
    /*I2C 主机以中断方式发送*/
    while (HAL_I2C_Master_Transmit_IT(&I2cHandle, I2C_ADDRESS, (uint8_t *)mI2cTxBuf, EXDATA_LEN) != HAL_OK)
        Error_Handler();
    app_i2c_wait();
    return HAL_OK;
}

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    uint8_t i = 0;
    
    if(hi2c->Instance != I2C) return;

    printf("Master received via I2C: %d bytes\r\n", EXDATA_LEN);
    for(i = 0; i < EXDATA_LEN; i++) printf("%02d ", mI2cRxBuf[i]);
    printf("\r\n");
    
    return;
}

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    uint8_t i = 0;
    
    if(hi2c->Instance != I2C) return;

    printf("Master data sent via I2C: %d bytes\r\n", EXDATA_LEN);
    for(i = 0; i < EXDATA_LEN; i++) printf("%02d ", mI2cTxBuf[i]);
    printf("\r\n");
    
    return;
}

#endif

有一部分代码已经在从机配置中实现了,做主机时使用 I2C_SLAVE 开关把主从两部分的数据做一个区分。

  • 从机的发送缓冲区填充 1~15;主机的发送缓冲区填充 16~30,以示区别。
  • app_i2c_init() 函数和 app_i2c_wait() 函数主从机共用。
  • 根据 I2C_SLAVE 开关分别实现主/从机的发送(Transmit)、接收(Receive)、发送中断服务回调函数(TxCpltCallback)和接收中断服务回调函数(RxCpltCallback)这四对函数。
  • I2C 主从收发函数结构相似,注意 HAL_I2C_Master_Transmit_IT() 函数比 HAL_I2C_Slave_Transmit_IT() 函数多了一个参数:I2C_Address。HAL_I2C_Master_Receive_IT() 函数比 HAL_I2C_Receive_IT() 函数也多了这个参数。这个参数用于区分 I2C 从机,以便在 RxCpltCallback 函数中处理。区分的 I2C 从机的参数在 hi2c 指针中,在 RxCpltCallback 中读取 hi2c->Devaddress 的值可区分数据是来自哪一个从机。

3. py32_f0xx_hal_msp.c 中分配管脚和DMA,并进行初始化

#include "main.h"

static DMA_HandleTypeDef HdmaCh1;
static DMA_HandleTypeDef HdmaCh2;

...
...

#if(I2C_COMM_TEST)
/**
 * -----------------------------------------------------------------------
 * @name   : void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
 * @brief  : 初始化I2C相关MSP
 * @param  : [in] *hi2c, I2C handler pointer
 * @retval : void
 * @remark :
 * -----------------------------------------------------------------------
*/
void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_SYSCFG_CLK_ENABLE(); //SYSCFG时钟使能
    __HAL_RCC_DMA_CLK_ENABLE();    //DMA时钟使能
    __HAL_RCC_I2C_CLK_ENABLE();    //I2C时钟使能
    __HAL_RCC_GPIOA_CLK_ENABLE();  //GPIOA时钟使能

    /**I2C GPIO Configuration
    PA3 : I2C1_SCL
    PA2 : I2C1_SDA
    */
    GPIO_InitStruct.Pin       = GPIO_PIN_3 | GPIO_PIN_2;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_OD;           // 推挽方式
    GPIO_InitStruct.Pull      = GPIO_PULLUP;               // 上拉
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_I2C;             // 复用为I2C
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);                // GPIO初始化
    
    __HAL_RCC_I2C_FORCE_RESET();   // 复位I2C  
    __HAL_RCC_I2C_RELEASE_RESET();

    /* I2C1 interrupt Init */
    HAL_NVIC_SetPriority(I2C1_IRQn, 0, 0); // 中断优先级设置
    HAL_NVIC_EnableIRQ(I2C1_IRQn);         // 使能I2C中断

    //DMA配置
    HAL_SYSCFG_DMA_Req(9);     //DMA1_CH1 MAP选择为 IIC_TX
    HAL_SYSCFG_DMA_Req(0xA00); //DMA1_CH2 MAP选择为 IIC_RX

    /* Configure the DMA handler for Transmission process */
    HdmaCh1.Instance                 = DMA1_Channel1;          // 选择DMA通道1
    HdmaCh1.Init.Direction           = DMA_MEMORY_TO_PERIPH;   // 方向为从存储器到外设
    HdmaCh1.Init.PeriphInc           = DMA_PINC_DISABLE;       // 禁止外设地址增量
    HdmaCh1.Init.MemInc              = DMA_MINC_ENABLE;        // 使能存储器地址增量
    HdmaCh1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;    // 外设数据宽度为8位
    HdmaCh1.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;    // 存储器数据宽度位8位
    HdmaCh1.Init.Mode                = DMA_NORMAL;             // 禁止循环模式
    HdmaCh1.Init.Priority            = DMA_PRIORITY_VERY_HIGH; // 通道优先级为"很高"
    HAL_DMA_Init(&HdmaCh1);                                    // 初始化DMA通道1
    __HAL_LINKDMA(hi2c, hdmatx, HdmaCh1);                      // DMA1 关联 IIC_TX

    /* Configure the DMA handler for Receiving process */
    HdmaCh2.Instance                 = DMA1_Channel2;          // 选择DMA通道1
    HdmaCh2.Init.Direction           = DMA_PERIPH_TO_MEMORY;   // 方向为从外设到存储
    HdmaCh2.Init.PeriphInc           = DMA_PINC_DISABLE;       // 禁止外设地址增量
    HdmaCh2.Init.MemInc              = DMA_MINC_ENABLE;        // 使能存储器地址增量
    HdmaCh2.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;    // 外设数据宽度为8位
    HdmaCh2.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;    // 存储器数据宽度位8位
    HdmaCh2.Init.Mode                = DMA_NORMAL;             // 禁止循环模式
    HdmaCh2.Init.Priority            = DMA_PRIORITY_HIGH;      // 通道优先级为高
    HAL_DMA_Init(&HdmaCh2);                                    // 初始化DMA通道1
    __HAL_LINKDMA(hi2c, hdmarx, HdmaCh2);                      // DMA1 关联 IIC_RX

    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 1);            // 中断优先级设置
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);                    // 使能DMA通道1中断
    HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0, 1);          // 中断优先级设置
    HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);                  // 使能DMA通道2_3中断
}
#endif

主从机的这部分代码相同,请参考我的上一篇《普冉(PUYA)单片机开发笔记(10): I2C通信-配置从机-CSDN博客》中的说明,这里不再重复。

4. 重定向 I2C_IRQHandler

#include "main.h"
#include "py32f0xx_it.h"

...
...

#if(I2C_COMM_TEST)
extern I2C_HandleTypeDef I2cHandle; // Defined in app_i2c.c
#endif

...
...

void DMA1_Channel1_IRQHandler(void)
{
#if(ADC_SAMPLE_TEST)
    HAL_DMA_IRQHandler(hadcdma.DMA_Handle);
#endif

#if(I2C_COMM_TEST)
    HAL_DMA_IRQHandler(I2cHandle.hdmatx);
#endif
}

void DMA1_Channel2_3_IRQHandler(void)
{
#if(I2C_COMM_TEST)
    HAL_DMA_IRQHandler(I2cHandle.hdmarx);
#endif
}

...
...

void I2C1_IRQHandler(void)
{
#if(I2C_SLAVE)
    HAL_I2C_ER_IRQHandler(&I2cHandle);
#endif
    
    HAL_I2C_EV_IRQHandler(&I2cHandle);
}

...
...

这部分的修改,主从机也差不多相同。

5. main.c 中进行调用

int main(void)
{
    HAL_Init();             // systick初始化
    SystemClock_Config();   // 配置系统时钟
    
    if(USART_Config() != HAL_OK) Error_Handler();         
    printf("\r\n\r\n\r\n"
           "[SYS_INIT] Debug port initilaized.\r\n");

#if(I2C_COMM_TEST)
    if(app_i2c_init() != HAL_OK) Error_Handler();
    printf("[SYS_INIT] I2C initialized.\r\n");
#endif

    printf("\r\n+---------------------------------------+"
           "\r\n|        PY32F003 MCU is ready.         |"
           "\r\n+---------------------------------------+"
           "\r\n         10 digits sent to you!          "
           "\r\n+---------------------------------------+"
           "\r\n");
           
    HAL_Delay(0);
    if (DBG_UART_Start() != HAL_OK) Error_Handler();
    HAL_Delay(0);

#if(I2C_COMM_TEST)
#if(I2C_SLAVE)
    printf("[SYS_INIT] Slave listening...\r\n");
    app_i2c_slave_receive();  app_i2c_wait();
    app_i2c_slave_transmit(); app_i2c_wait();
#else
    printf("[SYS_INIT] Press User button...\r\n");
    while (BSP_PB_GetState(BUTTON_KEY) == 1) { };

    app_i2c_master_transmit(); // app_i2c_wait();
    app_i2c_master_receive();  // app_i2c_wait();
    
#endif
#endif

    while (1)
    { 
        BSP_LED_Toggle(LED3);
        HAL_Delay(500);
    }
}

请注意 app_i2c_master_transmit(); 和 app_i2c_master_receive(); 这两句后面的 app_i2c_wait(); 被注释掉了,这是因为这两个函数被移到了 xxx_transmit(); 和 xxx_receive(); 中去了。

实验结果

  1. 定义 I2C_SLAVE 为 1,编译和烧录从机一侧的代码,在从机开发板上观察 LED 亮起又熄灭,说明代码运行到了 I2C 接收处,同时从 XCOM 的 COM3 窗口可以看到 “Slave listening...”的打印。
  2. 定义 I2C_SLAVE 为 0,编译和烧录主机一侧的代码,在主机开发板上观察 LED 亮起又熄灭,从 XCOM 的 COM32 窗口可以看到 “Press User button...” 的提示。
  3. 按下主机的“用户按钮”(注意不是复位按钮),观察到主机和从机窗口有预计的打印。
  4. 复位从机,再复位主机,按下主机的用户按钮,收发的打印可以复现。

实验的串口截图如下所示。

总结

  • 使用 PY32F003 进行 I2C 的主从机交互的配置比较繁杂,需要为 I2C 分配复用端口,分配中断优先级,分配 DMA 资源才能顺利完成。占用资源不少,如果使用了 I2C,DMA 就不能用于其它功能了。
  • 配置 DMA 时,要注意 TX/RX 两个方向的 DMA 搬运方向要对应,发送通道的搬运方向是内存->外设,接收通道的搬运方向是外设->内存。
  • PY32F003 的 I2C 使用 DMA 的单字节缓冲,因此 DMA 的PeriphDataAlignment 和 MemDataAlignment 都要设置为 BYTE 型。实测如果设置为 HALF_WORD 或者 WORD 会卡死。
  • PY32F003 的 I2C 管脚是 PA3/PA2, PA7/PA12,PB6/PB7,它们属于不同 AF 组,如果有其它的功能需要集成在一起,需要仔细地分配这些管脚,避免复用的冲突。
  • 主从机通信时,它们必须可靠地共地。实测共地不牢靠,会在收发时卡死。不过在实用产品设计中做到这一点倒不难 ;)
  • 作为一颗直连硬件的 MCU,PY32F003 在 I2C 通信中往往充当从机的角色。

因为我的实验都用这个工程,以前的实验用到了 TIM, PWM,DMA,Flash 等,本文整理的代码,尤其是初始化代码,颇费了一些周折。后来还是使用预编译开关把用不到的代码都 #if(0) 掉了才得以顺利运行。

也是的,在实用中,每一个 MCU 的功能一般是较为单一的,无非就是和上位机交换报文,根据通信协议解析报文,然后要么模拟采样,要么PWM输出,要么控制GPIO。上位机通信接口往往也是单一的,要么是 UART, 要么是 I2C,连 SPI 用的都很不多。在 PY32F003 这样一颗 Cortex-M0+ 内核的芯片上完成多种复杂的功能,不免有些为难人家了。实用中多种功能集成在一起的场合也不常见。

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

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

相关文章

001 Windows虚拟机

一、虚拟机安装Windows10 选自定义安装 升级是针对你电脑上有系统的情况下&#xff0c;你要升级&#xff1b;没有系统就选择自定义。 硬盘60G 直接单击下一步就是一个盘 如果你想对磁盘进行分区 分第一个区的时候它会去创建系统的保留分区和系统分区&#xff0c;然后还剩20…

Linux下C++程序瘦身

目录 一.前言二.如何瘦身三.如何读取调试信息文件四.其他 一.前言 我们知道&#xff0c;C程序如果带着调试信息的话会比较大&#xff0c;所以一般发布版本都会去掉调试信息&#xff0c;但是我们又希望如果程序崩溃了可以使用core转储文件进行调试&#xff0c;如果不带调试信息…

数据结构之----贪心算法

数据结构之----贪心算法 什么是贪心算法&#xff1f; 贪心算法是一种常见的解决优化问题的算法&#xff0c;其基本思想是在问题的每个决策阶段&#xff0c;都选择当前看起来最优的选择&#xff0c;即贪心地做出局部最优的决策&#xff0c;以期望获得全局最优解。 贪心算法简…

SpringBoot配置文件加载的优先级及自定义配置

Spring Boot使用一个非常特殊的PropertySource顺序&#xff0c;旨在允许合理的值重写&#xff0c;越靠前优先级越高。属性按以下顺序考虑&#xff1a; 开发者工具Devtools全局配置参数 在IDEA或Eclipse中&#xff0c;安装并启用Spring Boot Devtools插件。打开项目的Settings…

[Big Bird]论文解读:Big Bird: Transformers for Longer Sequences

文章目录 1 介绍2 模型架构3 结果 论文&#xff1a;Big Bird: Transformers for Longer Sequences 作者&#xff1a;Manzil Zaheer, Guru Guruganesh, Avinava Dubey, Joshua Ainslie, Chris Alberti, Santiago Ontanon, Philip Pham, Anirudh Ravula, Qifan Wang, Li Yang, Am…

【Java 并发】三大特性

在 Java 的高并发中&#xff0c;对于线程并发问题的分析通常可以通过 2 个主核心进行分析 JMM 抽象内存模型和 Happens-Before 规则三大特性: 原子性, 有序性和可见性 JMM 抽象内存模型和 Happens-Before 规则, 前面我们讨论过了。这里讨论一下三大特性。 1 原子性 定义: 一个…

HarmonyOS4.0从零开始的开发教程17给您的应用添加通知

HarmonyOS&#xff08;十五&#xff09;给您的应用添加通知 通知介绍 通知旨在让用户以合适的方式及时获得有用的新消息&#xff0c;帮助用户高效地处理任务。应用可以通过通知接口发送通知消息&#xff0c;用户可以通过通知栏查看通知内容&#xff0c;也可以点击通知来打开应…

基于CNN+数据增强+残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)+数据集+模型(一)

系列文章目录 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型&#xff08;一&#xff09; 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型&#xf…

Win11极速安装Tensorflow-gpu+CUDA+cudnn

文章目录 0.pip/conda换默认源1.Anacondapython虚拟环境2.安装CUDA以及cudnn测试tensorflow的GPU版本安装成功的办法 0.pip/conda换默认源 为了高效下载&#xff0c;建议先把默认源换了&#xff0c;很简单这里不再赘述。&#xff08;我用梯子&#xff0c;所以没换源&#x1f6…

数据分析(一)(附带实例和源码)

一、主要目的&#xff1a; 主要利用Python包&#xff0c;如Numpy、Pandas和Scipy等常用分析工具并结合常用的统计量来进行数据的描述&#xff0c;把数据的特征和内在结构展现出来。熟悉在Python开发环境中支持数据分析的可用模块以及其中的方法&#xff0c;基于一定的样例数据…

【Java】使用递归的方法获取层级关系数据demo

使用递归来完善各种业务数据的层级关系的获取 引言&#xff1a;在Java开发中&#xff0c;我们通常会遇到层层递进的关系型数据的获取问题&#xff0c;有时是树状解构&#xff0c;或金字塔结构&#xff0c;怎么描述都行&#xff0c;错综复杂的关系在程序中还是可以理清的。 这…

服务器RAID配置及功能介绍

服务器RAID配置及功能介绍 一、RAID磁盘阵列详解1.RAID磁盘阵列介绍2.RAID 03.RAID14.RAID35.RAID56.RAID67.RAID 10总结阵列卡介绍 一、RAID磁盘阵列详解 1.RAID磁盘阵列介绍 ①是Redundant Array of lndependent Disks的缩写中文简称为独立冗余磁盘阵列。 ②把多块独立的物…

nginx_rtmp_module 之 ngx_rtmp_mp4_module 的mp4源码分析

一&#xff1a;整体代码函数预览 static ngx_int_t ngx_rtmp_mp4_postconfiguration(ngx_conf_t *cf) {ngx_rtmp_play_main_conf_t *pmcf;ngx_rtmp_play_fmt_t **pfmt, *fmt;pmcf ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_play_module);pfmt ngx_ar…

Prometheus 监控笔记(1):你真的会玩监控吗?

认识Prometheus Prometheus 是一种开源的系统和服务监控工具&#xff0c;最初由 SoundCloud 开发&#xff0c;后来成为继 Kubernetes 之后云原生生态系统中的一部分。在 Kubernetes 容器管理系统中&#xff0c;通常会搭配 Prometheus 进行监控&#xff0c;同时也支持多种 Expo…

Node.js安装教程

虽然网上Node.js的安装教程有很多&#xff0c;但是基本上都是千篇一律。虽然跟着网上内容安装&#xff0c;却总会遇到乱七八糟的问题。为此&#xff0c;我写下这篇文章&#xff0c;除了描述node的安装教程&#xff0c;还会解释这样安装的过程起到一个什么作用。 文章大致上分为…

visual studio 2019 移除/卸载项目已经如何再加载项目

文章目录 移除解决方案下的某个项目添加已移除的项目移除项目加载已卸载的项目注意事项 移除解决方案下的某个项目 在项目名称上&#xff0c;点击鼠标右键&#xff0c;弹出右键工具栏&#xff0c;找到 移除 功能。 然后鼠标左键点击 移除。 弹出的模态框&#xff0c;选择确定…

《点云处理》平面拟合

前言 在众多点云处理算法中&#xff0c;其中关于平面拟合的算法十分广泛。本篇内容主要是希望总结归纳各类点云平面拟合算法&#xff0c;并且将代码进行梳理保存。 环境&#xff1a; VS2019 PCL1.11.1 1.RANSAC 使用ransac对平面进行拟合是非常常见的用法&#xff0c;PCL…

josef约瑟 时间继电器 DS-23/C AC220V 10S柜内板前接线

系列型号&#xff1a; DS-21时间继电器 &#xff1b;DS-22时间继电器&#xff1b; DS-23时间继电器&#xff1b;DS-24时间继电器&#xff1b; DS-21C时间继电器&#xff1b;DS-22C时间继电器&#xff1b; DS-23C时间继电器&#xff1b; DS-25时间继电器&#xff1b;DS-26…

Delphi 编译关闭时 Stack overflow 错误

本人工程文件&#xff0c;编译EXE文件&#xff0c;程序关闭时出现 Stack overflow 错误。网搜索一些解决办法&#xff1a;比如&#xff0c;加大堆栈...&#xff0c;均不能问题。虽然&#xff0c;生成的EXE文件&#xff0c;执行时&#xff0c;无任何问题。 Stack overflow 错误&…

【面试】测试/测开(NIG2)

145. linux打印前row行日志 参考&#xff1a;linux日志打印 前10行日志 head -n 10 xx.log后10行日志 tail -n 10 xx.log tail -10f xx.log使用sed命令 sed -n 9,10p xx.log #打印第9、10行使用awk命令 awk NR10 xx.log #打印第10行 awk NR>7 && NR<10 xx.log …