# STM32F103 SD卡读写程序

下面是一个基于STM32F103系列微控制器的SD卡读写完整程序,使用标准外设库(StdPeriph)和FatFs文件系统。

硬件准备

  • STM32F103C8T6开发板(或其他F103系列)
  • SD卡模块(SPI接口)
  • 连接线缆

硬件连接

复制代码
SD卡模块  STM32F103
CS   -> PA4 (SPI1_NSS)
SCK  -> PA5 (SPI1_SCK)
MISO -> PA6 (SPI1_MISO)
MOSI -> PA7 (SPI1_MOSI)
VCC  -> 3.3V
GND  -> GND

完整代码

1. 主程序 (main.c)

c 复制代码
#include "stm32f10x.h"
#include "ff.h"
#include "diskio.h"
#include "spi_sd.h"
#include <stdio.h>
#include <string.h>

FATFS fs;       /* FatFs文件系统对象 */
FIL fil;        /* 文件对象 */
FRESULT res;    /* FatFs函数返回结果 */
UINT bw;        /* 读写字节数 */

void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART1_Init(void);

int main(void)
{
    char buffer[128];
    
    /* 系统时钟配置 */
    RCC_Configuration();
    
    /* GPIO配置 */
    GPIO_Configuration();
    
    /* USART1初始化 */
    USART1_Init();
    
    printf("STM32F103 SD Card Test\r\n");
    
    /* 挂载文件系统 */
    if(f_mount(&fs, "0:", 1) != FR_OK)
    {
        printf("Mount SD Card Failed!\r\n");
        while(1);
    }
    printf("SD Card Mounted Successfully!\r\n");
    
    /* 打开/创建文件 */
    res = f_open(&fil, "0:test.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
    if(res != FR_OK)
    {
        printf("Open File Failed!\r\n");
        while(1);
    }
    
    /* 写入数据 */
    f_lseek(&fil, 0);
    f_printf(&fil, "Hello, STM32 SD Card!\r\n");
    f_printf(&fil, "This is a test file.\r\n");
    
    /* 读取数据 */
    f_lseek(&fil, 0);
    while(f_gets(buffer, sizeof(buffer), &fil))
    {
        printf("%s", buffer);
    }
    
    /* 关闭文件 */
    f_close(&fil);
    
    /* 卸载文件系统 */
    f_mount(NULL, "0:", 1);
    
    while(1)
    {
    }
}

void RCC_Configuration(void)
{
    /* 复位RCC时钟配置 */
    RCC_DeInit();
    
    /* 使能外部高速晶振 */
    RCC_HSEConfig(RCC_HSE_ON);
    
    /* 等待HSE就绪 */
    if(RCC_WaitForHSEStartUp() == SUCCESS)
    {
        /* 设置HCLK = SYSCLK */
        RCC_HCLKConfig(RCC_SYSCLK_Div1);
        
        /* 设置PCLK2 = HCLK */
        RCC_PCLK2Config(RCC_HCLK_Div1);
        
        /* 设置PCLK1 = HCLK/2 */
        RCC_PCLK1Config(RCC_HCLK_Div2);
        
        /* 设置FLASH延时 */
        FLASH_SetLatency(FLASH_Latency_2);
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
        
        /* 设置PLL时钟源为HSE, 9倍频 */
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
        
        /* 使能PLL */
        RCC_PLLCmd(ENABLE);
        
        /* 等待PLL就绪 */
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
        
        /* 设置PLL为系统时钟源 */
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        
        /* 等待系统时钟源切换完成 */
        while(RCC_GetSYSCLKSource() != 0x08);
    }
    
    /* 使能SPI1时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    
    /* 使能GPIOA时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    /* 使能USART1时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
}

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    /* 配置SPI1引脚: SCK, MISO, MOSI */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* 配置SPI1 CS引脚 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA, GPIO_Pin_4);  // CS高电平
    
    /* 配置USART1 TX引脚 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* 配置USART1 RX引脚 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void USART1_Init(void)
{
    USART_InitTypeDef USART_InitStructure;
    
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    
    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}

/* 重定向printf到USART1 */
int fputc(int ch, FILE *f)
{
    USART_SendData(USART1, (uint8_t)ch);
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    return ch;
}

2. SPI和SD卡驱动 (spi_sd.c)

c 复制代码
#include "spi_sd.h"
#include "stm32f10x.h"
#include "delay.h"

/* SPI1读写一个字节 */
uint8_t SPI1_ReadWriteByte(uint8_t TxData)
{
    /* 等待发送缓冲区空 */
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    
    /* 发送数据 */
    SPI_I2S_SendData(SPI1, TxData);
    
    /* 等待接收缓冲区非空 */
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    
    /* 返回接收到的数据 */
    return SPI_I2S_ReceiveData(SPI1);
}

/* SPI1初始化 */
void SPI1_Init(void)
{
    SPI_InitTypeDef SPI_InitStructure;
    
    /* SPI1配置 */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    
    SPI_Init(SPI1, &SPI_InitStructure);
    SPI_Cmd(SPI1, ENABLE);
    
    /* CS高电平 */
    SD_CS_HIGH();
}

/* SD卡初始化 */
uint8_t SD_Initialize(void)
{
    uint8_t retry = 0;
    uint8_t i;
    uint8_t buf[4];
    uint16_t count = 0;
    
    /* 初始化SPI */
    SPI1_Init();
    
    /* 延时至少74个时钟周期 */
    SD_CS_HIGH();
    for(i = 0; i < 10; i++)
    {
        SPI1_ReadWriteByte(0xFF);
    }
    
    /* 发送CMD0进入空闲状态 */
    while(SD_SendCmd(CMD0, 0, 0x95) != 0x01)
    {
        retry++;
        if(retry > 100)
        {
            return 1; // 超时
        }
    }
    
    /* 发送CMD8检查SD卡版本 */
    if(SD_SendCmd(CMD8, 0x1AA, 0x87) == 0x01)
    {
        /* SD卡v2.0 */
        for(i = 0; i < 4; i++)
        {
            buf[i] = SPI1_ReadWriteByte(0xFF);
        }
        
        /* 初始化SD卡 */
        while(SD_SendCmd(ACMD41, 0x40000000, 0x01) != 0x00)
        {
            count++;
            if(count > 0xFFFE)
            {
                return 2; // 初始化失败
            }
        }
        
        /* 读取OCR寄存器 */
        if(SD_SendCmd(CMD58, 0, 0x01) != 0x00)
        {
            return 3;
        }
        
        for(i = 0; i < 4; i++)
        {
            buf[i] = SPI1_ReadWriteByte(0xFF);
        }
        
        /* 检查CCS位 */
        if(buf[0] & 0x40)
        {
            CardType = SD_TYPE_V2HC; // SDHC/SDXC
        }
        else
        {
            CardType = SD_TYPE_V2;   // SDv2
        }
    }
    else
    {
        /* SD卡v1.x或MMC */
        if(SD_SendCmd(ACMD41, 0, 0x01) <= 1)
        {
            /* SD卡v1.x */
            CardType = SD_TYPE_V1;
            count = 0;
            
            /* 等待初始化完成 */
            while(SD_SendCmd(ACMD41, 0, 0x01) != 0x00)
            {
                count++;
                if(count > 0xFFFE)
                {
                    return 4; // 初始化失败
                }
            }
        }
        else
        {
            /* MMC卡 */
            CardType = SD_TYPE_MMC;
            count = 0;
            
            /* 等待初始化完成 */
            while(SD_SendCmd(CMD1, 0, 0x01) != 0x00)
            {
                count++;
                if(count > 0xFFFE)
                {
                    return 5; // 初始化失败
                }
            }
        }
    }
    
    /* 设置SPI时钟为高速模式 */
    SPI1_SetSpeed(SPI_BaudRatePrescaler_4);
    
    return 0; // 初始化成功
}

/* 发送SD卡命令 */
uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
    uint8_t res;
    
    /* 等待SD卡准备就绪 */
    SD_WaitReady();
    
    /* 发送命令 */
    SD_CS_LOW();
    SPI1_ReadWriteByte(cmd | 0x40);
    SPI1_ReadWriteByte(arg >> 24);
    SPI1_ReadWriteByte(arg >> 16);
    SPI1_ReadWriteByte(arg >> 8);
    SPI1_ReadWriteByte(arg);
    SPI1_ReadWriteByte(crc);
    
    /* 等待响应 */
    if(cmd == CMD12)
    {
        SPI1_ReadWriteByte(0xFF); // 跳过停止命令后的一个字节
    }
    
    /* 等待响应 */
    uint8_t retry = 0;
    do
    {
        res = SPI1_ReadWriteByte(0xFF);
        retry++;
    } while((res & 0x80) && (retry < 200));
    
    return res;
}

