STM32实战:基于STM32F103的智能语音识别系统(LD3320)

文章目录

一、项目概述

本项目基于STM32F103C8T6 单片机+LD3320语音识别模块实现非特定人语音识别功能,无需训练、无需外接Flash,可直接识别预设的中文语音指令,实现语音控制LED、继电器、蜂鸣器等外设,适合零基础小白入门智能语音控制项目。

核心硬件

  1. 主控:STM32F103C8T6最小系统板
  2. 语音模块:LD3320(集成ASR语音识别,支持50条指令)
  3. 外设:LED灯、杜邦线、USB转TTL模块、5V电源
  4. 辅助:面包板

核心功能

  1. 识别预设语音指令(开灯、关灯、打开风扇、关闭风扇等)
  2. 串口打印识别结果
  3. 根据识别结果控制外设输出
  4. 模块状态实时反馈

二、硬件接线

LD3320模块与STM32F103采用SPI通信,接线严格按照下表操作,避免接错烧毁模块:

STM32F103引脚 LD3320模块引脚 功能说明
3.3V VCC 模块供电(必须3.3V,严禁接5V)
GND GND 公共地
PA4 CS SPI片选
PA5 SCK SPI时钟
PA7 MOSI SPI主机发送
PA6 MISO SPI主机接收
PB0 RST 模块复位
PB1 IRQ 模块中断(识别成功触发)

外设接线

  1. LED正极 → PA0
  2. LED负极 → GND(串联220Ω电阻)

三、开发环境准备

  1. 软件:Keil5 MDK-ARM(安装STM32F103芯片包)
  2. 固件库:STM32F10x标准库(3.5版本)
  3. 烧录工具:FlyMcu/ST-Link
  4. 串口调试助手:XCOM

四、Mermaid系统流程图



开灯指令
关灯指令
其他指令
系统上电初始化
STM32初始化

GPIO/SPI/串口
LD3320模块复位
LD3320初始化

写入识别指令
开启语音识别模式
检测到IRQ中断?
读取识别结果
串口打印识别指令
指令匹配
PA0输出高电平 点亮LED
PA0输出低电平 熄灭LED
执行对应外设操作
清除中断标志

五、工程创建与代码实现

本项目基于STM32标准库开发,所有代码文件直接复制到工程对应目录即可使用。

1. 创建工程目录结构

复制代码
STM32_LD3320
├─ User
│  ├─ main.c                 // 主函数
│  ├─ ld3320.c               // LD3320驱动代码
│  ├─ ld3320.h               // LD3320头文件
│  ├─ spi.c                  // SPI驱动
│  ├─ spi.h                  // SPI头文件
│  └─ sys.h                  // 系统配置
├─ STM32F10x_StdPeriph_Driver // 标准库文件
└─ System
   └─ system_stm32f10x.c     // 系统时钟

2. 文件1:sys.h(系统宏定义)

c 复制代码
#ifndef __SYS_H
#define __SYS_H

#include "stm32f10x.h"

// 位带操作
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

#define GPIOA_ODR_Addr    (GPIOA_BASE+12)
#define GPIOB_ODR_Addr    (GPIOB_BASE+12)
#define GPIOC_ODR_Addr    (GPIOC_BASE+12)

#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)
#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)

#endif

3. 文件2:spi.h(SPI引脚定义)

c 复制代码
#ifndef __SPI_H
#define __SPI_H

#include "sys.h"

// SPI引脚定义
#define LD3320_SPI_CS    PAout(4)
#define LD3320_SPI_SCK   PAout(5)
#define LD3320_SPI_MOSI  PAout(7)
#define LD3320_SPI_MISO  PAin(6)

void SPI1_Init(void);
u8 SPI1_ReadWriteByte(u8 TxData);

#endif

4. 文件3:spi.c(SPI驱动实现)

c 复制代码
#include "spi.h"
#include "sys.h"

// SPI1初始化 主机模式
void SPI1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    // PA4/5/7 推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // PA6 上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // SPI配置
    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_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);
}

// SPI读写一个字节
u8 SPI1_ReadWriteByte(u8 TxData)
{
    u8 retry = 0;
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
    {
        retry++;
        if(retry > 200) return 0;
    }
    SPI_I2S_SendData(SPI1, TxData);
    retry = 0;
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
    {
        retry++;
        if(retry > 200) return 0;
    }
    return SPI_I2S_ReceiveData(SPI1);
}

5. 文件4:ld3320.h(LD3320驱动头文件)

c 复制代码
#ifndef __LD3320_H
#define __LD3320_H

#include "sys.h"
#include "spi.h"

// LD3320控制引脚
#define LD3320_RST_LOW     PBout(0) = 0
#define LD3320_RST_HIGH    PBout(0) = 1
#define LD3320_IRQ         PBin(1)

// 函数声明
void LD3320_Init(void);
void LD3320_WriteReg(u8 addr, u8 data);
u8 LD3320_ReadReg(u8 addr);
void LD3320_Reset(void);
void LD3320_Clear(void);
u8 LD3320_ReadResult(void);
void LD3320_AddCommand(u8 num, char *str);
void LD3320_Start(void);

