日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
鴻蒙Hi3861測溫濕度顯示一個(gè)新手開發(fā)調(diào)試過程

想了解更多內(nèi)容,請?jiān)L問:

豐臺網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、自適應(yīng)網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)成立與2013年到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。

和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos./#zz

使用的器件:Hi3861 + AHT20 + SSD1306

配好開發(fā)環(huán)境輕松做完點(diǎn)燈任務(wù)后,便想搞個(gè)像樣點(diǎn)的應(yīng)用,然后……決定做一個(gè)測量溫濕度計(jì),開始覺得這個(gè)實(shí)現(xiàn)比較簡單,通過Hi3861讀取AHT20測出的溫濕度值,然后通過OLED顯示出來。

首先我的嵌入式開發(fā)知識非常少,曾經(jīng)做過幾年Java開發(fā),玩過一點(diǎn)51單片機(jī),在Linux上部署過一些Web服務(wù)。僅此而已,所以我的知識無法宏觀上把這個(gè)事情想的很清楚,只能一步一步的試。如果你也是新入手嵌入式開發(fā)的朋友,那我的過程可能會對你有借鑒。

先構(gòu)建一個(gè)技術(shù)輪廓:每種電子傳感器都是包括3類端口,(1)第1類電源:必需要2根電源線給模塊供電,正負(fù)極,有的模塊有多組供電;(2)第2類控制:然后有的模塊會有一些控制端口,每種模塊都不經(jīng)相同,有的模塊直接讀數(shù)是沒有這種控制端口的;(3)第3類通信線:跟核心單片機(jī)通信的信號端口,根據(jù)采用的通信協(xié)議不同,端口數(shù)量不同。要使用的AHT20和SSD1306采用的都是I2C通信協(xié)議,所以都是2根通信線。

我所用到的模塊都很簡單,主要涵蓋的都是這3類端口,所以不管看到模塊打扮成什么樣,所要解決的主要問題都是類似的。主要做的就是通過通信端口向模塊讀寫數(shù)據(jù)。

模塊的配置:各種模塊控制和存儲數(shù)據(jù)都是由一組組8位的寄存器控制的,每個(gè)寄存器里有8位,每一位可以存儲1或者0,組成1個(gè)字節(jié)值,每種模塊都有自己的功能設(shè)置和存儲設(shè)置,可以想象成高階語言里的關(guān)鍵字,寄存器值就是它本來的樣子,一組組數(shù)字直接看是不會看懂它代表什么意思的,所以要依靠模塊提供的技術(shù)手冊做指導(dǎo),一邊看手冊一邊設(shè)置,單片機(jī)開發(fā)就這是這么樸實(shí)無華。

關(guān)于通信協(xié)議:要使用的這2個(gè)模塊采用的都是I2C通信協(xié)議,2根線一根信號一根時(shí)鐘,通信雙方就是通過互相占用通信線,相互發(fā)送高低電平傳遞消息,就是他們不能同步通信的,一方發(fā)送一方只能接收。因?yàn)橛玫木€少所以通信過程非常繁瑣,一方喊話問某地址的模塊是否在線,然后等待,對方如果收到喊話,然后給個(gè)應(yīng)答,當(dāng)收到應(yīng)答,再發(fā)送指令告訴他準(zhǔn)備干什么,然后等待確認(rèn),模塊收到后發(fā)確認(rèn)…………,這個(gè)過程我在51上模擬過,好痛苦啊,一個(gè)時(shí)鐘信號一個(gè)數(shù)據(jù)信號的數(shù)……,但是!!!在鴻蒙上所有的繁瑣過程都被封裝好了,我們只需要簡單的調(diào)用系統(tǒng)提供的I2C操作方法,具體過程完全不用考慮,經(jīng)過使用,真的好用,非常好用,好簡單啊!所以I2C基本流程熟悉一下即可,在開發(fā)過程中具體的工作非常少。

SSD1306

