当前位置: 首页 > news >正文

正点原子讲解SPI学习,驱动编程NOR FLASH实战

配置SPI传输速度时,需要先失能SPI,__HAL_SPI_DISABLE,然后操作SPI_CR1中的波特率设置位,再使能SPI,

NM25Q128驱动步骤

myspi.c


#include "./BSP/MYSPI/myspi.h"SPI_HandleTypeDef g_spi1_handler;  /* SPI句柄 */void spi1_init(void)
{g_spi1_handler.Instance					= SPI1_SPI;         //SPI1基地址g_spi1_handler.Init.Mode				= SPI_MODE_MASTER;  //设置SPI模式g_spi1_handler.Init.Direction			= SPI_DIRECTION_2LINES; //spi读写g_spi1_handler.Init.DataSize            = SPI_DATASIZE_8BIT;   //g_spi1_handler.Init.CLKPolarity			= SPI_POLARITY_HIGH;   //时钟相位CPOL为1g_spi1_handler.Init.CLKPhase            = SPI_PHASE_2EDGE;    //时钟极性CPLA为1g_spi1_handler.Init.NSS					= SPI_NSS_SOFT;   //NSS管理是软件g_spi1_handler.Init.BaudRatePrescaler   = SPI_BAUDRATEPRESCALER_256;  //波特率分频最大g_spi1_handler.Init.FirstBit			= SPI_FIRSTBIT_MSB;     //发送高位在前g_spi1_handler.Init.TIMode              = SPI_TIMODE_DISABLE;  //帧格式失能g_spi1_handler.Init.CRCCalculation		= SPI_CRCCALCULATION_DISABLE;  //CRC校验失能g_spi1_handler.Init.CRCPolynomial       = 7;    //设置CRC校验多项式(0~65535),默认值7HAL_SPI_Init(&g_spi1_handler);
}//SPI2底层驱动,时钟使能,引脚配置
//此函数会被HAL_SPI_Init()调用
void HAL_SPI_Msp_Init(SPI_HandleTypeDef *hspi)
{GPIO_InitTypeDef gpio_init_struct;if(hspi->Instance == SPI1_SPI) //基地址正确{SPI1_SCK_GPIO_CLK_ENABLE();  /* SPI1_SCK脚时钟使能 */SPI1_MISO_GPIO_CLK_ENABLE(); /* SPI1_MISO脚时钟使能 */SPI1_MOSI_GPIO_CLK_ENABLE(); /* SPI1_MOSI脚时钟使能 *//*SCK引脚模式设置(复用输出)*/gpio_init_struct.Pin = SPI1_SCK_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_AF_PP;  //复用推挽输出gpio_init_struct.Pull = GPIO_PULLUP;   gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(SPI1_SCK_GPIO_PORT, &gpio_init_struct);/*MISO引脚模式设置(复用输出)*/gpio_init_struct.Pin = SPI1_MISO_GPIO_PIN;HAL_GPIO_Init(SPI1_MISO_GPIO_PORT, &gpio_init_struct);/*MOSI引脚模式配置(复用输出)*/gpio_init_struct.Pin = SPI1_MOSI_GPIO_PIN;HAL_GPIO_Init(SPI1_MISO_GPIO_PORT, &gpio_init_struct);}
}//spi发送接收函数
uint8_t spi1_read_write_byte(uint8_t data)
{uint8_t rec_data = 0;//发送data,接收到rec_data,HAL_SPI_TransmitReceive(&g_spi1_handler, &data, &rec_data, 1, 1000);return rec_data;
}//SPI1速度设置函数
void spi1_set_speed(uint8_t speed)
{assert_param(IS_SPI_BAUDRATE_PRESCALER(speed));  /* 判断有效性 */__HAL_SPI_DISABLE(&g_spi1_handler);  /*关闭SPI*/g_spi1_handler.Instance->CR1 &= 0xFFC7;  /* 位3-5清零,用来设置1波特率 */g_spi1_handler.Instance->CR1 |= speed << 3;   /* 设置SPI速度 */__HAL_SPI_ENABLE(&g_spi1_handler);  /*打开SPI*/
}

myspi.h


#ifndef __MYSPI_H
#define __MYSPI_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* SPI2 引脚 定义 */#define SPI1_SCK_GPIO_PORT              GPIOB
#define SPI1_SCK_GPIO_PIN               GPIO_PIN_10
#define SPI1_SCK_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */#define SPI1_MISO_GPIO_PORT             GPIOB
#define SPI1_MISO_GPIO_PIN              GPIO_PIN_11
#define SPI1_MISO_GPIO_CLK_ENABLE()     do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */#define SPI1_MOSI_GPIO_PORT             GPIOB
#define SPI1_MOSI_GPIO_PIN              GPIO_PIN_12
#define SPI1_MOSI_GPIO_CLK_ENABLE()     do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 *//* SPI2相关定义 */
#define SPI1_SPI                        SPI1
#define SPI1_SPI_CLK_ENABLE()           do{ __HAL_RCC_SPI1_CLK_ENABLE(); }while(0)    /* SPI2时钟使能 *//******************************************************************************************//* SPI总线速度设置 */
#define SPI_SPEED_2         0
#define SPI_SPEED_4         1
#define SPI_SPEED_8         2
#define SPI_SPEED_16        3
#define SPI_SPEED_32        4
#define SPI_SPEED_64        5
#define SPI_SPEED_128       6
#define SPI_SPEED_256       7void spi1_init(void);
void spi1_set_speed(uint8_t speed);
uint8_t spi1_read_write_byte(uint8_t txdata);     
void spi1_set_speed(uint8_t speed);#endif

