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

使用platformio平台的libopencm3开发框架来开发stm32g0,下面介绍sd卡模块的使用方法。
1 新建项目在pio主页新建项目spi_sdcard,框架选择libopencm3,开发板选择 monkeypi_stm32_g070rb;新建完成后在src目录新建主程序文件main.c;然后更改项目文件platformio.ini的烧写和调试方式:1upload_protocol = cmsis-dap2debug_tool = cmsis-dap2 编写程序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 */1112#ifndef _spi_sd_head_h_13#define _spi_sd_head_h_1415#include 1617/**18 * @brief init sd card19 * 20 */21uint8_t spi_sd_init();2223/**24 * @brief spi read sd25 * 26 * @param buff 27 * @param sector 28 */29uint8_t spi_sd_read(uint8_t *buff, uint32_t sector);3031/**32 * @brief spi write sd33 * 34 * @param buff 35 * @param sector 36 */37uint8_t spi_sd_write(uint8_t *buff, uint32_t sector);3839#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 15#include 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 // }106107 // 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 }111112 }113 //cardtype = ty;114115116 spi_cs_deselect();117 spi_read_write8(sdspi,0xff);118 while (spi_sr(sdspi) & spi_sr_bsy);119120 set_spi_fast();121122 return ty;123}124125/**126 * @brief spi read sd127 * 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 и 96137 if (result){spi_cs_deselect(); return 5;} //выйти, если результат не 0x00138139 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;}143144 for (cnt=0;cnt<512;cnt++) *buff++=spi_read_write8(sdspi,0xff); 145146 spi_read_write8(sdspi,0xff); 147 spi_read_write8(sdspi,0xff);148 spi_cs_deselect();149 spi_read_write8(sdspi,0xff);150151 return 0;152}153154/**155 * @brief spi write sd156 * 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 1) return res;222 }223224 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);241242 while (spi_sr(sdspi) & spi_sr_bsy); //wait if busy243244 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}20212223/*-----------------------------------------------------------------------*/24/* write sector(s) */25/*-----------------------------------------------------------------------*/2627#if ff_fs_readonly == 02829dresult 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=pa510 * mosi=pa7 11 * miso=pa612 * 13 * for sd card14 * sdcs pa415 */1617 //mosi & sck & miso18 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);2122 //sdcs 23 gpio_mode_setup(gpioa,gpio_mode_output,gpio_pupd_none,gpio4);2425 gpio_set(gpioa,gpio4);2627 /* reset spi, spi_cr1 register cleared, spi is disabled */28 spi_reset(spi1);2930 /* set up spi in master mode with:31 * clock baud rate32 * clock polarity33 * clock phase34 * frame format msb35 */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);4041 spi_set_data_size(spi1,spi_cr2_ds_8bit);42 spi_set_full_duplex_mode(spi1);4344 spi_fifo_reception_threshold_8bit(spi1);4546 spi_cr2(spi1) |= spi_cr2_nssp; //nssp, clock continus47 // spi_set_unidirectional_mode(spi1);48 // spi_cr2(spi1) &= (~spi_cr2_frf_ti_mode); //motorala mode49 // spi_cr2(spi1) |= spi_cr2_frf_ti_mode;5051 /*52 * set nss management to software.53 *54 * note:55 * setting nss high is very important, even if we are controlling 56 * the gpio57 * ourselves this bit needs to be at least set to 1, otherwise the spi58 * peripheral will not send any data out.59 */60 spi_enable_software_slave_management(spi1);61 spi_set_nss_high(spi1);6263 /* enable spi1 periph. */64 spi_enable(spi1);6566}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 . 0x0c10// printf( sd_type: %x\\n, sd_type);1112fatfs 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}2122fil fd;2324res = 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}3031char *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}3940res = f_close(&fd);41if(res != fr_ok){42 printf( close fs failed.\\n);43}else{44 printf( close fs ok.\\n);45}4647res = 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

TKMD全自动SF6密度继电器校验仪的功能
LSI推出PCIe闪存LSI Nytro MegaRAID卡
基于具有程序化功能的核壳结构微针阵列贴片设计
机器替代人类是必然,对机器人征税是制约
又一家台湾PCB厂商恩德被黑客攻击
STM32G0开发笔记:SD卡模块的使用方法
国芯科技获2023年度“优秀密码应用方案奖“
联通千兆宽带用户突破100万大关 启动2000M宽带体验活动
射频(RF)采样:数字混频器能使混频操作妙趣横生
汽车物联网解决方案的创新效率将确保企业获得大量收益
去耦电容和旁路电容区别
书架式音箱的设计制作,Sound Box design
滤波器分类及特点
发布多款MiniLED产品,FFALCON雷鸟鹤7 24款电视、Q8/U8显示器首发亮相
金士顿推出2.5英寸的KC600 SATA SSD新产品
持续上新丨美格智能推出高算力AI模组SNM930,支持运行Linux Ubuntu
高通霸主地位不保 华为碾压高通拿下5G时代!
联想投身智慧医疗 为健康领域创造更多可能
使用POWERLINK+APROL系统 革新纤维生产设备
晶振起振时间测试原理、测试方法