STM32G0开发笔记:SD卡模块的使用方法

描述

使用Platformio平台的libopencm3开发框架来开发STM32G0,下面介绍SD卡模块的使用方法。

1 新建项目

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

2 编写程序

2.1 加入FATFS库
  • 从网站fatfs http://elm-chan.org/fsw/ff/00index_e.html 下载最新的源码;
  • 工程目录lib下新建fatfs文件夹;
  • 然后将fatfs源码的source目录下所有文件放置到工程的lib\\fatfs目录下;
  • 将diskio.c文件移动到src目录下,这个文件是需要我们实现的底层接口;
2.2 实现SPI接口的SD读写

在src目录下新建spi_sd.h 和 spi_sd.c 文件,现在目录结构如下:

模块

image-20220918102647364

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

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

  • diskio.c 调用 spi_sd的读写接口:
1/*-----------------------------------------------------------------------*/
 2/* Read Sector(s)                                                        */
 3/*-----------------------------------------------------------------------*/
 4
 5DRESULT disk_read (
 6    BYTE pdrv,      /* Physical drive nmuber to identify the drive */
 7    BYTE *buff,     /* Data buffer to store read data */
 8    LBA_t sector,   /* Start sector in LBA */
 9    UINT count      /* Number of sectors to read */
10)
11{
12    while(count){
13        if (spi_sd_read(buff,sector)) return RES_ERROR;
14        --count;
15        ++sector;
16        buff+=512;
17    }
18    return RES_OK;
19}
20
21
22
23/*-----------------------------------------------------------------------*/
24/* Write Sector(s)                                                       */
25/*-----------------------------------------------------------------------*/
26
27#if FF_FS_READONLY == 0
28
29DRESULT disk_write (
30    BYTE pdrv,          /* Physical drive nmuber to identify the drive */
31    const BYTE *buff,   /* Data to be written */
32    LBA_t sector,       /* Start sector in LBA */
33    UINT count          /* Number of sectors to write */
34)
35{
36    while(count){
37        if(spi_sd_write(buff,sector)) return RES_ERROR;
38        --count;
39        ++sector;
40        buff+=512;
41    }
42    return RES_OK;
43}
2.3 SPI接口配置
1static void spi1_init(void){
 2    //spi1 - display
 3    /* Enable SPI1 Periph and gpio clocks */
 4    rcc_periph_clock_enable(RCC_SPI1);
 5    rcc_periph_clock_enable(RCC_GPIOA);
 6
 7    /* Configure GPIOs:
 8     * 
 9     * SCK=PA5
10     * MOSI=PA7 
11     * MISO=PA6
12     * 
13     * for SD card
14     * SDCS PA4
15     */
16
17    //MOSI & SCK & MISO
18    gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE,GPIO5|GPIO7|GPIO6);
19    gpio_set_af(GPIOA,GPIO_AF0,GPIO5|GPIO7|GPIO6);
20    gpio_set_output_options(GPIOA, GPIO_OTYPE_PP,GPIO_OSPEED_LOW,GPIO5|GPIO7|GPIO6);
21
22    //SDCS 
23    gpio_mode_setup(GPIOA,GPIO_MODE_OUTPUT,GPIO_PUPD_NONE,GPIO4);
24
25    gpio_set(GPIOA,GPIO4);
26
27  /* Reset SPI, SPI_CR1 register cleared, SPI is disabled */
28    spi_reset(SPI1);
29
30  /* Set up SPI in Master mode with:
31   * Clock baud rate
32   * Clock polarity
33   * Clock phase
34   * Frame format MSB
35   */
36    spi_init_master(SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_128, 
37                    SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE,
38                    SPI_CR1_CPHA_CLK_TRANSITION_1,
39                    SPI_CR1_MSBFIRST);
40
41    spi_set_data_size(SPI1,SPI_CR2_DS_8BIT);
42    spi_set_full_duplex_mode(SPI1);
43
44    spi_fifo_reception_threshold_8bit(SPI1);
45
46    SPI_CR2(SPI1) |= SPI_CR2_NSSP; //NSSP, ?? clock continus
47    // spi_set_unidirectional_mode(SPI1);
48    // SPI_CR2(SPI1) &= (~SPI_CR2_FRF_TI_MODE); //motorala mode
49    // SPI_CR2(SPI1) |= SPI_CR2_FRF_TI_MODE;
50
51  /*
52   * Set NSS management to software.
53   *
54   * Note:
55   * Setting nss high is very important, even if we are controlling 
56   * the GPIO
57   * ourselves this bit needs to be at least set to 1, otherwise the spi
58   * peripheral will not send any data out.
59   */
60    spi_enable_software_slave_management(SPI1);
61    spi_set_nss_high(SPI1);
62
63  /* Enable SPI1 periph. */
64    spi_enable(SPI1);
65
66}

SPI使用的SPI1,CS使用软件控制;

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

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

3 硬件连接

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

模块

image-20220918112747620

4 烧写测试

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

模块

image-20220918113435030

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分