norflash.c  跟着原子教学写的


#include "./BSP/MYSPI/norflash.h"
#include "./BSP/MYSPI/myspi.h"void norflash_init(void)
{NORFLASH_CS_GPIO_CLK_ENABLE();   /*NOR flash时钟使能*/GPIO_InitTypeDef gpio_init_struct;gpio_init_struct.Pin = NORFLASH_CS_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;gpio_init_struct.Pull = GPIO_PULLUP;gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(NORFLASH_CS_GPIO_PORT, &gpio_init_struct);spi1_init();  //spi1初始化,SCK,MOSI,MISO引脚配置时钟使能spi1_read_write_byte(0xFF);  //发送0xFF,清除DR的作用NORFLASH_CS(1);   //拉高片选
}//读nm25q128状态寄存器1
//读nm25q128状态寄存器BUSY位,SR1的第一位是BUSY,WEL,写使能锁存是第二位
//busy位=0是空闲状态,=1是忙状态
//WEL位=1,是可以写,=0是禁止写
uint8_t norflash_rd_sr1(void)
{uint8_t rec_data = 0;NORFLASH_CS(0);rec_data = spi1_read_write_byte(0x05);   /*读状态寄存器1*/NORFLASH_CS(1); //拉高片选 return rec_data;
}/*读flash指定地址一字节数据*/
uint8_t norflash_read_data(uint32_t addr)
{uint8_t rec_data = 0;//拉低片选NORFLASH_CS(0);/*发送读命令*/spi1_read_write_byte(0x03);  //NM25Q128发送读命令指令/*发送地址,有24位,一次只发一个字节,需要移位操作*/spi1_read_write_byte(addr >> 16);spi1_read_write_byte(addr >> 8);spi1_read_write_byte(addr);/*接收数据*/rec_data = spi1_read_write_byte(0xFF); //发送0xFF过去,交换数据NORFLASH_CS(1);  //拉高片选return rec_data;  //返回读取的一字节数据
}//擦除扇区
void norflash_erase_sector(uint32_t addr)
{//1.写使能,执行页写,扇区擦除,块擦除,片擦除都需要写使能NORFLASH_CS(0);spi1_read_write_byte(0x06);  //主机向NM25Q128写使能指令NORFLASH_CS(1); //拉高片选//2.等待空闲while(norflash_rd_sr1() & 0x01);  //检测写使能是否完成//3.发送擦除扇区指令NORFLASH_CS(0);spi1_read_write_byte(0x20);/*4.发送地址,有24位,一次只发一个字节,需要移位操作*/spi1_read_write_byte(addr >> 16);spi1_read_write_byte(addr >> 8);spi1_read_write_byte(addr); //会自动将该地址的扇区擦除/*5.等待空闲*/while(norflash_rd_sr1() & 0x01);  //检测擦除完成
}//norflash页写
void norflash_write_page(uint8_t data, uint32_t addr)
{/*1.擦除扇区*/norflash_erase_sector(addr);/*2. 写使能*/NORFLASH_CS(0);spi1_read_write_byte(0x06);  //主机向NM25Q128写使能指令NORFLASH_CS(1); //拉高片选/*3.发送页写指令*/NORFLASH_CS(0);spi1_read_write_byte(0x02);/*4.发送地址,有24位,一次只发一个字节,需要移位操作*/spi1_read_write_byte(addr >> 16);spi1_read_write_byte(addr >> 8);spi1_read_write_byte(addr); /*5.要写入的数据*/spi1_read_write_byte(data);NORFLASH_CS(1); //拉高片选/*6.等待写入完成,等待空闲*/while(norflash_rd_sr1() & 0x01);  //等待空闲
}

norflash.h   跟着自己开发板改的引脚,跟着原子教学写的

#ifndef __NORFLASH_H
#define __NORFLASH_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* NORFLASH 片选 引脚 定义 */#define NORFLASH_CS_GPIO_PORT           GPIOB
#define NORFLASH_CS_GPIO_PIN            GPIO_PIN_14
#define NORFLASH_CS_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 *//******************************************************************************************//* NORFLASH 片选信号 */
#define NORFLASH_CS(x)      do{ x ? \HAL_GPIO_WritePin(NORFLASH_CS_GPIO_PORT, NORFLASH_CS_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(NORFLASH_CS_GPIO_PORT, NORFLASH_CS_GPIO_PIN, GPIO_PIN_RESET); \}while(0)/* FLASH芯片列表 */
#define W25Q80      0XEF13          /* W25Q80   芯片ID */
#define W25Q16      0XEF14          /* W25Q16   芯片ID */
#define W25Q32      0XEF15          /* W25Q32   芯片ID */
#define W25Q64      0XEF16          /* W25Q64   芯片ID */
#define W25Q128     0XEF17          /* W25Q128  芯片ID */
#define W25Q256     0XEF18          /* W25Q256  芯片ID */
#define BY25Q64     0X6816          /* BY25Q64  芯片ID */
#define BY25Q128    0X6817          /* BY25Q128 芯片ID */
#define NM25Q64     0X5216          /* NM25Q64  芯片ID */
#define NM25Q128    0X5217          /* NM25Q128 芯片ID *//* 指令表 */
#define FLASH_WriteEnable           0x06 
#define FLASH_WriteDisable          0x04 
#define FLASH_ReadStatusReg1        0x05 
#define FLASH_ReadStatusReg2        0x35 
#define FLASH_ReadStatusReg3        0x15 
#define FLASH_WriteStatusReg1       0x01 
#define FLASH_WriteStatusReg2       0x31 
#define FLASH_WriteStatusReg3       0x11 
#define FLASH_ReadData              0x03 
#define FLASH_FastReadData          0x0B 
#define FLASH_FastReadDual          0x3B 
#define FLASH_FastReadQuad          0xEB  
#define FLASH_PageProgram           0x02 
#define FLASH_PageProgramQuad       0x32 
#define FLASH_BlockErase            0xD8 
#define FLASH_SectorErase           0x20 
#define FLASH_ChipErase             0xC7 
#define FLASH_PowerDown             0xB9 
#define FLASH_ReleasePowerDown      0xAB 
#define FLASH_DeviceID              0xAB 
#define FLASH_ManufactDeviceID      0x90 
#define FLASH_JedecDeviceID         0x9F 
#define FLASH_Enable4ByteAddr       0xB7
#define FLASH_Exit4ByteAddr         0xE9
#define FLASH_SetReadParam          0xC0 
#define FLASH_EnterQPIMode          0x38
#define FLASH_ExitQPIMode           0xFFextern uint16_t norflash_TYPE;      /* 定义FLASH芯片型号 *//* 普通函数 */void norflash_init(void);                   /* 初始化25QXX */
void norflash_erase_sector(uint32_t addr);   /* 扇区擦除 */
uint8_t norflash_rd_sr1(void);     /*读状态寄存器1*/
uint8_t norflash_read_data(uint32_t addr);  /*读flash指定地址一字节数据*/
void norflash_write_page(uint8_t data, uint32_t addr);  //写页#endif