首先是點(diǎn)亮屏幕,一旦能使用屏幕了,等于單片機(jī)對你打開了一扇窗戶。SSD1306并不是OLED,它是驅(qū)動OLED顯示的控制芯片,很多模塊本身就是一個(gè)復(fù)雜的單片機(jī),我們用的OLED屏幕是128*64像素組成的,本質(zhì)上你可以簡單的理解為高階點(diǎn)燈。對SSD1306的控制也是通過I2C實(shí)現(xiàn)的,雖然它支持很多種通信協(xié)議,但是惜端如金的Hi3861采用了端口占用最少的I2C。

我們只是需要向SSD1306發(fā)送數(shù)據(jù),沒有反饋值。所以通信過程比較簡單SSD1306的地址0x78,0x00為接收命令,0x40為接收數(shù)據(jù)。把這個(gè)高度重復(fù)的過程做到1個(gè)函數(shù)里,直接調(diào)用就好。

 
 
 
 
  1. // I2C協(xié)議 讀寫函數(shù) 只有寫需求, cd = 0 寫指令 cd = 1 寫數(shù)據(jù) byt 要寫入的值 
  2. void SSD1306_I2C_W(unsigned char cd, unsigned char byt)
  3. {
  4.     unsigned int state = 0; // I2C 運(yùn)行 狀態(tài)
  5.     WifiIotI2cIdx id = WIFI_IOT_I2C_IDX_0;  //I2C 通道 0
  6.     unsigned short deviceAddr = 0x78; // SSD1306 地址 
  7.     WifiIotI2cData i2cData = {0};   // 接收發(fā)送信息的數(shù)組 查 wifiiot_i2c.h 看詳細(xì)說明
  8.     unsigned char buf[] = {0x00, byt};  //默認(rèn) 0x00 寫入 指令集 byt 要寫入的指令  
  9.     if(cd == 1)     // 輸入 數(shù)據(jù)
  10.     {
  11.         buf[0] = 0x40;  // 0x40 表示寫入的是數(shù)據(jù) byt 就是要寫入的數(shù)據(jù)              
  12.     }
  13.     i2cData.sendBuf = buf;
  14.     i2cData.sendLen = 2;
  15.     state = I2cWrite(id, deviceAddr, &i2cData);
  16.     
  17.     if(state != WIFI_IOT_SUCCESS)
  18.     {
  19.         printf("[SSD1306_I2C_W] write error : < %d > !!! \r\n", state);   // 如果狀態(tài)異常 就打印 錯(cuò)誤信息
  20.     }
  21.     // return state;    // 也可以作為返回值 
  22. }

 驅(qū)動命令比較多,這是遇到的第一個(gè)障礙,看了手冊,還有網(wǎng)絡(luò)上各種例子,各式各樣,雖然大同小異但是更是一頭霧水。然后……以手冊流程圖為準(zhǔn)自己寫。不要怕,大膽試,好不好用試了才知道。

有的設(shè)置是需要成對出現(xiàn)的,一個(gè)命令配一個(gè)參數(shù),但是很多例子全部放在一起,一邊看參數(shù)一邊對照命令表……崩潰,雖然我現(xiàn)在也沒搞懂有些命令的功能,但是以手冊默認(rèn)值為準(zhǔn),最后運(yùn)行的很好。哈。

驅(qū)動流程:

大多例子都是默認(rèn)用頁顯示的方式,開始我也是用頁顯示的方式,用用就根據(jù)自己的需要改成水平方式了,建1個(gè)2維數(shù)組存放顯示的信息,顯示函數(shù)跟畫面函數(shù)分離,這樣做畫面的時(shí)候?qū)W⒆霎嬅?。這樣還有個(gè)好處,就是以后代碼的重用會比較方便。這樣做還是為了簡單實(shí)現(xiàn)在任意坐標(biāo)顯示,以后畫個(gè)波顯示更方便一點(diǎn)。

