项目使用stm32L072,需要将采样的数据保存到SD卡中。SD卡程序参考ST公司的官方STM32L073Z_EVAL开发板的例程。需要修改的地方如下:
1、 修改stm32L073z_eval.h中SPI的管脚配置、AF配置;
2、 官方开发板使用了一片STM32L152CCT6扩展了STM32L073的IO口,并通过I2C接口连接。在SD卡例程中,扩展IO口用于检测SD卡是否连接好。我们的板子中没有设计SD卡硬件检测的功能,所以在stm32L073z_eval_sd.c的BSP_SD_Init函数中将有关SD卡detect的代码屏蔽,只保留调用SD_IO_Init函数和return。
3、 在stm320L0xx_hal_conf.h中,开启FLASH模块,屏蔽I2C模块。如下:
#define HAL_FLASH_MODULE_ENABLED
#define HAL_GPIO_MODULE_ENABLED
/* #define HAL_I2C_MODULE_ENABLED */
4、 在stm32l073z_eval.c中SD卡部分有如下语句:
#if defined(HAL_SPI_MODULE_ENABLED) && defined(HAL_I2C_MODULE_ENABLED)
因为开发板中SD卡部分同时用到了SPI和I2C模块,但我们的板子中没用到I2C,所以将上述语句中的I2C屏蔽,即:
#if defined(HAL_SPI_MODULE_ENABLED) //&& defined(HAL_I2C_MODULE_ENABLED)
至此,源文件基本修改完毕,可以运行程序了。但是在main函数中发现调用函数f_mkfs((TCHAR const*)SDPath, 0, 0);时返回FR_DISK_ERR错误。单步调试发现错误发生在文件dickio.c中的函数disk_write,该函数的定义如下:
#if _USE_WRITE == 1
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
DRESULT res;
res = disk.drv[pdrv]->disk_write(disk.lun[pdrv], buff, sector, count);
return res;
}
这就有点凌乱了,从形式上看,函数disk_write调用了它自己!这个函数是怎么调用底层的SPI写函数,在工程中无论怎么搜索disk_write都未发现。继续单步调试,发现的确跳转到了stm32l073z_eval_sd.c文件中的SD_write函数。那暂时先不管怎么调用的SD_write函数,先找为什么报错吧。继续单步调试,发现SD_write调用BSP_SD_WriteBlocks,在BSP_SD_WriteBlocks中错误发生在如下语句:
ptr = malloc(sizeof(uint8_t)*BlockSize);
哦,是动态分配内存时出错!sizeof(uint8_t)*BlockSize的大小经测试为512,尝试把上述语句直接改为:
ptr = malloc(256);
不再报错。这样,问题就清楚了。是因为动态分配较大的空间时发生错误,而发生错误的原因就是堆栈设置较小。
在启动文件(.s)中将语句
Heap_Size EQU 0x00000200
改为
Heap_Size EQU 0x00000400
再次测试,程序通过。烧录程序,执行正常。
最后,还有一个遗留问题。函数disk_write是怎么调用函数SD_write的呢?既然搜索disk_write查不到结果,那就搜索SD_write试试。果然,在sd_diskio.c中发现如下定义:
const Diskio_drvTypeDef SD_Driver =
{
SD_initialize,
SD_status,
SD_read,
#if _USE_WRITE == 1
SD_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
SD_ioctl,
#endif /* _USE_IOCTL == 1 */
};
而结构体Diskio_drvTypeDef的定义为:
/**
* @brief Disk IO Driver structure definition
*/
typedef struct
{
DSTATUS (*disk_initialize) (BYTE); /*!< Initialize Disk Drive */
DSTATUS (*disk_status) (BYTE); /*!< Get Disk Status */
DRESULT (*disk_read) (BYTE, BYTE*, DWORD, UINT); /*!< Read Sector(s) */
#if _USE_WRITE == 1
DRESULT (*disk_write) (BYTE, const BYTE*, DWORD, UINT); /*!< Write Sector(s) when _USE_WRITE = 0 */
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT (*disk_ioctl) (BYTE, BYTE, void*); /*!< I/O control operation when _USE_IOCTL = 1 */
#endif /* _USE_IOCTL == 1 */
}Diskio_drvTypeDef;
这就说明,SD_Driver是一个结构体,它里面有一个函数指针DRESULT (*disk_write),该指针指向SD_write函数。看来,SD_write函数是通过SD_Driver调用的。搜索SD_Driver,发现在main函数的刚开始有如下语句:
FATFS_LinkDriver( &SD_Driver, SDPath ) == 0
研究下这个函数,就可以发现函数SD_write是通过这个link函数与全局变量disk连接在一起的,然后在函数disk_write中调用。
总结一点就是:
(再把函数disk_write贴一遍)
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
DRESULT res;
res = disk.drv[pdrv]->disk_write(disk.lun[pdrv], buff, sector, count);
return res;
}
函数disk_write中调用的disk_write并不是函数disk_write本身!而是结构体disk.drv中的函数指针,该指针指向了函数SD_write函数。两个disk_write起的名字一样,所以引起了误解。O( ̄) ̄)o