Python语音机器人控制STM32开发流程

一:Python语音机器人控制STM32开发流程

1. 上位机的开发用到的工具:

1.1. Python3.9
1.2. Pycahrm社区版

2. 上位机开发用到的模块:

 
import sys # 使用sys来退出程序
import webbrowser # 用来模拟提交模拟浏览器提交
import requests # 获取API请求
import json # 使用JSON把获取的数据专成Python可读的Json代码
import playsound # 播放声音
import os # 使用os.remove移除录音文件
import win32api # 执行绝对路径的程序
import pyaudio # 录音
import wave # 转换声道
import threading # 线程
from aip import AipSpeech # 百度语音模块
from pynput.keyboard import Listener # 键盘监听
import serial # 与STM32串口通信模块

 

3. 下位机开发用到的工具:

3.1. Keil MDK
3.2. MCUISP

4. 使用Keil MDK开发STM32的核心代码:

main.c文件


// 头部引入"led.h"
// 头部引入"sys.h"
// 头部引入"usart.h"
int main(void)
{
u8 t;
u8 len;
u16 times=0;
uart_init(9600); //串口初始化为9600
LED_Init(); //³初始化与LED连接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//获取上位机发送的字符串的长度
if(len==3)

LED0=!LED0;
LED1=!LED1;
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口一发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
USART_RX_STA=0;

}else
{
times++;
if(times%30==0)LED0=!LED0;
}
}

}

led.h文件


//#ifndef __LED_H
//#define __LED_H
//#include "sys.h"
//#define LED0 PAout(8) // PA8
//#define LED1 PDout(2) // PD2
void LED_Init(void);//初始化
//#endif

led.c文件


//#include "led.h"
void LED_Init() //LED的初始化函数
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体,配置IO口的状态

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE); // PA,PD端口时钟
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8; //led0-->PA.8端口配置
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//配置推挽输出模式,
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//设置输出速度50MHz
GPIO_SetBits(GPIOA,GPIO_Pin_8);//输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //LED1-->PD.2 端口配置 推挽输出
GPIO_Init(GPIOD, &GPIO_InitStructure); //设置输出速度50MHz
GPIO_SetBits(GPIOD,GPIO_Pin_2); //PD.2 输出高

}

usart.h文件


#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收

extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节, 末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
//串口中断接收
void uart_init(u32 bound);
#endif

usart.c文件


//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;

};

FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif



#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记

void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟

//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10

//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器

//USART 初始化设置

USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1

}

void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据

if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
#endif

sys.h文件


//IO口操作宏定义
#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))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C

#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08

//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入

#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入

#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入

#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入

#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入

#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入

#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入

//以下为汇编函数
void WFI_SET(void); //执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void); //开启所有中断
void MSR_MSP(u32 addr); //设置堆栈地址

#endif

sys.c文件


//采用如下方法实现执行汇编指令WFI
void WFI_SET(void)
{
__ASM volatile("wfi");
}
//关闭所有中断
void INTX_DISABLE(void)
{
__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
__ASM volatile("cpsie i");
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}

Python里的代码我贴一部分串口通信的:

 
语音识别核心技术
代码部分:
count = 1   # 计数
run = False
# pyaudio参数
CHUNK = 1024    # 数据包或者数据片段
FORMAT = pyaudio.paInt16    # pyaudio.paInt16表示我们使用量化位数 16位来进行录音
CHANNELS = 1    # 声道,1为单声道,2为双声道
RATE = 16000    # 采样率,每秒钟16000次
# RECORD_SECONDS = 5  # 录音时间
WAVE_OUTPUT_FILENAME = "output.wav"     # 保存录音文件名
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)     # 百度语音接口
// 在这里加载之前配置的Pyaudio参数来设置
def recoder():
    _frames = [] // 定义空数组
    global run // 把之前的局部变量提升为全局变量
    p = pyaudio.PyAudio()
    stream = p.open(channels=CHANNELS, // 声道
                    format=FORMAT, // 量化位数16来进行录音
                    rate=RATE, // 采样率
                    input=True, // 输入为真
                    frames_per_buffer=CHUNK) // 数据包
    while run: // 标识符
        data = stream.read(CHUNK) // 读取数据包
        _frames.append(data) 添加到之前设置的数组里
// 在这里把对象都关闭,以免浪费资源
    stream.stop_stream()
    stream.close()
    p.terminate()
//  创建wave音频,并写入数据,就不一一写注释了
    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(p.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(_frames))
    wf.close()
# 读取录音文件字节码
def get_file_content(filePath):
    with open(filePath, 'rb') as fp:
        return fp.read()
# 识别录音,语音转文字
def lic():
    res = client.asr(get_file_content(WAVE_OUTPUT_FILENAME), 'wav', 16000, {
        'dev_pid': 1537, // 这里有必要注释下:
                        //1537  普通话(纯中文识别)  
                        //1737  英语
                        //1637  粤语  
                        //1837  四川话 
                        //1936  普通话远场
    })
    text = res['result'][0]
    # print(text)
    return text
# 语音合成
def speak_(text):
// 这里参数说明下:test:合成的文本,zh:固定语音模式,客户端模式:默认1,
    result = client.synthesis(text, 'zh', 1, {
        'vol': 5, // 音量0-15
        'per': 0, // 基础语音库可选:0,1,3,4
        'spd': 5, // 语速0-15
        'pit': 5 //  音调0-15
    })
    # 识别正确返回语音二进制 错误则返回dict 参照下面错误码
    if not isinstance(result, dict):
        with open('audio.mp3', 'wb') as f:
            f.write(result)
        playsound.playsound('audio.mp3')
        os.remove('audio.mp3')