這里補(bǔ)充1點(diǎn),我開始按以前做小游戲的習(xí)慣做的畫面控制,單片機(jī)還是模塊好像都吃不消,看來還是越簡單越好。

然后要用到1個(gè)輔助工具,PCtoLCD2002完美版-(字符模式),這個(gè)字模工具超好用,這里向作者表示由衷的感情,讓最繁瑣的工作變得非常輕松。使用的時(shí)候注意點(diǎn)選項(xiàng)設(shè)置,主要是方向,寫段代碼測一下就好了。

SSD1306一次接收1個(gè)字節(jié)的數(shù)據(jù),表示對1列8個(gè)像素的開關(guān)控制,每個(gè)字節(jié)數(shù)據(jù)轉(zhuǎn)成二進(jìn)制代碼,比如0xFF二進(jìn)制1111 1111,每個(gè)1都代表點(diǎn)亮1個(gè)像素。0x00二進(jìn)制0000 0000,就是關(guān)閉8個(gè)像素。

AHT20

先看一下AHT20的技術(shù)手冊,這個(gè)手冊可以百度到(國內(nèi)最小的半導(dǎo)體溫濕度傳感器AHT20研發(fā)成功,百度的結(jié)果,哈哈),在官網(wǎng)還可以下載到它的例程,這個(gè)模塊功能很簡單,所以手冊看的很輕松。

列一下工作流程:

1、上電等待40ms

2、發(fā)送0x71 查看AHT20狀態(tài)指令

查看狀態(tài)值 [3] 是否為 1

如果為1可以發(fā)送測量指令

如果為0需要初始化:發(fā)送0xBE + 0x08 + 0x00初始化,初始化過程需要等待10ms

3、發(fā)送0xAC + 0x33 + 0x00測量指令,測量過程需要等待80ms

再發(fā)送0x71查看狀態(tài)值[7]是否為0,如果為0表示測量完畢,否者等待。

接收測量結(jié)果,收到7個(gè)字節(jié)的數(shù)據(jù)。

主要2個(gè)步驟,查狀態(tài),以及測量讀結(jié)果。

我看到一個(gè)文章講I2C協(xié)議是有專利權(quán)的,所以一般的產(chǎn)品使用這個(gè)協(xié)議都會或多或少的改一點(diǎn),但是基本過程是一樣的,并不會影響使用,這只是傳聞我并沒有證實(shí)。

AHT20地址0x38換成2進(jìn)制格式 111000,向左移1位結(jié)果是1110000,如果在最后1位就是[0]位設(shè)為0(1110000),就是發(fā)送讀信息,如果[0]位設(shè)置為1(1110001)就是寫信息。

