加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 問題由來
    • 聲音傳感器模塊
    •  
    • 原理圖
    •  
    • 硬件連接
    • STM32進(jìn)行AD轉(zhuǎn)換步驟
    •  
    • 獲取聲音傳感器的輸出值
    • ADC結(jié)果驗(yàn)證
    • 通訊協(xié)議
    • Modbus協(xié)議的格式
    • 串口接收及發(fā)送
    •  
    • 調(diào)試驗(yàn)證
    •  
    • 總結(jié)
    • 參考閱讀
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

上位機(jī)如何獲得環(huán)境聲音強(qiáng)度的大小

2021/06/29
309
閱讀需 18 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

大家好,我是小哈哥,最近幾篇網(wǎng)文,會給大家分享一個知識星球球友的問答,希望感興趣的持續(xù)關(guān)注。

問題由來

星球網(wǎng)友的提問,有問必答:

咱們把這個問題拆分開,由接下來幾篇網(wǎng)文回答:

  • 基于Modbus協(xié)議將電壓數(shù)據(jù)上傳至上位機(jī)Qt程序解析Modbus協(xié)議,并將解析之后的結(jié)果顯示在曲線中將溫度數(shù)據(jù)保存至Excel中

這三篇內(nèi)容為本問題涉及的三個知識點(diǎn),今天來分享第一個問題,其他問題,稍后陸續(xù)分享。

聲音傳感器模塊

聲音傳感器的作用相當(dāng)于一個話筒(麥克風(fēng)),它用來接收聲波。

該傳感器內(nèi)置一個對聲音敏感的電容駐極體話筒。聲波使話筒內(nèi)的駐極體薄膜振動,導(dǎo)致電容的變化,進(jìn)而產(chǎn)生與之變化對應(yīng)的微小電壓。

這一電壓隨后經(jīng)放大器被轉(zhuǎn)化成0~VCC的電壓,電壓值的大小等價于聲音強(qiáng)度的大小,經(jīng)過A/D轉(zhuǎn)換即可求得相對聲音強(qiáng)度的AD值。

 

原理圖

傳感器模塊上的麥克風(fēng)可將音頻信號轉(zhuǎn)換為電信號(模擬量),然后通過STM32自帶ADC功能將模擬量轉(zhuǎn)換為數(shù)字量。

LM386是一款功率放大器,具有自身功耗低、更新內(nèi)鏈增益可調(diào)整、電源電壓范圍大、外接元件少和總諧波失真小等優(yōu)點(diǎn)的功率放大器,廣泛應(yīng)用于錄音機(jī)和收音機(jī)之中。

麥克風(fēng)將聲音信號轉(zhuǎn)換為電信號,然后將信號發(fā)送到LM386的引腳3,并通過內(nèi)部電路將它們輸出到引腳5(模塊的引腳OUT)。然后使用STM32中具有ADC功能的引腳,讀取模擬值。

 

硬件連接

聲音傳感器端 STM32端
OUT PA1
VCC 5V
GND GND

注意:模塊介紹里要求VCC為5V供電,不過我測試使用3.3V供電也是可以的。

STM32進(jìn)行AD轉(zhuǎn)換步驟

引用adc功能

要使用ADC功能,必需引用stm32f10x_adc.c 文件和 stm32f10x_adc.h 文件。

ADC功能初始化

開啟對應(yīng)GPIO口和ADC功能時鐘,設(shè)置使用的GPIO為模擬輸入。

我們這里選用核心板上預(yù)留的PA1。

查詢STM32F103的數(shù)據(jù)手冊如下:

由上圖,我們知道PA1引腳有ADC123_IN1標(biāo)識,ADC123_IN1代表ADC1的通道1、ADC2的通道1、ADC3的通道1都在同一個管腳PA1上。

STM32 的 ADC 通道與 GPIO 對應(yīng)表

我們這里選擇ADC1(選擇ADC2和ADC3亦可)。

void  Adc_Init(void)
{  
 ADC_InitTypeDef ADC_InitStructure; 
 GPIO_InitTypeDef GPIO_InitStructure;

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE );   //使能ADC1通道時鐘
 
 RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //設(shè)置ADC分頻因子6 72M/6=12,ADC最大時間不能超過14M

 //PA1引腳初始化
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  //注意此處,模擬輸入引腳
 GPIO_Init(GPIOA, &GPIO_InitStructure);

 ADC_DeInit(ADC1);  //復(fù)位ADC1,將外設(shè) ADC1 的全部寄存器重設(shè)為缺省值

 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在獨(dú)立模式
 ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模數(shù)轉(zhuǎn)換工作在單通道模式,不使用掃描
 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模數(shù)轉(zhuǎn)換工作在單次轉(zhuǎn)換模式
 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //轉(zhuǎn)換由軟件而不是外部觸發(fā)啟動
 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC數(shù)據(jù)右對齊
 ADC_InitStructure.ADC_NbrOfChannel = 1; //順序進(jìn)行規(guī)則轉(zhuǎn)換的ADC通道的數(shù)目
 ADC_Init(ADC1, &ADC_InitStructure); //根據(jù)ADC_InitStruct中指定的參數(shù)初始化外設(shè)ADCx的寄存器   
  
 ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
 
 ADC_ResetCalibration(ADC1); //使能復(fù)位校準(zhǔn)  
  
 while(ADC_GetResetCalibrationStatus(ADC1)); //等待復(fù)位校準(zhǔn)結(jié)束
 
 ADC_StartCalibration(ADC1);  //開啟AD校準(zhǔn)
 
 while(ADC_GetCalibrationStatus(ADC1));  //等待校準(zhǔn)結(jié)束
}

 

