使用Platformio平台的libopencm3开发框架来开发STM32G0,下面介绍SD卡模块的使用方法。
1upload_protocol = cmsis-dap
2debug_tool = cmsis-dap
在src目录下新建spi_sd.h 和 spi_sd.c 文件,现在目录结构如下:
image-20220918102647364
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_
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的判断;
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}
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使用软件控制;
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卡挂载、读写;
硬件引脚按如下方式连接到SPI1:
image-20220918112747620
将程序烧写到开发板后,打开串口,可以看到测试成功,卡中写入文件在电脑显示正确:
image-20220918113435030
全部0条评论
快来发表一下你的评论吧 !