注意,本博客适合像我一样的小白,会的不多,但是想快速做些东西,不适合会写驱动的大佬。
本文使用stm32f103c8t6,裸机开发。
/**
* 硬件连接:
* CS -> PB12 连接SD卡CS
* SCK -> PB13 连接SD卡SCK
* MISO -> PB14 连接SD卡MISO
* MOSI -> PB15 连接SD卡MOSI
* 注意:
* 有的SD卡是带有AMS1117的,其VCC要接在5V上,
* STM32在只有jinl供电时5V是不会输出的,要用USB给STM32供电!!!
* 有的SD卡不支持SPI模式,我尝试后发现SNMSUNG的SD卡不支持,但是KINGSTON的可以
*/
【免费】stm32f103c8t6SD卡驱动(Fatfs)_103C8T6的FAFS文件系统资源-CSDN文库
一、SD卡驱动
(一)copy源码,移植
我在CSDN上找到了一位大佬用HAL库和fatfs实现stm32f103c8t6对micro SD卡读写的驱动。于是借(抄)鉴(袭)之。
1.编写SPI初始化函数(CubeIDE编辑生成的初始化代码好像在main.c和xxx_msp.c,比较散乱,于是直接上手写)
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //开启SPI2的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB12引脚初始化为推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB13和PB15引脚初始化为复用推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB14引脚初始化为上拉输入
/*SPI初始化*/
SPI_InitTypeDef SPI_InitStructure; //定义结构体变量
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //模式,选择为SPI主模式
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //方向,选择2线全双工
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据宽度,选择为8位
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //先行位,选择高位先行
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //波特率分频,选择128分频
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //SPI极性,选择低极性
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS,选择由软件控制
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC多项式,暂时用不到,给默认值7
SPI_Init(SPI2, &SPI_InitStructure); //将结构体变量交给SPI_Init,配置SPI1
SPI_Cmd(SPI2, ENABLE); //使能SPI2,开始运行
/*设置默认电平*/
SD_CS(1); //SS默认高电平
从原子哥的讲解来看,我们还需要更改SPI的速度,(也就是SPI_baudratePrescaler)可是 标准库当中并没有给出,我们只好操作寄存器,编写该函数(我比较菜,直接从原子哥的fatfs的SPI源码中copying过来)
SPI速度设置函数如下
/**
* @brief 设置SPI2的速度为低速
*/
void sd_spi_speed_low(void)
{
SPI2->CR1&=0XFFC7;
SPI2->CR1|=SPI_BaudRatePrescaler_256; //����SPI2�ٶ�
SPI_Cmd(SPI2,ENABLE);
}
/**
* @brief 设置SPI2的速度为高速
*/
void sd_spi_speed_high(void)
{
SPI2->CR1&=0XFFC7;
SPI2->CR1|=SPI_BaudRatePrescaler_2; //����SPI2�ٶ�
SPI_Cmd(SPI2,ENABLE);
}
之后出现了一些灵异事件!
main.c中有一个读写文件的示例函数:
void WritetoSD(char SD_FileName[], uint8_t write_buff[],uint8_t bufSize)
// {
// FATFS fs;
// FIL file;
// uint8_t res=0;
// UINT Bw;
// res = SD_init(); //SD卡初始化
// if(res == 1)
// {
// UsartPrintf(USART_DEBUG,"SD卡初始化失败! \r\n");
// OLED_ShowString(1,1,"failed!");
// }
// else
// {
// UsartPrintf(USART_DEBUG,"SD卡初始化成功! \r\n");
// OLED_ShowString(1,1,"init successed!");
// }
// res=f_mount(&fs,"0:",1); //挂载
// // if(test_sd == 0) //用于测试格式化
// if(res == FR_NO_FILESYSTEM) //没有文件系统,格式化
// {
// // test_sd =1; //用于测试格式化
// UsartPrintf(USART_DEBUG,"没有文件系统! \r\n");
// OLED_ShowString(2,1,"no file system!");
// res = f_mkfs("", 0, 0); //格式化sd卡
// if(res == FR_OK)
// {
// UsartPrintf(USART_DEBUG,"格式化成功! \r\n");
// OLED_ShowString(1,1,"geshihua sed!");
// res = f_mount(NULL,"0:",1); //格式化后先取消挂载
// res = f_mount(&fs,"0:",1); //重新挂载
// if(res == FR_OK)
// {
// UsartPrintf(USART_DEBUG,"SD卡已经成功挂载,可以进进行文件写入测试!\r\n");
// OLED_ShowString(3,1,"can test");
// }
// }
// else
// {
// UsartPrintf(USART_DEBUG,"格式化失败! \r\n");
// OLED_ShowString(1,1,"geshihua fad!");
// }
// }
// else if(res == FR_OK)
// {
// UsartPrintf(USART_DEBUG,"挂载成功! \r\n");
// OLED_ShowString(1,1,"guazai sed!");
// }
// else
// {
// UsartPrintf(USART_DEBUG,"挂载失败! \r\n");
// OLED_ShowString(1,1,"guazai fad!");
// }
// res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);
// if((res & FR_DENIED) == FR_DENIED)
// {
// UsartPrintf(USART_DEBUG,"卡存储已满,写入失败!\r\n");
// OLED_ShowString(1,1,"write sed!");
// }
// f_lseek(&file, f_size(&file));//确保写词写入不会覆盖之前的数据
// if(res == FR_OK)
// {
// UsartPrintf(USART_DEBUG,"打开成功/创建文件成功! \r\n");
// OLED_ShowString(1,1,"create sed!");
// res = f_write(&file,write_buff,bufSize,&Bw); //写数据到SD卡
// if(res == FR_OK)
// {
// UsartPrintf(USART_DEBUG,"文件写入成功! \r\n");
// OLED_ShowString(1,1,"write sed!");
// }
// else
// {
// UsartPrintf(USART_DEBUG,"文件写入失败! \r\n");
// OLED_ShowString(1,1,"write fad!");
// }
// }
// else
// {
// OLED_ShowString(1,1,"open fad!");
// UsartPrintf(USART_DEBUG,"打开文件失败!\r\n");
// }
// f_close(&file); //关闭文件
// f_mount(NULL,"0:",1); //取消挂载
// }
这个函数在运行时一直卡住,在函数第一行写了USARTprintf也没反应!检查接线也没问题!我删除以上代码,直接在main的开头写USART_Printf电脑却能收到!因此,我认为应该是乱码的问题(因为vscode中所有中文注释都不能正常识别,应该是编码错误),导致了一些编译上的错误 ,于是把所有的printf中的中文全部重写,然后再次编译,结果正常。
但是,我发现SPI一直卡在等待发送完成标志位上,检查连接没有问题。苦苦debug两天,最后,在一位大佬的博客中看到RCC_APB1PeriphClockCmd写错导致SPI没时序,我瞬间明白!!!
注意!!!SPI2是APB1上的外设,开启时钟要用RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);不要写成RCC_APB2PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);或者RCC_AHBPeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);写代码或者移植代码时一定要仔细查验。
之后试验卡在进入IDLe状态,仔细查之,发现SPI的MISO配置成了复用输出!!!改成上拉输入,最终成功!
(二)分析源码
课件图片+源代码(分析都写在注释里了)
/**
* @brief 向SD卡发送指令,指令共48位
* @param cmd 命令 8 bit
* @param arg 命令参数 32 bit
* @param crc crc校验值 8 bit
*/
int SD_sendcmd(uint8_t cmd,uint32_t arg,uint8_t crc){
uint8_t r1;
uint8_t retry;
SD_CS(0);
Delay_ms(20);
SD_CS(1);
//看看SD卡是否准备好
//因为向SD卡发送数据时,MISO会拉低,直到完成写入才会为1
do{
retry=spi_readwrite(DFF);
}while(retry!=0xFF);
spi_readwrite(cmd | 0x40);
//从高位开始发送
spi_readwrite(arg >> 24);
spi_readwrite(arg >> 16);
spi_readwrite(arg >> 8);
spi_readwrite(arg);
spi_readwrite(crc);
if(cmd==CMD12)spi_readwrite(DFF);//跳过无效字节
do
{
r1=spi_readwrite(0xFF);
}while(r1&0X80);
return r1;
}
/**
* @brief 读SD卡
* @param buf 数据缓冲区
* @param sector 扇区
* @param CNT 扇区数
* @retval
*/
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
if(SD_TYPE!=V2HC)sector <<= 9;//转换为字节地址 <<9 就是*=512
if(cnt==1)
{
r1=SD_sendcmd(CMD17,sector,0X01);//读命令
if(r1==0)//指令发送成功
{
r1=SD_ReceiveData(buf,512);//接收512个字节
}
}else
{
r1=SD_sendcmd(CMD18,sector,0X01);//连续读命令
do
{
r1=SD_ReceiveData(buf,512);//接收512个字节
buf+=512;
}while(--cnt && r1==0);
SD_sendcmd(CMD12,0,0X01); //发送停止命令
}
SD_CS(0);//取消片选
return r1;
}
/**
* @brief 写入数据
* @param buf 缓冲区
* @param sector 开始扇区
* @param cnt 扇区数
* @retval 0 成功; 其他 失败
*
*/
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
if(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!=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);//发送连续写结束命令
}
}
SD_CS(0);//取消片选
return r1;
}
二、移植Fatfs
轻量级的网址如下
Petit FAT File System Module (elm-chan.org)
(一)分析源码
pffconf.h
这个是确定要使用的函数和文件系统的
/*---------------------------------------------------------------------------/ / Function Configurations (0:Disable, 1:Enable) /---------------------------------------------------------------------------*/ #define PF_USE_READ 1 /* pf_read() function */ #define PF_USE_DIR 1 /* pf_opendir() and pf_readdir() function */ #define PF_USE_LSEEK 1 /* pf_lseek() function */ #define PF_USE_WRITE 1 /* pf_write() function */ #define PF_FS_FAT12 0 /* FAT12 */ #define PF_FS_FAT16 1 /* FAT16 */ #define PF_FS_FAT32 0 /* FAT32 */
diskio文件中的接口是要自己实现的,实现之后pff.h中的函数就可以直接拿来用
也可以使用标准FATfsFatFs - Generic FAT Filesystem Module (elm-chan.org)
(二)移植
disk_ioctl
详解SD协议与FatFs文件系统中的Block、Sector的差异,与FatFs中底层驱动disk_ioctl的编写及思路_sd卡一个block多大-CSDN博客
read/write直接调用sd的read/write 即可
三、移植USB驱动
我使用Cube Mx 生成代码,打开CubeMx,选择stm32f103c8t6芯片,"connectivity"中选择USB,middleWare选择USB_DEVICE->Mass Storage xxx,生成源码
(一)找驱动
(一)用Cube生成(失败)
1.生成源码的父文件夹中有MiddleWare子文件夹,找ST-》\STM32_USB_Device_Library,把里面的所有文件取出备用,还有根目录下\USB_DEVICE文件夹里面的所有文件也取出备用(总之就是所有usbd开头的)。
2.尝试编译,发现各种报错,最终找到这几个文件引用的hal库是“stm32f1xxx_pcd.h”“stm32f1xxx_pcd.c”“stm32f1xxx_pcd_ex.h”“stm32f1xxx_pcd_ex.c”“stm32f1xxx_ll_usb.h”“stm32f1xxx_ll_usb.c”,又发现还引用了USB_BASE(在stm32f1xx_HAL.h里面),除此之外还有许多杂散的内容,报错未定义标识符后查找该标识符的位置,然后全部扔进一个stm32f1xx_hal_def.h文件中,再把上述几个文件中引用的“stm32f1xx_hal.h”换成"stm32f10x.h"和"stm32f1xx_hal_def.h",这样就确保这些文件引用的HAL库的东西都在。
还需注意的是许多文件中有宏判断HAL_PCD_MODULE_ENABLED,找到这些宏,直接去掉判断,不然会导致有些函数没编译,链接时会报错。之后Error_Handler我直接替换成usart_Printf,方便调试。
调试发现在USBD_LL_START报错,设备管理器这边显示:
由于该设备有问题,Windows 已将其停止。 (代码 43)
请求 USB 设备描述符失败。
放置printf调试,发现串口这边只接受到了USBD_LL,后面的没收到,估计是进中断了
/**
* @brief Starts the low level portion of the device driver.
* @param pdev: Device handle
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
UsartPrintf(USART_DEBUG, "USBD_LL_Start start\n");
hal_status = HAL_PCD_Start(pdev->pData);
usb_status = USBD_Get_USB_Status(hal_status);
UsartPrintf(USART_DEBUG, "USBD_LL_Start end\n");
return usb_status;
}
因为HAl库和标准库混用,有没有找到中断,最后尝试了改大堆栈、PA12接上拉电阻等等,都没有成功
2.去官网找
um0424 - 工具与软件 - Search STMicrocontrollers
解压之后Device_Lib_V4.1.0\Libraries\STM32_USB-FS-Device_Driver里面的文件都要拷贝到自己的工程里面,之后根据编译报错提示,把STM32_USB-FS-Device_Lib_V4.1.0\Projects\Mass_Storage例程里面的mass_mal.h/.c, hw_config.h/.c, platform_config.h, usb_desc.c/.h, usb_pwr.c/.h, usb_lib.c/.h,"usb_istr.h""usb_istr.c", “usb_conf.h”, “memory.c/.h”, “usb_scsi.h/.c”, “usb_bot.c/.h”都加入进来
(二)、移植
1.hw_config.h
1.加入中断处理函数(很重要,估计之前那个就是没有中断导致Windows卡在设备标识符识别上了)
//USB唤醒中断
void USBWakeUp_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line18);
}
void USB_LP_CAN1_RX0_IRQHandler(void)
{
USB_Istr();//处理各种USB中断事件
}
/*******************************************************************************
* Function Name : Leave_LowPowerMode
* Description : Restores system clocks and power while exiting suspend mode
* Input : None.
* Return : None.
*******************************************************************************/
void Enter_LowPowerMode(void)
{
bDeviceState = SUSPENDED;
}
/*******************************************************************************
* Function Name : Leave_LowPowerMode
* Description : Restores system clocks and power while exiting suspend mode
* Input : None.
* Return : None.
*******************************************************************************/
void Leave_LowPowerMode(void)
{
DEVICE_INFO *pInfo = &Device_Info;
/* Set the device state to the correct state */
if (pInfo->Current_Configuration != 0)
{
/* Device configured */
bDeviceState = CONFIGURED;
}
else
{
bDeviceState = ATTACHED;
}
}
2.修改函数
/*******************************************************************************
* Function Name : Set_USBClock
* Description : Configures USB Clock input (48MHz)
* Input : None.
* Return : None.
*******************************************************************************/
void Set_USBClock(void)
{
/* Select USBCLK source */
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
/* Enable the USB clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
}
/*******************************************************************************
* Function Name : USB_Interrupts_Config
* Description : Configures the USB interrupts
* Input : None.
* Return : None.
*******************************************************************************/
void USB_Interrupts_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
/*配置USB的EXTI中断*/
EXTI_ClearITPendingBit(EXTI_Line18);
EXTI_InitStructure.EXTI_Line = EXTI_Line18;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/*配置USB的EXTI中断*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable the USB Wake-up interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_Init(&NVIC_InitStructure);
}
3.USB_Cable_Config函数实际上实在PA12没有上拉电阻的时候配置的。DISABLE:不上拉电阻,ENABLE:上拉电阻
4.删除IntToUnicode的static修饰
5.删除MAL_Config,USB_Disconnect_Config函数,加一个函数控制USB通断。
/*******************************************************************************
* Function Name : MAL_Config
* Description : MAL_layer configuration
* Input : None.
* Return : None.
*******************************************************************************/
void USB_PortSet(FunctionalState newState)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
if (newState == ENABLE)
{
_SetCNTR(_GetCNTR() & (~2));//连接
}
else
{
_SetCNTR(_GetCNTR() | (2));//断开
GPIOA->CRH &= 0xFFF00FFF;
GPIOA->CRH |= 0x00033000;
}
}
6.把LED控制相关的函数都留空,或放置自己的代码,之后相应的更该头文件,并加入inttounicode的函数声明;
2.mass_mal.c
1.includeSD驱动代码
2.修改数组定义(MAX_LUN即支持磁盘的数量,这里只支持SD卡,MAX_LUN为一)
long long Mass_Memory_Size[MAX_LUN+1];//超过4G的卡无法存储
uint32_t Mass_Block_Size[MAX_LUN+1];
uint32_t Mass_Block_Count[MAX_LUN+1];
3.修改Init
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name : MAL_Init
* Description : Initializes the Media on the STM32
* Input : None
* Output : None
* Return : None
*******************************************************************************/
uint16_t MAL_Init(uint8_t lun)
{
uint16_t status = MAL_OK;
if (sd_init() == SD_ERROR)
{
status = MAL_FAIL;
}
return status;
}
4.修改read,write(对于4G以上的SD卡,uint32_t改为uint64_t, SD驱动也要改)
/*******************************************************************************
* Function Name : MAL_Write
* Description : Write sectors
* Input : None
* Output : None
* Return : None
*******************************************************************************/
uint16_t MAL_Write(uint8_t lun, uint32_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length)
{
//SD卡一个扇区的数据是512byte,所以除以2**9
sd_write_disk((uint8_t *)Writebuff, Memory_Offset>>9, Transfer_Length>>9);
return MAL_OK;
}
/*******************************************************************************
* Function Name : MAL_Read
* Description : Read sectors
* Input : None
* Output : None
* Return : Buffer pointer
*******************************************************************************/
uint16_t MAL_Read(uint8_t lun, uint32_t Memory_Offset, uint32_t *Readbuff, uint16_t Transfer_Length)
{
//SD卡一个扇区的数据是512byte,所以除以2**9
sd_read_disk((uint8_t *)Readbuff, Memory_Offset>>9, Transfer_Length>>9);
return MAL_OK;
}
5.修改函数
/*******************************************************************************
* Function Name : MAL_GetStatus
* Description : Get status
* Input : None
* Output : None
* Return : None
*******************************************************************************/
uint16_t MAL_GetStatus (uint8_t lun)
{
return MAL_OK;
}
3.修改memory.h
1.Read_Memory,Write。。。类型改为uint64_t
4.修改scsi_data.c
给磁盘改名称
uint8_t Standard_Inquiry_Data[] =
{
0x00, /* Direct Access Device */
0x80, /* RMB = 1: Removable Medium */
0x02, /* Version: No conformance claim to standard */
0x02,
36 - 4, /* Additional Length */
0x00, /* SCCS = 1: Storage Controller Component */
0x00,
0x00,
/* Vendor Identification */
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ',
/* Product Identification */
'S', 'D', ' ', 'F', 'l', 'a', 's', 'h', ' ',
'D', 'i', 's', 'k', ' ', ' ', ' ',
/* Product Revision Level */
'1', '.', '0', ' '
};
uint8_t Standard_Inquiry_Data2[] =
{
0x00, /* Direct Access Device */
0x80, /* RMB = 1: Removable Medium */
0x02, /* Version: No conformance claim to standard */
0x02,
36 - 4, /* Additional Length */
0x00, /* SCCS = 1: Storage Controller Component */
0x00,
0x00,
/* Vendor Identification */
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ',
/* Product Identification */
'N', 'A', 'N', 'D', ' ', 'F', 'l', 'a', 's', 'h', ' ',
'D', 'i', 's', 'k', ' ',
/* Product Revision Level */
'1', '.', '0', ' '
};
5.修改usb_prop.c
uint32_t Max_Lun = MAX_LUN;
这样方便设置磁盘数量
6.修改usb_pwr.c
/**
* Function Name : Suspend
* Description : sets suspend mode operating conditions
* Input : None.
* Output : None.
* Return : USB_SUCCESS.
*/
void Suspend(void)
{
uint32_t i = 0;
uint16_t wCNTR;
__IO uint32_t savePWR_CR = 0;
//Store CNTR value
wCNTR = _GetCNTR();
for (i=0; i<8; i++)
{
EP[i] = _GetENDPOINT(i);
}
//unmark RESET flag
wCNTR |= CNTR_RESETM;
_SetCNTR(wCNTR);
//apply FRES
wCNTR |= CNTR_FRES;
_SetCNTR(wCNTR);
//claer FRES
wCNTR &= ~CNTR_FRES;
_SetCNTR(wCNTR);
//poll for RESET flag in ISTR
while ((_GetISTR()&ISTR_RESET) == 0);
//claer RESET flag in ISTR
_SetISTR((uint16_t)CLR_RESET);
//restore Endpoints
for (i=0; i<8; i++)
{
_SetENDPOINT(i, EP[i]);
}
wCNTR |= CNTR_FSUSP;
_SetCNTR(wCNTR);
//force low-power mode in the macrocell
wCNTR = _GetCNTR();
wCNTR |= CNTR_LPMODE;
_SetCNTR(wCNTR);
Enter_LowPowerMode();
}
7.优化数组定义
mass_mal.h添加
extern long long Mass_Memory_Size[MAX_LUN+1];
extern uint32_t Mass_Block_Size[MAX_LUN+1];
extern uint32_t Mass_Block_Count[MAX_LUN+1];
memory.c数组改指针(用malloc申请),防止内存不够
extern uint32_t *Data_Buffer; /* 512 bytes*/
uint8_t TransferState = TXFR_IDLE;
/* Extern variables ----------------------------------------------------------*/
extern uint8_t *Bulk_Data_Buff; /* data buffer*/
memory.h 声明一下这两个数组
usb_scsi,usb_bot去掉这两个数组,然后我们在main.c申请内存即可
7.修改main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "..\Storage\SDdriver.h"
#include "..\Storage\USB\mass_mal.h"
#include "..\Storage\USB\usb_lib.h"
#include "..\Storage\USB\usb_bot.h"
#include "..\Storage\USB\usb_pwr.h"
#include "..\Storage\USB\usb_init.h"
#include "..\Storage\USB\hw_config.h"
#include "..\Storage\USB\memory.h"
#include "..\Interface\usart.h"
//#include "../Games/dino.h"
//STM32F103C8
int main(void)
{
Usart1_Init(115200);
UsartPrintf(USART1, "Start to init sd card\n");
if (sd_init() == SD_OK)
{
UsartPrintf(USART1, "Succeed to init sd card\n");
}
else
{
UsartPrintf(USART1, "Failed to initialize sd card\n");
}
UsartPrintf(USART1, "Start to init usb device\n");
uint32_t *Data_Buffer = (uint32_t *)malloc(BULK_MAX_PACKET_SIZE *2*sizeof(uint32_t)); /* 512 bytes*/
uint8_t *Bulk_Data_Buff = (uint8_t *)malloc(BULK_MAX_PACKET_SIZE*sizeof(uint8_t)); /* data buffer*/
Delay_ms(1000);
USB_PortSet(DISABLE);
Delay_ms(500);
USB_PortSet(ENABLE);
USB_Interrupts_Config();
Set_USBClock();
USB_Init();
while (1)
{
}
}
之后编译正常,并且电脑能识别到stm32为大容量设备
(三)无法访问
之后想到程序设置中MAX_LUN为1,然而我的U盘有2个分区,导致读写错误,于是用Windows自带的分区工具分区
win+X,点磁盘管理 ,先删除两个分区,之后右键可分配的内存,格式化即可,但是再次尝试还是失败。。。估计是mass_mal的read\write那有问题。。。
后来测试发现无法正常读取容量,在这篇文章中找到答案,降低分频,虽能读到容量,但Windows仍然提示函数错误,估计还是硬件连接导致的,下次画PCB再测试STM32单片机使用SPI模式驱动micro sd卡模块,无法读取CSD寄存器,无法读取扇区,初始化能够成功_micro sd卡模块与stm32-CSDN博客