大家好,我是小哈哥,最近幾篇網(wǎng)文,會(huì)給大家分享一個(gè)知識(shí)星球球友的問答,希望感興趣的持續(xù)關(guān)注。
問題由來(lái)
星球網(wǎng)友的提問,有問必答:
咱們把這個(gè)問題拆分開,由接下來(lái)幾篇網(wǎng)文回答:
- 基于Modbus協(xié)議將電壓數(shù)據(jù)上傳至上位機(jī)Qt程序解析Modbus協(xié)議,并將解析之后的結(jié)果顯示在曲線中將溫度數(shù)據(jù)保存至Excel中
這三篇內(nèi)容為本問題涉及的三個(gè)知識(shí)點(diǎn),今天來(lái)分享第一個(gè)問題,其他問題,稍后陸續(xù)分享。
聲音傳感器模塊
聲音傳感器的作用相當(dāng)于一個(gè)話筒(麥克風(fēng)),它用來(lái)接收聲波。
該傳感器內(nèi)置一個(gè)對(duì)聲音敏感的電容式駐極體話筒。聲波使話筒內(nèi)的駐極體薄膜振動(dòng),導(dǎo)致電容的變化,進(jìn)而產(chǎn)生與之變化對(duì)應(yīng)的微小電壓。
這一電壓隨后經(jīng)放大器被轉(zhuǎn)化成0~VCC的電壓,電壓值的大小等價(jià)于聲音強(qiáng)度的大小,經(jīng)過A/D轉(zhuǎn)換即可求得相對(duì)聲音強(qiáng)度的AD值。
原理圖
傳感器模塊上的麥克風(fēng)可將音頻信號(hào)轉(zhuǎn)換為電信號(hào)(模擬量),然后通過STM32自帶ADC功能將模擬量轉(zhuǎn)換為數(shù)字量。
LM386是一款功率放大器,具有自身功耗低、更新內(nèi)鏈增益可調(diào)整、電源電壓范圍大、外接元件少和總諧波失真小等優(yōu)點(diǎn)的功率放大器,廣泛應(yīng)用于錄音機(jī)和收音機(jī)之中。
麥克風(fēng)將聲音信號(hào)轉(zhuǎn)換為電信號(hào),然后將信號(hào)發(fā)送到LM386的引腳3,并通過內(nèi)部電路將它們輸出到引腳5(模塊的引腳OUT)。然后使用STM32中具有ADC功能的引腳,讀取模擬值。
硬件連接
聲音傳感器端 | STM32端 |
---|---|
OUT | PA1 |
VCC | 5V |
GND | GND |
注意:模塊介紹里要求VCC為5V供電,不過我測(cè)試使用3.3V供電也是可以的。
STM32進(jìn)行AD轉(zhuǎn)換步驟
引用adc功能
要使用ADC功能,必需引用stm32f10x_adc.c 文件和 stm32f10x_adc.h 文件。
ADC功能初始化
開啟對(duì)應(yīng)GPIO口和ADC功能時(shí)鐘,設(shè)置使用的GPIO為模擬輸入。
我們這里選用核心板上預(yù)留的PA1。
查詢STM32F103的數(shù)據(jù)手冊(cè)如下:
由上圖,我們知道PA1引腳有ADC123_IN1
標(biāo)識(shí),ADC123_IN1
代表ADC1的通道1、ADC2的通道1、ADC3的通道1都在同一個(gè)管腳PA1上。
STM32 的 ADC 通道與 GPIO 對(duì)應(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通道時(shí)鐘
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //設(shè)置ADC分頻因子6 72M/6=12,ADC最大時(shí)間不能超過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ā)啟動(dòng)
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC數(shù)據(jù)右對(duì)齊
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é)束
}
獲取聲音傳感器的輸出值
聲音傳感器的輸出值——電壓值,該電壓值的大小,間接等價(jià)于聲音的大小。
上面已經(jīng)完成了PA1引腳的初始化,要想求得該引腳的輸入電壓,我們封裝一個(gè)獲取ADC值的函數(shù)u16 Get_Adc(u8 ch)
。
u16 Get_Adc(u8 ch)
{
//設(shè)置指定ADC的規(guī)則組通道,一個(gè)序列,采樣時(shí)間
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采樣時(shí)間為239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的軟件轉(zhuǎn)換啟動(dòng)功能
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ù)的返回值即獲得的電壓值。
有時(shí)為了減小隨機(jī)誤差,我們可以對(duì)求得的ADC結(jié)果進(jìn)行多次測(cè)量取平均值。
ADC結(jié)果驗(yàn)證
可以將PA1引腳通過杜邦線接觸核心板上的3.3V和GND,看看結(jié)果是否為4095和0附近的值。
或者直接連接聲音傳感器模塊,改變周圍環(huán)境的聲音大小,看看輸出值是否隨著聲音的大小變化而變化,如果變化規(guī)律一致,說明ADC求得值應(yīng)該問題不大。
通訊協(xié)議
經(jīng)過上面幾步,聲音傳感器的值我們得到了,那么怎么將這個(gè)值上傳至上位機(jī)呢?
要想傳輸數(shù)據(jù),我們首先要選擇與上位機(jī)的通訊方式,常見的通訊方式有RS232、RS485、USB、網(wǎng)絡(luò)等方式。
這里我們選用串口實(shí)現(xiàn)下位機(jī)與上位機(jī)的通信,串口通信最簡(jiǎn)單,耗費(fèi)資源也最少。
有了通信方式,為了保證通信雙方可以正常交互,接來(lá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è)備獲得的參數(shù),可讀但是不可寫,常用于模擬量輸入。
按照Modbus對(duì)寄存器的分類,電流、電壓值屬于模擬量,只能讀不能寫,屬于輸入寄存器類別,這里嚴(yán)格講只能用0x04功能碼。
Modbus協(xié)議的格式
主機(jī)發(fā)送的指令:
地址 | 功能碼 | 寄存器起始地址 | 寄存器數(shù)量 | CRC校驗(yàn)位 |
---|---|---|---|---|
01H | 04H | 0x0000 | 0x0001 | 前面所有字節(jié)的CRC16校驗(yàn)和,2個(gè)字節(jié),低字節(jié)在前 |
從機(jī)返回:
地址 | 功能碼 | 寄存器字節(jié)總數(shù) | 寄存器數(shù)據(jù) | CRC校驗(yàn)位 |
---|---|---|---|---|
01H | 04H | 0x02 | 電壓值 | 前面所有字節(jié)的CRC16校驗(yàn)和,2個(gè)字節(jié),低字節(jié)在前 |
我們使用STM32F103自帶的ADC功能,因?yàn)樗腁D是12位的,所以我們用一個(gè)16位整數(shù)表示該電壓值即可,所以我們從機(jī)返回的寄存器數(shù)據(jù)占用2個(gè)字節(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ī)主動(dòng)發(fā)送指令,從機(jī)被動(dòng)接收指令。
從機(jī)接收到指令后,對(duì)接收到的指令進(jìn)行解析,然后根據(jù)指令向主機(jī)返回對(duì)應(yīng)的內(nèi)容。
當(dāng)串口接收超時(shí)時(shí),我們將獲得一幀數(shù)據(jù),這個(gè)數(shù)據(jù)保存至USART_RX_BUF
數(shù)組中,接下來(lái)我們就對(duì)這個(gè)數(shù)組中的內(nèi)容進(jìn)行解析:
//解析接收到的串口數(shù)據(jù)
//串口1收到的信息
if(USART_RX_STA&0x8000)
{
uart1Len=USART_RX_STA&0x3f; //得到此次接收到的數(shù)據(jù)長(zhǎng)度
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ī)返回對(duì)應(yīng)的數(shù)據(jù)給主機(jī)。
從機(jī)發(fā)送的數(shù)據(jù)要滿足Modbus協(xié)議返回?cái)?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軟件對(duì)我們實(shí)現(xiàn)的下位機(jī)程序進(jìn)行驗(yàn)證。
ModScan32是一個(gè)運(yùn)行在Windows下,作為在RTU或者ASCII傳輸模式下的Modbus協(xié)議主設(shè)備的應(yīng)用程序。
通過ModScan32軟件,我們可以得到實(shí)時(shí)的聲音強(qiáng)度大小。
總結(jié)
這樣我們就把聲音傳感器的數(shù)值通過串口上傳到了上位機(jī)中,實(shí)現(xiàn)了Modbus協(xié)議的主機(jī)、從機(jī)的交互,對(duì)于不同傳感器、不同節(jié)點(diǎn),我們只需要設(shè)定不同的地址即可。
參考閱讀
土壤濕度傳感器,出遠(yuǎn)門再也不怕花沒人澆水了
STM32F103 串口的使用方法
[網(wǎng)友問答2]上位機(jī)如何與STM32進(jìn)行串口通信
干貨 | Modbus協(xié)議調(diào)試分享