公式:0x38<<1 | 0x1 = 0x70 寫地址; 0x38<<1 |0x0 = 0x71 讀地址;

 
 
 
 
  1. // 每個(gè)參數(shù) 都寫在函數(shù)里 是為了方便理解閱讀 最后做最終版 要盡量減少冗余操作
  2. // i2c寫入、讀出操作; rw=0 寫入 rw=1 讀出; *buff 數(shù)據(jù)數(shù)組,讀入就是指令集,返回就是空數(shù)組; leng 數(shù)組的長度 不可以為0; 
  3. void AHT20_I2C_RW(unsigned char rw, unsigned char *buff, unsigned int leng)
  4. {
  5.     unsigned int state = 0; // I2C 運(yùn)行 狀態(tài)值 ,單列出來是為了方便作為返回值 做判斷
  6.     WifiIotI2cIdx id = WIFI_IOT_I2C_IDX_0;  //設(shè)置I2C使用的通道
  7.     unsigned short writAddr = 0x70; // aht20 ((0x38<<1)|0x0)  寫入地址
  8.     unsigned short readAddr = 0x71; // aht20 ((0x38<<1)|0x1)  讀出地址   
  9.     
  10.     WifiIotI2cData i2cData = {0};   // 參考 wiffiiot_i2c.h 的說明 位置在 \base\iot_hardware\interfaces\kits\wifiiot_lite
  11.     if(rw == 0) // 寫入
  12.     {
  13.         i2cData.sendBuf = buff;      //unsigned char*    發(fā)送 數(shù)據(jù) 指針
  14.         i2cData.sendLen = leng;      //unsigned int      發(fā)送 數(shù)據(jù) 長度
  15.         state = I2cWrite(id, writAddr, &i2cData);   //i2c寫入方法 會有1個(gè)狀態(tài)返回值 WIFI_IOT_SUCCESS = 0 代表成功 出錯(cuò)會返回錯(cuò)誤代碼 需要加入 wifiiot_errno.h 頭文件        
  16.     }
  17.     else if(rw == 1) // 讀出
  18.     {
  19.         i2cData.receiveBuf = buff;   //unsigned char*    接收 數(shù)據(jù) 指針
  20.         i2cData.receiveLen = leng;   //unsigned int      接收 數(shù)據(jù) 長度
  21.         state = I2cRead(id, readAddr, &i2cData);    //i2c讀出方法       
  22.     }
  23.     if(state != WIFI_IOT_SUCCESS)   // 如果返回值 不等于 WIFI_IOT_SUCCESS 打印state 查詢 wifiiot_errno.h 看錯(cuò)哪了
  24.     {
  25.         printf("[AHT20_I2C_RW] ERROR !!! %d : %d \r\n", rw, state);    // 打印 錯(cuò)誤信息
  26.     }
  27. }

 這里要重點(diǎn)!重點(diǎn)!重點(diǎn)!的說一下,接收狀態(tài)值,不要!不要!不要!再發(fā)送0x71指令了,直接I2C讀,就會給你傳1個(gè)狀態(tài)值,默認(rèn)的狀態(tài)值的第[7]位是0,當(dāng)你發(fā)送測量指令的時(shí)候,會變成1,進(jìn)入測量狀態(tài),當(dāng)測量完以后會重新置為0。開始因?yàn)橐恢睙o法讀取到正確的狀態(tài)值……#¥%&#¥%#@%#,一言難盡啊,已經(jīng)過去了。

 
 
 
 
  1. // 返回 AHT20 的狀態(tài)值 i是第幾位 0~7 
  2. unsigned char AHT20_Status(unsigned char i)
  3. {
  4.     unsigned char buff[] = {0};
  5.     unsigned char leng = 1;
  6.     AHT20_I2C_RW(1, buff, leng);
  7.     unsigned char s;    //返回狀態(tài)值 0、1
  8.     s = (buff[0] >> i) & 0x01;  //狀態(tài)值是1個(gè)字節(jié)數(shù)據(jù),我們只需要知道某位的具體值就行 i就是第幾位
  9.     //printf("[AHT20_Status] AHT20_Status 0x%x [%d : %d] !!! \r\n", buff4[0], i, s);    // 此行 調(diào)試用 打印 狀態(tài)值
  10.     return s;
  11. }

結(jié)果會返回7個(gè)字節(jié)數(shù)據(jù),第1個(gè)字節(jié)是狀態(tài)信息,第2個(gè)、第3個(gè)、以及第4個(gè)字節(jié)的高4位[7][6][5][4]共同組成了濕度值,第4個(gè)字節(jié)的低4位[3][2][1][0]、第5位、第6位組成溫度值,最后第7個(gè)字節(jié)是效驗(yàn)位。

然后效驗(yàn),主要是目的是檢驗(yàn)接收到的數(shù)據(jù)是否在傳輸?shù)倪^程中出現(xiàn)錯(cuò)誤,具體原理和公式以及代碼不仔細(xì)說了,過程太碎了,幾次測試,代碼沒問題,具體過程百度一下好多資料,檢驗(yàn)的時(shí)候一定要包括第1個(gè)狀態(tài)字節(jié)。