/* 等待SD卡准备就绪 */
uint8_t SD_WaitReady(void)
{
    uint32_t timeout = 0;
    
    do
    {
        if(SPI1_ReadWriteByte(0xFF) == 0xFF)
        {
            return 0;
        }
        timeout++;
    } while(timeout < 0xFFFFFF);
    
    return 1;
}

/* 读取SD卡一个扇区 */
uint8_t SD_ReadDisk(uint8_t *buf, uint32_t sector, uint8_t cnt)
{
    uint8_t res;
    
    if(CardType == SD_TYPE_V2HC)
    {
        sector *= 512; // SDHC/SDXC卡使用字节地址
    }
    
    if(cnt == 1)
    {
        /* 读取单个块 */
        res = SD_SendCmd(CMD17, sector, 0x01);
        if(res == 0)
        {
            /* 等待数据开始标记 */
            if(!SD_RecvData(buf, 512))
            {
                res = 1;
            }
        }
    }
    else
    {
        /* 读取多个块 */
        res = SD_SendCmd(CMD18, sector, 0x01);
        do
        {
            if(!SD_RecvData(buf, 512))
            {
                res = 1;
                break;
            }
            buf += 512;
        } while(--cnt);
        
        /* 发送停止传输命令 */
        SD_SendCmd(CMD12, 0, 0x01);
    }
    
    SD_CS_HIGH();
    SPI1_ReadWriteByte(0xFF);
    return res;
}

