SD卡驱动_何時的博客-程序员宅基地_sd卡驱动

技术标签: SD  单片机  

一、介绍

1.SD 卡

安全数码卡, 它是在 MMC 的基础上发展而来, 是一种基于半导体快闪记忆器的新一代记忆设备。按容量分类,可以将SD 卡分为 3 类: SD 卡、 SDHC 卡、 SDXC 卡。SD卡(SDSC):0~2G SDHC卡:2~32G SDXC卡:32G~2T

2. SD的通讯模式

  • SD 卡模式(通过 SD 总线通信):允许 4 线的高速数据传输,只能使用 3.3V 的 IO 电平,所以, MCU 一定要能够支持 3.3V 的 IO 端口输出。
  • SPI 模式:同 SD 卡模式相比就是丧失了速度,在 SPI 模式下, CS/MOSI/MISO/CLK 都需要加 10~100K 左右的上拉电阻。
    请添加图片描述

3.SD引脚定义

请添加图片描述

二、SD卡驱动介绍

1.SD寄存器介绍

名称 宽度 描述
CID 128 卡识别寄存器
CSD 128 卡描述数据寄存器:卡操作条件相关的信息数据。
OCR 32 操作条件寄存器
RCA 16 相对卡地址寄存器:本地系统中卡的地址,动态变化,在卡的初始化时确定。(SPI模式中没有)
SCR 64 SD配置寄存器:SD卡特定信息数据

2、SD卡识别流程

请添加图片描述

