你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32G0开发笔记-Platformio+libopencm3-SPI接口SD卡使用

[复制链接]
STMCU小助手 发布时间:2023-2-22 17:41
使用Platformio平台的libopencm3开发框架来开发STM32G0,下面介绍SD卡模块的使用方法。

1 新建项目
  • 在PIO主页新建项目spi_sdcard,框架选择libopencm3,开发板选择 MonkeyPi_STM32_G070RB;
  • 新建完成后在src目录新建主程序文件main.c;
  • 然后更改项目文件platformio.ini的烧写和调试方式:

  1. 1upload_protocol = cmsis-dap
  2. 2debug_tool = cmsis-dap
复制代码


2 编写程序2.1 加入FATFS库
  • 工程目录lib下新建fatfs文件夹;
  • 然后将fatfs源码的source目录下所有文件放置到工程的lib\fatfs目录下;
  • 将diskio.c文件移动到src目录下,这个文件是需要我们实现的底层接口;

2.2 实现SPI接口的SD读写
在src目录下新建spi_sd.h 和 spi_sd.c 文件,现在目录结构如下:

微信图片_20230222174036.png


  • spi_sd.h 头文件

  1. 1/**
  2. 2 * @file spi_sd.h
  3. 3 * @author MakerInChina (makerinchina.cn)
  4. 4 * @brief
  5. 5 * @version 0.01
  6. 6 * @date 2022-09-18
  7. 7 *
  8. 8 * @copyright Copyright (c) 2022
  9. 9 *
  10. 10 */
  11. 11
  12. 12#ifndef _SPI_SD_HEAD_H_
  13. 13#define _SPI_SD_HEAD_H_
  14. 14
  15. 15#include <stdint.h>
  16. 16
  17. 17/**
  18. 18 * @brief init sd card
  19. 19 *
  20. 20 */
  21. 21uint8_t spi_sd_init();
  22. 22
  23. 23/**
  24. 24 * @brief spi read sd
  25. 25 *
  26. 26 * @param buff
  27. 27 * @param sector
  28. 28 */
  29. 29uint8_t spi_sd_read(uint8_t *buff, uint32_t sector);
  30. 30
  31. 31/**
  32. 32 * @brief spi write sd
  33. 33 *
  34. 34 * @param buff
  35. 35 * @param sector
  36. 36 */
  37. 37uint8_t spi_sd_write(uint8_t *buff, uint32_t sector);
  38. 38
  39. 39#endif //!_SPI_SD_HEAD_H_
