# 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卡读写程序对你有帮助!

相关推荐
知识噬元兽1 小时前
【工具使用】STM32CubeMX-FreeRTOS操作系统-信号标志、互斥锁、信号量篇
stm32·单片机·嵌入式硬件
Flag- L2 小时前
STM32标准库-TIM定时器
stm32·单片机·嵌入式硬件
2301_775602383 小时前
STM32什么是寄存器
stm32·单片机·嵌入式硬件
GenCoder5 小时前
Keil开发STM32生成hex文件/bin文件
stm32·bin文件生成·keil开发
憧憬一下6 小时前
FreeRTOS任务之深入篇
stm32·嵌入式·freertos
双古6 小时前
步进电机调试记录(先让我的步进电机转起来)
单片机·嵌入式硬件
lixzest6 小时前
STM32开发中,线程启动异常问题排查简述
stm32·嵌入式硬件
程序员JerrySUN7 小时前
全面理解 Linux 内核性能问题:分类、实战与调优策略
java·linux·运维·服务器·单片机
学习噢学个屁7 小时前
基于STM32语音识别柔光台灯
c语言·stm32·单片机·嵌入式硬件·语音识别
欢乐熊嵌入式编程9 小时前
欢乐熊大话蓝牙知识14:用 STM32 或 EFR32 实现 BLE 通信模块:从0到蓝牙,你也能搞!
stm32·单片机·嵌入式硬件