#endif

6. 文件5:ld3320.c(LD3320核心驱动)

c 复制代码
#include "ld3320.h"
#include "delay.h"
#include "usart.h"

// LD3320写入寄存器
void LD3320_WriteReg(u8 addr, u8 data)
{
    LD3320_SPI_CS = 0;
    SPI1_ReadWriteByte(0x00);
    SPI1_ReadWriteByte(addr);
    SPI1_ReadWriteByte(data);
    LD3320_SPI_CS = 1;
    delay_ms(1);
}

// LD3320读取寄存器
u8 LD3320_ReadReg(u8 addr)
{
    u8 temp;
    LD3320_SPI_CS = 0;
    SPI1_ReadWriteByte(0x01);
    SPI1_ReadWriteByte(addr);
    temp = SPI1_ReadWriteByte(0xFF);
    LD3320_SPI_CS = 1;
    return temp;
}

// LD3320硬件复位
void LD3320_Reset(void)
{
    LD3320_RST_LOW;
    delay_ms(100);
    LD3320_RST_HIGH;
    delay_ms(100);
}

// 清除LD3320状态
void LD3320_Clear(void)
{
    LD3320_WriteReg(0x42, 0x55);
    delay_ms(20);
}

// 初始化LD3320
void LD3320_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

    // PB0 复位输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // PB1 中断输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 外部中断配置
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

    EXTI_InitStructure.EXTI_Line = EXTI_Line1;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    LD3320_Reset();
    SPI1_Init();

    LD3320_WriteReg(0x06, 0x01);
    delay_ms(30);
    LD3320_WriteReg(0x06, 0x00);
    delay_ms(30);

    LD3320_WriteReg(0x17, 0x35);
    LD3320_WriteReg(0x18, 0xFF);
    LD3320_WriteReg(0x19, 0xFF);
    LD3320_WriteReg(0x1A, 0xFF);
    LD3320_WriteReg(0x1B, 0xFF);
    LD3320_WriteReg(0x1C, 0xFF);
    LD3320_WriteReg(0x1E, 0x03);
    LD3320_WriteReg(0x1F, 0x1F);
    LD3320_WriteReg(0x20, 0x03);
    LD3320_WriteReg(0x21, 0x1F);
    LD3320_WriteReg(0x22, 0x03);
    LD3320_WriteReg(0x23, 0x1F);
    LD3320_WriteReg(0x24, 0x03);
    LD3320_WriteReg(0x25, 0x1F);
    LD3320_WriteReg(0x26, 0x03);
    LD3320_WriteReg(0x27, 0x1F);
    LD3320_WriteReg(0x28, 0x03);
    LD3320_WriteReg(0x29, 0x1F);
    LD3320_WriteReg(0x2A, 0x03);
    LD3320_WriteReg(0x2B, 0x1F);
    LD3320_WriteReg(0x3E, 0x03);
    LD3320_WriteReg(0x3F, 0x1F);
    LD3320_WriteReg(0x41, 0x00);
    LD3320_WriteReg(0x42, 0x00);
}

// 添加语音指令
void LD3320_AddCommand(u8 num, char *str)
{
    u8 i;
    LD3320_WriteReg(0x40, num);
    for(i = 0; str[i] != '\0'; i++)
    {
        LD3320_WriteReg(0x41, str[i]);
    }
    LD3320_WriteReg(0x41, 0x00);
    delay_ms(20);
}

// 启动识别
void LD3320_Start(void)
{
    LD3320_WriteReg(0x29, 0x36);
    LD3320_WriteReg(0x2A, 0x01);
    LD3320_WriteReg(0x2B, 0x1F);
    LD3320_WriteReg(0x3E, 0x03);
    LD3320_WriteReg(0x3F, 0x1F);
    LD3320_WriteReg(0x41, 0x00);
    LD3320_WriteReg(0x42, 0x00);
    LD3320_WriteReg(0x37, 0x01);
    delay_ms(10);
}

// 读取识别结果
u8 LD3320_ReadResult(void)
{
    u8 res;
    res = LD3320_ReadReg(0x4B);
    return res;
}

// 中断服务函数
void EXTI1_IRQHandler(void)
{
    u8 num;
    if(EXTI_GetITStatus(EXTI_Line1) != RESET)
    {
        num = LD3320_ReadResult();
        printf("识别结果:%d\r\n", num);

        // 根据识别结果控制外设
        switch(num)
        {
            case 1:
                PAout(0) = 1;  // 开灯
                printf("指令:打开LED灯\r\n");
                break;
            case 2:
                PAout(0) = 0;  // 关灯
                printf("指令:关闭LED灯\r\n");
                break;
            case 3:
                printf("指令:打开风扇\r\n");
                break;
            case 4:
                printf("指令:关闭风扇\r\n");
                break;
            default:
                printf("未识别指令\r\n");
                break;
        }

        LD3320_Clear();
        LD3320_Start();
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}

7. 文件6:usart.h(串口头文件)

c 复制代码
#ifndef __USART_H
#define __USART_H

#include "sys.h"

void uart1_init(u32 bound);
void Usart1_SendByte(u8 data);
void Usart1_SendString(char *str);

#endif

8. 文件7:usart.c(串口驱动)

c 复制代码
#include "usart.h"
#include "sys.h"
#include "delay.h"

// 串口1初始化
void uart1_init(u32 bound)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    // PA9 发送
    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);

    // PA10 接收
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 串口配置
    USART_InitStructure.USART_BaudRate = bound;
    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);

    // 中断配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    USART_Cmd(USART1, ENABLE);
}