初始化过程

  1. 初始化与 SD 卡连接的硬件条件(MCU 的 SPI 配置, IO 口配置;
  2. 上电延时(>74 个 CLK)(因为 SD 卡内部有个供电电压上升时间,大概为 64 个 CLK,剩下的 10 个 CLK 用于 SD 卡同步,之后才能开始 CMD0 的操作);
  3. 复位卡(CMD0),进入 IDLE 状态;
  4. 发送 CMD8,检查是否支持 2.0 协议;
  5. 根据不同协议检查 SD 卡(命令包括: CMD55、 CMD41、 CMD58 和 CMD1 等);
  6. 取消片选,发多 8 个 CLK(提供 SD 卡额外的时钟,完成某些操作),结束初始化;

3、读写SD卡

请添加图片描述

SD卡读取数据(CMD17):

  • 发送 CMD17;
  • 接收卡响应 R1;
  • 接收数据起始令牌 0XFE;
  • 接收数据;
  • 接收 2 个字节的 CRC,如果不使用 CRC,这两个字节在读取后可以丢掉。
  • 禁止片选之后,发多 8 个 CLK;

SD卡写数据(CMD24):

  • 发送 CMD24;
  • 接收卡响应 R1;
  • 发送写数据起始令牌 0XFE;
  • 发送数据;
  • 发送 2 字节的伪 CRC;
  • 禁止片选之后,发多 8 个 CLK;

三、SD卡驱动源码

源文件

#include "mmc_sd.h"

uint8_t  SD_Type = 0;//SD卡的类型
uint32_t Capacity = 0; //可用扇区数

移植修改区///
//SPI初始化
void hal_spi_init(void)
{
    

}

/*****************************************************************************
** 描  述:写入一个字节 
** 入  参:Dat:待写入的数据
** 返回值:读出的数据
******************************************************************************/
uint8_t SD_SPI_ReadWriteByte(uint8_t Dat)
{
    
	/**
		根据不同平台进行实现
		****/
}

//取消选择,释放SPI总线
void SD_DisSelect(void)
{
    
    SD_CS_HIGH_HRS();
    SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
}

//选择sd卡,并且等待卡准备OK
//返回值:0,成功;1,失败;
uint8_t SD_Select(void)
{
    
    SD_CS_LOW_HRS();
    if(SD_WaitReady()==0)return 0;//等待成功
    SD_DisSelect();
    return 1;//等待失败
}
///

//等待卡准备好
//返回值:0,准备好了;其他,错误代码
uint8_t SD_WaitReady(void)
{
    
    uint32_t t=0;
    do
    {
    
        if(SD_SPI_ReadWriteByte(0XFF)==0XFF)return 0;//OK
        t++;
    }
    while(t<0XFFFFFF); //等待
    return 1;
}
//等待SD卡回应
//Response:要得到的回应值
//返回值:0,成功得到了该回应值
//    其他,得到回应值失败
uint8_t SD_GetResponse(uint8_t Response)
{
    
    uint16_t Count=0xFFF;//等待次数
    while ((SD_SPI_ReadWriteByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应
    if (Count==0)return MSD_RESPONSE_FAILURE;//得到回应失败
    else return MSD_RESPONSE_NO_ERROR;//正确回应
}
//从sd卡读取一个数据包的内容
//buf:数据缓存区
//len:要读取的数据长度.
//返回值:0,成功;其他,失败;
uint8_t SD_RecvData(uint8_t*buf,uint16_t len)
{
    
    if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
    while(len--)//开始接收数据
    {
    
        *buf=SD_SPI_ReadWriteByte(0xFF);
        buf++;
    }
    //下面是2个伪CRC(dummy CRC)
    SD_SPI_ReadWriteByte(0xFF);
    SD_SPI_ReadWriteByte(0xFF);
    return 0;//读取成功
}
//向sd卡写入一个数据包的内容 512字节
//buf:数据缓存区
//cmd:指令
//返回值:0,成功;其他,失败;
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd)
{
    
    uint16_t t;
    if(SD_WaitReady())return 1;//等待准备失效
    SD_SPI_ReadWriteByte(cmd);
    if(cmd!=0XFD)//不是结束指令
    {
    
        for(t=0; t<512; t++)SD_SPI_ReadWriteByte(buf[t]); //提高速度,减少函数传参时间
        SD_SPI_ReadWriteByte(0xFF);//忽略crc
        SD_SPI_ReadWriteByte(0xFF);
        t=SD_SPI_ReadWriteByte(0xFF);//接收响应
        if((t&0x1F)!=0x05)return 2;//响应错误
    }
    return 0;//写入成功
}

//向SD卡发送一个命令
//输入: uint8_t cmd   命令
//      uint32_t arg  命令参数
//      uint8_t crc   crc校验值
//返回值:SD卡返回的响应
uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
    
    uint8_t r1;
    uint8_t Retry=0;
    SD_DisSelect();//取消上次片选
    if(SD_Select())return 0XFF;//片选失效
    //发送
    SD_SPI_ReadWriteByte(cmd | 0x40);//分别写入命令
    SD_SPI_ReadWriteByte(arg >> 24);/* Argument[31..24] */
    SD_SPI_ReadWriteByte(arg >> 16);/* Argument[23..16] */
    SD_SPI_ReadWriteByte(arg >> 8);/* Argument[15..8] */
    SD_SPI_ReadWriteByte(arg);      /* Argument[7..0] */
    SD_SPI_ReadWriteByte(crc);
    if(cmd==CMD12)SD_SPI_ReadWriteByte(0xff);//Skip a stuff byte when stop reading
    //等待响应,或超时退出
    Retry=0X1F;
    do
    {
    
        r1=SD_SPI_ReadWriteByte(0xFF);
    }
    while((r1&0X80) && Retry--);
    //返回状态值
    return r1;
}
//获取SD卡的CID信息,包括制造商信息
//输入: uint8_t *cid_data(存放CID的内存,至少16Byte)
//返回值:0:NO_ERR
//		 1:错误
uint8_t SD_GetCID(uint8_t *cid_data)
{
    
    uint8_t r1;
    //发CMD10命令,读CID
    r1=SD_SendCmd(CMD10,0,0x01);
    if(r1==0x00)
    {
    
        r1=SD_RecvData(cid_data,16);//接收16个字节的数据
    }
    SD_DisSelect();//取消片选
    if(r1)return 1;
    else return 0;
}
//获取SD卡的CSD信息,包括容量和速度信息
//输入:uint8_t *cid_data(存放CID的内存,至少16Byte)
//返回值:0:NO_ERR
//		 1:错误
uint8_t SD_GetCSD(uint8_t *csd_data)
{
    
    uint8_t 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,则初始化不能通过.
uint32_t SD_GetSectorCount(void)
{
    
    uint8_t csd[16];
    uint32_t Capacity;
    uint8_t n;
    uint16_t csize;
    //取CSD信息,如果期间出错,返回0
    if(SD_GetCSD(csd)!=0) return 0;
    //如果为SDHC卡,按照下面方式计算
    if((csd[0]&0xC0)==0x40)	 //V2.00的卡
    {
    
        csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
        Capacity = (uint32_t)csize << 10;//得到扇区数
    }
    else //V1.XX的卡
    {
    
        n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
        csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
        Capacity= (uint32_t)csize << (n - 9);//得到扇区数
    }
    return Capacity;
}
uint8_t SD_Idle_Sta(void)
{
    
    uint16_t i;
    uint8_t retry;
    for(i=0; i<0xf00; i++); //纯延时,等待SD卡上电完成
    //先产生>74个脉冲,让SD卡自己初始化完成
    for(i=0; i<10; i++)SD_SPI_ReadWriteByte(0xFF);
    //-----------------SD卡复位到idle开始-----------------
    //循环连续发送CMD0,直到SD卡返回0x01,进入IDLE状态
    //超时则直接退出
    retry = 0;
    do
    {
    
        //发送CMD0,让SD卡进入IDLE状态
        i = SD_SendCmd(CMD0, 0, 0x95);
        retry++;
    }
    while((i!=0x01)&&(retry<200));
    //跳出循环后,检查原因:初始化成功?or 重试超时?
    if(retry==200)return 1; //失败
    return 0;//成功
}
//初始化SD卡
uint8_t SD_Initialize(void)
{
    
    uint8_t r1;      // 存放SD卡的返回值
    uint16_t retry;  // 用来进行超时计数
    uint8_t buf[4];
    uint16_t i;
    
     while(SD_Idle_Sta());
    
    SD_Type=0;//默认无卡
    if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
    {
    
        for(i=0; i<4; i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);	//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]=SD_SPI_ReadWriteByte(0XFF); //得到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_GetSectorCount();
    SD_DisSelect();//取消片选
    if(SD_Type)return 0;
    else if(r1)return r1;
    return 0xaa;//其他错误
}

//读SD卡
//buf:数据缓存区
//sector:扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
    
    uint8_t r1;
    if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;//转换为字节地址
    if(cnt==1)
    {
    
        r1=SD_SendCmd(CMD17,sector,0X01);//读命令
        if(r1==0)//指令发送成功
        {
    
            r1=SD_RecvData(buf,512);//接收512个字节
        }
    }
    else
    {
    
        r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令
        do
        {
    
            r1=SD_RecvData(buf,512);//接收512个字节
            buf+=512;
        }
        while(--cnt && r1==0);
        SD_SendCmd(CMD12,0,0X01);	//发送停止命令
    }
    SD_DisSelect();//取消片选
    return r1;//
}
//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
    
    uint8_t r1;
    if(SD_Type!=SD_TYPE_V2HC)sector *= 512;//转换为字节地址
    if(cnt==1)
    {
    
        r1=SD_SendCmd(CMD24,sector,0X01);//读命令
        if(r1==0)//指令发送成功
        {
    
            r1=SD_SendBlock(buf,0xFE);//写512个字节
        }
    }
    else
    {
    
        if(SD_Type!=SD_TYPE_MMC)
        {
    
            SD_SendCmd(CMD55,0,0X01);
            SD_SendCmd(CMD23,cnt,0X01);//发送指令
        }
        r1=SD_SendCmd(CMD25,sector,0X01);//连续读命令
        if(r1==0)
        {
    
            do
            {
    
                r1=SD_SendBlock(buf,0xFC);//接收512个字节
                buf+=512;
            }
            while(--cnt && r1==0);
            r1=SD_SendBlock(0,0xFD);//接收512个字节
        }
    }
    SD_DisSelect();//取消片选
    return r1;//
}

/* User defined Fatfs functions ----------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Get SD disk status                                                    */
/*-----------------------------------------------------------------------*/
uint8_t SD_disk_status(void)
{
    
    return 1;
}

/*-----------------------------------------------------------------------*/
/* Initialize SD disk drive                                              */
/*-----------------------------------------------------------------------*/
uint8_t SD_disk_initialize(void)
{
    
    if(SD_Initialize() == 0)
        return 1;
    else
        return 0;
}

头文件

#ifndef _MMC_SD_H_
#define _MMC_SD_H_
移植修改区///
#include <string.h>
#include "nrf_drv_common.h"
#include "nrf_drv_spi.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "nrf_gpio.h"
#include "boards.h"
#include "nrf_delay.h"
#include "ds1302.h"

#define SPI_INSTANCE  0 /**< SPI instance index. */

#define SPI_SS_PIN     28
#define SPI_SCK_PIN     29
#define SPI_MOSI_PIN     30
#define SPI_MISO_PIN     31

#define SD_CS_STANDARD0_NODRIVE_HRS() do {
       \
NRF_GPIO->PIN_CNF[SPI_SS_PIN] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
|(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos)    \
|(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)  \
|(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
|(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);  \
} while (0) /*!< Configures SCL pin to Standard-0, No-drive 1 */    

#define SD_CS_OUTPUT_HRS() do {
       NRF_GPIO->DIRSET = (1UL << SPI_SS_PIN);  } while(0) 