复制代码


  • spi_sd.c 实现

  1.   1/**
  2.   2 * @file spi_sd.c
  3.   3 * @author MakerInChina (makerinchina.cn)
  4.   4 * @brief
  5.   5 * @version 0.01
  6.   6 * @date 2022-09-18
  7.   7 *
  8.   8 * @copyright Copyright (c) 2022
  9.   9 *
  10. 10 */
  11. 11
  12. 12#include "spi_sd.h"
  13. 13
  14. 14#include <libopencm3/stm32/spi.h>
  15. 15#include <libopencm3/stm32/gpio.h>
  16. 16
  17. 17#define SDSPI    SPI1
  18. 18#define SD_CS    GPIO4
  19. 19#define SD_PORT  GPIOA
  20. 20
  21. 21#define spi_cs_deselect()   gpio_set(SD_PORT, SD_CS)
  22. 22#define spi_cs_select()     gpio_clear(SD_PORT, SD_CS)
  23. 23
  24. 24
  25. 25/* Definitions for MMC/SDC command */
  26. 26#define CMD0    (0x40+0)    /* GO_IDLE_STATE */
  27. 27#define CMD1    (0x40+1)    /* SEND_OP_COND (MMC) */
  28. 28#define ACMD41    (0xC0+41)   /* SEND_OP_COND (SDC) */
  29. 29#define CMD8    (0x40+8)    /* SEND_IF_COND */
  30. 30#define CMD9    (0x40+9)    /* SEND_CSD */
  31. 31#define CMD10    (0x40+10)   /* SEND_CID */
  32. 32#define CMD12    (0x40+12)   /* STOP_TRANSMISSION */
  33. 33#define ACMD13    (0xC0+13)   /* SD_STATUS (SDC) */
  34. 34#define CMD16    (0x40+16)   /* SET_BLOCKLEN */
  35. 35#define CMD17    (0x40+17)   /* READ_SINGLE_BLOCK */
  36. 36#define CMD18    (0x40+18)   /* READ_MULTIPLE_BLOCK */
  37. 37#define CMD23    (0x40+23)   /* SET_BLOCK_COUNT (MMC) */
  38. 38#define ACMD23    (0xC0+23)   /* SET_WR_BLK_ERASE_COUNT (SDC) */
  39. 39#define CMD24    (0x40+24)   /* WRITE_BLOCK */
  40. 40#define CMD25    (0x40+25)   /* WRITE_MULTIPLE_BLOCK */
  41. 41#define CMD55    (0x40+55)   /* APP_CMD */
  42. 42#define CMD58    (0x40+58)   /* READ_OCR */
  43. 43
  44. 44#define CT_MMC 0x01 /* MMC ver 3 */
  45. 45#define CT_SD1 0x02 /* SD ver 1 */
  46. 46#define CT_SD2 0x04 /* SD ver 2 */
  47. 47#define CT_SDC (CT_SD1|CT_SD2) /* SD */
  48. 48#define CT_BLOCK 0x08 /* Block addressing */
  49. 49
  50. 50static uint8_t spi_read_write8(uint32_t spi, uint8_t tx);
  51. 51static uint8_t wait_ready(void);
  52. 52static uint8_t send_cmd (uint8_t cmd,uint32_t arg);
  53. 53static void set_spi_slow();
  54. 54static void set_spi_fast();
  55. 55
  56. 56/**
  57. 57 * @brief init sd card
  58. 58 *
  59. 59 */
  60. 60uint8_t spi_sd_init()
  61. 61{
  62. 62    uint8_t n, cmd, ty, ocr[4];
  63. 63    uint16_t i;
  64. 64
  65. 65
  66. 66    //init with low speed
  67. 67    // set_spi_slow();
  68. 68
  69. 69    spi_cs_select();
  70. 70
  71. 71    for (n = 10; n; n--) spi_read_write8(SDSPI,0xff);   /* 80 dummy clocks */
  72. 72
  73. 73    ty = 0;
  74. 74
  75. 75    /* Enter Idle state */
  76. 76    ty = send_cmd(CMD0, 0);
  77. 77
  78. 78    // printf("  > enter idle:%d\n", ty);
  79. 79
  80. 80    /* Initialization timeout of 1000 milliseconds */
  81. 81    /* SDHC */
  82. 82
  83. 83    if(ty == 1){
  84. 84
  85. 85        if (send_cmd(CMD8, 0x1AA) == 1){ /* SDv2? */
  86. 86        /* Get trailing return value of R7 response */
  87. 87            for (n = 0; n < 4; n++) ocr[n] = spi_read_write8(SDSPI,0xff);
  88. 88            /* The card can work at VDD range of 2.7-3.6V */
  89. 89            if (ocr[2] == 0x01 && ocr[3] == 0xAA){
  90. 90                /* Wait for leaving idle state (ACMD41 with HCS bit) */
  91. 91                i=0xfff;
  92. 92                while (--i && send_cmd(ACMD41, 1UL << 30));
  93. 93                if (i && send_cmd(CMD58, 0) == 0){
  94. 94                /* Check CCS bit in the OCR */
  95. 95                    for (n = 0; n < 4; n++) ocr[n] = spi_read_write8(SDSPI,0xff);
  96. 96                    ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
  97. 97
  98. 98                }
  99. 99            }
  100. 100        }  else {    /* Not SDv2 card */
  101. 101            // if (send_cmd(ACMD41, 0) <= 1)    {   /* SDv1 or MMC? */
  102. 102            //  ty = CT_SD1; cmd = ACMD41;  /* SDv1 (ACMD41(0)) */
  103. 103            // } else {
  104. 104            //  ty = CT_MMC; cmd = CMD1;    /* MMCv3 (CMD1(0)) */
  105. 105            // }
  106. 106
  107. 107            // while (SPI_Timer_Status() && send_cmd(cmd, 0)) ;     /* Wait for end of initialization */
  108. 108            // if (!SPI_Timer_Status() || send_cmd(CMD16, 512) != 0)    /* Set block length: 512 */
  109. 109            //  ty = 0;
  110. 110        }
  111. 111
  112. 112    }
  113. 113    //CardType = ty;
  114. 114
  115. 115
  116. 116    spi_cs_deselect();
  117. 117    spi_read_write8(SDSPI,0xff);
  118. 118    while (SPI_SR(SDSPI) & SPI_SR_BSY);
  119. 119
  120. 120    set_spi_fast();
  121. 121
  122. 122    return ty;
  123. 123}
  124. 124
  125. 125/**
  126. 126 * @brief spi read sd
  127. 127 *
  128. 128 * @param buff
  129. 129 * @param sector
  130. 130 */
  131. 131uint8_t spi_sd_read(uint8_t *buff, uint32_t sector)
  132. 132{
  133. 133    uint8_t result;
  134. 134    uint16_t cnt=0xffff;
  135. 135    spi_cs_select();   
  136. 136    result=send_cmd(CMD17, sector); //CMD17 даташит стр 50 и 96
  137. 137    if (result){spi_cs_deselect(); return 5;} //Выйти, если результат не 0x00
  138. 138
  139. 139    spi_read_write8(SDSPI,0xff);
  140. 140    cnt=0;
  141. 141    do result=spi_read_write8(SDSPI,0xff); while ((result!=0xFE)&&--cnt);
  142. 142    if(!cnt){spi_cs_deselect(); return 5;}
  143. 143
  144. 144    for (cnt=0;cnt<512;cnt++) *buff++=spi_read_write8(SDSPI,0xff);
  145. 145
  146. 146    spi_read_write8(SDSPI,0xff);
  147. 147    spi_read_write8(SDSPI,0xff);
  148. 148    spi_cs_deselect();
  149. 149    spi_read_write8(SDSPI,0xff);
  150. 150
  151. 151    return 0;
  152. 152}
  153. 153
  154. 154/**
  155. 155 * @brief spi write sd
  156. 156 *
  157. 157 * @param buff
  158. 158 * @param sector
  159. 159 */
  160. 160uint8_t spi_sd_write(uint8_t *buff, uint32_t sector)
  161. 161{
  162. 162    uint8_t result;
  163. 163    uint16_t cnt=0xffff;
  164. 164    spi_cs_select();
  165. 165    result=send_cmd(CMD24,sector); //CMD24
  166. 166    if(result){spi_cs_deselect(); return 6;} //
  167. 167    spi_read_write8(SDSPI,0xff);
  168. 168    spi_read_write8(SDSPI,0xfe);//
  169. 169    for (cnt=0;cnt<512;cnt++) spi_read_write8(SDSPI,buff[cnt]); //Данные
  170. 170    spi_read_write8(SDSPI,0xff);
  171. 171    spi_read_write8(SDSPI,0xff);
  172. 172    result=spi_read_write8(SDSPI,0xff);
  173. 173    //result=wait_ready();
  174. 174    if((result&0x05)!=0x05){spi_cs_deselect(); return 6;}
  175. 175    //spi_read_write8(SDSPI,0xff);
  176. 176    while (SPI_SR(SDSPI) & SPI_SR_BSY);
  177. 177    //
  178. 178    spi_cs_deselect();
  179. 179    spi_read_write8(SDSPI,0xff);
  180. 180    return 0;
  181. 181}
  182. 182
  183. 183
  184. 184static void set_spi_slow()
  185. 185{
  186. 186    // spi_disable(SDSPI);
  187. 187    spi_set_baudrate_prescaler(SDSPI,SPI_CR1_BAUDRATE_FPCLK_DIV_128);
  188. 188    // spi_enable(SDSPI);
  189. 189}
  190. 190
  191. 191static void set_spi_fast()
  192. 192{
  193. 193    // spi_disable(SDSPI);
  194. 194    spi_set_baudrate_prescaler(SDSPI,SPI_CR1_BAUDRATE_FPCLK_DIV_8);
  195. 195    // spi_enable(SDSPI);
  196. 196}
  197. 197
  198. 198static uint8_t spi_read_write8(uint32_t spi, uint8_t tx)
  199. 199{
  200. 200    spi_send8(spi, tx);
  201. 201    return spi_read8(spi);
  202. 202}
  203. 203
  204. 204static uint8_t wait_ready(void)
  205. 205{
  206. 206    uint8_t res;
  207. 207    uint16_t cnt=0xffff;
  208. 208    spi_read_write8(SDSPI, 0xff);
  209. 209    do res = spi_read_write8(SDSPI, 0xff); while ((res!=0xFF)&& --cnt );
  210. 210    return res;
  211. 211}
  212. 212
  213. 213static uint8_t send_cmd (uint8_t cmd,uint32_t arg)
  214. 214{
  215. 215    uint8_t n, res;
  216. 216
  217. 217    /* ACMD<n> is the command sequence of CMD55-CMD<n> */
  218. 218    if (cmd & 0x80){
  219. 219        cmd &= 0x7F;
  220. 220        res = send_cmd(CMD55, 0);
  221. 221        if (res > 1) return res;
  222. 222    }
  223. 223
  224. 224    if (wait_ready()!=0xFF) return 0xFF;
  225. 225    /* Send command packet */
  226. 226    spi_read_write8(SDSPI, cmd);                    /* Start + Command index */
  227. 227    spi_read_write8(SDSPI,(uint8_t)(arg >> 24));    /* Argument[31..24] */
  228. 228    spi_read_write8(SDSPI,(uint8_t)(arg >> 16));    /* Argument[23..16] */
  229. 229    spi_read_write8(SDSPI,(uint8_t)(arg >> 8)); /* Argument[15..8] */
  230. 230    spi_read_write8(SDSPI,(uint8_t)arg);            /* Argument[7..0] */
  231. 231    n = 0x01;                               /* Dummy CRC + Stop */
  232. 232    if (cmd == CMD0) n = 0x95;              /* Valid CRC for CMD0(0) */
  233. 233    if (cmd == CMD8) n = 0x87;              /* Valid CRC for CMD8(0x1AA) */
  234. 234    spi_read_write8(SDSPI,n);
  235. 235    /* Receive command response */
  236. 236    if (cmd == CMD12) spi_read_write8(SDSPI,0xff);  
  237. 237    /* Skip a stuff byte when stop reading */
  238. 238    /* Wait for a valid response in timeout of 10 attempts */
  239. 239    n = 10;
  240. 240    do res=spi_read_write8(SDSPI,0xff); while ((res & 0x80) && --n);
  241. 241
  242. 242    while (SPI_SR(SDSPI) & SPI_SR_BSY); //wait if busy
  243. 243
  244. 244    return res;                    /* Return with the response value */
  245. 245}
