开发环境:
stm32cubeMx:version 5.6.1
固件库:STM32Cube FW_F7 V1.16.0
MCU:STM32F767IGTx
Cubemx的配置:
按照上图的配置进行sd的pinmux配置
Sd功能配置,使用fatfs的话,需要用到dma,要添加进去
Fatfs配置:
这个需要配置个检测管脚,有些sd卡可能没有接这个pin检测管脚,后面可以在代码中进行屏蔽掉;
时钟配置:堆栈大小设置(看过其他文章,因为要用到fatfs,这个要设置的大一些,如果设置小了可能会影响功能):
然后点击右上角的generate codes自动生成代码:
Keil调试:
先查看仿真器能否连接上,如果hw Version显示识别不了,则需要更新仿真器驱动;下图是仿真器连接上的截图
代码进行如下修改:
1) Main.h添加如下代码,由于没有连接串口查看调试信息,这个结构体主要是用于调试debug用。Fatfstest函数用于测试sd卡功能
2) Main.c中添加fatfstest代码:
tSD_FATFS_DAT g_sd_fatfs_data;
BYTE ReadBuffer[512]={0}; /* 读缓冲区 */
BYTE WriteBuffer[]= “xinjianwenjianxitong\n”; //新建文件系统测试文件
void FatFsTest(void)
{
FIL file; /* 文件对象 */
FRESULT f_res; /* 文件操作结果 */
UINT fnum; /* 文件成功读写数量 */
//BYTE ReadBuffer[512]={0}; /* 读缓冲区 */
//BYTE WriteBuffer[]= "xinjianwenjianxitong\n"; //新建文件系统测试文件
BYTE work[512];
//在SD卡挂载文件系统,文件系统挂载时会对SD卡初始化
f_res = f_mount(&SDFatFS,(TCHAR const*)SDPath,1);
/*----------------------- 格式化测试 ---------------------------*/
/* 如果没有文件系统就格式化创建创建文件系统 */
if(f_res == FR_NO_FILESYSTEM) //FR_NO_FILESYSTEM //heyi add:先强制格式化
{
//printf("》SD卡还没有文件系统,即将进行格式化...\n");
g_sd_fatfs_data.current_step = 1;
/* 格式化 */
f_res=f_mkfs((TCHAR const*)SDPath,FM_ANY,0,work,512); //
if(f_res == FR_OK)
{
//printf("》SD卡已成功格式化文件系统。\n");
/* 格式化后,先取消挂载 */
f_res = f_mount(NULL,(TCHAR const*)SDPath,1);
/* 重新挂载 */
f_res = f_mount(&SDFatFS,(TCHAR const*)SDPath,1);
g_sd_fatfs_data.current_step |= 1<<1;
}
else
{
//printf("《《格式化失败。》》\n");
g_sd_fatfs_data.current_status |= 1<<2;
while(1);
}
}
else if(f_res!=FR_OK)
{
//printf("!!SD卡挂载文件系统失败。(%d)\n",f_res);
g_sd_fatfs_data.current_status |= 1<<2;
g_sd_fatfs_data.sd_ret = f_res;
while(1);
}
else
{
//printf("》文件系统挂载成功,可以进行读写测试\n");
g_sd_fatfs_data.current_status |= 1<<3;
g_sd_fatfs_data.sd_ret = f_res;
}
/*----------------------- 文件系统测试:写测试 -----------------------------*/
/* 打开文件,如果文件不存在则创建它 */
//printf("****** 即将进行文件写入测试... ******\n");
f_res = f_open(&file, "FatFs.txt",FA_CREATE_ALWAYS | FA_WRITE ); //FatFs读写测试文件.txt
if ( f_res == FR_OK )
{
//printf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\n");
g_sd_fatfs_data.current_status |= 1<<4;
/* 将指定存储区内容写入到文件内 */
f_res=f_write(&file,WriteBuffer,sizeof(WriteBuffer),&fnum);
if(f_res==FR_OK)
{
//printf("》文件写入成功,写入字节数据:%d\n",fnum);
//printf("》向文件写入的数据为:\n%s\n",WriteBuffer);
g_sd_fatfs_data.current_status |= 1<<5;
}
else
{
//printf("!!文件写入失败:(%d)\n",f_res);
g_sd_fatfs_data.current_step |= 1<<3;
}
/* 不再读写,关闭文件 */
f_close(&file);
}
else
{
//printf("!!打开/创建文件失败。\n");
g_sd_fatfs_data.current_step |= 1<<4;
g_sd_fatfs_data.current_status |= 1<<5;
g_sd_fatfs_data.sd_ret = f_res;
}
/*------------------- 文件系统测试:读测试 ------------------------------------*/
//printf("****** 即将进行文件读取测试... ******\n");
f_res = f_open(&file, "FatFs.txt", FA_OPEN_EXISTING | FA_READ);
if(f_res == FR_OK)
{
//printf("》打开文件成功。\n");
g_sd_fatfs_data.current_step |= 1<<5;
g_sd_fatfs_data.sd_ret = f_res;
f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &g_sd_fatfs_data.fnum); //fnum
if(f_res==FR_OK)
{
//printf("》文件读取成功,读到字节数据:%d\n",fnum);
//printf("》读取得的文件数据为:\n%s \n", ReadBuffer);
g_sd_fatfs_data.current_step |= 1<<6;
}
else
{
//printf("!!文件读取失败:(%d)\n",f_res);
g_sd_fatfs_data.current_step |= 1<<7;
}
}
else
{
//printf("!!打开文件失败。\n");
g_sd_fatfs_data.current_step |= 1<<7;
}
/* 不再读写,关闭文件 */
f_close(&file);
/* 不再使用,取消挂载 */
f_res = f_mount(NULL,(TCHAR const*)SDPath,1); //heyi dis
/* 注销一个FatFS设备:SD卡 */
FATFS_UnLinkDriver(SDPath);
}
3) Freertos.c中修改如下代码
线程的空间要设置大一些,否则会进入到hardfault函数中死掉;
4) 使用debug进行调试,正常的情况下会有如下的debug信息:
调试中遇到的问题:
1) f_res = f_mount(&SDFatFS,(TCHAR const*)SDPath,1);不通过,一步步跟进去,检测函数报错,stm32默认是低电平为插入sd卡,而实际设计的硬件是sd插入后,检测管脚检测到的信号为低电平,不插入时候为高电平;需要进行如下修改;
2) 线程的空间要设置大一点,否则程序会跑飞,进入到hardfault函数死掉,修改些代码,死掉前的函数就在不一样的地方,定位都不好定位。
定位方式:
STM32出现硬件错误可能有以下原因:
(1)数组越界操作;
(2)内存溢出,访问越界;
(3)堆栈溢出,程序跑飞;
(4)中断处理错误;
遇到这种情况,可以通过以下2种方式来定位到出错代码段。
方法1:
1.1在硬件中断函数HardFault_Handler里的while(1)处打调试断点,程序执行到断点处时点击“STOP”停止仿真。
1.2 在Keil菜单栏点击“View”——“Registers Window”,在寄存器查看窗口查找R14(LR)的值。如果R14(LR) = 0xFFFFFFE9,继续查看MSP(主堆栈指针)的值,如果R14(LR) = 0xFFFFFFFD,继续查看PSP(进程栈指针)的值。我的程序R14(LR) = 0xFFFFFFF9,接下来以此为例。
1.3 在Keil菜单栏点击“View”——“Memory Windows”——“Memory1”,在“Address”地址栏中输入MSP的值:0x20001288,然后在对应的行里找到地址。地址一般以0x08开头的32位数。本例中,地址为0x08003CB9。
1.4 在Keil菜单栏点击“View”——“Disassembly Window”,在“Disassembly”窗口中右击,在下拉菜单中选择“Show Disassemblyat Address…”。在弹出框“Show Code atAdress”的地址框中输入地址0x08003CB9进行搜索,然后就会找到相对应的代码。这里的代码就是进入循环中断之前的情况。仔细查看附近区域的相关代码来排查错误具体原因。
方法2:
2.1在硬件中断函数HardFault_Handler里的while(1)处打调试断点,程序执行到断点处时点击“STOP”停止仿真。
2.2 在Keil菜单栏点击“View”——“Call Stack Window”弹出“Call Stack + Locals”对话框。然后在对话框中右键选择“Show Caller Code”,就会跳转到出错之前的函数处,仔细查看这部分函数被调用或者数组内存使用情况。
3)第二种定位方式更方便一些,右键HardFault_Handler函数,可以跳转到上一个函数,内存越界后,修改了代码,跑死在上一个函数也是在变化中的。建议大的内存分配,最好使用全局变量,提前分配好。测试函数使用的是网上的程序,当初没有考虑内存这块,导致卡死了很长时间。
3) sd卡如果是新卡的话,建议先格式化一下。
4) debug的时候,会进入到stream收发中断里面