ah20温度采集
文章目錄
- 實(shí)驗(yàn)?zāi)康?/li>
- 實(shí)驗(yàn)材料
- 軟件
- 硬件
- AHT20
- 實(shí)驗(yàn)原理
- 什么是I2C
- 物理層
- 邏輯層
- 硬件I2C與軟件I2C
- 項(xiàng)目制作
- 制作一個標(biāo)準(zhǔn)庫項(xiàng)目
- 導(dǎo)入AHT20模塊與USART配置
- 修改main函數(shù)
- 實(shí)驗(yàn)現(xiàn)象
- 總結(jié)
- 參考資料
實(shí)驗(yàn)?zāi)康?/h1>
學(xué)習(xí)I2C總線通信協(xié)議,使用STM32F103完成基于I2C協(xié)議的AHT20溫濕度傳感器的數(shù)據(jù)采集,并將采集的溫度-濕度值通過串口輸出。
具體任務(wù):
1)解釋什么是“軟件I2C”和“硬件I2C”? (閱讀野火配套教材的第23章“I2C–讀寫EEPROM”原理章節(jié))
2)閱讀AHT20數(shù)據(jù)手冊,編程實(shí)現(xiàn):每隔2秒鐘采集一次溫濕度數(shù)據(jù),并通過串口發(fā)送到上位機(jī)(win10)。
實(shí)驗(yàn)材料
軟件
- KEIL5
- FlyMcu
- FireTools串口助手
硬件
- STM32F103C8T6最小開發(fā)板
- AHT20溫濕度傳感器
- CH340模塊
- 面包板一塊
- 杜邦線若干
AHT20
AHT20是奧松電子生產(chǎn)的,一種基于IIC協(xié)議的溫濕度傳感器。單片機(jī)通過給AHT20通過IIC協(xié)議發(fā)送指令,可以從AHT20讀取溫濕度數(shù)據(jù)到單片機(jī)。
我在本實(shí)驗(yàn)中使用的芯片的引腳圖如下:
根據(jù)引腳定義進(jìn)行連接,按規(guī)定編寫代碼給傳感器發(fā)送指令,讀取溫濕度數(shù)據(jù)。
實(shí)驗(yàn)原理
什么是I2C
I2C 通訊協(xié)議(Inter-Integrated Circuit)是由 Phiilps 公司開發(fā)的,由于它引腳少,硬件實(shí)現(xiàn)簡單,可擴(kuò)展性強(qiáng),不需要 USART、CAN 等通訊協(xié)議的外部收發(fā)設(shè)備,現(xiàn)在被廣泛地使用在系統(tǒng)內(nèi)多個集成電路(IC)間的通訊。
I2C可以分成物理層與協(xié)議層。
物理層
I2C是一個支持設(shè)備的總線??蛇B接多個 I2C 通訊設(shè)備,支持多個通訊主機(jī)及多個通訊從機(jī)。對于I2C 總線,只使用兩條總線線路,一條雙向串行數(shù)據(jù)線(SDA) ,一條串行時鐘線(SCL)。
連接到總線上的設(shè)備在輸出時使用開漏輸出的模式。當(dāng)總線上沒有設(shè)備發(fā)數(shù)據(jù)時,大家輸出高阻態(tài),總線被上拉電阻設(shè)置為高電平。當(dāng)有設(shè)備發(fā)送數(shù)據(jù)時,如果發(fā)送1,設(shè)備輸出高阻態(tài),接收設(shè)備與上拉電阻相連得到1;如果輸出0,由于上拉電阻把總線與電源隔開,整條總線被設(shè)置成低電平。
邏輯層
首先主機(jī)向總線發(fā)送開始信號,發(fā)送之后再發(fā)送從機(jī)地址(7位或10位)+讀或?qū)?#xff08;0或1),對應(yīng)的從機(jī)再給主機(jī)發(fā)送確認(rèn)信號。
如果是主機(jī)發(fā)數(shù)據(jù),主機(jī)會發(fā)送8位數(shù)據(jù)到從機(jī),從機(jī)發(fā)送確認(rèn)信號,主機(jī)再接著發(fā)下8位數(shù)據(jù)。發(fā)送完畢后,主機(jī)發(fā)送停止信號,傳輸結(jié)束。
如果是主機(jī)讀數(shù)據(jù),從機(jī)會發(fā)送8位數(shù)據(jù)給主機(jī),主機(jī)收到后發(fā)送確認(rèn)信號,接著收下8位數(shù)據(jù)。主機(jī)可以發(fā)送非確認(rèn)信號停止接收。發(fā)送非停止信號后,主機(jī)再發(fā)送結(jié)束信號。
I2C常使用復(fù)合模式,有兩次起始信號。主機(jī)找到從機(jī)后,再發(fā)送傳感器里面的寄存器地址,再進(jìn)行讀寫。
野火的資料里面有圖片讀寫過程的示意圖:
硬件I2C與軟件I2C
硬件I2C是直接利用 STM32 芯片中的硬件 I2C 外設(shè),在初始化好 I2C 外設(shè)后,只需要把某寄存器位置 1,此時外設(shè)就會控制對應(yīng)的 SCL 及 SDA 線自動產(chǎn)生 I2C 起始信號,不需要內(nèi)核直接控制引腳的電平。
軟件I2C直接使用 CPU 內(nèi)核按照 I2C 協(xié)議的要求控制 GPIO 輸出高低電平,從而模擬I2C。
本實(shí)驗(yàn)中,使用I2C讀寫寄存器的具體實(shí)現(xiàn)已經(jīng)被官方寫成函數(shù)給我們使用,我們僅需要根據(jù)示例代碼,按自己需要進(jìn)行調(diào)用即可。但是想要分析它的源碼,就必須先了解I2C協(xié)議。
項(xiàng)目制作
制作一個標(biāo)準(zhǔn)庫項(xiàng)目
制作標(biāo)準(zhǔn)庫項(xiàng)目過程較為復(fù)雜,野火的資料庫提供了項(xiàng)目模板。我也寫好了一個模板在這個鏈接里面,可以下載好后根據(jù)文本文件里面的提示進(jìn)行構(gòu)建。
導(dǎo)入AHT20模塊與USART配置
構(gòu)建好標(biāo)準(zhǔn)庫項(xiàng)目后,使用空main函數(shù)編譯無誤后,進(jìn)行這一步。
由于本實(shí)驗(yàn)實(shí)現(xiàn)的需求是使用AHT20模塊采集溫濕度數(shù)據(jù),并將采集到的數(shù)據(jù)發(fā)送給上位機(jī)顯示。因此要使用AHT20相關(guān)的代碼以及配置串口。
對于AHT20的代碼,可以下載官方的文檔。點(diǎn)擊這里下載。
注意,下載到的代碼有可能會有bug,下載好代碼后,進(jìn)入.c文件查看Init_I2C_Sensor_Port(void)函數(shù),看SDA對應(yīng)的GPIO口以及是否為開漏輸出。
檢查IIC的GPIO口配置正確后,剪切AHT20-21_DEMO_V1_3.c文件中的示例主函數(shù)到main.c中。
接著進(jìn)行USART配置。具體的配置過程不再贅述,我貼出自己寫好的模塊化代碼,可以分別復(fù)制到頭文件與源文件中導(dǎo)入項(xiàng)目。
我的代碼僅做了對USART1的配置:
//usart.h #ifndef __PSD_USART_H__ #define __PSD_USART_H__ #include "stm32f10x.h" void Usart_1_Config();//usart1初始配置 void Usart_SendByte(USART_TypeDef *pUSARTx, uint8_t ch);//發(fā)送字符 void Usart_SendString(USART_TypeDef *pUSARTx, char *ch);//發(fā)送字符串 void Usart_SendHalfWord(USART_TypeDef *pUSARTx, uint16_t ch);//發(fā)送無符號16位數(shù)據(jù) void Usart_SendWord(USART_TypeDef *pUSARTx, uint32_t ch);//發(fā)送無符號32位數(shù)據(jù) void Usart_SendNum_Uint8(USART_TypeDef *pUSARTx, uint8_t num);//發(fā)送Uint8類型數(shù)字 void Usart_SendNum_Uint16(USART_TypeDef *pUSARTx, uint16_t num);//發(fā)送Uint16類型數(shù)字 void Usart_SendNum_Uint32(USART_TypeDef *pUSARTx, uint32_t num);//發(fā)送Uint32類型數(shù)字 void Usart_SendNum_Int8(USART_TypeDef *pUSARTx, int8_t num);//發(fā)送int8類型數(shù)字 void Usart_SendNum_Int16(USART_TypeDef *pUSARTx, int16_t num);//發(fā)送int16類型數(shù)字 void Usart_SendNum_Int32(USART_TypeDef *pUSARTx, int32_t num);//發(fā)送int32類型數(shù)字 void Usart_SendNum_Double(USART_TypeDef *pUSARTx, double num);//發(fā)送double類型數(shù)字 #endif //usart.c #include "psd_usart.h" #include "math.h" static void NVIC_Configuration(){NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;NVIC_Init(&NVIC_InitStruct); } void Usart_1_Config(){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打開串口GPIO時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//打開串口時鐘//配置RX,TX的GPIO口GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; //將TX配置為浮空輸入模式GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //將RX配置為復(fù)用推挽輸出模式GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//配置USART1USART_InitTypeDef USART_InitStruct;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_BaudRate = 115200;USART_InitStruct.USART_Parity = USART_Parity_No;USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);NVIC_Configuration();USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); }void Usart_SendByte(USART_TypeDef *pUSARTx, uint8_t ch){USART_SendData(pUSARTx,ch);while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET); }void Usart_SendString(USART_TypeDef *pUSARTx, char *ch){unsigned int k = 0;do{Usart_SendByte(pUSARTx,*(ch+k));k++;}while(*(ch+k)!='\0');while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET); }void Usart_SendHalfWord(USART_TypeDef *pUSARTx, uint16_t ch){uint8_t temp_h, temp_l;temp_h = (ch&0xFF00)>>8;temp_l = ch&0xff;Usart_SendByte(pUSARTx, temp_h);Usart_SendByte(pUSARTx, temp_l);}void Usart_SendWord(USART_TypeDef *pUSARTx, uint32_t ch){uint8_t temp_h, temp_l;temp_h = (ch&0xFFFF0000)>>16;temp_l = ch&0xffff;Usart_SendHalfWord(pUSARTx, temp_h);Usart_SendHalfWord(pUSARTx, temp_l);}void Usart_SendNum_Uint8(USART_TypeDef *pUSARTx, uint8_t num){if (num == 0){Usart_SendByte(pUSARTx,'0');return;}char dat[4];dat[3] = '\0';uint8_t i = 0;while(num>0){uint8_t n = num%10;if(n==0) dat[2-i] = '0';else if(n==1) dat[2-i] = '1';else if(n==2) dat[2-i] = '2';else if(n==3) dat[2-i] = '3';else if(n==4) dat[2-i] = '4';else if(n==5) dat[2-i] = '5';else if(n==6) dat[2-i] = '6';else if(n==7) dat[2-i] = '7';else if(n==8) dat[2-i] = '8';else if(n==9) dat[2-i] = '9';i++;num/=10;}uint8_t a = 3-i;Usart_SendString(pUSARTx,&dat[a]); } void Usart_SendNum_Uint16(USART_TypeDef *pUSARTx, uint16_t num){if (num == 0){Usart_SendByte(pUSARTx,'0');return;}char dat[6];dat[5] = '\0';uint8_t i = 0;while(num>0){uint8_t n = num%10;if(n==0) dat[4-i] = '0';else if(n==1) dat[4-i] = '1';else if(n==2) dat[4-i] = '2';else if(n==3) dat[4-i] = '3';else if(n==4) dat[4-i] = '4';else if(n==5) dat[4-i] = '5';else if(n==6) dat[4-i] = '6';else if(n==7) dat[4-i] = '7';else if(n==8) dat[4-i] = '8';else if(n==9) dat[4-i] = '9';i++;num/=10;}uint8_t a = 5-i;Usart_SendString(pUSARTx,&dat[a]); } void Usart_SendNum_Uint32(USART_TypeDef *pUSARTx, uint32_t num){if (num == 0){Usart_SendByte(pUSARTx,'0');return;}char dat[11];dat[10] = '\0';uint8_t i = 0;while(num>0){uint8_t n = num%10;if(n==0) dat[9-i] = '0';else if(n==1) dat[9-i] = '1';else if(n==2) dat[9-i] = '2';else if(n==3) dat[9-i] = '3';else if(n==4) dat[9-i] = '4';else if(n==5) dat[9-i] = '5';else if(n==6) dat[9-i] = '6';else if(n==7) dat[9-i] = '7';else if(n==8) dat[9-i] = '8';else if(n==9) dat[9-i] = '9';i++;num/=10;}uint8_t a = 10-i;Usart_SendString(pUSARTx,&dat[a]); } void Usart_SendNum_Double(USART_TypeDef *pUSARTx, double num){int32_t num1 = floor(num);int32_t num2 = floor((num-num1)*1000);Usart_SendNum_Int32(pUSARTx,num1);Usart_SendByte(pUSARTx,'.');Usart_SendNum_Int32(pUSARTx,num2); } void Usart_SendNum_Int8(USART_TypeDef *pUSARTx, int8_t num){if(num>=0) Usart_SendNum_Uint8(pUSARTx,num);else{if(num == -128){Usart_SendString(pUSARTx,"-128");}else{num = 0 - num;Usart_SendByte(pUSARTx,'-');Usart_SendNum_Uint8(pUSARTx,num);}} } void Usart_SendNum_Int16(USART_TypeDef *pUSARTx, int16_t num){if(num>=0) Usart_SendNum_Uint16(pUSARTx,num);else{if(num == -32768){Usart_SendString(pUSARTx,"-32768");}else{num = 0 - num;Usart_SendByte(pUSARTx,'-');Usart_SendNum_Uint16(pUSARTx,num);}} } void Usart_SendNum_Int32(USART_TypeDef *pUSARTx, int32_t num){if(num>=0) Usart_SendNum_Uint32(pUSARTx,num);else{if(num == -2147483648){Usart_SendString(pUSARTx,"-2147483648");}else{num = 0 - num;Usart_SendByte(pUSARTx,'-');Usart_SendNum_Uint32(pUSARTx,num);}} }修改main函數(shù)
官方的實(shí)例代碼已經(jīng)讀出了放大10倍的溫濕度數(shù)據(jù),在main.c函數(shù)中找到對應(yīng)的位置,在讀到的數(shù)據(jù)后面使用串口通信代碼,將溫濕度除以10之后將數(shù)據(jù)發(fā)送給串口助手。
代碼如下:
int32_t main(void) {Usart_1_Config();//配置Usart1uint32_t CT_data[2];volatile int c1,t1;/***********************************************************************************//**///上電初始化SDA,SCL的IO口/***********************************************************************************/Init_I2C_Sensor_Port();/***********************************************************************************//**///①剛上電,產(chǎn)品芯片內(nèi)部就緒需要時間,延時100~500ms,建議500ms/***********************************************************************************/Delay_1ms(500);/***********************************************************************************//**///②上電第一次發(fā)0x71讀取狀態(tài)字,判斷狀態(tài)字是否為0x18,如果不是0x18,進(jìn)行寄存器初始化/***********************************************************************************/if((AHT20_Read_Status()&0x18)!=0x18){AHT20_Start_Init(); //重新初始化寄存器Delay_1ms(10);}/***********************************************************************************//**///③根據(jù)客戶自己需求發(fā)測量命令讀取溫濕度數(shù)據(jù),當(dāng)前while(1)循環(huán)發(fā)測量命令讀取溫濕度數(shù)據(jù),僅供參考/***********************************************************************************/while(1){AHT20_Read_CTdata_crc(CT_data);if(CT_data[0]==0xff&&CT_data[1]==0xff){Usart_SendString(USART1,"CRC檢驗(yàn)未通過!");}else{c1 = CT_data[0]*100*10/1024/1024; //計(jì)算得到濕度值c1(放大了10倍)t1 = CT_data[1]*200*10/1024/1024-500;//計(jì)算得到溫度值t1(放大了10倍)//下一步客戶處理顯示數(shù)據(jù),Usart_SendString(USART1,"溫度:");Usart_SendNum_Double(USART1,t1/10.0,1);Usart_SendByte(USART1,'\n');Usart_SendString(USART1,"濕度:");Usart_SendNum_Double(USART1,c1/10.0,1);Usart_SendByte(USART1,'\n');Usart_SendString(USART1,"數(shù)據(jù)通過檢驗(yàn)!\n");Usart_SendByte(USART1,'\n');}Delay_1ms(2000);}}實(shí)驗(yàn)現(xiàn)象
查看SDA,SCL對應(yīng)的GPIO口,將AHT20芯片的VCC,SDA,GND,SCL四個引腳與芯片對應(yīng)的引腳在面包板上連接好,連接好硬件后燒錄程序到芯片中,打開串口助手,發(fā)現(xiàn)接收到溫度與濕度數(shù)據(jù)。
根據(jù)上圖的函數(shù)查看自己下的代碼使用的哪個GPIO口。
總結(jié)
本實(shí)驗(yàn)實(shí)現(xiàn)了使用AHT20傳感器收集溫濕度數(shù)據(jù),并發(fā)送到串口助手。涉及到I2C與Usart配置。為了完成這個實(shí)驗(yàn),我將Usart模塊化,制作了輸出中文,浮點(diǎn)數(shù),數(shù)字的函數(shù),鞏固了之前所學(xué)習(xí)的知識。此外還通過AHT20的官方例子學(xué)習(xí)了I2C的應(yīng)用。
參考資料
-
https://blog.csdn.net/qq_43279579/article/details/111597278
-
《零死角玩轉(zhuǎn)STM32——F103指南者》
-
《AHT20產(chǎn)品手冊》
總結(jié)
- 上一篇: 方便面又好卖了!康师傅天猫618狂增10
- 下一篇: 大厂的人自带光环,但光环是从哪儿来的呢?