#define SD_CS_HIGH_HRS()   do {
       NRF_GPIO->OUTSET = (1UL << SPI_SS_PIN);  } while(0)   /*!< Pulls SDA line high */
#define SD_CS_LOW_HRS()    do {
       NRF_GPIO->OUTCLR = (1UL << SPI_SS_PIN);  } while(0)   /*!< Pulls SDA line low  */    

///
// 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
		    	               
extern uint8_t  SD_Type;//SD卡的类型
extern uint32_t Capacity;

/*********fatfs(文件系统)************/

#define SECTOR_SIZE     512 //扇区大小
#define BLOCK_SIZE      1   //块大小
uint8_t SD_disk_status(void);
uint8_t SD_disk_initialize(void);
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);		//读块
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);		//写块
/*********************/
//函数申明区
uint8_t SD_WaitReady(void);							//等待SD卡准备
uint8_t SD_GetResponse(uint8_t Response);					//获得相应
uint8_t SD_Initialize(void);							//初始化
uint32_t SD_GetSectorCount(void);   					//读扇区数
uint8_t SD_GetCID(uint8_t *cid_data);                     //读SD卡CID
uint8_t SD_GetCSD(uint8_t *csd_data);                     //读SD卡CSD
void hal_spi_init(void);
void Set_spi_speed(void);