// 发送字节
void Usart1_SendByte(u8 data)
{
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, data);
}

// 发送字符串
void Usart1_SendString(char *str)
{
    u8 i = 0;
    while(str[i] != '\0')
    {
        Usart1_SendByte(str[i]);
        i++;
    }
}

// 重定向printf
int fputc(int ch, FILE *f)
{
    while((USART1->SR & 0X40) == 0);
    USART1->DR = (u8) ch;
    return ch;
}

9. 文件10:main.c(主函数)

c 复制代码
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "spi.h"
#include "ld3320.h"

// LED初始化
void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    PAout(0) = 0; // 默认关灯
}

int main(void)
{
    delay_init();       // 延时初始化
    uart1_init(9600);   // 串口初始化 波特率9600
    LED_Init();         // LED初始化
    LD3320_Init();      // LD3320初始化

    printf("LD3320语音识别系统初始化完成\r\n");
    printf("开始添加语音指令...\r\n");

    // 添加语音指令 编号1~4
    LD3320_AddCommand(1, "打开灯光");
    LD3320_AddCommand(2, "关闭灯光");
    LD3320_AddCommand(3, "打开风扇");
    LD3320_AddCommand(4, "关闭风扇");

    printf("指令添加完成,启动识别\r\n");
    LD3320_Start(); // 启动语音识别

    while(1)
    {
        // 系统循环等待中断触发
    }
}

六、工程配置步骤

  1. 打开Keil5,创建新工程,选择芯片STM32F103C8T6
  2. 添加标准库文件到工程
  3. 将上述所有代码文件添加到工程对应分组
  4. 打开魔术棒→Target,设置外部晶振为8MHz
  5. 打开魔术棒→Output,勾选Create HEX File
  6. 编译工程,无报错则生成HEX文件

七、程序烧录与测试

  1. 使用USB转TTL连接STM32下载口(TX-RX、RX-TX、GND-GND、3.3V-3.3V)
  2. 打开FlyMcu,选择生成的HEX文件,点击开始编程
  3. 烧录完成后,打开串口调试助手,波特率9600
  4. 给系统上电,串口打印初始化信息
  5. 对着LD3320麦克风说出指令:
    • 打开灯光 → LED点亮,串口打印识别结果
    • 关闭灯光 → LED熄灭,串口打印识别结果

八、常见问题解决

  1. 模块无响应

    • 检查3.3V供电,严禁接5V
    • 检查SPI接线是否正确
    • 复位引脚是否正常输出高电平
  2. 识别率低

    • 远离噪音环境
    • 说话距离模块5-20cm
    • 指令使用简洁的中文词汇
  3. 串口乱码

    • 检查波特率是否为9600
    • 检查系统时钟配置是否正确

九、项目扩展

  1. 增加继电器模块,实现语音控制家电
  2. 添加蜂鸣器,识别成功后提示音
  3. 扩展指令数量,最多支持50条
  4. 结合蓝牙模块,实现远程语音控制
相关推荐
Jason_zhao_MR1 小时前
RK3576 MIPI Camera ISP调试:客观标定与环境准备(上)
人工智能·嵌入式硬件·机器人·嵌入式·接口隔离原则
深圳市晶科鑫实业有限公司1 小时前
RTC模块vs. 32.768KHz晶振:深度对比与选型指南
stm32·单片机·嵌入式硬件·实时音视频·rtc
weixin_452077642 小时前
VS code 使用STM32CubelDE for Visual Studio Code环境,如何配置CMakeLists.txt新增其他.C文件路径
c语言·vscode·stm32
jghhh012 小时前
STM32F103 驱动 BMP180 气压传感器源码
stm32·单片机·嵌入式硬件
俊基科技2 小时前
A-29 双数字麦阵列回音消除模块:超大音量场景下的全功能语音处理解决方案
语音识别·ai降噪·回音消除·语音模组·语音处理模块
踏着七彩祥云的小丑2 小时前
嵌入式测试学习第 5 天:电阻分类、色环电阻读数、贴片电阻
单片机·嵌入式硬件
代码又报错la2 小时前
5、电源保护板
单片机·嵌入式硬件
leo__52010 小时前
STM32 MAX30102 心率血氧测量代码
stm32·单片机·嵌入式硬件
金色光环14 小时前
【DSP学习】DSP28335 点亮LED
嵌入式硬件·学习·dsp开发