基于STM32采用CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试
一、前言
在STM32项目开发中,经常会用到存储芯片存储数据。 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复;在存储芯片里也会存放很多资源文件。比如,开机音乐,界面上的菜单图标,字库文件,方便设备开机加载。
为了让单片机更加方便的读写这些资源文件,通常都会加文件系统,如果没有文件系统,直接读取写扇区的方式,对数据不好管理。 这篇文章就手把手教大家,在STM32上完成FATFS文件系统的移植;主控芯片采用STM32F103ZET6, 存储芯片我这里采用(雷龙) CS创世 SD NAND 。 SD NAND 简单来说就是贴片式SD卡,使用起来与普通的SD卡一样,简单的区别就是:比TF卡稳定,比eMMC便宜。 下面章节里会详细介绍下 CS创世 SD NAND。
下面是CS创世 SD NAND 与STM32开发的板的接线实物图:
这是读写扇区测试的结果:
二、SD NAND 介绍
我当前使用的SD NAND型号是,CSNP32GCR01-AOW,容量是4GB。
下面是通过编写STM32代码读取的存储信息:
-
Card Type:SDHC V2.0
-
Card ManufacturerID:102
-
Card RCA:5000
-
Card Capacity:3696 MB
-
Card BlockSize:512
芯片的详细参数如下:
【1】不用写驱动程序自带坏块管理
【2】尺寸小巧,简单易用,兼容性强,稳定可靠,固件可定制,LGA-8封装
【3】标准SDIO接口,兼容SPI,兼容拔插式TF卡/SD卡,可替代普通TF卡/SD卡
【4】尺寸6.2x8mm,直接贴片,不占空间
【5】内置平均读写算法,通过1万次随机掉电测试
【6】耐高低温,机贴手贴都非常方便
【7】速度级别Class10(读取速度23.5MB/S写入速度12.3MB/S)
【8】支持标准的SD 2.0协议,用户可以直接移植标准驱动代码,省去了驱动代码编程环节。支持TF卡启动的SOC都可以用SD NAND
【9】比TF卡稳定,比eMMC便宜
**下面是芯片的实物图: ** 这是官网申请的样品,焊接了转接板,可以直接插在SD卡卡槽上测试。 最终选型之后,设计PCB板时,设计接口,直接贴片上去使用,非常稳定,抖动也不会导致,外置卡TF卡这种容易松动的问题。
这是雷龙的官网: http://www.longsto.com/product/35.html
三、编写SD NAND驱动代码
SD NAND 的驱动代码与正常的SD卡协议是一样的,支持标准的SD 2.0协议,下面我就直接贴出写好的驱动代码。
包括了模拟SPI,硬件SPI,SDIO等3种方式,完成对SD NAND 的读写。我当前使用的主控板子是STM32F103ZET6,如果你使用的板子不是这一款,可能还是其他的CPU也没关系;我这里直接贴出了SPI模拟时序的驱动代码,可以直接移植到任何单片机上使用,代码拷贝过去也只需要修改GPIO口即可,非常方便。
3.1 SPI模拟时序驱动方式
(1)整体工程代码
这是当前工程的截图: 代码采用寄存器风格编写,非常简洁。
当前工程完成SD NAND卡初始化,扇区的读写,测试芯片基本的使用情况。
(2) sd.c
#include "sdcard.h"
static u8 SD_Type=0; //存放SD卡的类型
/*
函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节
函数参数:data是要写入的数据
返 回 值:读到的数据
*/
u8 SDCardReadWriteOneByte(u8 DataTx)
{
u8 i;
u8 data=0;
for(i=0;i<8;i++)
{
SDCARD_SCK=0;
if(DataTx&0x80)SDCARD_MOSI=1;
else SDCARD_MOSI=0;
SDCARD_SCK=1;
DataTx<<=1;
data<<=1;
if(SDCARD_MISO)data|=0x01;
}
return data;
}
//4种: 边沿两种、电平是两种
/*
函数功能:底层SD卡接口初始化
本程序SPI接口如下:
PC11 片选 SDCardCS
PC12 时钟 SDCardSCLK
PD2 输出 SPI_MOSI--主机输出从机输入
PC8 输入 SPI_MISO--主机输入从机输出
*/
void SDCardSpiInit(void)
{
/*1. 开启时钟*/
RCC->APB2ENR|=1<<5; //使能PORTD时钟
RCC->APB2ENR|=1<<4; //使能PORTC时钟
/*2. 配置GPIO口模式*/
GPIOC->CRH&=0xFFF00FF0;
GPIOC->CRH|=0x00033008;
GPIOD->CRL&=0xFFFFF0FF;
GPIOD->CRL|=0x00000300;
/*3. 上拉*/
GPIOC->ODR|=1<<8;
GPIOC->ODR|=1<<11;
GPIOC->ODR|=1<<12;
GPIOD->ODR|=1<<2;
}
/*
函数功能:取消选择,释放SPI总线
*/
void SDCardCancelCS(void)
{
SDCARD_CS=1;
SDCardReadWriteOneByte(0xff);//提供额外的8个时钟
}
/*
函数 功 能:选择sd卡,并且等待卡准备OK
函数返回值:0,成功;1,失败;
*/
void SDCardSelectCS(void)
{
SDCARD_CS=0;
SDCardWaitBusy();//等待成功
}
/*
函数 功 能:等待卡准备好
函数返回值:0,准备好了;其他,错误代码
*/
void SDCardWaitBusy(void)
{
while(SDCardReadWriteOneByte(0XFF)!=0XFF){}
}
/*
函数功能:等待SD卡回应
函数参数:
Response:要得到的回应值
返 回 值:
0,成功得到了该回应值
其他,得到回应值失败
*/
u8 SDCardGetAck(u8 Response)
{
u16 Count=0xFFFF;//等待次数
while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应
if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回应失败
else return SDCard_RESPONSE_NO_ERROR;//正确回应
}
/*
函数功能:从sd卡读取一个数据包的内容
函数参数:
buf:数据缓存区
len:要读取的数据长度.
返回值:
0,成功;其他,失败;
*/
u8 SDCardRecvData(u8*buf,u16 len)
{
if(SDCardGetAck(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
while(len--)//开始接收数据
{
*buf=SDCardReadWriteOneByte(0xFF);
buf++;
}
//下面是2个伪CRC(dummy CRC)
SDCardReadWriteOneByte(0xFF);
SDCardReadWriteOneByte(0xFF);
return 0;//读取成功
}
/*
函数功能:向sd卡写入一个数据包的内容 512字节
函数参数:
buf 数据缓存区
cmd 指令
返 回 值:0表示成功;其他值表示失败;
*/
u8 SDCardSendData(u8*buf,u8 cmd)
{
u16 t;
SDCardWaitBusy(); //等待忙状态
SDCardReadWriteOneByte(cmd);
if(cmd!=0XFD)//不是结束指令
{
for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,减少函数传参时间
SDCardReadWriteOneByte(0xFF); //忽略crc
SDCardReadWriteOneByte(0xFF);
t=SDCardReadWriteOneByte(0xFF); //接收响应
if((t&0x1F)!=0x05)return 2; //响应错误
}
return 0;//写入成功
}
/*
函数功能:向SD卡发送一个命令
函数参数:
u8 cmd 命令
u32 arg 命令参数
u8 crc crc校验值
返回值:SD卡返回的响应
*/
u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
{
u8 r1;
SDCardCancelCS(); //取消上次片选
SDCardSelectCS(); //选中SD卡
//发送数据
SDCardReadWriteOneByte(cmd | 0x40);//分别写入命令
SDCardReadWriteOneByte(arg >> 24);
SDCardReadWriteOneByte(arg >> 16);
SDCardReadWriteOneByte(arg >> 8);
SDCardReadWriteOneByte(arg);
SDCardReadWriteOneByte(crc);
if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading
do
{
r1=SDCardReadWriteOneByte(0xFF);
}while(r1&0x80); //等待响应,或超时退出
return r1; //返回状态值
}
/*
函数功能:获取SD卡的CID信息,包括制造商信息
函数参数:u8 *cid_data(存放CID的内存,至少16Byte)
返 回 值:
0:成功,1:错误
*/
u8 GetSDCardCISDCardOutnfo(u8 *cid_data)
{
u8 r1;
//发SDCard_CMD10命令,读CID
r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
if(r1==0x00)
{
r1=SDCardRecvData(cid_data,16);//接收16个字节的数据
}
SDCardCancelCS();//取消片选
if(r1)return 1;
else return 0;
}
/*
函数说明:
获取SD卡的CSD信息,包括容量和速度信息
函数参数:
u8 *cid_data(存放CID的内存,至少16Byte)
返 回 值:
0:成功,1:错误
*/
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data)
{
u8 r1;
r1=SendSDCardCmd(SDCard_CMD9,0,0x01); //发SDCard_CMD9命令,读CSD
if(r1==0)
{
r1=SDCardRecvData(csd_data, 16);//接收16个字节的数据
}
SDCardCancelCS();//取消片选
if(r1)return 1;
else return 0;
}
/*
函数功能:获取SD卡的总扇区数(扇区数)
返 回 值:
0表示容量检测出错,其他值表示SD卡的容量(扇区数/512字节)
说 明:
每扇区的字节数必为512字节,如果不是512字节,则初始化不能通过.
*/
u32 GetSDCardSectorCount(void)
{
u8 csd[16];
u32 Capacity;
u16 csize;
if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0; //取CSD信息,如果期间出错,返回0
if((csd[0]&0xC0)==0x40) //SDHC卡,按照下面方式计算
{
csize = csd[9] + ((u16)csd[8] << 8) + 1;
Capacity = (u32)csize << 10;//得到扇区数
}
return Capacity;
}
/*
函数功能: 初始化SD卡
返 回 值: 非0表示初始化失败!
*/
u8 SDCardDeviceInit(void)
{
u8 r1; // 存放SD卡的返回值
u8 buf[4];
u16 i;
SDCardSpiInit();//初始化底层IO口
for(i=0;i<10;i++)SDCardReadWriteOneByte(0xFF); //发送最少74个脉冲
do
{
r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//进入IDLE状态 闲置
}while(r1!=0X01);
SD_Type=0; //默认无卡
if(r1==0X01)
{
if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1) //SD V2.0
{
for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);
if(buf[2]==0X01&&buf[3]==0XAA) //卡是否支持2.7~3.6V
{
do
{
SendSDCardCmd(SDCard_CMD55,0,0X01); //发送SDCard_CMD55
r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//发送SDCard_CMD41
}while(r1);
if(SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
{
for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//得到OCR值
if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC; //检查CCS
else SD_Type=SDCard_TYPE_V2;
}
}
}
}
printf("SD_Type=0x%X\r\n",SD_Type);
SDCardCancelCS(); //取消片选
if(SD_Type)return 0; //初始化成功返回0
else if(r1)return r1; //返回值错误值
return 0xaa; //其他错误
}
/*
函数功能:读SD卡
函数参数:
buf:数据缓存区
sector:扇区
cnt:扇区数
返回值:
0,ok;其他,失败.
说 明:
SD卡一个扇区大小512字节
*/
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt)
{
u8 r1;
if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//转换为字节地址
if(cnt==1)
{
r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//读命令
if(r1==0) //指令发送成功
{
r1=SDCardRecvData(buf,512); //接收512个字节
}
}else
{
r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//连续读命令
do
{
r1=SDCardRecvData(buf,512);//接收512个字节
buf+=512;
}while(--cnt && r1==0);
SendSDCardCmd(SDCard_CMD12,0,0X01); //发送停止命令
}
SDCardCancelCS();//取消片选
return r1;//
}
/*
函数功能:向SD卡写数据
函数参数:
buf:数据缓存区
sector:起始扇区
cnt:扇区数
返回值:
0,ok;其他,失败.
说 明:
SD卡一个扇区大小512字节
*/
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt)
{
u8 r1;
if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//转换为字节地址
if(cnt==1)
{
r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//读命令
if(r1==0)//指令发送成功
{
r1=SDCardSendData(buf,0xFE);//写512个字节
}
}
else
{
if(SD_Type!=SDCard_TYPE_MMC)
{
SendSDCardCmd(SDCard_CMD55,0,0X01);
SendSDCardCmd(SDCard_CMD23,cnt,0X01);//发送指令
}
r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//连续读命令
if(r1==0)
{
do
{
r1=SDCardSendData(buf,0xFC);//接收512个字节
buf+=512;
}while(--cnt && r1==0);
r1=SDCardSendData(0,0xFD);//接收512个字节
}
}
SDCardCancelCS();//取消片选
return r1;//
}
(3) sd.h
#ifndef SD_H
#define SD_H_
#include "stm32f10x.h"
#include "led.h"
#include "usart.h"
/*----------------------------------------------
本程序SPI接口如下:
PC11 片选 SDCardCS
PC12 时钟 SDCardSCLK
PD2 输出 SPI_MOSI--主机输出从机输入
PC8 输入 SPI_MISO--主机输入从机输出
------------------------------------------------*/
#define SDCARD_CS PCout(11)
#define SDCARD_SCK PCout(12)
#define SDCARD_MOSI PDout(2)
#define SDCARD_MISO PCin(8)
// SD卡类型定义
#define SDCard_TYPE_ERR 0X00 //卡类型错误
#define SDCard_TYPE_MMC 0X01 //MMC卡
#define SDCard_TYPE_V1 0X02
#define SDCard_TYPE_V2 0X04
#define SDCard_TYPE_V2HC 0X06
// SD卡指令表
#define SDCard_CMD0 0 //卡复位
#define SDCard_CMD1 1
#define SDCard_CMD8 8 //命令8 ,SEND_IF_COND
#define SDCard_CMD9 9 //命令9 ,读CSD数据
#define SDCard_CMD10 10 //命令10,读CID数据
#define SDCard_CMD12 12 //命令12,停止数据传输
#define SDCard_CMD13 16 //命令16,设置扇区大小 应返回0x00
#define SDCard_CMD17 17 //命令17,读扇区
#define SDCard_CMD18 18 //命令18,读Multi 扇区
#define SDCard_CMD23 23 //命令23,设置多扇区写入前预先擦除N个block
#define SDCard_CMD24 24 //命令24,写扇区
#define SDCard_CMD25 25 //命令25,写多个扇区
#define SDCard_CMD41 41 //命令41,应返回0x00
#define SDCard_CMD55 55 //命令55,应返回0x01
#define SDCard_CMD58 58 //命令58,读OCR信息
#define SDCard_CMD59 59 //命令59,使能/禁止CRC,应返回0x00、
/*SD卡回应标记字*/
#define SDCard_RESPONSE_NO_ERROR 0x00 //正确回应
#define SDCard_SD_IN_IDLE_STATE 0x01 //闲置状态
#define SDCard_SD_ERASE_RESET 0x02 //擦除复位
#define SDCard_RESPONSE_FAILURE 0xFF //响应失败
//函数声明
u8 SDCardReadWriteOneByte(u8 data); //底层接口,SPI读写字节函数
void SDCardWaitBusy(void); //等待SD卡准备
u8 SDCardGetAck(u8 Response); //获得应答
u8 SDCardDeviceInit(void); //初始化
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt); //读块(扇区)
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt); //写块(扇区)
u32 GetSDCardSectorCount(void); //读扇区数
u8 GetSDCardCISDCardOutnfo(u8 *cid_data); //读SD卡CID
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data); //读SD卡CSD
#endif
(4)运行效果
3.2 SPI硬件时序方式
上面的3.1小节是采用SPI模拟时序驱动SD NAND,STM32本身集成有SPI硬件模块,可以直接利用STM32硬件SPI接口读写。
下面贴出底层的适配代码。 上面贴出的驱动代码里,已经将驱动接口部分和协议逻辑部分区分开了,替换底层的SIP读写代码非常方便。
(1)主要替换的代码
/*
函数功能:SPI初始化(模拟SPI)
硬件连接:
MISO--->PB14
MOSI--->PB15
SCLK--->PB13
*/
void SPI_Init(void)
{
/*开启时钟*/
RCC->APB1ENR|=1<<14; //开启SPI2时钟
RCC->APB2ENR|=1<<3; //PB
GPIOB->CRH&=0X000FFFFF; //清除寄存器
GPIOB->CRH|=0XB8B00000;
GPIOB->ODR|=0X7<<13; //PB13/14/15上拉--输出高电平
/*SPI2基本配置*/
SPI2->CR1=0X0; //清空寄存器
SPI2->CR1|=0<<15; //选择“双线双向”模式
SPI2->CR1|=0<<11; //使用8位数据帧格式进行发送/接收;
SPI2->CR1|=0<<10; //全双工(发送和接收);
SPI2->CR1|=1<<9; //启用软件从设备管理
SPI2->CR1|=1<<8; //NSS
SPI2->CR1|=0<<7; //帧格式,先发送高位
SPI2->CR1|=0x0<<3;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。
SPI2->CR1|=1<<2; //配置为主设备
SPI2->CR1|=1<<1; //空闲状态时, SCK保持高电平。
SPI2->CR1|=1<<0; //数据采样从第二个时钟边沿开始。
SPI2->CR1|=1<<6; //开启SPI设备。
}
/*
函数功能:SPI读写一个字节
*/
u8 SPI_ReadWriteOneByte(u8 data_tx)
{
u16 cnt=0;
while((SPI2->SR&1<<1)==0) //等待发送区空--等待发送缓冲为空
{
cnt++;
if(cnt>=65530)return 0; //超时退出 u16=2个字节
}
SPI2->DR=data_tx; //发送一个byte
cnt=0;
while((SPI2->SR&1<<0)==0) //等待接收完一个byte
{
cnt++;
if(cnt>=65530)return 0; //超时退出
}
return SPI2->DR; //返回收到的数据
}
函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节
函数参数:data是要写入的数据
返 回 值:读到的数据
*/
u8 SDCardReadWriteOneByte(u8 DataTx)
{
return SPI_ReadWriteOneByte(DataTx);
}
(2)运行效果
相关文章:
基于STM32采用CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试
一、前言 在STM32项目开发中,经常会用到存储芯片存储数据。 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复;在存储芯片里也会存放很多资源文件。比如,开机音乐,界面上的菜单图…...
K_A12_007 基于STM32等单片机驱动AS608光学指纹识别模块 OLED0.96显示
K_A12_007 基于STM32等单片机驱动AS608光学指纹识别模块 OLED0.96显示一、资源说明二、基本参数参数引脚说明三、驱动说明对应程序:四、部分代码说明1、接线引脚定义1.1、STC89C52RCAS608光学指纹模块1.2、STM32F103C8T6AS608光学指纹模块五、基础知识学习与相关资料下载六、视…...
map和set介绍及其底层模拟实现
致努力前行的人: 要努力,但不要着急,繁花锦簇,硕果累累都需要过程! 目录 1.关联式容器 2.键值对 3.树形结构的关联式容器 3.1set的介绍 3.2set的使用 3.3multiset的使用 3.4map的使用 3.5multimap的使用 4.常见的面试题…...
实现一个比ant功能更丰富的Modal组件
普通的modal组件如下: 我们写的modal额外支持,后面没有蒙版,并且Modal框能够拖拽 还支持渲染在文档流里,上面的都是fixed布局,我们这个正常渲染到文档下面: render部分 <RenderDialog{...restState}visi…...
2023美赛F题思路数据代码分享
文章目录赛题思路2023年美国大学生数学建模竞赛选题&论文一、关于选题二、关于论文格式三、关于论文提交四、论文提交流程注意不要手滑美赛F题思路数据代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片,加入获取一手资源 202…...
Flutter如何与Native(Android)进行交互
前言 上一篇文章《Flutter混合开发:Android中如何启动Flutter》中我们介绍了如何在Native(Android项目)中启动Flutter,展示Flutter页面。但是在开发过程中,很多时候并不是简单的展示一个页面即可,还会涉及…...
数据库主从复制和读写分离
主从数据库和数据库集群的一些问题 数据库集群和主从数据库最本质的区别,其实也就是data-sharing和nothing-sharing的区别。集群是共享存储的。主从复制中没有任何共享。每台机器都是独立且完整的系统。 什么是主从复制? 主从复制,是用来建立一个和主数…...
Java并发编程面试题——线程安全(原子性、可见性、有序性)
文章目录一、原子性高频问题1.1 Java中如何实现线程安全?1.2 CAS底层实现1.3 CAS的常见问题1.4 四种引用类型 ThreadLocal的问题?二、可见性高频问题2.1 Java的内存模型2.2 保证可见性的方式2.3 volatile修饰引用数据类型2.4 有了MESI协议,为啥还有vol…...
DialogFragment内存泄露问题能不能一次性改好
孽缘 自DialogFragment在Android3.0之后作为一种特殊的Fragment引入,官方建议使用DialogFragment代替Dialog或者AllertDialog来实现弹框的功能,因为它可以更好的管理Dialog的生命周期以及可以更好复用。 然而建议虽好,实用须谨慎,…...
java学习--多线程
多线程 了解多线程 多线程是指从软件或者硬件上实现多个线程并发执行的技术。 具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能。 并发和并行 并行:在同一时刻,有多个指令在CPU上同时执行并发࿱…...
90后阿里P7技术专家晒出工资单:狠补了这个,真香...
最近一哥们跟我聊天装逼,说他最近从阿里跳槽了,我问他跳出来拿了多少?哥们表示很得意,说跳槽到新公司一个月后发了工资,月入5万多,表示很满足!这样的高薪资着实让人羡慕,我猜这是税后…...
2023美赛C题:Wordle筛选算法
Wordle 规则介绍 Wordle 每天会更新一个5个字母的单词,在6次尝试中猜出单词就算成功。每个猜测必须是一个有效的单词(不能是不能组成单词的字母排列)。 每次猜测后,字母块的颜色会改变,颜色含义如下: 程…...
SpringBoot 集成 Kafka
SpringBoot 集成 Kafka1 安装 Kafka2 创建 Topic3 Java 创建 Topic4 SpringBoot 项目4.1 pom.xml4.2 application.yml4.3 KafkaApplication.java4.4 CustomizePartitioner.java4.5 KafkaInitialConfig.java4.6 SendMessageController.java5 测试1 安装 Kafka Docker 安装 Kafk…...
OpenCV 图像金字塔算子
本文是OpenCV图像视觉入门之路的第14篇文章,本文详细的介绍了图像金字塔算子的各种操作,例如:高斯金字塔算子 、拉普拉斯金字塔算子等操作。 高斯金字塔中的较高级别(低分辨率)是通过先用高斯核对图像进行卷积再删除偶…...
【自学Linux】Linux一切皆文件
Linux一切皆文件 Linux一切皆文件教程 Linux 中所有内容都是以文件的形式保存和管理的,即一切皆文件,普通文件是文件,目录是文件,硬件设备(键盘、监视器、硬盘、打印机)是文件,就连套接字&…...
CUDA C++扩展的详细描述
CUDA C扩展的详细描述 文章目录CUDA C扩展的详细描述CUDA函数执行空间说明符B.1.1 \_\_global\_\_B.1.2 \_\_device\_\_B.1.3 \_\_host\_\_B.1.4 Undefined behaviorB.1.5 __noinline__ and __forceinline__B.2 Variable Memory Space SpecifiersB.2.1 \_\_device\_\_B.2.2. \_…...
为什么重写equals必须重写hashCode
关于这个问题,看了网上很多答案,感觉都参差不齐,没有答到要点,这次就记录一下! 首先我们为什么要重写equals?这个方法是用来干嘛的? public boolean equals (Object object&#x…...
< 每日小技巧:N个很棒的 Vue 开发技巧, 持续记录ing >
每日小技巧:6 个很棒的 Vue 开发技巧👉 ① Watch 妙用> watch的高级使用> 一个监听器触发多个方法> watch 监听多个变量👉 ② 自定义事件 $emit() 和 事件参数 $event👉 ③ 监听组件生命周期常规写法hook写法ὄ…...
数据结构与算法之二分查找分而治之思想
决定我们成为什么样人的,不是我们的能力,而是我们的选择。——《哈利波特与密室》二分查找是查找算法里面是很优秀的一个算法,特别是在有序的数组中,这种算法思想体现的淋漓尽致。一.题目描述及其要求请实现无重复数字的升序数组的…...
训练自己的中文word2vec(词向量)--skip-gram方法
训练自己的中文word2vec(词向量)–skip-gram方法 什么是词向量 将单词映射/嵌入(Embedding)到一个新的空间,形成词向量,以此来表示词的语义信息,在这个新的空间中,语义相同的单…...
ubuntu系统环境配置和常用软件安装
系统环境 修改文件夹名称为英文 参考链接 export LANGen_US xdg-user-dirs-gtk-update 常用软件安装 常用工具 ping 和ifconfig工具 sudo apt install -y net-tools inetutils-ping 截图软件 sudo apt install -y net-tools inetutils-ping flameshot 录屏 sudo apt-get i…...
【1139. 最大的以 1 为边界的正方形】
来源:力扣(LeetCode) 描述: 给你一个由若干 0 和 1 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0。 示例 1&#…...
windows11安装sqlserver2022报错
window11安装SQL Server 2022 报错 糟糕… 无法安装SQL Server (setup.exe)。此 SQL Server安装程序介质不支持此OS的语言,或没有SQL Server英语版本的安装文件。请使用匹配的特定语言SQL Server介质;或安装两个特定语言MUI,然后通过控制面板的区域设置…...
Python快速上手系列--日志模块--详解篇
前言本篇主要说说日志模块,在写自动化测试框架的时候我们就需要用到这个模块了,方便我们快速的定位错误,了解软件的运行情况,更加顺畅的调试程序。为什么要用到日志模块,直接print不就好了!那得写多少print…...
【THREE.JS学习(1)】绘制一个可以旋转、放缩的立方体
学习新技能,做一下笔记。在使用ThreeJS的时候,首先创建一个场景const scene new THREE.Scene();接着,创建一个相机其中,THREE.PerspectiveCamera()四个参数分别为:1.fov 相机视锥体竖直方向视野…...
数仓实战 - 滴滴出行
项目大致流程: 1、项目业务背景 1.1 目的 本案例将某出行打车的日志数据来进行数据分析,例如:我们需要统计某一天订单量是多少、预约订单与非预约订单的占比是多少、不同时段订单占比等 数据海量 – 大数据 hive比MySQL慢很多 1.2 项目架…...
python虚拟环境与环境变量
一、环境变量 1.环境变量 在命令行下,使用可执行文件,需要来到可执行文件的路径下执行 如果在任意路径下执行可执行文件,能够有响应,就需要在环境变量配置 2.设置环境变量 用户变量:当前用户登录到系统,…...
BeautifulSoup文档4-详细方法 | 用什么方法对文档树进行搜索?
4-详细方法 | 用什么方法对文档树进行搜索?1 过滤器1.1 字符串1.2 正则表达式1.3 列表1.4 True1.5 可以自定义方法2 find_all()2.1 参数原型2.2 name参数2.3 keyword 参数2.4 string 参数2.5 limit 参数2.6 recursive 参数3 find()4 find_parents()和find_parent()5…...
初识Tkinter界面设计
目录 前言 一、初识Tkinter 二、Label控件 三、Button控件 四、Entry控件 前言 本文简单介绍如何使用Python创建一个界面。 一、初识Tk...
软件测试面试题中的sql题目你会做吗?
目录 1.学生表 2.一道SQL语句面试题,关于group by表内容: 3.表中有A B C三列,用SQL语句实现:当A列大于B列时选择A列否则选择B列,当B列大于C列时选择B列否则选择C列 4. 5.姓名:name 课程:subject 分数&…...
小程序免费制作平台小程序/网站关键词优化
来源:http://rrd.me/eqCsw1、为什么用HashMap?2、HashMap的工作原理是什么?3、有什么方法可以减少碰撞?4、HashMap中hash函数怎么是是实现的?5、拉链法导致的链表过深问题为什么不用二叉查找树代替,而选择红黑树&…...
电商学院建设设计网站/怎么在百度推广自己的网站
译者:whistleofmysonggmail.com 博客 www.singmelody.com 我们已经学习过如何创建一个复杂的场景。但是如果没有光源和阴影,那么这次场景将是不完整的。 在这章,我们将会学习到: * Ogre3D支持的不同类型的光源和它们是如何使用的…...
wordpress附件管理/百度优化插件
2019独角兽企业重金招聘Python工程师标准>>> MAVEN的生命周期和插件 maven是通过插件来实现功能的。所谓的生命周期就是我们在构建项目时,maven默认需要是想的一些功能,而每一个功能就通过插件的某一功能来实现。 每个插件会有一个或多个功能…...
有什么做兼职的医疗网站/专门用来查找网址的网站
这与其他一些问题有关,如:this,还有一些其他问题。在this question和其他人中,我们看到我们可以在一个很好的步骤中声明和初始化字符串数组,例如:const char* const list[] {"zip", "zam&q…...
企业网站备案好不好/排名优化价格
1, 应用程序层 顶层中有所有的Android应用程序,包括通讯录、浏览器等,你写的应用程序也被安装在这层;所有的的应用程序都是使用Java语言编写的。 2,应用框架层 这一层主要提供构建应用程序是可能用到的各种API&…...
网站建设方案是什么/刷赞抖音推广网站
垃圾回收算法 1.标记-清除算法 标记-清除算法是最基本的算法,和他的名字一样,分为两个步骤,一个步骤是标记需要回收的对象。在标记完成后统一回收被标记的对象。这个算法两个问题。一个是效率问题,标记和清除的效率不高。第二个问…...