历史上的今天
返回首页

历史上的今天

今天是:2025年02月19日(星期三)

正在发生

2019年02月19日 | STM32学习笔记9——stm32L072 SD卡程序移植记录

2019-02-19 来源:eefocus

项目使用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

1

研究下这个函数,就可以发现函数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起的名字一样,所以引起了误解。



推荐阅读

史海拾趣

Eska公司的发展小趣事

Eska公司成立于荷兰北部,自创立之初就专注于造纸业务。公司引进了先进的造纸技术和设备,建立了两座灰板纸厂,配备了3台技术领先的纸板机和2台造纸机。凭借高效的生产能力和优质的产品质量,Eska迅速在造纸行业中崭露头角。Eska的灰板纸以“Eska(青蛙)”品牌销售,因其优良且稳定的质量而享誉全球。

Defense Supply Center Columbus公司的发展小趣事

Defense Supply Center Columbus公司自成立之初,就深知技术创新对于电子行业的重要性。公司投入大量资源,建立起一支专业的研发团队,专注于研发先进的军事电子设备和系统。通过不断的技术创新,公司成功开发出了一系列高性能、高可靠性的军事电子产品,为国防事业做出了重要贡献。这些产品的成功推出,不仅提升了公司的市场地位,也为公司赢得了众多客户的信赖和好评。

Components Corporation公司的发展小趣事

Defense Supply Center Columbus公司自成立之初,就深知技术创新对于电子行业的重要性。公司投入大量资源,建立起一支专业的研发团队,专注于研发先进的军事电子设备和系统。通过不断的技术创新,公司成功开发出了一系列高性能、高可靠性的军事电子产品,为国防事业做出了重要贡献。这些产品的成功推出,不仅提升了公司的市场地位,也为公司赢得了众多客户的信赖和好评。

Aearo Technologies公司的发展小趣事

尽管Aearo Technologies在行业中享有盛誉,但近年来也面临了一些挑战。公司生产的一款名为“Combat Arms”的军用耳塞被指存在设计缺陷和质量问题,导致部分使用者听力受损。这一事件引发了广泛的关注和诉讼。面对这一挑战,Aearo Technologies积极应对,与相关部门合作进行调查,并采取了一系列措施改进产品质量和确保用户安全。

Emmoco公司的发展小趣事

在稳固了电子元器件和模块市场地位后,Emmoco开始寻求多元化发展。公司利用自身的技术优势和市场经验,逐步拓展产品线,涉足智能家居、物联网等领域。通过不断的技术创新和市场拓展,Emmoco成功实现了产品线的多元化,为客户提供更加全面和多样化的解决方案。

得倍(DBIC)公司的发展小趣事

在激烈的市场竞争中,倍(DBIC)公司不断优化供应链管理,降低成本,提高效率。公司与全球多家供应商建立了长期稳定的合作关系,确保原材料的稳定供应。同时,倍(DBIC)公司还加强了对生产过程的监控和管理,确保产品质量和交货期。这些措施使倍(DBIC)公司在成本控制和交付能力方面具备了明显的竞争优势。

问答坊 | AI 解惑

急聘高级硬件工程师和硬件开发部经理(广州)

高级硬件工程师,年薪最低10万,根据个人能力而定: 1 熟悉单片机,数字电路,熟悉VC/C++,Protel99,USB及NT下的编程 2 有32位单片机应用开发经验者优先 3 电子,通讯类相关专业,有大型嵌入式开发经验者优先 硬件开发部经理,年薪最低12万,根据个人能力 ...…

查看全部问答>

鼠标移动检测。

嵌入式系统,usb的鼠标,已经检测到了mouse0设备了,并且上层程序可以打开open了设备了,如何检测鼠标移动了呢?…

查看全部问答>

ARM9开发板的选购

请大家帮忙推荐一款ARM9开发板吧,主要是用来学习,首要考虑的是其提高的资料是否齐全,其次是价格。请大家帮忙推荐一下吧,小弟不太懂,在此谢过了!…

查看全部问答>

电子设计大赛实用资料

本帖最后由 paulhyde 于 2014-9-15 09:35 编辑 电子设计大赛的一些高频模块资料,蛮好  …

查看全部问答>

【我给XILINX资源中心做贡献】EEWORD史上最全的FPGA论文集

EEWORD史上最全的FPGA论文集 大小 200M 传上来不容易呀 [ 本帖最后由 wanghongyang 于 2011-4-27 12:18 编辑 ]…

查看全部问答>

大家好新人

大家好新人大家好新人大家好新人…

查看全部问答>

TMS320F28035 eQEP、 编码器 学习

     由于DIY电源中用到QEP,就临时学习一下      这两天看了关于编码器的资料对编码器有一个大概的理解。我吧我的理解写出来,一则可以让大牛指点一下,另外可以让像我一样的小白做一个参考。  & ...…

查看全部问答>

大家推荐一款直流信号发生器(直流稳压电源)

麻烦大家推荐一款双通道直流信号发生器(直流稳压电源),输出电压0-30V(低于30V也行),稳定度高,纹波系数小,价格便宜1K以下,品牌还比较好!…

查看全部问答>

分粥的故事

有七个人曾经住在一起,每天分一大桶粥。要命的是,粥每天都是不够的。      一开始,他们抓阄决定谁来分粥,每天轮一个。于是乎每周下来,他们只有一天是饱的,就是自己分粥的那一天。后来他们开始推选出一个道德高尚的人出来分粥。强权就会 ...…

查看全部问答>

关于充电指示问题

请问 此电路里的充电指示灯 为什么会是两个 是怎样工作的 他不是充电灯就亮吗 …

查看全部问答>