#endif

使用指南

在不同的平台下实现以下函数

  • 初始化SPI函数:**void hal_spi_init(void);**
  • 实现SPI的传输数据函数: **uint8_t SD_SPI_ReadWriteByte(uint8_t Dat)**
  • 实现对片选(CS)脚的控制:void SD_DisSelect(void)uint8_t SD_Select(void)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_45531642/article/details/121035050

智能推荐

关于字符数组以及字符串数组_一尾鱼汤的博客-程序员宅基地

例题13://13.编写一程序,将两个字符串连接起来,结果取代第一个字符串(mark下来加深印象),加油加油加油!(1)自己编写一个strcat函数int main(){ int strcat(char a[100],char b[100]); char a[100]={0}, b[100]={0}; //初始化字符数组; int i=0,j=0; cout&lt;&lt;"请输入两个字符串:"; cin&gt;&gt;a&gt;&gt;b;

Python配置Qt Designer与PyUIC_热心市民付先生的博客-程序员宅基地_python pyuic

Python配置Qt Designer与PyUIC安装pyqt相关包pip install pyqtpip install pyqt5-toolsPyCharm中选择相应的python解释器3. 添加外部工具名称填写自己方便易记得即可,我的名称添加为QTDesigner程序选择designer.exe,位置为当前解释器路径下的Lib\site-packages\pyqt5-tools\designer.exe工作目录为当前项目文件的目录添加选择完毕界面为:确定即可,再次

