既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
#include “nrf_drv_common.h”
#include “nrf_drv_spi.h”
#include “app_util_platform.h”
#include “app_error.h”
#include “w25q16.h”
#include “nrf_gpio.h”
#include “boards.h”
#include “nrf_delay.h”
#define SPI_INSTANCE 0 /**< SPI instance index. */
static volatile bool spi_xfer_done; //SPI数据传输完成标志
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE); /**< SPI instance. */
static uint8_t spi_tx_buf[256]; /**< TX buffer. */
static uint8_t spi_rx_buf[256]; /**< RX buffer. */
/**
* @brief SPI user event handler.
* @param event
*/
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
void * p_context)
{
spi_xfer_done = true;
}
void hal_spi_init(void)
{
// nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG(SPI_INSTANCE);
// spi_config.ss_pin = SPI_CS_PIN;
// APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler));
nrf\_drv\_spi\_config\_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
spi_config.ss_pin = SPI_SS_PIN;
spi_config.miso_pin = SPI_MISO_PIN;
spi_config.mosi_pin = SPI_MOSI_PIN;
spi_config.sck_pin = SPI_SCK_PIN;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_init(&spi, &spi_config, spi_event_handler, NULL));
}
/*****************************************************************************
** 描 述:读出一个字节
** 入 参:无
** 返回值:读出的数据
******************************************************************************/
uint8_t SpiFlash_ReadOneByte(void)
{
uint8_t len = 1;
spi_tx_buf[0] = 0xFF;
spi_xfer_done = false;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
return (spi_rx_buf[0]);
}
/*****************************************************************************
** 描 述:写入一个字节
** 入 参:Dat:待写入的数据
** 返回值:无
******************************************************************************/
void SpiFlash_WriteOneByte(uint8_t Dat)
{
uint8_t len = 1;
spi_tx_buf[0] = Dat;
spi_xfer_done = false;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
}
/*****************************************************************************
** 描 述:写入命令
** 入 参:*CMD:指向待写入的命令
** 返回值:RET_SUCCESS
******************************************************************************/
uint8_t SpiFlash_Write_CMD(uint8_t *CMD)
{
uint8_t len = 3;
spi_tx_buf[0] = \*CMD;
spi_tx_buf[1] = \*(CMD+1);
spi_tx_buf[2] = \*(CMD+2);
spi_xfer_done = false;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
return RET_SUCCESS;
}
/*****************************************************************************
** 描 述:写使能
** 入 参:无
** 返回值:无
******************************************************************************/
void SpiFlash_Write_Enable(void)
{
spi_xfer_done = false;
SpiFlash_WriteOneByte(SPIFlash_WriteEnable_CMD);
while(!spi_xfer_done);
}
/*****************************************************************************
** 描 述:擦除扇区,W25Q128FVSIG最小的擦除单位是扇区
** 入 参:Block_Num:块号
** Sector_Number:扇区号
** 返回值:
******************************************************************************/
void SPIFlash_Erase_Sector(uint8_t Block_Num,uint8_t Sector_Number)
{
SpiFlash_Write_Enable();
spi_tx_buf[0] = SPIFlash_SecErase_CMD;
spi_tx_buf[1] = Block_Num;
spi_tx_buf[2] = Sector_Number<<4;
spi_tx_buf[3] = 0x00;
spi_xfer_done = false;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_transfer(&spi, spi_tx_buf, 4, spi_rx_buf, 4));
while(!spi_xfer_done);
nrf\_delay\_ms(10); //每次擦除数据都要延时等待写入结束
return ;
}
/*****************************************************************************
** 描 述:向指定的地址写入数据
** *pBuffer:指向待写入的数据
** WriteAddr:写入的起始地址
** WriteBytesNum:读出的字节数
** 返回值:RET_SUCCESS
******************************************************************************/
uint8_t SpiFlash_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t WriteBytesNum)
{
uint8_t len;
SpiFlash\_Write\_Enable();
spi_tx_buf[0] = SPIFlash_PageProgram_CMD;
spi_tx_buf[1] = (uint8\_t)((WriteAddr&0x00ff0000)>>16);
spi_tx_buf[2] = (uint8\_t)((WriteAddr&0x0000ff00)>>8);
spi_tx_buf[3] = (uint8\_t)WriteAddr;
memcpy(&spi_tx_buf[4],pBuffer,WriteBytesNum);
len = WriteBytesNum + 4;
spi_xfer_done = false;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_transfer(&spi, spi_tx_buf, len, spi_rx_buf, 0));
while(!spi_xfer_done);
return RET_SUCCESS;
}
/*****************************************************************************
** 描 述:从指定的地址读出指定长度的数据
** 入 参:pBuffer:指向存放读出数据的首地址
** ReadAddr:待读出数据的起始地址
** ReadBytesNum:读出的字节数
** 返回值:
******************************************************************************/
uint8_t SpiFlash_Read(uint8_t *pBuffer,uint32_t ReadAddr,uint32_t ReadBytesNum)
{
uint8_t len;
spi_tx_buf[0] = SPIFlash_ReadData_CMD;
spi_tx_buf[1] = (uint8\_t)((ReadAddr&0x00ff0000)>>16);
spi_tx_buf[2] = (uint8\_t)((ReadAddr&0x0000ff00)>>8);
spi_tx_buf[3] = (uint8\_t)ReadAddr;
len = ReadBytesNum + 4;
spi_xfer_done = false;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
memcpy(pBuffer,&spi_rx_buf[4],ReadBytesNum);
return RET_SUCCESS;
}
/********************************************END FILE*******************************************/
### MicroSD卡(TF卡)SPI测试
SD卡往往有多种通讯方式(上电后需要主机告诉SD卡采用什么方式,所以需要SD卡初始化程序):
* 标准的SPI
* SD模式 —— 一根线的SDIO接口
* SD模式 —— 四根线的SDIO接口,一次可以4个数据出,4个数据回
#### Micro SD卡SPI模式基础知识
Micro SD(TF)卡的引脚说明:
![在这里插入图片描述](https://img-blog.csdnimg.cn/13e1193eff3e473895447ec4ac4a7ac3.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)
Micro SD卡只有8个引脚是比SD卡少了一个Vss。可以买个SD卡套套在Micro SD卡上,这样一来大小就和SD卡一样大,这时候卡套上的9个引脚就和SD卡一样了,可以完全当做SD卡来操作。
在《SD卡接口规范》文档中,有SD卡工作在SPI模式的引脚定义:
![在这里插入图片描述](https://img-blog.csdnimg.cn/9228fcf389d04b9188b1fdba8663c741.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)SD卡和Micro SD卡的SPI操作方式是一样的。
SD卡的 SPI 时钟空闲时为高电平,在时钟的第二个边沿,也就是时钟线的电平由低变高时 采集数据,所以配置 SPI 的极性和相位: CPOL = 1, CPHA = 1。由上面第一章的内容可知 **读取SD卡 nRF52832工作在模式 3**。
下面的图片参考博文:[MicroSD卡(TF卡)SPI模式实现方法](https://bbs.csdn.net/topics/618631832)
![在这里插入图片描述](https://img-blog.csdnimg.cn/92749f70d052401e92f7126bf3c39fd4.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)![在这里插入图片描述](https://img-blog.csdnimg.cn/3a2d8cd25b484a718ddaafcdd102a99c.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)![在这里插入图片描述](https://img-blog.csdnimg.cn/c77f8c25455b454085a2339b3a4b29d8.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
#### Micro SD卡SPI 程序移植测试
研究了这么多,最好还是回到当初STM32正点原子的教程里面有SD的读写,驱动,想着直接移植过来试试,最后应该是成功了,没有做过多的测试读写,只是读了一下扇区大小。
* 程序中主要注意 读取SD卡 nRF52832工作在模式 3;
* 速度设置需要额外更改;
**.h部分**
//省略
typedef unsigned char u8;
typedef unsigned long int u32;
typedef unsigned int u16;
// SD卡类型定义
#define SD_TYPE_ERR 0X00
#define SD_TYPE_MMC 0X01
#define SD_TYPE_V1 0X02
#define SD_TYPE_V2 0X04
#define SD_TYPE_V2HC 0X06
// SD卡指令表
#define CMD0 0 //卡复位
#define CMD1 1
#define CMD8 8 //命令8 ,SEND_IF_COND
#define CMD9 9 //命令9 ,读CSD数据
#define CMD10 10 //命令10,读CID数据
#define CMD12 12 //命令12,停止数据传输
#define CMD16 16 //命令16,设置SectorSize 应返回0x00
#define CMD17 17 //命令17,读sector
#define CMD18 18 //命令18,读Multi sector
#define CMD23 23 //命令23,设置多sector写入前预先擦除N个block
#define CMD24 24 //命令24,写sector
#define CMD25 25 //命令25,写Multi sector
#define CMD41 41 //命令41,应返回0x00
#define CMD55 55 //命令55,应返回0x01
#define CMD58 58 //命令58,读OCR信息
#define CMD59 59 //命令59,使能/禁止CRC,应返回0x00
//数据写入回应字意义
#define MSD_DATA_OK 0x05
#define MSD_DATA_CRC_ERROR 0x0B
#define MSD_DATA_WRITE_ERROR 0x0D
#define MSD_DATA_OTHER_ERROR 0xFF
//SD卡回应标记字
#define MSD_RESPONSE_NO_ERROR 0x00
#define MSD_IN_IDLE_STATE 0x01
#define MSD_ERASE_RESET 0x02
#define MSD_ILLEGAL_COMMAND 0x04
#define MSD_COM_CRC_ERROR 0x08
#define MSD_ERASE_SEQUENCE_ERROR 0x10
#define MSD_ADDRESS_ERROR 0x20
#define MSD_PARAMETER_ERROR 0x40
#define MSD_RESPONSE_FAILURE 0xFF
void hal_spi_init(void);
uint8_t SpiFlash_ReadOneByte(void);
void SpiFlash_WriteOneByte(uint8_t Dat);
void spi_set_highspeed(void);
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc);
u8 SD_Initialize(void);
u8 SD_GetResponse(u8 Response);
u8 SD_RecvData(u8*buf,u16 len);
u8 SD_GetCSD(u8 *csd_data);
u32 SD_GetSectorCount(void);
//省略
**.c部分**
//省略
#define SPI_INSTANCE 0 /**< SPI instance index. */
#define SDCARD 1
u8 SD_Type=0;//SD卡的类型
static volatile bool spi_xfer_done; //SPI数据传输完成标志
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE); /**< SPI instance. */
static uint8_t spi_tx_buf[256]; /**< TX buffer. */
static uint8_t spi_rx_buf[256]; /**< RX buffer. */
static nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
/**
* @brief SPI user event handler.
* @param event
*/
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
void * p_context)
{
spi_xfer_done = true;
}
void hal_spi_init(void)
{
spi_config.ss_pin = SPI_SS_PIN;
spi_config.miso_pin = SPI_MISO_PIN;
spi_config.mosi_pin = SPI_MOSI_PIN;
spi_config.sck_pin = SPI_SCK_PIN;
spi_config.frequency = NRF_DRV_SPI_FREQ_250K;
#ifdef SDCARD
spi_config.mode = NRF_DRV_SPI_MODE_3;
#endif
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}
void spi_set_highspeed(void)
{
nrf_drv_spi_uninit(&spi); //改之前必须uninit
spi_config.frequency = NRF_DRV_SPI_FREQ_4M;
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}
/*****************************************************************************
** 描 述:读出一个字节
** 入 参:无
** 返回值:读出的数据
******************************************************************************/
uint8_t SpiFlash_ReadOneByte(void)
{
uint8_t len = 1;
spi_tx_buf[0] = 0xFF;
spi_xfer_done = false;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
return (spi_rx_buf[0]);
}
/*****************************************************************************
** 描 述:写入一个字节
** 入 参:Dat:待写入的数据
** 返回值:无
******************************************************************************/
void SpiFlash_WriteOneByte(uint8_t Dat)
{
uint8_t len = 1;
spi_tx_buf[0] = Dat;
spi_xfer_done = false;
APP\_ERROR\_CHECK(nrf\_drv\_spi\_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
}
//向SD卡发送一个命令
//输入: u8 cmd 命令
// u32 arg 命令参数
// u8 crc crc校验值
//返回值:SD卡返回的响应
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
{
u8 r1;
u8 Retry=0;
// SD_DisSelect();//取消上次片选
// if(SD_Select())return 0XFF;//片选失效
//发送
SpiFlash_WriteOneByte(cmd | 0x40);//分别写入命令
SpiFlash_WriteOneByte(arg >> 24);
SpiFlash_WriteOneByte(arg >> 16);
SpiFlash_WriteOneByte(arg >> 8);
SpiFlash_WriteOneByte(arg);
SpiFlash_WriteOneByte(crc);
if(cmd==CMD12)SpiFlash_WriteOneByte(0xff);//Skip a stuff byte when stop reading
//等待响应,或超时退出
Retry=0X1F;
do
{
r1=SpiFlash_ReadOneByte();
}while((r1&0X80) && Retry–);
//返回状态值
return r1;
}
u8 SD_Initialize(void)
{
u8 r1; // 存放SD卡的返回值
u16 retry; // 用来进行超时计数
u8 buf[4];
u16 i;
hal\_spi\_init();
for(i=0;i<10;i++)SpiFlash\_WriteOneByte(0XFF);//发送最少74个脉冲
retry=20;
do
{
r1=SD\_SendCmd(CMD0,0,0x95);//进入IDLE状态
}while((r1!=0X01) && retry--);
SD_Type=0;//默认无卡
if(r1==0X01)
{
if(SD\_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
{
for(i=0;i<4;i++)buf[i]=SpiFlash\_ReadOneByte(); //Get trailing return value of R7 resp
if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
{
retry=0XFFFE;
do
{
SD\_SendCmd(CMD55,0,0X01); //发送CMD55
r1=SD\_SendCmd(CMD41,0x40000000,0X01);//发送CMD41
}while(r1&&retry--);
if(retry&&SD\_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
{
for(i=0;i<4;i++)buf[i]=SpiFlash\_ReadOneByte();//得到OCR值
if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC; //检查CCS
else SD_Type=SD_TYPE_V2;
}
}
}else//SD V1.x/ MMC V3
{
SD\_SendCmd(CMD55,0,0X01); //发送CMD55
r1=SD\_SendCmd(CMD41,0,0X01); //发送CMD41
if(r1<=1)
{
SD_Type=SD_TYPE_V1;
retry=0XFFFE;
do //等待退出IDLE模式
{
SD\_SendCmd(CMD55,0,0X01); //发送CMD55
r1=SD\_SendCmd(CMD41,0,0X01);//发送CMD41
}while(r1&&retry--);
}else
{
SD_Type=SD_TYPE_MMC;//MMC V3
retry=0XFFFE;
do //等待退出IDLE模式
{
r1=SD\_SendCmd(CMD1,0,0X01);//发送CMD1
}while(r1&&retry--);
}
if(retry==0||SD\_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;//错误的卡
}
}
// SD\_DisSelect();//取消片选
spi\_set\_highspeed();//高速
if(SD_Type)return 0;
else if(r1)return r1;
return 0xaa;//其他错误
}
//等待SD卡回应
//Response:要得到的回应值
//返回值:0,成功得到了该回应值
// 其他,得到回应值失败
u8 SD_GetResponse(u8 Response)
{
u16 Count=0xFFFF;//等待次数
while ((SpiFlash_ReadOneByte()!=Response)&&Count)Count–;//等待得到准确的回应
if (Count==0)return MSD_RESPONSE_FAILURE;//得到回应失败
else return MSD_RESPONSE_NO_ERROR;//正确回应
}
//从sd卡读取一个数据包的内容
//buf:数据缓存区
//len:要读取的数据长度.
//返回值:0,成功;其他,失败;
u8 SD_RecvData(u8*buf,u16 len)
{
if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
while(len–)//开始接收数据
{
*buf=SpiFlash_ReadOneByte();
buf++;
}
//下面是2个伪CRC(dummy CRC)
SpiFlash_WriteOneByte(0xFF);
SpiFlash_WriteOneByte(0xFF);
return 0;//读取成功
}
//获取SD卡的CSD信息,包括容量和速度信息
//输入:u8 *cid_data(存放CID的内存,至少16Byte)
//返回值:0:NO_ERR
// 1:错误
u8 SD_GetCSD(u8 *csd_data)
{
u8 r1;
r1=SD_SendCmd(CMD9,0,0x01);//发CMD9命令,读CSD
if(r1==0)
{
r1=SD_RecvData(csd_data, 16);//接收16个字节的数据
}
// SD_DisSelect();//取消片选
if(r1)return 1;
else return 0;
}
//获取SD卡的总扇区数(扇区数)
//返回值:0: 取容量出错
// 其他:SD卡的容量(扇区数/512字节)
//每扇区的字节数必为512,因为如果不是512,则初始化不能通过.
u32 SD_GetSectorCount(void)
{
u8 csd[16];
u32 Capacity;
u8 n;
u16 csize;
//取CSD信息,如果期间出错,返回0
if(SD_GetCSD(csd)!=0) return 0;
//如果为SDHC卡,按照下面方式计算
if((csd[0]&0xC0)==0x40) //V2.00的卡
{
csize = csd[9] + ((u16)csd[8] << 8) + 1;
Capacity = (u32)csize << 10;//得到扇区数
}else//V1.XX的卡
{
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
Capacity= (u32)csize << (n - 9);//得到扇区数
}
return Capacity;
}
/********************************************END FILE*******************************************/
最后测试是在主函数调用`SD_GetSectorCount`函数计算扇区大小:
![在这里插入图片描述](https://img-blog.csdnimg.cn/cad021496eda4c2ab8382ed8078cb16c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)![在这里插入图片描述](https://img-blog.csdnimg.cn/10d4832e1510401d82c725f6a3d25b0e.png)
换了一个512MB的卡:
![在这里插入图片描述](https://img-blog.csdnimg.cn/a1511eef9c57434984d83b955daac654.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_19,color_FFFFFF,t_70,g_se,x_16)
#### 最后的疑问
![在这里插入图片描述](https://img-blog.csdnimg.cn/12a5ca8ae37841d394f57239f1e752db.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)
最后还是有一个问题的,我开始读取的是 8G的卡,扇区读出来正常,但是变成 MB 就是3290MB 了,看到网上有个视频教学也是使用的这个驱动,读取8G的卡居然和我这个数据一样 3290 MB。4G和512MB的卡读出来计算出来都正常,感觉是数据类型 低级问题导致的,但是一下子还真不知道,还希望有人能够指点……
![img](https://img-blog.csdnimg.cn/img_convert/08093e7e95ae9bca146dd0b12fb57a2c.png)
![img](https://img-blog.csdnimg.cn/img_convert/acc44da05cacb5688995bf3e6ec42241.png)
![img](https://img-blog.csdnimg.cn/img_convert/74fed897ed5f0add5c6c03c838221c53.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618631832)**
BA55-c6L6w5omA6Ie0,size_20,color_FFFFFF,t_70,g_se,x_16)
最后还是有一个问题的,我开始读取的是 8G的卡,扇区读出来正常,但是变成 MB 就是3290MB 了,看到网上有个视频教学也是使用的这个驱动,读取8G的卡居然和我这个数据一样 3290 MB。4G和512MB的卡读出来计算出来都正常,感觉是数据类型 低级问题导致的,但是一下子还真不知道,还希望有人能够指点……
[外链图片转存中...(img-VkkDN3QK-1715811523575)]
[外链图片转存中...(img-Xds1Zyhw-1715811523576)]
[外链图片转存中...(img-PpatS6KM-1715811523576)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618631832)**