/* 接收数据 */
uint8_t SD_RecvData(uint8_t *buf, uint16_t len)
{
    uint16_t i;
    uint8_t res;
    
    /* 等待数据开始标记 */
    uint16_t retry = 0;
    do
    {
        res = SPI1_ReadWriteByte(0xFF);
        retry++;
    } while((res == 0xFF) && (retry < 0xFFFF));
    
    if(res != 0xFE)
    {
        return 1; // 数据开始标记错误
    }
    
    /* 接收数据 */
    for(i = 0; i < len; i++)
    {
        buf[i] = SPI1_ReadWriteByte(0xFF);
    }
    
    /* 接收CRC */
    SPI1_ReadWriteByte(0xFF);
    SPI1_ReadWriteByte(0xFF);
    
    return 0;
}

/* 写入SD卡一个扇区 */
uint8_t SD_WriteDisk(const uint8_t *buf, uint32_t sector, uint8_t cnt)
{
    uint8_t res;
    
    if(CardType == SD_TYPE_V2HC)
    {
        sector *= 512; // SDHC/SDXC卡使用字节地址
    }
    
    if(cnt == 1)
    {
        /* 写入单个块 */
        res = SD_SendCmd(CMD24, sector, 0x01);
        if(res == 0)
        {
            /* 发送数据开始标记 */
            if(!SD_SendData(buf, 0xFE))
            {
                res = 1;
            }
        }
    }
    else
    {
        /* 写入多个块 */
        if(CardType != SD_TYPE_MMC)
        {
            SD_SendCmd(CMD55, 0, 0x01);
            SD_SendCmd(CMD23, cnt, 0x01);
        }
        
        res = SD_SendCmd(CMD25, sector, 0x01);
        if(res == 0)
        {
            do
            {
                if(!SD_SendData(buf, 0xFC))
                {
                    res = 1;
                    break;
                }
                buf += 512;
            } while(--cnt);
            
            /* 发送停止传输标记 */
            if(!SD_SendData(0, 0xFD))
            {
                res = 1;
            }
        }
    }
    
    SD_CS_HIGH();
    SPI1_ReadWriteByte(0xFF);
    return res;
}