獲取聲音傳感器的輸出值

聲音傳感器的輸出值——電壓值,該電壓值的大小,間接等價于聲音的大小。

上面已經(jīng)完成了PA1引腳的初始化,要想求得該引腳的輸入電壓,我們封裝一個獲取ADC值的函數(shù)u16 Get_Adc(u8 ch)。

u16 Get_Adc(u8 ch)   
{
   //設(shè)置指定ADC的規(guī)則組通道,一個序列,采樣時間
 ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采樣時間為239.5周期     
  
 ADC_SoftwareStartConvCmd(ADC1, ENABLE);  //使能指定的ADC1的軟件轉(zhuǎn)換啟動功能 
  
 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待轉(zhuǎn)換結(jié)束

 return ADC_GetConversionValue(ADC1); //返回最近一次ADC1規(guī)則組的轉(zhuǎn)換結(jié)果
}

要想求得PA1引腳的ADC值,只需要這樣調(diào)用即可:Get_Adc(1) ; ,函數(shù)的返回值即獲得的電壓值。

有時為了減小隨機(jī)誤差,我們可以對求得的ADC結(jié)果進(jìn)行多次測量取平均值。

ADC結(jié)果驗(yàn)證

可以將PA1引腳通過杜邦線接觸核心板上的3.3V和GND,看看結(jié)果是否為4095和0附近的值。

或者直接連接聲音傳感器模塊,改變周圍環(huán)境的聲音大小,看看輸出值是否隨著聲音的大小變化而變化,如果變化規(guī)律一致,說明ADC求得值應(yīng)該問題不大。

通訊協(xié)議

經(jīng)過上面幾步,聲音傳感器的值我們得到了,那么怎么將這個值上傳至上位機(jī)呢?

要想傳輸數(shù)據(jù),我們首先要選擇與上位機(jī)的通訊方式,常見的通訊方式有RS232、RS485、USB、網(wǎng)絡(luò)等方式。

這里我們選用串口實(shí)現(xiàn)下位機(jī)與上位機(jī)的通信,串口通信最簡單,耗費(fèi)資源也最少。

有了通信方式,為了保證通信雙方可以正常交互,接來下我們要規(guī)定一下通訊協(xié)議。

一般主機(jī)端獲取傳感器值,一般都是一問一答情況,工業(yè)控制領(lǐng)域最常使用的通訊協(xié)議就是Modbus協(xié)議。

因?yàn)槲覀円x取的電壓值為只讀,所以我們這里選用功能碼0x04即可。

04:INPUT REGISTER:輸入寄存器,讀WORD類型,字操作,輸入?yún)?shù),控制器運(yùn)行時從外部設(shè)備獲得的參數(shù),可讀但是不可寫,常用于模擬量輸入。

按照Modbus對寄存器的分類,電流、電壓值屬于模擬量,只能讀不能寫,屬于輸入寄存器類別,這里嚴(yán)格講只能用0x04功能碼。

Modbus協(xié)議的格式

主機(jī)發(fā)送的指令:

地址 功能碼 寄存器起始地址 寄存器數(shù)量 CRC校驗(yàn)
01H 04H 0x0000 0x0001 前面所有字節(jié)的CRC16校驗(yàn)和,2個字節(jié),低字節(jié)在前

從機(jī)返回:

地址 功能碼 寄存器字節(jié)總數(shù) 寄存器數(shù)據(jù) CRC校驗(yàn)位
01H 04H 0x02 電壓值 前面所有字節(jié)的CRC16校驗(yàn)和,2個字節(jié),低字節(jié)在前

我們使用STM32F103自帶的ADC功能,因?yàn)樗腁D是12位的,所以我們用一個16位整數(shù)表示該電壓值即可,所以我們從機(jī)返回的寄存器數(shù)據(jù)占用2個字節(jié)。

上位機(jī)發(fā)送的指令:

讀取當(dāng)前電壓:nAddr, 0x04, 0x00, 0x00, 0x00, 0x01, checkBitHig, checkBitLow

即:01 04 00 00 00 01 31 CA

串口接收及發(fā)送

為了方便調(diào)試,我們使用最小系統(tǒng)核心板的串口1接收和發(fā)送傳感器數(shù)據(jù)。

從機(jī)接收指令