复制代码


注:这里初始化部分只写了SDv2的判断;

  • diskio.c 调用 spi_sd的读写接口:

  1. 1/*-----------------------------------------------------------------------*/
  2. 2/* Read Sector(s)                                                        */
  3. 3/*-----------------------------------------------------------------------*/
  4. 4
  5. 5DRESULT disk_read (
  6. 6    BYTE pdrv,      /* Physical drive nmuber to identify the drive */
  7. 7    BYTE *buff,     /* Data buffer to store read data */
  8. 8    LBA_t sector,   /* Start sector in LBA */
  9. 9    UINT count      /* Number of sectors to read */
  10. 10)
  11. 11{
  12. 12    while(count){
  13. 13        if (spi_sd_read(buff,sector)) return RES_ERROR;
  14. 14        --count;
  15. 15        ++sector;
  16. 16        buff+=512;
  17. 17    }
  18. 18    return RES_OK;
  19. 19}
  20. 20
  21. 21
  22. 22
  23. 23/*-----------------------------------------------------------------------*/
  24. 24/* Write Sector(s)                                                       */
  25. 25/*-----------------------------------------------------------------------*/
  26. 26
  27. 27#if FF_FS_READONLY == 0
  28. 28
  29. 29DRESULT disk_write (
  30. 30    BYTE pdrv,          /* Physical drive nmuber to identify the drive */
  31. 31    const BYTE *buff,   /* Data to be written */
  32. 32    LBA_t sector,       /* Start sector in LBA */
  33. 33    UINT count          /* Number of sectors to write */
  34. 34)
  35. 35{
  36. 36    while(count){
  37. 37        if(spi_sd_write(buff,sector)) return RES_ERROR;
  38. 38        --count;
  39. 39        ++sector;
  40. 40        buff+=512;
  41. 41    }
  42. 42    return RES_OK;
  43. 43}