/* 发送数据 */
uint8_t SD_SendData(const uint8_t *buf, uint8_t token)
{
    uint16_t i;
    uint8_t res;
    
    /* 等待SD卡准备就绪 */
    SD_WaitReady();
    
    /* 发送数据开始标记 */
    SPI1_ReadWriteByte(token);
    
    /* 发送数据 */
    if(token != 0xFD)
    {
        for(i = 0; i < 512; i++)
        {
            SPI1_ReadWriteByte(buf[i]);
        }
        
        /* 发送CRC */
        SPI1_ReadWriteByte(0xFF);
        SPI1_ReadWriteByte(0xFF);
        
        /* 获取数据响应 */
        res = SPI1_ReadWriteByte(0xFF);
        if((res & 0x1F) != 0x05)
        {
            return 1;
        }
    }
    
    /* 等待SD卡完成写入 */
    SD_WaitReady();
    return 0;
}

/* 设置SPI速度 */
void SPI1_SetSpeed(uint16_t Speed)
{
    SPI_Cmd(SPI1, DISABLE);
    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = Speed;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStructure);
    SPI_Cmd(SPI1, ENABLE);
}

3. 头文件 (spi_sd.h)

c 复制代码
#ifndef __SPI_SD_H
#define __SPI_SD_H

#include "stm32f10x.h"

/* SD卡类型定义 */
#define SD_TYPE_ERR     0x00
#define SD_TYPE_MMC     0x01
#define SD_TYPE_V1      0x02
#define SD_TYPE_V2      0x04
#define SD_TYPE_V2HC    0x06

/* SD卡命令定义 */
#define CMD0    0       // 复位SD卡
#define CMD1    1
#define CMD8    8       // 发送接口条件
#define CMD9    9       // 读取CSD数据
#define CMD10   10      // 读取CID数据
#define CMD12   12      // 停止数据传输
#define CMD16   16      // 设置块大小
#define CMD17   17      // 读取单个块
#define CMD18   18      // 读取多个块
#define CMD23   23      // 设置预擦除块数
#define CMD24   24      // 写入单个块
#define CMD25   25      // 写入多个块
#define CMD41   41      // 发送主机容量支持信息
#define CMD55   55      // 应用特定命令
#define CMD58   58      // 读取OCR寄存器
#define CMD59   59      // 设置CRC开/关
#define ACMD41  0x41    // ACMD41

/* SD卡片选控制 */
#define SD_CS_LOW()     GPIO_ResetBits(GPIOA, GPIO_Pin_4)
#define SD_CS_HIGH()    GPIO_SetBits(GPIOA, GPIO_Pin_4)

/* 全局变量 */
extern uint8_t CardType;

/* 函数声明 */
uint8_t SD_Initialize(void);
uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc);
uint8_t SD_WaitReady(void);
uint8_t SD_ReadDisk(uint8_t *buf, uint32_t sector, uint8_t cnt);
uint8_t SD_WriteDisk(const uint8_t *buf, uint32_t sector, uint8_t cnt);
uint8_t SD_RecvData(uint8_t *buf, uint16_t len);
uint8_t SD_SendData(const uint8_t *buf, uint8_t token);
void SPI1_Init(void);
uint8_t SPI1_ReadWriteByte(uint8_t TxData);
void SPI1_SetSpeed(uint16_t Speed);

#endif

4. FatFs磁盘接口 (diskio.c)

c 复制代码
#include "diskio.h"
#include "spi_sd.h"

/* 获取磁盘状态 */
DSTATUS disk_status(BYTE pdrv)
{
    return 0;
}

/* 初始化磁盘 */
DSTATUS disk_initialize(BYTE pdrv)
{
    if(SD_Initialize() == 0)
    {
        return 0;
    }
    return STA_NOINIT;
}

/* 读取扇区 */
DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
{
    if(SD_ReadDisk(buff, sector, count) == 0)
    {
        return RES_OK;
    }
    return RES_ERROR;
}