檢驗(yàn)這步驟不是必須的,但是我開始的時(shí)候發(fā)現(xiàn)讀到的數(shù)錯(cuò)的離譜,開始不清楚是我代碼寫的問題還是模塊本身的問題,然后就把檢驗(yàn)這個(gè)步驟也寫上了,最后發(fā)現(xiàn)模塊沒問題,代碼也沒問題,問題是模塊上有個(gè)氣體傳感器會發(fā)熱,所以溫度值高。真的好暈。

最后一步就是就是把讀到的數(shù)值轉(zhuǎn)化為正常的10進(jìn)制值。

技術(shù)手冊濕度的公式后面有個(gè)%號,那個(gè)是濕度百分比的意思,我還糾結(jié)過×100%有啥意義呢?當(dāng)你沒最后完全走通的時(shí)候就像在黑暗里摸索,一個(gè)小坑都能把你絆的夠嗆。

轉(zhuǎn)化的過程要注意數(shù)值的類型,本來這個(gè)取值就是取很小的零頭,如果類型用錯(cuò)了,就給抹掉了。哈。

 
 
 
 
  1. // AHT20 測量溫濕度值
  2. unsigned char AHT20_Measure(float *ht)
  3. {
  4.     // 發(fā)送 測量指令
  5.     unsigned char buff1[] = {0xac, 0x33, 0x00};
  6.     unsigned char leng1 = 3;
  7.     AHT20_I2C_RW(0, buff1, leng1);  // 默認(rèn)狀態(tài)值第[7]位是0,發(fā)送完測量指令后狀態(tài)值[7]會置1
  8.     usleep(80*1000); //等待 80ms 時(shí)鐘偏快的 所以這個(gè)時(shí)間內(nèi) 總是不能完成測量
  9.     unsigned char t = 10;    //等待時(shí)間的值
  10.     while(AHT20_Status(7) != 0) //檢查 狀態(tài)值第[7]位是否從1變?yōu)?,如果沒變就等待5ms,如果已經(jīng)置0說明測量完成
  11.     {
  12.         usleep(10*1000);   //10ms 這個(gè)時(shí)間不要設(shè)置太長,也不要設(shè)置太短,太長時(shí)間,小器件很難長時(shí)間存儲測量結(jié)果,太短反復(fù)應(yīng)答也能會影響測量的穩(wěn)定性,一般情況等1次就會過 
  13.         if(--t == 0)  // 如果等待時(shí)間很長依然沒有變0,說明設(shè)備可能出現(xiàn)異常,為了避免死機(jī),返回0,重新測量 這個(gè)情況我還沒遇到
  14.         {
  15.             return 0;
  16.         }
  17.     }
  18.     // 接收 測量結(jié)果
  19.     unsigned char buff2[7] = {0};
  20.     unsigned char leng2 = 7;
  21.     AHT20_I2C_RW(1, buff2, leng2);  // 讀返回結(jié)果,一共7個(gè)字節(jié),第1個(gè)字節(jié)是狀態(tài)值 最后1個(gè)字節(jié)是效驗(yàn)值
  22.     unsigned char i, j;
  23.     unsigned char crc = 0xFF;   // 效驗(yàn) 初值
  24.     // CRC 效驗(yàn) 固定算法 
  25.     for(i=0; i<6; i++)
  26.     {
  27.         crc ^= (buff2[i]);
  28.         
  29.         for(j=8; j>0; --j)
  30.         {
  31.             if(crc & 0x80)
  32.             {
  33.                 crc = (crc << 1) ^ 0x31;
  34.             }
  35.             else
  36.             {
  37.                 crc = (crc << 1);
  38.             } 
  39.         }
  40.     }
  41.     if(buff2[6] != crc) //CRC值不對 說明傳輸過程可能有干擾 出錯(cuò)了
  42.     {
  43.         //printf(" CRC8 NO \r\n");
  44.         return 0;   // 效驗(yàn)不正確 回執(zhí)1個(gè)錯(cuò)誤信息
  45.     }
  46.     unsigned int dat1 = 0;  // 濕度
  47.     unsigned int dat2 = 0;  // 溫度
  48.     dat1 = (dat1 | buff2[1]) << 8;
  49.     dat1 = (dat1 | buff2[2]) << 8;
  50.     dat1 = (dat1 | buff2[3]) >> 4;
  51.     dat2 = (dat2 | buff2[3]) << 8;
  52.     dat2 = (dat2 | buff2[4]) << 8;
  53.     dat2 = (dat2 | buff2[5]) & 0xfffff;
  54.     // 這1大段搬來搬去的 主要是因?yàn)?nbsp;buff2[3] 前4位屬于濕度 后4位屬于 溫度
  55.     // 單片機(jī)處理能力有限,主要是針對寄存器值的處理,使用位運(yùn)算,這樣能節(jié)省算力
  56.     // 處理數(shù)據(jù) 單列出來 便于理解,代碼寫的太簡練 不容易看懂 最終 不需要這么繁瑣
  57.     float hum = 0;  // 溫度
  58.     float tem = 0;  // 濕度
  59.     // 2^20=1048576 要先類型轉(zhuǎn)換 暫時(shí)先這么寫 以后再改得順溜點(diǎn)
  60.     hum = ((float)dat1 / (float)1048576) * (float)100;                 // 濕度
  61.     tem = ((float)dat2 / (float)1048576) * (float)200 - (float)50;     // 溫度
  62.     ht[0] = hum;
  63.     ht[1] = tem;
  64.     return 1;   // 測量完畢
  65. }

 軟復(fù)位,無需關(guān)閉再次打開電源的情況下重新啟動傳感器。就是軟重啟,長時(shí)間停用的再次訪問的時(shí)候使用,我這個(gè)小應(yīng)用基本用不到,但是還是寫上吧。輸入指令0xBA需要等待20ms。