另外,有几个norflash写的函数,当写入字节超过剩余页字节容量

下面是正点例程处理写入超过当前页剩余字节数,超过扇区剩余字节数,扇区内有字节已经被写1,只能整块擦除,的norflash.c代码,我已经加了注释,非常巧妙

/******************************************************************************************************* @file        norflash.c* @author      正点原子团队(ALIENTEK)* @version     V1.0* @date        2021-10-23* @brief       NOR FLASH(25QXX) 驱动代码* @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司***************************************************************************************************** @attention** 实验平台:正点原子 探索者 F407开发板* 在线视频:www.yuanzige.com* 技术论坛:www.openedv.com* 公司网址:www.alientek.com* 购买地址:openedv.taobao.com** 修改说明* V1.0 20211023* 第一次发布******************************************************************************************************/#include "./BSP/SPI/spi.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/NORFLASH/norflash.h"uint16_t g_norflash_type = W25Q128;     /* 默认是NM25Q128 *//*** @brief       初始化SPI NOR FLASH* @param       无* @retval      无*/
void norflash_init(void)
{uint8_t temp;NORFLASH_CS_GPIO_CLK_ENABLE();      /* NORFLASH CS脚 时钟使能 */GPIO_InitTypeDef gpio_init_struct;gpio_init_struct.Pin = NORFLASH_CS_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;gpio_init_struct.Pull = GPIO_PULLUP;gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(NORFLASH_CS_GPIO_PORT, &gpio_init_struct); /* CS引脚模式设置(复用输出) */NORFLASH_CS(1);                         /* 取消片选 */spi1_init();                            /* 初始化SPI1 */spi1_set_speed(SPI_SPEED_4);            /* SPI1 切换到高速状态 21Mhz */g_norflash_type = norflash_read_id();   /* 读取FLASH ID. */if (g_norflash_type == W25Q256)         /* SPI FLASH为W25Q256, 必须使能4字节地址模式 */{temp = norflash_read_sr(3);         /* 读取状态寄存器3,判断地址模式 */if ((temp & 0X01) == 0)             /* 如果不是4字节地址模式,则进入4字节地址模式 */{norflash_write_enable();        /* 写使能 */temp |= 1 << 1;                 /* ADP=1, 上电4位地址模式 */norflash_write_sr(3, temp);     /* 写SR3 */NORFLASH_CS(0);spi1_read_write_byte(FLASH_Enable4ByteAddr);    /* 使能4字节地址指令 */NORFLASH_CS(1);}}//printf("ID:%x\r\n", g_norflash_type);
}/*** @brief       等待空闲* @param       无* @retval      无*/
static void norflash_wait_busy(void)
{while ((norflash_read_sr(1) & 0x01) == 0x01);   /* 等待BUSY位清空 */
}/*** @brief       25QXX写使能*   @note      将S1寄存器的WEL置位* @param       无* @retval      无*/
void norflash_write_enable(void)
{NORFLASH_CS(0);spi1_read_write_byte(FLASH_WriteEnable);   /* 发送写使能 */NORFLASH_CS(1);
}/*** @brief       25QXX发送地址*   @note      根据芯片型号的不同, 发送24ibt / 32bit地址* @param       address : 要发送的地址* @retval      无*/
static void norflash_send_address(uint32_t address)
{if (g_norflash_type == W25Q256)                     /* 只有W25Q256支持4字节地址模式 */{spi1_read_write_byte((uint8_t)((address)>>24)); /* 发送 bit31 ~ bit24 地址 */} spi1_read_write_byte((uint8_t)((address)>>16));     /* 发送 bit23 ~ bit16 地址 */spi1_read_write_byte((uint8_t)((address)>>8));      /* 发送 bit15 ~ bit8  地址 */spi1_read_write_byte((uint8_t)address);             /* 发送 bit7  ~ bit0  地址 */
}/*** @brief       读取25QXX的状态寄存器,25QXX一共有3个状态寄存器*   @note      状态寄存器1:*              BIT7  6   5   4   3   2   1   0*              SPR   RV  TB BP2 BP1 BP0 WEL BUSY*              SPR:默认0,状态寄存器保护位,配合WP使用*              TB,BP2,BP1,BP0:FLASH区域写保护设置*              WEL:写使能锁定*              BUSY:忙标记位(1,忙;0,空闲)*              默认:0x00**              状态寄存器2:*              BIT7  6   5   4   3   2   1   0*              SUS   CMP LB3 LB2 LB1 (R) QE  SRP1**              状态寄存器3:*              BIT7      6    5    4   3   2   1   0*              HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS** @param       regno: 状态寄存器号,范:1~3* @retval      状态寄存器值*/
uint8_t norflash_read_sr(uint8_t regno)
{uint8_t byte = 0, command = 0;switch (regno){case 1:command = FLASH_ReadStatusReg1;  /* 读状态寄存器1指令 */break;case 2:command = FLASH_ReadStatusReg2;  /* 读状态寄存器2指令 */break;case 3:command = FLASH_ReadStatusReg3;  /* 读状态寄存器3指令 */break;default:command = FLASH_ReadStatusReg1;break;}NORFLASH_CS(0);spi1_read_write_byte(command);      /* 发送读寄存器命令 */byte = spi1_read_write_byte(0Xff);  /* 读取一个字节 */NORFLASH_CS(1);return byte;
}/*** @brief       写25QXX状态寄存器*   @note      寄存器说明见norflash_read_sr函数说明* @param       regno: 状态寄存器号,范:1~3* @param       sr   : 要写入状态寄存器的值* @retval      无*/
void norflash_write_sr(uint8_t regno, uint8_t sr)
{uint8_t command = 0;switch (regno){case 1:command = FLASH_WriteStatusReg1;  /* 写状态寄存器1指令 */break;case 2:command = FLASH_WriteStatusReg2;  /* 写状态寄存器2指令 */break;case 3:command = FLASH_WriteStatusReg3;  /* 写状态寄存器3指令 */break;default:command = FLASH_WriteStatusReg1;break;}NORFLASH_CS(0);spi1_read_write_byte(command);  /* 发送读寄存器命令 */spi1_read_write_byte(sr);       /* 写入一个字节 */NORFLASH_CS(1);
}/*** @brief       读取芯片ID* @param       无* @retval      FLASH芯片ID*   @note      芯片ID列表见: norflash.h, 芯片列表部分*/
uint16_t norflash_read_id(void)
{uint16_t deviceid;NORFLASH_CS(0);spi1_read_write_byte(FLASH_ManufactDeviceID);   /* 发送读 ID 命令 */spi1_read_write_byte(0);                        /* 写入一个字节 */spi1_read_write_byte(0);spi1_read_write_byte(0);deviceid = spi1_read_write_byte(0xFF) << 8;     /* 读取高8位字节 */deviceid |= spi1_read_write_byte(0xFF);         /* 读取低8位字节 */NORFLASH_CS(1);return deviceid;
}/*** @brief       读取SPI FLASH*   @note      在指定地址开始读取指定长度的数据* @param       pbuf    : 数据存储区* @param       addr    : 开始读取的地址(最大32bit)* @param       datalen : 要读取的字节数(最大65535)* @retval      无*/
void norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{uint16_t i;NORFLASH_CS(0);spi1_read_write_byte(FLASH_ReadData);       /* 发送读取命令 */norflash_send_address(addr);                /* 发送地址 */for (i = 0; i < datalen; i++){pbuf[i] = spi1_read_write_byte(0XFF);   /* 循环读取 */}NORFLASH_CS(1);
}/*** @brief       SPI在一页(0~65535)内写入少于256个字节的数据*   @note      在指定地址开始写入最大256字节的数据* @param       pbuf    : 数据存储区* @param       addr    : 开始写入的地址(最大32bit)* @param       datalen : 要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!* @retval      无*/
static void norflash_write_page(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{uint16_t i;norflash_write_enable();                    /* 写使能 */NORFLASH_CS(0);spi1_read_write_byte(FLASH_PageProgram);    /* 发送写页命令 */norflash_send_address(addr);                /* 发送地址 */for (i = 0; i < datalen; i++){spi1_read_write_byte(pbuf[i]);          /* 循环读取 */}NORFLASH_CS(1);norflash_wait_busy();       /* 等待写入结束 */
}/*** @brief       无检验写SPI FLASH*   @note      必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!*              具有自动换页功能*              在指定地址开始写入指定长度的数据,但是要确保地址不越界!** @param       pbuf    : 数据存储区* @param       addr    : 开始写入的地址(最大32bit)* @param       datalen : 要写入的字节数(最大65535)* @retval      无*/
static void norflash_write_nocheck(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{uint16_t pageremain;pageremain = 256 - addr % 256;  /* 单页剩余的字节数 */if (datalen <= pageremain)      /* 不大于256个字节 */{
//1,和2两个操作都是为了让写入256个字节和写入小于当前页剩余字节数的函数3为一个函数,所以1,2做了这样处理pageremain = datalen; //1}while (1){/* 当写入字节比页内剩余地址还少的时候, 一次性写完* 当写入直接比页内剩余地址还多的时候, 先写完整个页内剩余地址, 然后根据剩余长度进行不同处理*///先写满首地址剩余字节,当写入字节小于剩余当前字节页字节时norflash_write_page(pbuf, addr, pageremain);   //3if (datalen == pageremain)      /* 写入结束了 */{break;}else                            /* datalen > pageremain */{pbuf += pageremain;         /* pbuf指针地址偏移,前面已经写了pageremain字节 */addr += pageremain;         /* 写地址偏移,前面已经写了pageremain字节 */datalen -= pageremain;      /* 写入总长度减去已经写入了的字节数 */if (datalen > 256)          /* 剩余数据还大于一页,可以一次写一页 */{pageremain = 256;       /* 一次可以写入256个字节 */}else                        /* 剩余数据小于一页,可以一次写完 */{//2pageremain = datalen;   /* 不够256个字节了 */  }}}
}/*** @brief       写SPI FLASH*   @note      在指定地址开始写入指定长度的数据 , 该函数带擦除操作!*              SPI FLASH 一般是: 256个字节为一个Page, 4Kbytes为一个Sector, 16个扇区为1个Block*              擦除的最小单位为Sector.** @param       pbuf    : 数据存储区* @param       addr    : 开始写入的地址(最大32bit)* @param       datalen : 要写入的字节数(最大65535)* @retval      无*/
uint8_t g_norflash_buf[4096];   /* 扇区缓存 */void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{uint32_t secpos;uint16_t secoff;uint16_t secremain;uint16_t i;uint8_t *norflash_buf;norflash_buf = g_norflash_buf;secpos = addr / 4096;       /* 扇区地址 */secoff = addr % 4096;       /* 在扇区内的偏移 */secremain = 4096 - secoff;  /* 扇区剩余空间大小 *///printf("ad:%X,nb:%X\r\n", addr, datalen); /* 测试用 */if (datalen <= secremain){secremain = datalen;    /* 不大于4096个字节 */}while (1){norflash_read(norflash_buf, secpos * 4096, 4096);   /* 读出整个扇区的内容 */for (i = 0; i < secremain; i++)     /* 校验数据 */{if (norflash_buf[secoff + i] != 0XFF){break;                      /* 需要擦除, 直接退出for循环 */}}//上面if执行break话,就是扇区剩余扇区不需要擦除,执行完后i==secremainif (i < secremain)                  /* 需要擦除 */{norflash_erase_sector(secpos);  /* 擦除这个扇区 */for (i = 0; i < secremain; i++) /* 复制 */{norflash_buf[i + secoff] = pbuf[i];}norflash_write_nocheck(norflash_buf, secpos * 4096, 4096);  /* 写入整个扇区 */}else    /* 写已经擦除了的,直接写入扇区剩余区间. */{norflash_write_nocheck(pbuf, addr, secremain);              /* 直接写扇区 */}if (datalen == secremain){break;  /* 写入结束了 */}else        /* 写入未结束 */{secpos++;               /* 扇区地址增1 */secoff = 0;             /* 偏移位置为0 */pbuf += secremain;      /* 指针偏移 */addr += secremain;      /* 写地址偏移 */datalen -= secremain;   /* 字节数递减 */if (datalen > 4096){secremain = 4096;   /* 下一个扇区还是写不完 */}else{secremain = datalen;/* 下一个扇区可以写完了 */}}}
}/*** @brief       擦除整个芯片*   @note      等待时间超长...* @param       无* @retval      无*/
void norflash_erase_chip(void)
{norflash_write_enable();    /* 写使能 */norflash_wait_busy();       /* 等待空闲 */NORFLASH_CS(0);spi1_read_write_byte(FLASH_ChipErase);  /* 发送读寄存器命令 */ NORFLASH_CS(1);norflash_wait_busy();       /* 等待芯片擦除结束 */
}/*** @brief       擦除一个扇区*   @note      注意,这里是扇区地址,不是字节地址!!*              擦除一个扇区的最少时间:150ms* * @param       saddr : 扇区地址 根据实际容量设置* @retval      无*/
void norflash_erase_sector(uint32_t saddr)
{//printf("fe:%x\r\n", saddr);   /* 监视falsh擦除情况,测试用 */saddr *= 4096;norflash_write_enable();        /* 写使能 */norflash_wait_busy();           /* 等待空闲 */NORFLASH_CS(0);spi1_read_write_byte(FLASH_SectorErase);    /* 发送写页命令 */norflash_send_address(saddr);   /* 发送地址 */NORFLASH_CS(1);norflash_wait_busy();           /* 等待扇区擦除完成 */
}

下面是norflash.h

/******************************************************************************************************* @file        norflash.h* @author      正点原子团队(ALIENTEK)* @version     V1.0* @date        2021-10-23* @brief       NOR FLASH(25QXX) 驱动代码* @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司***************************************************************************************************** @attention** 实验平台:正点原子 探索者 F407开发板* 在线视频:www.yuanzige.com* 技术论坛:www.openedv.com* 公司网址:www.alientek.com* 购买地址:openedv.taobao.com** 修改说明* V1.0 20211023* 第一次发布******************************************************************************************************/#ifndef __norflash_H
#define __norflash_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* NORFLASH 片选 引脚 定义 */#define NORFLASH_CS_GPIO_PORT           GPIOB
#define NORFLASH_CS_GPIO_PIN            GPIO_PIN_14
#define NORFLASH_CS_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 *//******************************************************************************************//* NORFLASH 片选信号 */
#define NORFLASH_CS(x)      do{ x ? \HAL_GPIO_WritePin(NORFLASH_CS_GPIO_PORT, NORFLASH_CS_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(NORFLASH_CS_GPIO_PORT, NORFLASH_CS_GPIO_PIN, GPIO_PIN_RESET); \}while(0)/* FLASH芯片列表 */
#define W25Q80      0XEF13          /* W25Q80   芯片ID */
#define W25Q16      0XEF14          /* W25Q16   芯片ID */
#define W25Q32      0XEF15          /* W25Q32   芯片ID */
#define W25Q64      0XEF16          /* W25Q64   芯片ID */
#define W25Q128     0XEF17          /* W25Q128  芯片ID */
#define W25Q256     0XEF18          /* W25Q256  芯片ID */
#define BY25Q64     0X6816          /* BY25Q64  芯片ID */
#define BY25Q128    0X6817          /* BY25Q128 芯片ID */
#define NM25Q64     0X5216          /* NM25Q64  芯片ID */
#define NM25Q128    0X5217          /* NM25Q128 芯片ID *//* 指令表 */
#define FLASH_WriteEnable           0x06 
#define FLASH_WriteDisable          0x04 
#define FLASH_ReadStatusReg1        0x05 
#define FLASH_ReadStatusReg2        0x35 
#define FLASH_ReadStatusReg3        0x15 
#define FLASH_WriteStatusReg1       0x01 
#define FLASH_WriteStatusReg2       0x31 
#define FLASH_WriteStatusReg3       0x11 
#define FLASH_ReadData              0x03 
#define FLASH_FastReadData          0x0B 
#define FLASH_FastReadDual          0x3B 
#define FLASH_FastReadQuad          0xEB  
#define FLASH_PageProgram           0x02 
#define FLASH_PageProgramQuad       0x32 
#define FLASH_BlockErase            0xD8 
#define FLASH_SectorErase           0x20 
#define FLASH_ChipErase             0xC7 
#define FLASH_PowerDown             0xB9 
#define FLASH_ReleasePowerDown      0xAB 
#define FLASH_DeviceID              0xAB 
#define FLASH_ManufactDeviceID      0x90 
#define FLASH_JedecDeviceID         0x9F 
#define FLASH_Enable4ByteAddr       0xB7
#define FLASH_Exit4ByteAddr         0xE9
#define FLASH_SetReadParam          0xC0 
#define FLASH_EnterQPIMode          0x38
#define FLASH_ExitQPIMode           0xFFextern uint16_t norflash_TYPE;      /* 定义FLASH芯片型号 *//* 静态函数 */
static void norflash_wait_busy(void);               /* 等待空闲 */
static void norflash_send_address(uint32_t address);/* 发送地址 */
static void norflash_write_page(uint8_t *pbuf, uint32_t addr, uint16_t datalen);    /* 写入page */
static void norflash_write_nocheck(uint8_t *pbuf, uint32_t addr, uint16_t datalen); /* 写flash,不带擦除 *//* 普通函数 */
void norflash_init(void);                   /* 初始化25QXX */
uint16_t norflash_read_id(void);            /* 读取FLASH ID */
void norflash_write_enable(void);           /* 写使能 */
uint8_t norflash_read_sr(uint8_t regno);    /* 读取状态寄存器 */
void norflash_write_sr(uint8_t regno,uint8_t sr);   /* 写状态寄存器 */void norflash_erase_chip(void);             /* 整片擦除 */
void norflash_erase_sector(uint32_t saddr); /* 扇区擦除 */
void norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen);     /* 读取flash */
void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen);    /* 写入flash */#endif

页写入无校验就是写入地址全是0xFF的情况和写入地址带校验有的不是0xFF带擦除的两个函数非常有逻辑性,单独列出来

,后面跟上擦除整个扇区和擦除整个芯片的操作,因为norflash最小擦除单位是扇区,nandflash最小擦除单位是块,1个扇区4096byte,一个页256Byte,一个块65536字节

扇区擦除函数不同与教学的是传入扇区地址,在函数乘以了4096恢复了直接地址

/*** @brief       无检验写SPI FLASH*   @note      必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!*              具有自动换页功能*              在指定地址开始写入指定长度的数据,但是要确保地址不越界!** @param       pbuf    : 数据存储区* @param       addr    : 开始写入的地址(最大32bit)* @param       datalen : 要写入的字节数(最大65535)* @retval      无*/
static void norflash_write_nocheck(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{uint16_t pageremain;pageremain = 256 - addr % 256;  /* 单页剩余的字节数 */if (datalen <= pageremain)      /* 不大于256个字节 */{
//1,和2两个操作都是为了让写入256个字节和写入小于当前页剩余字节数的函数3为一个函数,所以1,2做了这样处理pageremain = datalen; //1}while (1){/* 当写入字节比页内剩余地址还少的时候, 一次性写完* 当写入直接比页内剩余地址还多的时候, 先写完整个页内剩余地址, 然后根据剩余长度进行不同处理*///先写满首地址剩余字节,当写入字节小于剩余当前字节页字节时norflash_write_page(pbuf, addr, pageremain);   //3if (datalen == pageremain)      /* 写入结束了 */{break;}else                            /* datalen > pageremain */{pbuf += pageremain;         /* pbuf指针地址偏移,前面已经写了pageremain字节 */addr += pageremain;         /* 写地址偏移,前面已经写了pageremain字节 */datalen -= pageremain;      /* 写入总长度减去已经写入了的字节数 */if (datalen > 256)          /* 剩余数据还大于一页,可以一次写一页 */{pageremain = 256;       /* 一次可以写入256个字节 */}else                        /* 剩余数据小于一页,可以一次写完 */{//2pageremain = datalen;   /* 不够256个字节了 */  }}}
}/*** @brief       写SPI FLASH*   @note      在指定地址开始写入指定长度的数据 , 该函数带擦除操作!*              SPI FLASH 一般是: 256个字节为一个Page, 4Kbytes为一个Sector, 16个扇区为1个Block*              擦除的最小单位为Sector.** @param       pbuf    : 数据存储区* @param       addr    : 开始写入的地址(最大32bit)* @param       datalen : 要写入的字节数(最大65535)* @retval      无*/
uint8_t g_norflash_buf[4096];   /* 扇区缓存 */void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{uint32_t secpos;uint16_t secoff;uint16_t secremain;uint16_t i;uint8_t *norflash_buf;norflash_buf = g_norflash_buf;secpos = addr / 4096;       /* 扇区地址 */secoff = addr % 4096;       /* 在扇区内的偏移 */secremain = 4096 - secoff;  /* 扇区剩余空间大小 *///printf("ad:%X,nb:%X\r\n", addr, datalen); /* 测试用 */if (datalen <= secremain){secremain = datalen;    /* 不大于4096个字节 */}while (1){norflash_read(norflash_buf, secpos * 4096, 4096);   /* 读出整个扇区的内容 */for (i = 0; i < secremain; i++)     /* 校验数据 */{if (norflash_buf[secoff + i] != 0XFF){break;                      /* 需要擦除, 直接退出for循环 */}}//上面if执行break话,就是扇区剩余扇区不需要擦除,执行完后i==secremainif (i < secremain)                  /* 需要擦除 */{norflash_erase_sector(secpos);  /* 擦除这个扇区 */for (i = 0; i < secremain; i++) /* 复制 */{norflash_buf[i + secoff] = pbuf[i];}norflash_write_nocheck(norflash_buf, secpos * 4096, 4096);  /* 写入整个扇区 */}else    /* 写已经擦除了的,直接写入扇区剩余区间. */{norflash_write_nocheck(pbuf, addr, secremain);              /* 直接写扇区 */}if (datalen == secremain){break;  /* 写入结束了 */}else        /* 写入未结束 */{secpos++;               /* 扇区地址增1 */secoff = 0;             /* 偏移位置为0 */pbuf += secremain;      /* 指针偏移 */addr += secremain;      /* 写地址偏移 */datalen -= secremain;   /* 字节数递减 */if (datalen > 4096){secremain = 4096;   /* 下一个扇区还是写不完 */}else{secremain = datalen;/* 下一个扇区可以写完了 */}}}
}/*** @brief       擦除整个芯片*   @note      等待时间超长...* @param       无* @retval      无*/
void norflash_erase_chip(void)
{norflash_write_enable();    /* 写使能 */norflash_wait_busy();       /* 等待空闲 */NORFLASH_CS(0);spi1_read_write_byte(FLASH_ChipErase);  /* 发送读寄存器命令 */ NORFLASH_CS(1);norflash_wait_busy();       /* 等待芯片擦除结束 */
}/*** @brief       擦除一个扇区*   @note      注意,这里是扇区地址,不是字节地址!!*              擦除一个扇区的最少时间:150ms* * @param       saddr : 扇区地址 根据实际容量设置* @retval      无*/
void norflash_erase_sector(uint32_t saddr)
{//printf("fe:%x\r\n", saddr);   /* 监视falsh擦除情况,测试用 */saddr *= 4096;norflash_write_enable();        /* 写使能 */norflash_wait_busy();           /* 等待空闲 */NORFLASH_CS(0);spi1_read_write_byte(FLASH_SectorErase);    /* 发送写页命令 */norflash_send_address(saddr);   /* 发送地址 */NORFLASH_CS(1);norflash_wait_busy();           /* 等待扇区擦除完成 */
}

SPI总结

时钟CPOL,CPHA

NSS就是片选的意思,代码中是软件片选,CS引脚给从设备低电平,即代表开始与从设备通信

SPI相关寄存器,h7有所不同,SPI_DR是由硬件操作

SPI相关HAL库驱动

NMq25q128简介,支持SPI工作模式0和工作模式3

简介

读,擦除,写

NORflash驱动步骤

相关文章:

正点原子讲解SPI学习,驱动编程NOR FLASH实战

配置SPI传输速度时&#xff0c;需要先失能SPI,__HAL_SPI_DISABLE,然后操作SPI_CR1中的波特率设置位&#xff0c;再使能SPI, NM25Q128驱动步骤 myspi.c #include "./BSP/MYSPI/myspi.h"SPI_HandleTypeDef g_spi1_handler; /* SPI句柄 */void spi1_init(void) {g_spi…...

低代码开发助力中小企业数字化转型难度持续降低

随着信息技术的飞速发展&#xff0c;数字化转型已成为企业持续发展的关键驱动力。对于中小企业而言&#xff0c;数字化转型不仅意味着提升效率、降低成本&#xff0c;更是实现业务模式创新和市场竞争力提升的重要途径。然而&#xff0c;传统软件开发模式的高成本、长周期和复杂…...

【Linux】:线程控制

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家带来线程控制相关代码和知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数…...

大数据-174 Elasticsearch Query DSL - 全文检索 full-text query 匹配、短语、多字段 详细操作

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…...

Spring Boot视频网站:构建可扩展的视频服务平台

6系统测试 为了保证所开发出来的系统质量过关&#xff0c;让所开发出来的系统具备可靠性并能够投入运行使用&#xff0c;这就需要进行系统开发的最后一个关键步骤&#xff0c;那就是系统测试。可以说系统测试就是对系统开发前面的步骤&#xff0c;比如系统分析与设计等进行复查…...

护眼台灯横评:书客、柏曼、明基哪款使用体验好,又能护眼?

如果你使用过护眼台灯&#xff0c;就太能理解为什么护眼台灯会诞生了。护眼台灯确实有一定的护眼作用&#xff0c;光线柔和不刺眼&#xff0c;许多护眼台灯还有智能调光、定时休息等人性化功能。在当今这个数字化时代&#xff0c;长时间面对电脑屏幕或埋头于书本已成为许多人的…...

RDMA笔记

目录 1. RDMA简介1.1. 比较Socket与RDMA的通信1.2. RDMA优势1.3. RDMA 2. RDMA基本元素2.1. QPSQ, SQE & RQ, RQEQPNQPC 2.2. CQ2.3. MR2.4. PD 3. RDMA基本操作3.1. Send & Receive3.2. RDMA Write3.3. RDMA Read 阅读RDMA相关资料&#xff0c;从硬件开发角度对RDMA作…...

Collection 单列集合 List Set

集合概念 集合是一种特殊类 ,这些类可以存储任意类对象,并且长度可变, 这些集合类都位于java.util中,使用的话必须导包 按照存储结构可以分为两大类 单列集合 Collection 双列集合 Map 两种 区别如下 Collection 单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两…...

LabVIEW提高开发效率技巧----跨平台开发

在如今的多平台环境下&#xff0c;开发者常常面临不同操作系统的需求&#xff0c;如Windows、Linux和RT&#xff08;实时&#xff09;系统等。而LabVIEW作为一种强大的开发工具&#xff0c;提供了支持跨平台开发的能力&#xff0c;但要使其无缝迁移&#xff0c;开发者需要掌握一…...

创建uniCloud新项目并且是新服务空间,运行会报Error: Invalid uni-id config file错误

问题说明 新创建的服务空间&#xff0c;新起的项目&#xff0c;运行查询数据库就会报错&#xff0c;Uncaught (in promise) Error: Invalid uni-id config file&#xff0c;我记得在原来创建项目的时候&#xff0c;是不需要进行配置的&#xff0c;最近创建新项目出现了这个错误…...

七、IPD 方法论框架(IPD的组织架构)

IPD的组织架构 在IPD(集成产品开发)方法论中,组织架构是确保跨职能团队高效协作、快速响应市场需求的关键要素之一。IPD的组织架构通常打破传统的职能部门隔离,倡导跨职能团队和矩阵式管理模式,使各职能部门在项目开发中紧密合作,从而提高开发效率,降低沟通成本。 IPD…...

iPad mini 7惨遭暗砍一刀

大屏是工作&#xff0c;小屏才是生活。 iPad mini系列&#xff0c;一直被誉为最适合普罗大众的平板。热爱学习、工作的卷王不多&#xff0c;沉迷游戏、追剧的俗人不少。 对娱乐场景而言&#xff0c;便携性是核心属性。iPad mini不大不小&#xff0c;只有两台手机的大小&#x…...

【计算机网络 - 基础问题】每日 3 题(三十六)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…...

Docker镜像

Docker是一个开源的容器化平台&#xff0c;它可以帮助开发人员打包应用程序及其依赖项为轻量级、可移植的容器&#xff0c;以实现快速部署和可扩展性。下面是关于Docker的一些基本概念和优势&#xff1a; 容器&#xff1a;Docker使用容器来封装应用程序和其所有依赖项&#xff…...

Golang | Leetcode Golang题解之第478题在圆内随机生成点

题目&#xff1a; 题解&#xff1a; type Solution struct {radius, xCenter, yCenter float64 }func Constructor(radius, xCenter, yCenter float64) Solution {return Solution{radius, xCenter, yCenter} }func (s *Solution) RandPoint() []float64 {r : math.Sqrt(rand.…...

菜鸟笔记006 截图识别文字插件 textOCR

随手可得的截图识别文字插件 textOCR&#xff0c;识别出来的文字可直接输入到illustrator的当前文档中&#xff1a; 执行条件 1、需截图软件支持&#xff0c;推荐笔记截图工具 2、截好图片直接拖入面板即可完成识别 ****后期可完成实现在illustrator选择图片对象完成文字识别。…...

MySQL【知识改变命运】07

MySQL 1&#xff1a;Group by 分组查询1.1&#xff1a;语法&#xff1a;1.2&#xff1a;练习 2&#xff1a;having⼦句3回顾&#xff1a;3&#xff1a;内置函数3.1 :⽇期函数 1&#xff1a;Group by 分组查询 可以根据某列&#xff0c;进行分组查询&#xff0c;比如学校里面的…...

Matlab自学笔记三十八:日期时间序列的创建方法

1.概念 时间序列是指&#xff0c;某一时间段的时间&#xff0c;也就是说&#xff0c;一组日期时间数据组成的序列&#xff0c;例如&#xff0c;1.1~1.10&#xff0c;1点~5点等&#xff0c;在Matlab中&#xff0c;使用向量表示这种时间序列&#xff0c;例如[2025.1.1 2025.1.2 …...

fiber的原理

React Fiber 的主要原理包括动态优先级、可中断的工作、增量渲染和协作式多任务 React Fiber 是 React 16 引入的一种新的协调&#xff08;reconciliation&#xff09;引擎&#xff0c;它旨在提高 React 应用的性能和响应性。Fiber 的核心原理主要包括以下几个方面&#xff1a…...

重塑输电线路运维管理,巡检管理系统守护电网稳定运行

在输电线路巡检管理中&#xff0c;一个高效、直接的巡检系统对于确保电力供应的稳定性和安全性至关重要。巡检系统能够直接对接运维需求&#xff0c;减少繁琐流程&#xff0c;并强化数据分析能力&#xff0c;这无疑为输电线路的运维管理带来了诸多优势。以下是对这些优势的具体…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...