复制代码


2.3 SPI接口配置
  1. 1static void spi1_init(void){
  2. 2    //spi1 - display
  3. 3    /* Enable SPI1 Periph and gpio clocks */
  4. 4    rcc_periph_clock_enable(RCC_SPI1);
  5. 5    rcc_periph_clock_enable(RCC_GPIOA);
  6. 6
  7. 7    /* Configure GPIOs:
  8. 8     *
  9. 9     * SCK=PA5
  10. 10     * MOSI=PA7
  11. 11     * MISO=PA6
  12. 12     *
  13. 13     * for SD card
  14. 14     * SDCS PA4
  15. 15     */
  16. 16
  17. 17    //MOSI & SCK & MISO
  18. 18    gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE,GPIO5|GPIO7|GPIO6);
  19. 19    gpio_set_af(GPIOA,GPIO_AF0,GPIO5|GPIO7|GPIO6);
  20. 20    gpio_set_output_options(GPIOA, GPIO_OTYPE_PP,GPIO_OSPEED_LOW,GPIO5|GPIO7|GPIO6);
  21. 21
  22. 22    //SDCS
  23. 23    gpio_mode_setup(GPIOA,GPIO_MODE_OUTPUT,GPIO_PUPD_NONE,GPIO4);
  24. 24
  25. 25    gpio_set(GPIOA,GPIO4);
  26. 26
  27. 27  /* Reset SPI, SPI_CR1 register cleared, SPI is disabled */
  28. 28    spi_reset(SPI1);
  29. 29
  30. 30  /* Set up SPI in Master mode with:
  31. 31   * Clock baud rate
  32. 32   * Clock polarity
  33. 33   * Clock phase
  34. 34   * Frame format MSB
  35. 35   */
  36. 36    spi_init_master(SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_128,
  37. 37                    SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE,
  38. 38                    SPI_CR1_CPHA_CLK_TRANSITION_1,
  39. 39                    SPI_CR1_MSBFIRST);
  40. 40
  41. 41    spi_set_data_size(SPI1,SPI_CR2_DS_8BIT);
  42. 42    spi_set_full_duplex_mode(SPI1);
  43. 43
  44. 44    spi_fifo_reception_threshold_8bit(SPI1);
  45. 45
  46. 46    SPI_CR2(SPI1) |= SPI_CR2_NSSP; //NSSP, ?? clock continus
  47. 47    // spi_set_unidirectional_mode(SPI1);
  48. 48    // SPI_CR2(SPI1) &= (~SPI_CR2_FRF_TI_MODE); //motorala mode
  49. 49    // SPI_CR2(SPI1) |= SPI_CR2_FRF_TI_MODE;
  50. 50
  51. 51  /*
  52. 52   * Set NSS management to software.
  53. 53   *
  54. 54   * Note:
  55. 55   * Setting nss high is very important, even if we are controlling
  56. 56   * the GPIO
  57. 57   * ourselves this bit needs to be at least set to 1, otherwise the spi
  58. 58   * peripheral will not send any data out.
  59. 59   */
  60. 60    spi_enable_software_slave_management(SPI1);
  61. 61    spi_set_nss_high(SPI1);
  62. 62
  63. 63  /* Enable SPI1 periph. */
  64. 64    spi_enable(SPI1);
  65. 65
  66. 66}