/* 写入扇区 */
DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
{
    if(SD_WriteDisk(buff, sector, count) == 0)
    {
        return RES_OK;
    }
    return RES_ERROR;
}

/* 其他控制命令 */
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
{
    switch(cmd)
    {
        case CTRL_SYNC:
            return RES_OK;
            
        case GET_SECTOR_COUNT:
            *(DWORD*)buff = 1024*1024; // 假设SD卡容量为512MB
            return RES_OK;
            
        case GET_SECTOR_SIZE:
            *(WORD*)buff = 512;
            return RES_OK;
            
        case GET_BLOCK_SIZE:
            *(DWORD*)buff = 8;
            return RES_OK;
            
        default:
            return RES_PARERR;
    }
}

5. 头文件 (diskio.h)

c 复制代码
#ifndef _DISKIO
#define _DISKIO

#include "integer.h"

/* 状态码 */
#define STA_NOINIT      0x01    /* 驱动器未初始化 */
#define STA_NODISK      0x02    /* 无介质 */
#define STA_PROTECT     0x04    /* 写保护 */

/* 命令码 */
#define CTRL_SYNC       0       /* 完成挂起的写入过程 */
#define GET_SECTOR_COUNT 1      /* 获取扇区数 */
#define GET_SECTOR_SIZE 2       /* 获取扇区大小 */
#define GET_BLOCK_SIZE  3       /* 获取擦除块大小 */
#define CTRL_POWER      4       /* 控制电源状态 */
#define CTRL_LOCK       5       /* 锁定/解锁介质移除 */
#define CTRL_EJECT      6       /* 弹出介质 */
#define MMC_GET_TYPE    10      /* 获取卡类型 */
#define MMC_GET_CSD     11      /* 读取CSD */
#define MMC_GET_CID     12      /* 读取CID */
#define MMC_GET_OCR     13      /* 读取OCR */
#define MMC_GET_SDSTAT  14      /* 读取SD状态 */

/* 结果码 */
typedef enum {
    RES_OK = 0,         /* 成功 */
    RES_ERROR,          /* 读写错误 */
    RES_WRPRT,          /* 写保护 */
    RES_NOTRDY,         /* 设备未就绪 */
    RES_PARERR          /* 无效参数 */
} DRESULT;

/* 函数声明 */
DSTATUS disk_initialize(BYTE pdrv);
DSTATUS disk_status(BYTE pdrv);
DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff);

#endif

使用说明

  1. 将上述代码分别保存到对应的文件中
  2. 创建一个新的STM32工程,添加这些文件
  3. 需要包含FatFs文件系统(可以从官网http://elm-chan.org/fsw/ff/00index_e.html下载)
  4. 编译并下载到STM32F103开发板
  5. 连接SD卡模块
  6. 通过串口调试工具(如Putty)查看输出结果

功能说明

这个程序实现了以下功能:

  1. 初始化SD卡(支持SDv1, SDv2和SDHC卡)
  2. 挂载FAT文件系统
  3. 创建/打开文件"test.txt"
  4. 向文件写入两行文本
  5. 从文件读取内容并通过串口输出
  6. 关闭文件并卸载文件系统

注意事项

  1. 确保SD卡已格式化为FAT16或FAT32文件系统
  2. 如果使用不同的SPI引脚,需要修改GPIO配置
  3. SD卡工作电压为3.3V,不要使用5V电压
  4. 如果读写失败,可以尝试降低SPI时钟速度

希望这个完整的SD卡读写程序对你有帮助!

相关推荐
芯岭技术2 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt3 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘3 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang3 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n5 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件
慕尘9 小时前
Clion配置51单片机开发环境
单片机
良许Linux11 小时前
32岁入行STM32迟吗?
stm32·单片机·嵌入式硬件
m0_4666077011 小时前
【STM32CubeMX】ST官网MCU固件库下载及安装
stm32·单片机·嵌入式硬件
Wallace Zhang15 小时前
STM32F103_Bootloader程序开发11 - 实现 App 安全跳转至 Bootloader
stm32·嵌入式硬件·安全
GodKK老神灭15 小时前
STM32 CCR寄存器
stm32·单片机·嵌入式硬件