关于WebSocket_彪彪_的博客-程序员宅基地

原文链接:https://www.liaoxuefeng.com/wiki/1022910821149312/1103303693824096WebSocket是HTML5新增的协议,它的目的是在浏览器和服务器之间建立一个不受限的双向通信的通道,比如说,服务器可以在任意时刻发送消息给浏览器。为什么传统的HTTP协议不能做到WebSocket实现的功能?这是因为HTTP协议是一个请求-响应协议,...

FineUI初学手册_star_2008_的博客-程序员宅基地

女朋友鄙视我原创少...1.下载 进入官方论坛:http://www.fineui.com/bbs/要用到下载源代码和空项目下载http://fineui.codeplex.com/http://fineui.com/bbs/forum.php?mod=viewthread&tid=2123源代码直接下载,注意FineUI版本空项目里下载 对应版本的空项

NSStringDrawingOptions_yinachong的博客-程序员宅基地

NSStringDrawingTruncatesLastVisibleLine:如果文本内容超出指定的矩形限制,文本将被截去并在最后一个字符后加上省略号。如果没有指定NSStringDrawingUsesLineFragmentOrigin选项,则该选项被忽略。NSStringDrawingUsesLineFragmentOrigin:绘制文本时使用 line fragement or

随便推点

HTML5 APP----2014年H5没火,why?2016年H5能火,why?_谷震平的博客-程序员宅基地

0 前言        HTML5做跨平台的APP,在大多数人的脑子里没有什么好感,我身边的朋友也这么说。Anyway,我用完以后得出这样的结论:HTML5跨平台APP开发,在2015年以后会越来越火。    在2014年以前,HTML5的性能和能力都不够充足。特别是性能,因为Android4.4以下版本不能支持webGL技术,所以大部分低端Android手机无法流畅运行手机APP。D

Chromium多进程架构初探-兼谈Android平台版本_coloriy的博客-程序员宅基地

Chromium以多进程架构著称,它主要包含四类进程,分别是Browser进程、Render进程、GPU进程和Plugin进程。之所以要将Render进程、GPU进程和Plugin进程独立出来,是为了解决它们的不稳定性问题。也就是说,Render进程、GPU进程和Plugin进程由于不稳定而引发的Crash不会导致整个浏览器崩溃。本文就对Chromium的多进程架构进行简要介绍,以及制定学习计划。

Mybatis源码分析_风铃峰顶的博客-程序员宅基地

实例import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.*;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.List; private SqlSessionF

Composer常见问题汇总_cicibi6696的博客-程序员宅基地

问题一: [Composer\Downloader\TransportException] ...

什么时候不能使用箭头函数_一水茶缘YY的博客-程序员宅基地

共 2670 字,读完需 5 分钟。编译自 Dmitri Pavlutin 的文章,对原文内容做了精简和代码风格优化。ES6 中引入的箭头函数可以让我们写出更简洁的代码,但是部分场景下使用箭头函数会带来严重的问题,有哪些场景?会导致什么问题?该怎么解决,容我慢慢道来。能见证每天在用的编程语言不断演化是一件让人非常兴奋的事情,从错误中学习、探索更好的语言实现、创造新的语言特性是推动编程语言版本迭代的动