剩下工作就是把AHT20的代碼和SSD1306的代碼整合到一起。這里要說1點(diǎn),I2C支持串聯(lián)多個(gè)設(shè)備,所以AHT20和SSD1306在一條I2C線上共同使用是沒有問題的。

C語言從來不是我主要的使用語言,所以超級菜啊,一邊寫一邊看C 語言教程。裝個(gè)Dev-C++編譯器,有些功能先寫個(gè)測試代碼看看。開始寫代碼,不要考慮效率問題,就只想怎么更適合閱讀。開始寫主要的目的是試錯(cuò),寫一步編譯一步,不要一次寫全所有功能。

我覺得鴻蒙系統(tǒng)編譯報(bào)錯(cuò)功能非常好,我的每個(gè)錯(cuò)誤都能被準(zhǔn)確的指出來。開始專用的查BUG功能對新手來說很難,可以用printf串口打印功能就行,真的好用,因?yàn)轼櫭墒嵌嗳蝿?wù)系統(tǒng),所有的功能都是一個(gè)單獨(dú)的任務(wù),即使你的代碼運(yùn)行跑壞了,但是系統(tǒng)不會崩,打印功能依然會給你打印出信息來。第一次用真覺得好高級,能直接看到單片機(jī)的回話了。

最后不斷對代碼迭代優(yōu)化,最終的目的讓代碼可以更好的被重用。以后還要用的嘛,重復(fù)的工作就不要做了。編程過程對我這種新手來說,真是經(jīng)歷情況太多,開發(fā)過程發(fā)生的各種事以后以后單開一篇碎碎念再講吧。

知識有限又剛剛嘗試,所以肯定會有很多錯(cuò)誤,歡迎給我指正。開始覺得弄這個(gè)很簡單,但是很快被現(xiàn)實(shí)教育,然后開始認(rèn)真讀各位大佬的教程,收獲很大,這里由衷的感謝。

想了解更多內(nèi)容,請?jiān)L問:

和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos./#zz


本文標(biāo)題:鴻蒙Hi3861測溫濕度顯示一個(gè)新手開發(fā)調(diào)試過程
路徑分享:http://www.dlmjj.cn/article/dheoigp.html