复制代码


2.4 SD卡读写测试
  1. 1printf(" init spi.\n");
  2. 2
  3. 3//spi
  4. 4spi1_init();
  5. 5
  6. 6//sd
  7. 7uint8_t sd_type = spi_sd_init();
  8. 8
  9. 9//sdv2: 0x08|0x04 . 0x0c
  10. 10// printf(" sd_type: %x\n", sd_type);
  11. 11
  12. 12FATFS fs;
  13. 13FRESULT res;
  14. 14res = f_mount(&fs, "", 0);
  15. 15if(res != FR_OK) {
  16. 16    printf("mount fs failed, res = %d\r\n", res);
  17. 17    return -1;
  18. 18}else{
  19. 19    printf(" mount fs OK.\n");
  20. 20}
  21. 21
  22. 22FIL fd;
  23. 23
  24. 24res = f_open(&fd, "test.txt", FA_CREATE_ALWAYS|FA_WRITE);
  25. 25if(res != FR_OK){
  26. 26    printf("open file failed: %d\n", res);
  27. 27}else{
  28. 28    printf("open file OK.\n");
  29. 29}
  30. 30
  31. 31char *buff = "test data to write to fs\n";
  32. 32uint32_t len = 0;
  33. 33res = f_write(&fd,buff, strlen(buff),&len);
  34. 34if(res != FR_OK){
  35. 35    printf(" write file failed.\n");
  36. 36}else{
  37. 37    printf(" write file OK, write size %d .\n", len);
  38. 38}
  39. 39
  40. 40res = f_close(&fd);
  41. 41if(res != FR_OK){
  42. 42    printf(" close fs failed.\n");
  43. 43}else{
  44. 44    printf(" close fs OK.\n");
  45. 45}
  46. 46
  47. 47res = f_unmount("");
  48. 48if(res != FR_OK) {
  49. 49    printf("Unmount fs failed, res = %d\r\n", res);
  50. 50    return -1;
  51. 51}else{
  52. 52    printf("Unmound fs OK.\n");
  53. 53}
复制代码


main中测试SD卡挂载、读写;

3 硬件连接
硬件引脚按如下方式连接到SPI1

微信图片_20230222174026.png



4 烧写测试
将程序烧写到开发板后,打开串口,可以看到测试成功,卡中写入文件在电脑显示正确:

微信图片_20230222174024.png

转载自:MakerInChina.cn


收藏 评论0 发布时间:2023-2-22 17:41

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版