【ESP32 IDF SPI硬件驱动W25Q64】
目录
- SPI
- SPI介绍
- idf配置
- 初始化配置
- 通信
- 驱动代码
SPI
SPI介绍
详细SPI介绍内容参考我之前写的内容【ESP32 IDF 软件模拟SPI驱动 W25Q64存储与读取数组】
idf配置
初始化配置
spi_bus_initialize()
参数1 :
spi几,例如spi2,spi3
参数2:
指向spi_bus_config_t的结构体,该结构体有较多值,取自己用的就行
参数3:
是否使用DMA
对于参数2
,有如下成员变量:
typedef struct {int miso_io_num; // MISO引脚号int mosi_io_num; // MOSI引脚号int sclk_io_num; // 时钟引脚号int quadwp_io_num; // 用于Quad模式的WP引脚号,未使用时设置为-1int quadhd_io_num; // 用于Quad模式的HD引脚号,未使用时设置为-1int max_transfer_sz; // 最大传输大小
} spi_bus_config_t;
设备配置
spi_bus_add_device()
参数1:和上面的一样,选择spi几
参数2:指向设备的结构体
参数3:返回该设备的句柄
其中参数2,成员变量如下:
参数二的结构体的成员变量也很多,我们一样是挑着用上的配置。
spi_device_handle_t dev_handle;spi_device_interface_config_t device_initer={.command_bits=0,//指令的位数.address_bits=0,//地址的位数.mode=0,//模式0,spi四种模式.spics_io_num=15,//ss/cs片选引脚号.clock_speed_hz=1000*1000//时钟通信频率};if(spi_bus_add_device(SPI2_HOST,&dev_handle,&dev_handle)!=ESP_OK) printf("add device success\r\n");
通信
spi_device_transmit()
参数二的结构体成员变量也不少,但是我们配置好要发送的数据和长度,以及接收数据的地方和长度即可。
举例
uint32_t bsp_spi_flash_ReadID(spi_device_handle_t handle)
{//00 定义错误标志esp_err_t e;//01 接收数据的时候发送的空指令uint8_t data[3];data[0] = 0xff;data[1] = 0xff;data[2] = 0xff;//02 定义用于返回的数据uint32_t Temp;//03 定义数据发送接收结构体spi_transaction_ext_t ext; //因为读取设备ID的指令结构与前面定义的默认的不一样,所以需要更改位长memset(&ext, 0, sizeof(ext)); //初始化结构体ext.command_bits = 8; //指令位长度为8ext.address_bits = 0; //地址位长度为0ext.base.cmd = W25X_JedecDeviceID; //设备IDext.base.length = 3 * 8; //要发送数据的长度ext.base.tx_buffer = data; //要发送数据的内容ext.base.rx_buffer = NULL; //接收数据buffer使用结构体内部带的ext.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_USE_RXDATA; //04 数据收发e=spi_device_polling_transmit(handle, &ext.base);if (e != ESP_OK){printf("get ID error!\n");return 0;}//05 返回获取的数据IDuint8_t temp0 = ext.base.rx_data[0];uint8_t temp1 = ext.base.rx_data[1];uint8_t temp2 = ext.base.rx_data[2];Temp = (temp0 << 16) | (temp1 << 8) | temp2;
驱动代码
w25q64.c
#include "W25Q64.h"/*********************************************************** 函 数 名 称:w25q64_init_config* 函 数 功 能:w25q64初始化* 传 入 参 数:无* 函 数 返 回:无* 备 注:无
**********************************************************/
esp_err_t w25q64_init_config(spi_device_handle_t* handle)
{ //00 定义错误标志esp_err_t e;//01 配置总线初始化结构体static spi_bus_config_t bus_cfg; //总线配置结构体bus_cfg.miso_io_num = Flash_SPI_MISO; //misobus_cfg.mosi_io_num = Flash_SPI_MOSI; //mosibus_cfg.sclk_io_num = Flash_SPI_SCLK; //sclkbus_cfg.quadhd_io_num = Flash_SPI_HD; // HDbus_cfg.quadwp_io_num = Flash_SPI_WP; // WPbus_cfg.max_transfer_sz = 4092; //非DMA最大64bytes,DMA最大4092bytes//bus_cfg.intr_flags = 0; //这个用于设置中断优先级的,0是默认bus_cfg.flags = SPICOMMON_BUSFLAG_MASTER;//这个用于设置初始化的时候要检测哪些选项。比如这里设置的是spi初始化为主机模式是否成功。检测结果通过spi_bus_initialize函数的//返回值进行返回。如果初始化为主机模式成功,就会返回esp_ok//02 初始化总线配置结构体e = spi_bus_initialize(Flash_SPI, &bus_cfg, SPI_DMA_CH_AUTO);if (e != ESP_OK){printf("bus initialize failed!\n");return e;}//03 配置设备结构体static spi_device_interface_config_t interface_cfg; //设备配置结构体interface_cfg.address_bits = Flash_Address_Bits; //配置地址位长度//(1)如果设置为0,在通讯的时候就不会发送地址位。//(2)如果设置了非零值,就会在spi通讯的地址发送阶段发送指定长度的address数据。//如果设置了非零值并且在后面数据发送结构体中没有定义addr的值,会默认发送指定长度0值//(3)我们后面发送数据会使用到spi_transaction_t结构体,这个结构体会使用spi_device_interface_config_t中定义好address、command和dummy的长度//如果想使用非固定长度,就要使用spi_transaction_ext_t结构体了。这个结构体包括了四个部分,包含了一个spi_transaction_t和address、command、dummy的长度。//我们要做的就是在spi_transaction_ext_t.base.flags中设置SPI_TRANS_VARIABLE_ADDR/CMD/DUMMY//然后定义好这三部分数据的长度,然后用spi_transaction_ext_t.base的指针代替spi_transaction_t的指针即可interface_cfg.command_bits = Flash_Command_Bits; //配置命令位长度//与address_bits是一样的interface_cfg.dummy_bits = Flash_Dummy_Bits; //配置dummy长度//这里的配置方法与address_bits是一样的。但是要着重说一下这个配置的意义,后面会再说一遍//(1)dummy_bits是用来用来补偿输入延迟。//(2)在read phase开始阶段之前被插入进去。在dummy_bits的时钟下,并不进行数据读取的工作//相当于这段时间发送的clock都是虚拟的时钟,并没有功能。在输入延迟最大允许时间不够的时候,可以通过这种方法进行配置,从而//能够使得系统工作在更高的时钟频率下。//(3)如果主机设备只进行write操作,可以在flags中设置SPI_DEVICE_NO_DUMMY,关闭dummy bits的发送。只有写操作的话,即使使用了gpio交换矩阵,时钟周期也可以工作在80MHZ//interface_cfg.input_delay_ns = 0; //配置输入延时的允许范围//时钟发出信号到miso进行输入直接会有延迟,这个参数就是配置这个允许的最大延迟时间。//如果主机接收到从机时钟,但是超过这个时间没有收到miso发来的输入信号,就会返回通讯失败。//这个时间即使设置为0,也能正常工作,但是最好通过手册或逻辑分析仪进行估算。能够实现更好的通讯。//超过8M的通讯都应该认真设置这个数字interface_cfg.clock_speed_hz = Flash_CLK_SPEED; //配置时钟频率//配置通讯的时钟频率。//这个频率受到io_mux和input_delay_ns限制。//如果是io直连的,时钟上限是80MHZ,如果是gpio交换矩阵连接进来的,时钟上限是40MHZ。//如果是全双工,时钟上限是26MHZ。并且还要考虑输入延时。在相同输入延时的条件下,使用gpio交换矩阵会比使用io mux最大允许的时钟频率小。可以通过//spi_get_freq_limit()来计算能够允许的最大时钟频率是多少//有关SPI通讯时钟极限和配置的问题,后面会详细说一下。interface_cfg.mode = 0; //设置SPI通讯的相位特性和采样边沿。包括了mode0-3四种。要看从设备能够使用哪种模式interface_cfg.spics_io_num = Flash_SPI_CS; //配置片选线interface_cfg.duty_cycle_pos = 0; //配置占空比//设置时钟的占空比,比例是 pos*1/256,默认为0,也就是50%占空比//interface_cfg.cs_ena_pretrans; //在传输之前,片选线应该保持激活状态多少个时钟,只有全双工的时候才需要配置//interface_cfg.cs_ena_posttrans; //在传输之后,片选线应该保持激活状态多少个时钟,只有全双工的时候才需要配置interface_cfg.queue_size = 6; //传输队列的长度,表示可以在通讯的时候挂起多少个spi通讯。在中断通讯模式的时候会把当前spi通讯进程挂起到队列中//interface_cfg.flags; //配置与从机有关的一些参数,比如MSB还是LSB,使不使用三线SPI//interface_cfg.pre_cb; //配置通讯前中断。比如不在这里配置cs片选线,把片选线作为自行控制的线,把片选线拉低放在通讯前中断中//interface_cfg.post_cb;//配置通讯后中断。比如不在这里配置cs片选线,把片选线作为自行控制的线,把片选线拉高放在通讯前中断中//04 设备初始化e = spi_bus_add_device(Flash_SPI, &interface_cfg, handle);if (e != ESP_OK){printf("device config error\n");return e;}return ESP_OK;
}uint32_t bsp_spi_flash_ReadID(spi_device_handle_t handle)
{//00 定义错误标志esp_err_t e;//01 接收数据的时候发送的空指令uint8_t data[3];data[0] = Dummy_Byte;data[1] = Dummy_Byte;data[2] = Dummy_Byte;//02 定义用于返回的数据uint32_t Temp;//03 定义数据发送接收结构体spi_transaction_ext_t ext; //因为读取设备ID的指令结构与前面定义的默认的不一样,所以需要更改位长memset(&ext, 0, sizeof(ext)); //初始化结构体ext.command_bits = 8; //指令位长度为8ext.address_bits = 0; //地址位长度为0ext.base.cmd = W25X_JedecDeviceID; //设备IDext.base.length = 3 * 8; //要发送数据的长度ext.base.tx_buffer = data; //要发送数据的内容ext.base.rx_buffer = NULL; //接收数据buffer使用结构体内部带的ext.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_USE_RXDATA; //04 数据收发e=spi_device_polling_transmit(handle, &ext.base);if (e != ESP_OK){printf("get ID error!\n");return 0;}//05 返回获取的数据IDuint8_t temp0 = ext.base.rx_data[0];uint8_t temp1 = ext.base.rx_data[1];uint8_t temp2 = ext.base.rx_data[2];Temp = (temp0 << 16) | (temp1 << 8) | temp2;return Temp;
}/**
* @breif flash写使能。在执行页写入和擦除命令之前,都必须执行一次页写入
* @param[in] handle: 提供SPI的操作句柄
* @retval 无
**/void bsp_spi_flash_WriteEnable(spi_device_handle_t handle)
{esp_err_t e; //错误标志位// 定义数据发送接收结构体spi_transaction_ext_t ext; //写使能的长度与默认的不同,需要修改memset(&ext, 0, sizeof(ext)); //初始化结构体ext.command_bits = 8; //指令位长度为8ext.address_bits = 0; //地址位长度为0ext.base.cmd = W25X_WriteEnable; //写使能ext.base.length = 0; //要发送数据的长度,这里不需要发送数据ext.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR;//发送指令e = spi_device_polling_transmit(handle, &ext.base);if (e != ESP_OK){printf("write enable failed!\n");}}/**
* @breif 等待flash完成当前操作
* @param[in] handle: 提供SPI的操作句柄
* @retval 无
**/void bsp_spi_flash_WaitForWriteEnd(spi_device_handle_t handle)
{// 定义数据发送接收结构体spi_transaction_ext_t ext; //写使能的长度与默认的不同,需要修改memset(&ext, 0, sizeof(ext)); //初始化结构体ext.command_bits = 8; //指令位长度为8ext.address_bits = 0; //地址位长度为 0ext.base.cmd = W25X_ReadStatusReg; //读取状态寄存器ext.base.length = 1 * 8; //要发送数据的长度,这里不需要发送数据ext.base.rx_buffer = NULL; //不使用外部数据ext.base.tx_buffer = NULL; //不使用外部数据ext.base.tx_data[0] = Dummy_Byte; //发送数据ext.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA;do{//发送指令spi_device_polling_transmit(handle, &ext.base);}while ( (ext.base.rx_data[0] & WIP_Flag )== WIP_SET);
}/**
* @breif 扇区擦除
* @param[in] handle: 提供SPI的操作句柄
* @param[in] SectorAddr: 要擦除的起始扇区地址
* @retval 无
**/void bsp_spi_flash_SectorErase(spi_device_handle_t handle,uint32_t SectorAddr)
{bsp_spi_flash_WriteEnable(handle);bsp_spi_flash_WaitForWriteEnd(handle);// 定义数据发送接收结构体spi_transaction_t t; //配置位与默认一致,不需要修改memset(&t, 0, sizeof(t)); //初始化结构体t.cmd = W25X_SectorErase; //擦除指令t.addr = SectorAddr; //擦除地址t.length = 0; //不需要额外数据了//发送指令spi_device_polling_transmit(handle, &t);//等待擦除完毕bsp_spi_flash_WaitForWriteEnd(handle);
}/**
* @breif 页写入
* @param[in] handle: 提供SPI的操作句柄
* @param[in] pBuffer:要写入的数据地址
* @param[in] WriteAddr:要写入的地址
* @param[in] NumByteToWrite: 要写入的长度
* @retval 无
**/void bsp_spi_flash_PageWrite(spi_device_handle_t handle, uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{bsp_spi_flash_WriteEnable(handle);// 定义数据发送接收结构体spi_transaction_t t; //配置位与默认一致,不需要修改memset(&t, 0, sizeof(t)); //初始化结构体t.cmd = W25X_PageProgram; //页写入t.addr = WriteAddr; //擦除地址t.length = 8*NumByteToWrite; //写入长度t.tx_buffer = pBuffer; //写入的数据t.rx_buffer = NULL; //不需要读取数据if (NumByteToWrite > SPI_Flash_PageSize){printf("length is too long!\n");return ;}//发送指令spi_device_polling_transmit(handle, &t);//等待擦除完毕bsp_spi_flash_WaitForWriteEnd(handle);}/**
* @breif 不定量数据写入
* @param[in] handle: 提供SPI的操作句柄
* @param[in] pBuffer:要写入的数据地址
* @param[in] WriteAddr:要写入的地址
* @param[in] NumByteToWrite: 要写入的长度
* @retval 无
**/void bsp_spi_flash_BufferWrite(spi_device_handle_t handle, uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;//进行取余运算,查看是否进行了页对齐Addr = WriteAddr % SPI_Flash_PageSize;//差count个数据值可以进行页对齐count = SPI_Flash_PageSize - Addr;//计算要写多少个完整的页NumOfPage = NumByteToWrite / SPI_Flash_PageSize;//计算剩余多少字节不满1页NumOfSingle = NumByteToWrite % SPI_Flash_PageSize;//如果Addr=0,也就是进行了页对齐if (Addr == 0){//如果写不满1页if (NumOfPage == 0){bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr, NumByteToWrite);}else{//如果超过1页,先把满的写了while (NumOfPage--){bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr, SPI_Flash_PageSize);WriteAddr += SPI_Flash_PageSize;pBuffer += SPI_Flash_PageSize;}//不满的1页再写bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr, NumOfSingle);}}//如果没有进行页对齐else{if (NumOfPage == 0){//如果当前页剩下的count个位置比NumOfSingle小,1页写不完if (NumOfSingle > count){//先把这页剩下的写了temp = NumOfSingle - count;bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr, count);WriteAddr += count;pBuffer += count;//再把多了的写了bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr, temp);}else{//如果剩下的空间足够大,就直接写bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr, NumByteToWrite);}}//如果不止1页else{//先把对不齐的字节写了NumByteToWrite -= count;NumOfPage = NumByteToWrite / SPI_Flash_PageSize;NumOfSingle = NumByteToWrite % SPI_Flash_PageSize;bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr,count);//重复地址对齐的情况WriteAddr += count;pBuffer += count;while (NumOfPage--){bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr, SPI_Flash_PageSize);pBuffer += SPI_Flash_PageSize;WriteAddr += SPI_Flash_PageSize;}if (NumOfSingle != 0){bsp_spi_flash_PageWrite(handle, pBuffer, WriteAddr, NumOfSingle);}}}}/**
* @breif 数据读取
* @param[in] handle: 提供SPI的操作句柄
* @param[out] pBuffer:要读取的数据buffer地址
* @param[in] WriteAddr:要写入的地址
* @param[in] NumByteToWrite: 要写入的长度
* @retval 无
**/void bsp_spi_flash_BufferRead(spi_device_handle_t handle, uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{bsp_spi_flash_WriteEnable(handle);// 定义数据发送接收结构体spi_transaction_t t; //配置位与默认一致,不需要修改memset(&t, 0, sizeof(t)); //初始化结构体t.cmd = W25X_ReadData; //读取数据t.addr = WriteAddr; //擦除地址t.length = 8 * NumByteToWrite; //读取长度t.tx_buffer = NULL; //不需要写入数据t.rx_buffer = pBuffer; //读取数据//发送指令spi_device_polling_transmit(handle, &t);//等待擦除完毕bsp_spi_flash_WaitForWriteEnd(handle);}
w25q64.h
/** @Author: i want to 舞动乾坤* @Date: 2024-07-27 09:26:08* @LastEditors: i want to 舞动乾坤* @LastEditTime: 2024-07-27 17:21:08* @FilePath: \spi_hardware_driver_w25q64\main\W25Q64.h* @Description: * * Copyright (c) 2024 by i want to 舞动乾坤, All Rights Reserved. */
#ifndef __W25Q64_H__
#define __W25Q64_H__#include "driver/spi_master.h"
#include "driver/spi_common.h"
#include "hal/gpio_types.h"
#include <string.h> //定义Flash实验所需要的引脚
#define Flash_SPI SPI3_HOST
#define Flash_SPI_MISO GPIO_NUM_19
#define Flash_SPI_MOSI GPIO_NUM_23
#define Flash_SPI_SCLK GPIO_NUM_18
#define Flash_SPI_CS GPIO_NUM_5
#define Flash_SPI_WP -1
#define Flash_SPI_HD -1
#define Flash_SPI_DMA SPI_DMA_CH1//定义设备参数
#define Flash_CLK_SPEED 6 * 1000 * 1000 //6M的时钟
#define Flash_Address_Bits 3*8 //地址位长度
#define Flash_Command_Bits 1*8 //命令位长度
#define Flash_Dummy_Bits 0*8 //dummy位长度
#define SPI_Flash_PageSize 256 //页写入最大值//定义命令指令
#define W25X_JedecDeviceID 0x9F //获取flashID的指令
#define W25X_WriteEnable 0x06 //写入使能
#define W25X_WriteDisable 0x04 //禁止写入
#define W25X_ReadStatusReg 0x05 //读取状态寄存器
#define W25X_SectorErase 0x20 //扇区擦除
#define W25X_BlockErase 0xD8 //块擦除
#define W25X_ChipErase 0xC7 //芯片擦除
#define W25X_PageProgram 0x02 //页写入
#define W25X_ReadData 0x03 //数据读取#define Dummy_Byte 0xFF //空指令,用于填充发送缓冲区
#define WIP_Flag 0x01 //flash忙碌标志位
#define WIP_SET 1 esp_err_t w25q64_init_config(spi_device_handle_t* handle);
uint32_t bsp_spi_flash_ReadID(spi_device_handle_t handle);void bsp_spi_flash_SectorErase(spi_device_handle_t handle,uint32_t SectorAddr);
void bsp_spi_flash_PageWrite(spi_device_handle_t handle, uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void bsp_spi_flash_BufferWrite(spi_device_handle_t handle, uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void bsp_spi_flash_BufferRead(spi_device_handle_t handle, uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);#endif
main.c
/** @Author: i want to 舞动乾坤* @Date: 2024-07-27 09:14:50* @LastEditors: i want to 舞动乾坤* @LastEditTime: 2024-07-27 17:16:23* @FilePath: \spi_hardware_driver_w25q64\main\main.c* @Description: * * Copyright (c) 2024 by i want to 舞动乾坤, All Rights Reserved. */#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "W25Q64.h"
#include <esp_log.h>spi_device_handle_t spi2_handle;
static const char * TAG = "Task";void app_main(void)
{uint8_t pBuffer[11] = { 0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x00 };uint8_t rBuffer[11] = {0};int id=0;//配置W25Q64w25q64_init_config(&spi2_handle);//读取器件IDid=bsp_spi_flash_ReadID(spi2_handle);ESP_LOGI(TAG,"id = %X\r\n",id);//向W25Q64的地址0写入10个数据bsp_spi_flash_SectorErase(spi2_handle, 0);ESP_LOGI(TAG,"Sector erase successfully\r\n");bsp_spi_flash_BufferWrite(spi2_handle, pBuffer, 0, 10);ESP_LOGI(TAG,"Write successfully\r\n");//向W25Q64的地址0读取10个数据bsp_spi_flash_BufferRead(spi2_handle, rBuffer, 0, 10);for (int i = 0; i < 10; i++){ESP_LOGI(TAG,"0x%x ", rBuffer[i]);}ESP_LOGI(TAG,".");while(1){ESP_LOGI(TAG,".");vTaskDelay(1000/portTICK_PERIOD_MS);}
}
项目结构:
调试
相关文章:

【ESP32 IDF SPI硬件驱动W25Q64】
目录 SPISPI介绍idf配置初始化配置通信 驱动代码 SPI SPI介绍 详细SPI介绍内容参考我之前写的内容【ESP32 IDF 软件模拟SPI驱动 W25Q64存储与读取数组】 idf配置 初始化配置 spi_bus_initialize() 参数1 :spi几,例如spi2,spi3 参数2:…...

太原高校大学智能制造实验室数字孪生可视化系统平台建设项目验收
随着科技的不断进步,智能制造已经成为推动制造业转型升级的重要力量。太原高校大学智能制造实验室紧跟时代步伐,积极推进数字孪生可视化系统平台的建设,并于近日圆满完成了项目的验收工作。这一里程碑式的成果,不仅标志着实验室在…...

Kafka消息队列
目录 什么是消息队列 高可用性 高扩展性 高可用性 持久化和过期策略 consumer group 分组消费 ZooKeeper 什么是消息队列 普通版消息队列 说白了就是一个队列,生产者生产多少,放在消息队列中存储,而消费者想要多少拿多少,按序列号消费 缓存信息 生产者与消费者解耦…...

@Transactional注解及其事务管理
1. 事务问题概述 事务问题主要来源于数据库,与数据库事务紧密相关。事务的四大特性(ACID): 原子性(Atomicity):事务要么完全执行,要么完全不执行。一致性(Consistency&a…...

ROS2入门到精通—— 3-1 ROS2实战:CasADi —— 优化计算的利器
0 前言 CasADi是一个强大的开源软件库,它提供了一种灵活且高效的方式来构建和解决复杂的非线性模型。通过其直观的API,开发者可以轻松地定义数学表达式并自动求解微分方程、优化问题以及符号计算等任务。 CasADi基于Python编写,但提供了C++和MATLAB接口,使得不同背景的开发…...

日拱一卒 | JVM
文章目录 什么是JVM?JVM的组成JVM的大致工作流程JVM的内存模型 什么是JVM? 我们知道Java面试,只要你的简历上写了了解JVM,那么你就必然会被问到以下问题: 什么是JVM?简单说一下JVM的内存模型?…...

乐尚代驾六订单执行一
加载当前订单 需求 无论是司机端,还是乘客端,遇到页面切换,重新登录小程序等,只要回到首页面,查看当前是否有正在执行订单,如果有跳转到当前订单执行页面 之前这个接口已经开发,为了测试&…...

SciPy 与 MATLAB 数组
SciPy 与 MATLAB 数组 SciPy 是一个开源的 Python 库,广泛用于科学和工程计算。它构建在 NumPy 数组的基础之上,提供了许多高级科学计算功能。MATLAB 是一个高性能的数值计算环境,它也使用数组作为其基础数据结构。在这篇文章中,我们将探讨 SciPy 和 MATLAB 在数组操作上的…...

基于vue-grid-layout插件(vue版本)实现增删改查/拖拽自动排序等功能(已验证、可正常运行)
前端时间有个需求,需要对33(不一定,也可能多行)的卡片布局,进行拖拽,拖拽过程中自动排序,以下代码是基于vue2,可直接运行,报错可评论滴我 部分代码优化来自于GPT4o和Clau…...

DBoW3相关优化脉络
1 DBow3 GitHub - rmsalinas/DBow3: Improved version of DBow2 2 优化后得到fbow GitHub - rmsalinas/fbow: FBOW (Fast Bag of Words) is an extremmely optimized version of the DBow2/DBow3 libraries. 其中fbow是ucoslam的一部分; ucoslam GitHub - la…...

qt 如何制作动态库插件
首先 首先第一点要确定我们的接口是固定的,也就是要确定 #ifndef RTSPPLUGIN_H #define RTSPPLUGIN_H #include "rtspplugin_global.h" typedef void (*func_callback)(uint8_t* data,int len,uint32_t ssrc,uint32_t ts,const char* ipfrom,uint16_t f…...

一种docker start放回Error response from daemon: task xxx错误的解决方式
1. 问题描述 执行systemctl daemon-reload与systemctl restart docker命令后,发现docker中有的应用无法启动,并显示出Exit(255)的错误提示。 重新执行docker start 容器id后发现返回,Error response from daemon: task xxx的错误。 2. 问题…...

规控面试常见问题
一、项目中遇到的困难或者挑战是什么? 二、A*算法原理(伪代码) 输入:代价地图、start 、 goal(Node结构,包含x、y、g、h、id、pid信息) 首先初始化:创建一个优先级队列openlist,它是一个最小堆,根据节点的f值排序 ( priority_queue<Node, std::vector<Node…...

代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙
代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙 文章目录 代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙17.太平洋大西洋水流问题一、DFS二、BFS三、本题总结 82…...

【全网最全】CSDN博客的文字颜色、字体和字号设置
文章目录 一、字体颜色二、字体大小三、字体类型四、字体背景色 在这篇博客中,我们将深入探讨如何在Markdown编辑器中设置文字颜色、大小、字体与背景色。Markdown本身并不直接支持这些功能,但通过结合HTML标签和CSS样式,我们可以实现这些视觉…...

C#实现数据采集系统-Mqtt实现采集数据转发
在数据采集系统中,通过ModbusTcp采集到数据之后,再通过MQTT转发到其他应用 MQTT操作 安装MQTT mqtt介绍和环境安装 使用MQTT 在C#/Net中使用Mqtt MQTT类封装 MQTT配置类 public class MqttConfig{public string Ip {get; set;...

common-intellisense:助力TinyVue 组件书写体验更丝滑
本文由体验技术团队Kagol原创~ 前两天,common-intellisense 开源项目的作者 Simon-He95 在 VueConf 2024 群里发了一个重磅消息: common-intellisense 支持 TinyVue 组件库啦! common-intellisense 插件能够提供超级强大的智能提示功能&…...

图片在线压缩有效方法详解,分享7款最佳图片压缩工具免费(全新)
当您的系统中图片数量不断增加,却无法删除时,那么就需要通过图片压缩来解决您的问题。随着图片文件的增大,高分辨率图片占据了大量存储空间。而此时系统中的存储空间也开始变得不够用,无法跟上高质量图片的增长。因此,…...

electron安装及快速创建
electron安装及快速创建 electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 详细内容见官网:https://www.electronjs.org/zh/docs/latest/。 今天来记录下练习中的安装过程和hello world的创建。 创建项目文件夹,并执行npm 初始化命…...

需要消化的知识点
需要消化 消灭清单 如何自定义一个Interceptor拦截器? 后端开发可以用上的前端技巧 10个堪称神器的 Java 学习网站. 【前端胖头鱼】11 chrome高级调试技巧,学会效率直接提升666% 【前端胖头鱼】10个我经常逛的“小网站” 【前端劝退师lv-6】Chrome D…...

2024年7月25日(Git gitlab以及分支管理 )
分布式版本控制系统 一、Git概述 Git 是一种分布式版本控制系统,用于跟踪和管理代码的变更。它是由Linus Torvalds创建的,最 初被设计用于Linux内核的开发。Git允许开发人员跟踪和管理代码的版本,并且可以在不同的开 发人员之间进行协作。 Github 用的就是Git系统来管理它们的…...

pdf格式过大怎么样变小 pdf文件过大如何缩小上传 超实用的简单方法
面对体积庞大的 PDF 文件,我们常常需要寻找有效的方法来缩减其大小。这不仅能够优化存储空间,还能提升文件的传输和打开速度。PDF文件以其稳定性和跨平台兼容性成为工作和学习中的重要文件格式。然而,当我们需要通过邮件发送或上传大文件时&a…...

前端文件下载word乱码问题
记录一次word下载乱码问题: 用的请求是axios库,然后用Blob去接收二进制文件 思路:现在的解决办法有以下几种,看看是对应哪种,可以尝试解决 1.将响应类型设为blob,这也是最重要的,如果没有解决…...

repo中的default.xml文件project name为什么一样?
文章目录 default.xml文件介绍为什么 name 是一样的,path 不一样?总结 default.xml文件介绍 在 repo 工具的 default.xml 文件中,定义了多个 project 元素,每个元素都代表一个 Git 仓库。 XML 定义了多个不同的 project 元素&…...

<section id=“nice“ data-tool=“mdnice编辑器“ data-webs
大模型日报 2024-07-24 大模型资讯 Meta发布最大Llama 3 AI模型,语言和数学能力提升 摘要: Meta公司发布了其迄今为止最大的Llama 3人工智能模型。该模型主要免费提供,具备多语言处理能力,并在语言和数学方面表现出显著提升。 Meta发布最强AI…...

作业7.26~28
全双工: 通信双方 既可以发送,也可以接收数据 1. 利用多线程 或者 多进程, 实现TCP服务器 和 客户端的全双工通信 思路: 服务器和客户端, 在建立通信以后,可以创建线程,在线程编写另一个功能代…...

自定义webIpad证件相机(webRTC)
该技术方案可用于各浏览器自定义相机开发 相机UI(index.html) <!DOCTYPE html> <html lang"zh" prew"-1"><head><meta charset"UTF-8"><meta name"viewport"content"user-sc…...

GO发票真伪批量查验方法、数电票查验接口
“教”给机器标注数据的正确率就决定了人工智能判断的正确率。翔云人工智能开放平台的OCR产品经过我们的开发人员精心调“教”,识别率高、识别速度快。 发票,是发生的成本、费用或收入的原始凭证。于公司来说,发票主要是公司做账的依据&…...

【Go系列】Go的UI框架Fyne
前言 总有人说Go语言是一门后端编程语言。 Go虽然能够很好地处理后端开发,但是者不代表它没有UI库,不能做GUI,我们一起来看看Go怎么来画UI吧。 正文 Go语言由于其简洁的语法、高效的性能和跨平台的编译能力,非常适合用于开发GUI…...

.NET MAUI:跨平台开发的未来
常用资源 (1).NET MAUI8构建应用文档。 Build your first .NET MAUI app - .NET MAUI | Microsoft Learn 一、什么是 .NET MAUI? .NET Multi-platform App UI (.NET MAUI) 是微软推出的一款跨平台开发框架。作为 Xamarin.Forms 的下一代产…...