stm32f4 声音传感器采集
最近在做基于stm32的声音采集与频谱分析,试用了几款声音传感器,今天分享一款名为SM7901B的声音传感器数据采集代码,同样使用SM7901B的友友可以参考,求赞~比♥
这款声音传感器通过 RS485 MODBUS-RTU 数据通信
下面是数据手册
可以看出,串口应该给声音传感器发送
u8 hex_data[BUFFER_SIZE] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A}; //需要发送的数据
stm32每发送给声音传感器一条数据,声音传感器就发送给stm32一条数据,其中寄存器地址 0001,的2位16进制数据就是声音数据
所以如果想只采集到寄存器地址 0001,的2位16进制数据,需要用到2个串口,一个串口需要连接串口-RS485模块,给声音传感器循环发送{0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A},我在这使用的是串口2
另一个串口(我使用的是串口1)需要把串口2接收的数据计数,只取寄存器地址 0001,的2位16进制数据,即声音数据,然后把16进制数据转换为十进制,发送到串口助手方便查看,下面上代码:
首先是串口2代码:
#include "uart2.h"
#include "string.h"
#include "stdlib.h"
#include "led.h"
#include "beep.h"
#include "uart1.h"
#include "stdio.h"
#include "stddef.h"
#include "string.h"//接收缓存区
u8 RS485_receive_str[16]; //接收缓冲,最大128个字节.
u8 uart_byte_count2=0; //接收到的数据长度u8 byte4, byte5, byte6, byte7;volatile u8 new_data_available = 0; //标志位 用来指示发送//初始化IO 串口2 bound:波特率
void uart2_init(u32 bound)
{ GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟//串口2引脚复用映射GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //GPIOA2复用为USART2GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); //GPIOA3复用为USART2//USART2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //GPIOA2与GPIOA3GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA2,PA3//PG8推挽输出,485模式控制 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOG6GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOG,&GPIO_InitStructure); //初始化PG8RS485_TX_EN=0; //初始化默认为接收模式 //USART2 初始化设置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(USART2, &USART_InitStructure); //初始化串口2USART_Cmd(USART2, ENABLE); //使能串口 2 USART_ClearFlag(USART2, USART_FLAG_TC);USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启接受中断//Usart2 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}//串口2接收中断服务函数
void USART2_IRQHandler(void)
{u8 rec_data;RS485_TX_EN=0; //初始化默认为接收模式if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)//接收到数据{ USART_ClearITPendingBit(USART2, USART_IT_RXNE);rec_data =(u8)USART_ReceiveData(USART2); //(USART2->DR) 读取接收到的数据uart_byte_count2++; RS485_receive_str[uart_byte_count2-1] = rec_data;if(uart_byte_count2>=16){byte4 = RS485_receive_str[10];byte5 = RS485_receive_str[11];byte6 = RS485_receive_str[12];byte7 = RS485_receive_str[13]; new_data_available = 1; //标志位=1 有数据要发送if(new_data_available == 0){memset(RS485_receive_str, 0x00, sizeof(RS485_receive_str));//串口2数组清零uart_byte_count2 = 0;//计数器归零}}} } //串口2的发送void RS485_Send_Data(u8 *buf,u8 len)
{u8 t;RS485_TX_EN=1; //设置为发送模式 for(t=0;t<len;t++) //循环发送数据{while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送结束 USART_SendData(USART2,buf[t]); //发送数据LED0=0;} while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送结束 RS485_TX_EN=0; //发送完设置为接收模式 LED0 = (LED0 == 0) ? 1 : 0;//发完闪烁一下
}
我上面的代码接收了4位,其实只需要用到两位
然后是串口1代码:
#include "uart1.h"
#include "string.h"
#include "stdlib.h"
#include "led.h"
#include "uart2.h"
#include "stdio.h"
#include "stddef.h"
#include "string.h"u8 receive_str[4]; //接收缓存数组,最大USART_REC_LEN个字节
u8 uart_byte_count1=0;void uart1_init(u32 bound)
{ //GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟 //串口1对应引脚复用映射GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1//USART1端口配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10//USART1 初始化设置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_Cmd(USART1, ENABLE); //使能串口1 USART_ClearFlag(USART1, USART_FLAG_TC);USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启相关中断//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}//串口1中断服务程序
void USART1_IRQHandler(void)
{u8 rec_data;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 {rec_data =(u8)USART_ReceiveData(USART1); //(USART1->DR) 读取接收到的数据if((uart_byte_count1>0)&&(uart_byte_count1<=512)){receive_str[uart_byte_count1-1]=rec_data;uart_byte_count1++;} }
} //串口1发送void uart1_send_num (u8 *buf,u8 len)
{u8 i; for(i=0;i<len;i++) //循环发送数据{while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); //等待发送结束 USART_SendData(USART1,buf[i]); //发送数据LED2 = (LED2 == 0) ? 1 : 0;//发完闪烁一下} while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); //等待发送结束 }
最后是main代码:
#include "led.h"
#include "beep.h"
#include "lcd.h"
#include "key.h"
#include "uart1.h"
#include "uart2.h"
#include "stdio.h"
#include "stddef.h"
#include "string.h"#define BUFFER_SIZE 8 // 发送的数据长度
u8 hex_data[BUFFER_SIZE] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A}; //需要发送的数据
u8 data[4];
u8 i;
u16 x_data;
u16 y_data;
s16 x_signed;
s16 y_signed;
float value;
char send_buffer[50];
char send_alarm[10];int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2delay_init(); //初始化延时函数LED_Init(); //初始化LED BEEP_Init(); //初始化蜂鸣器LCD_Init(); //LCD初始化 KEY_Init(); //按键初始化 LED1=0;uart2_init(115200);//uart1_init(115200);//while(1){RS485_Send_Data(hex_data, BUFFER_SIZE);//串口2发送delay_ms(100);if (new_data_available == 1){data[0]=byte4;data[1]=byte5;x_data = ((u16)byte4 << 8) | byte5;//拼接x_signed = 0;if ((byte4 & 0x80) != 0) // 处理x轴{x_data = (~x_data) + 1; // x_signed = -x_data;}else{x_signed = x_data;}//负数if (x_signed > 60000){x_signed -= 65536;}value = (float)x_signed / 10.0;snprintf(send_buffer, sizeof(send_buffer), "channel:%.2f,\n", value);uart1_send_num((u8 *)send_buffer, strlen(send_buffer));//串口1发送dB//uart1_send_num(data, 2);//串口1发送new_data_available = 0; memset(RS485_receive_str, 0x00, sizeof(RS485_receive_str));uart_byte_count2 = 0;delay_ms(100); }}
}
完整代码我上传到资源,下面分享一下测试结果,个人感觉这款传感器测试的结果我感觉很一般。
开始测试,红色为环境音,绿色是敲击桌面,区别不大
查找后,是声音采集传感器的收音口覆盖的黑色收音布隔绝了声音,把收音口黑布撕开
对着收音口输出音频
红色为一种音频,绿色为一种音频信号,蓝色是无音频输出时的信号,这样才有较大区别