Modbus協(xié)議一般用于一主多從結(jié)構(gòu),主機(jī)主動發(fā)送指令,從機(jī)被動接收指令。

從機(jī)接收到指令后,對接收到的指令進(jìn)行解析,然后根據(jù)指令向主機(jī)返回對應(yīng)的內(nèi)容。

當(dāng)串口接收超時時,我們將獲得一幀數(shù)據(jù),這個數(shù)據(jù)保存至USART_RX_BUF數(shù)組中,接下來我們就對這個數(shù)組中的內(nèi)容進(jìn)行解析:

//解析接收到的串口數(shù)據(jù)
//串口1收到的信息
if(USART_RX_STA&0x8000)
{ 
    uart1Len=USART_RX_STA&0x3f;               //得到此次接收到的數(shù)據(jù)長度   

    if(uart1Len==8)
    {
        crc16 = chkcrc(USART_RX_BUF, 6);
        checkBitLow = (u8)(crc16 & 0xff);    //校驗(yàn)位低8位
        checkBitHig = (u8)((crc16 >> 8) & 0xff);  //校驗(yàn)位高8位

        //低字節(jié)在前
        if(checkBitLow==USART_RX_BUF[6] && checkBitHig==USART_RX_BUF[7])
        {
            if(USART_RX_BUF[0] == 0x01)             //我們可以規(guī)定地址0x01即為獲取聲音傳感器的值
            {
                //... ...
            }
        }
    }


    USART_RX_STA=0;   
    memset(USART_RX_BUF, 0, sizeof(USART_RX_BUF));                          //清空數(shù)組  
} 

 

從機(jī)發(fā)送數(shù)據(jù)

根據(jù)主機(jī)的指令,從機(jī)返回對應(yīng)的數(shù)據(jù)給主機(jī)。

從機(jī)發(fā)送的數(shù)據(jù)要滿足Modbus協(xié)議返回數(shù)據(jù)的幀格式。

具體發(fā)送數(shù)據(jù)的代碼如下:

u16 crc16; 
u8 checkBitLow, checkBitHig;
u8 sendBuf[20];
u16 nADCValue = 0;
//獲取AD的值
nADCValue = Get_Adc(1); 

//格式化待發(fā)送數(shù)據(jù)
sendBuf[0] = 0x01;
sendBuf[1] = 0x04;
sendBuf[2] = 0x02;
sendBuf[3] = ((nADCValue >> 8) & 0xff); //0x18;//
sendBuf[4] = (nADCValue & 0xff);        //0xD5;//
crc16 = chkcrc(sendBuf, 5);
checkBitLow = (u8)(crc16 & 0xff);    //校驗(yàn)位低8位
checkBitHig = (u8)((crc16 >> 8) & 0xff);  //校驗(yàn)位高8位
sendBuf[5] = checkBitLow;
sendBuf[6] = checkBitHig;

//串口發(fā)送,發(fā)送結(jié)果數(shù)據(jù)至主機(jī)
USART_OUT(sendBuf, 7);

上位機(jī)打開串口助手,以十六進(jìn)制的方式發(fā)送數(shù)據(jù)幀:01 04 00 00 00 01 31 CA 。

STM32端收到串口指令之后,解析此數(shù)據(jù)幀,數(shù)據(jù)幀驗(yàn)證通過之后,將當(dāng)前的聲音傳感器的值封裝到待發(fā)送的數(shù)據(jù)幀中,然后發(fā)送給上位機(jī)。

 

調(diào)試驗(yàn)證

使用ModScan32軟件對我們實(shí)現(xiàn)的下位機(jī)程序進(jìn)行驗(yàn)證。

ModScan32是一個運(yùn)行在Windows下,作為在RTU或者ASCII傳輸模式下的Modbus協(xié)議主設(shè)備的應(yīng)用程序。

通過ModScan32軟件,我們可以得到實(shí)時的聲音強(qiáng)度大小。

 

總結(jié)

這樣我們就把聲音傳感器的數(shù)值通過串口上傳到了上位機(jī)中,實(shí)現(xiàn)了Modbus協(xié)議的主機(jī)、從機(jī)的交互,對于不同傳感器、不同節(jié)點(diǎn),我們只需要設(shè)定不同的地址即可。

參考閱讀

土壤濕度傳感器,出遠(yuǎn)門再也不怕花沒人澆水了

STM32F103 串口的使用方法

[網(wǎng)友問答2]上位機(jī)如何與STM32進(jìn)行串口通信

干貨 | Modbus協(xié)議調(diào)試分享

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號『嵌入式從0到1』,號主:程序員小哈,是一個軟硬件全棧開發(fā)工程師(12年工作經(jīng)驗(yàn)的老司機(jī)),電子發(fā)燒友論壇鴻蒙版塊版主,公眾號內(nèi)容專注于嵌入式學(xué)習(xí)。堅持原創(chuàng),寫有圖、有視頻的保姆級教程文章,篇篇有干貨。做一個講清楚,說明白,大家學(xué)得會的交流平臺。