调用speek_()函数就可以实现语音说话了

最后。使用MCUISP进行烧录c写的编译后的.hex程序到单片机里即可,下来使用我们的Python程序里的serial模块的write对我们的程序进行发送数据,当下位机接收到三个字符的字符串时会先点亮led灯,再发送一次底层读取led状态然后反转。即实现开关led灯的功能

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: 1. 首先需要在stm32f中配置串口通信模块,包括波特率、数据位、停止位、校验位等参数。 2. 在python中使用pyserial库进行串口通信,需要指定串口号、波特率等参数。 3. 在python中发送数据到stm32f,可以使用serial.write()函数,将数据以字节形式发送到串口。 4. 在stm32f中接收数据,可以使用串口中断或者轮询方式读取串口接收缓冲区中的数据。 5. 在stm32f中对接收到的数据进行处理,可以根据协议进行解析,例如将数据转换成数字或者字符串等格式。 6. 在stm32f中根据接收到的数据进行相应的控制操作,例如控制LED灯的亮灭、驱动电机等。 ### 回答2: Python串口通信控制STM32F是一种常见的应用场景,主要是通过将PythonSTM32F进行连接,实现实时控制和数据传输。下面详细介绍一下Python串口通信控制STM32F的实现步骤。 第一步,配置STM32F的串口参数,通常是使用STM32CubeMX进行配置,并生成对应的代码。在生成的代码中,可以找到STM32F的串口接收和发送函数的代码,这些函数将用于接收和发送数据。 第二步,配置Python的串口参数。Python串口通信控制STM32F通常使用pyserial库实现,pyserial是Python的一个串口通信库,可以使用pip进行安装。在使用pyserial时,需要指定串口的参数,例如波特率、数据位、停止位、校验位等。 第三步,编写Python控制程序。编写Python程序实现与STM32F的串口通信和数据传输。具体来说,需要在Python程序中设置串口参数,建立串口,接收STM32F发送过来的数据,对数据进行处理,然后发送控制命令给STM32F。 第四步,编写STM32F程序。编写STM32F程序实现接收Python发送的数据,根据数据进行控制和处理,然后将处理结果返回给Python。 第五步,测试程序功能。在将PythonSTM32F进行连接后,需要进行测试程序功能,确保数据传输和控制命令的正确性。可以使用Python的串口测试工具对程序进行测试,确保正确性和稳定性。 总之,Python串口通信控制STM32F是一种非常常见的应用场景,在工业自动化和机器人控制等领域具有广泛的应用前景。需要掌握一定的PythonSTM32F编程技能,以及串口通信的知识,才能实现成功。 ### 回答3: 最近,我在研究如何使用Python控制STM32F的串口通信。在这个过程中,我需要先了解一些基本的知识,如何设置串口参数和如何发送/接收数据。 首先,我下载了PySerial模块,这是一个用于串口通信的Python库。在使用这个库之前,我需要先了解一些常用的串口参数,如波特率,数据位,停止位和校验位。这些参数是设置串口通信的必要条件,如果不设置或者设置错误,将会导致通信失败。 使用PySerial库时,首先需要通过Serial类定义串口对象,并通过设置参数来初始化它。例如,下面的代码设置了波特率为9600,数据位为8位,无校验位和一个停止位。我们需要替换端口参数,'COM4'是windows系统下的串口端口。 ```python import serial ser = serial.Serial("COM4", 9600, 8, 'N', 1) ``` 设置完参数后,我们可以开始发送数据。通过串口对象的write函数,我们可以将需要发送的数据发送到串口上。例如,下面的代码将字符串“Hello World”发送到串口上: ```python ser.write(b’Hello World’) ``` 接收串口数据也是类似的。我们可以通过串口对象的read函数读取串口上的数据。例如,下面的代码将从串口上接收10个字节的数据,并将它们打印出来: ```python data = ser.read(10) print(data) ``` 最后,控制STM32F的串口通信需要在MCU的代码中加入串口接收和发送函数。在MCU的代码中,我们可以使用HAL库的函数来实现串口通信。例如,下面的代码设置了波特率为9600,数据位为8位,无校验位和一个停止位。同时,串口接收和发送函数也已经定义好了。 ```c #include “stm32f4xx_hal.h” UART_HandleTypeDef huart2; void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 9600; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart) { if (huart == &huart2) { // 进行串口数据接收处理 } } void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart) { if (huart == &huart2) { // 进行串口数据发送处理 } } void UART_SendData(UART_HandleTypeDef* huart, uint8_t *pData, uint16_t Size) { HAL_UART_Transmit_IT(huart, pData, Size); } void UART_ReceiveData(UART_HandleTypeDef* huart, uint8_t *pData, uint16_t Size) { HAL_UART_Receive_IT(huart, pData, Size); } ``` 通过上述的代码和Python串口通信代码的组合,我们可以实现控制STM32F的串口通信。比如,我们可以通过串口发送变量来控制STM32F的LED灯闪烁。具体做法是在MCU代码中加入LED控制函数,不断读取串口数据并根据接收到的数据改变LED灯的状态。而在Python代码中,则可以发送控制命令来控制LED灯的闪烁。这样,我们便可以用Python控制STM32F的功能,也可以通过PythonSTM32F之间的串口通信来实现数据的传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唯一_1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值