真誠分享,技術交友,歡迎交流。
Hi,大家好,寫這篇文章的時候“IAMLIUBO的神奇物聯網之旅”專欄關注人數已經有186人了,首先很感謝大家的關注和相信,開始寫第一篇的文章的時候也沒想過會有這么多人感興趣,看到關注的人數越來越多,當然對文章的質量也要有更高的要求,力求每篇文章沒有錯別字和錯誤的敘述,也希望大家可以幫我一起勘誤。寫這些文章的目的就是希望可以幫助更多人,當然也收獲了很多知友,也有很多知友加我微信咨詢一些問題,希望我的每一個回答都對你有所幫助,這篇文章是外設篇的第一篇,也是知友小牛最近在微信咨詢我的一點問題,這里就記錄成文章跟大家分享一下。
閑話少說,咱們開始來說正事,相信大家在做一些實際項目或者小發明創造的時候,多多少少都會用過溫濕度傳感器,或者說會采集溫濕度數據加以展示和判斷處理,那么我們這里說的DHT11就是一款非常不錯的溫濕度傳感器,是廣州奧松電子生產的一款溫濕度傳感器,在開發當中我是用的比較多的,上一張圖給大家看一下。
當然,這款傳感器尺寸不是很小,做一些對尺寸要求比較嚴格的產品還是不怎么推薦的,一般做比較小的產品,還是比較推薦盛思瑞的SHT系列,當然價格就不是那么美麗了,但是精度相對來說還是比較不錯的,當然對大小和空間都有要求的話推薦美信的DS18B20。
下面我們來看一下這款傳感器的具體參數:
上面這三個表,就可以很直觀的看出這款傳感器的參數了,這里就不再做過多的文字敘述了。
了解完了這些基本的,那我們再來看一下我們是如何從傳感器取到溫濕度的值呢?說到這里,那就不得不提一個概念了,相信搞嵌入式開發的都知道,那就是單總線通信方式,那么什么是單總線通信方式呢?它與其他通信方式又有什么不同呢?下面我們先來看一下百科:
單總線是美國DALLAS公司推出的外圍串行擴展總線技術。與 SPI、 I²C串行數據通信方式不同.它采用單根 信號線,既傳輸 時鍾又傳輸數據,而且 數據傳輸是雙向的,具有節省I/O口線、資源結構簡單、成本低廉、便於總線擴展和維護等諸多優點。(百度百科)
簡單來說,就是雙方通信只通過一根線解決,不像其他通信方式有時鍾線和數據線,這樣的好處是極大的節省了I/O資源,當然這種通信方式的速率自然也會有所降低,像前面說的DS18B20也是采用單總線通信方式。
那么我們再來看一下DHT11通過單總線發送的數據格式,了解了數據格式可以更好的幫助我們理解程序是如何讀取溫濕度的,因為數據傳輸在硬件層就是電平高低,轉化為計算機語言就是0/1,我們就是通過DHT11發送的這些0/1中找出溫濕度的值:
可以看到一共是有40位數據的,每8位是一組,包含了溫濕度和校驗值,校驗值等於溫濕度的和,如果不理解,那么我們通過一個實際例子來看一下:
這就是一個完整的數據包過來,我們如何解包和驗證,那么我們看一下代碼:
1 static uint8_t ICACHE_FLASH_ATTR dht11ReadBit(void) 2 { 3 uint8_t retry=0; 4 5 while(DHT11_IN&&retry<100) //wait become Low level 6 { 7 retry++; 8 tempHumDelay(1); 9 } 10 11 retry=0; 12 while(!DHT11_IN&&retry<100) //wait become High level 13 { 14 retry++; 15 tempHumDelay(1); 16 } 17 18 tempHumDelay(40); //wait 40us 19 20 if(DHT11_IN) 21 return 1; 22 else 23 return 0; 24 } 25 26 static uint8_t ICACHE_FLASH_ATTR hdt11ReadByte(void) 27 { 28 uint8_t i; 29 uint8_t dat=0; 30 31 for (i=0; i<8; i++) 32 { 33 dat<<=1; 34 dat |= dht11ReadBit(); 35 } 36 37 return dat; 38 } 39 40 static uint8_t ICACHE_FLASH_ATTR dht11ReadData(u8 * temperature, u8 * humidity) 41 { 42 uint8_t i; 43 uint8_t buf[5]; 44 45 dht11Rst(); 46 if(0 == dht11Check()) 47 { 48 for(i=0; i<5; i++) 49 { 50 buf[i] = hdt11ReadByte(); 51 } 52 if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4]) 53 { 54 *humidity=buf[0]; 55 *temperature=buf[2]; 56 } 57 } 58 else 59 { 60 return 1; 61 } 62 63 return 0; 64 } 65 66 uint8_t ICACHE_FLASH_ATTR dh11Read(uint8_t * temperature, uint8_t * humidity) 67 { 68 uint8_t ret = 0; 69 uint8_t cur_i = 0; 70 uint8_t curTem = 0; 71 uint8_t curHum = 0; 72 uint16_t temMeans = 0; 73 uint16_t hum_means = 0; 74 75 ret = dht11ReadData(&curTem, &curHum); 76 77 if(0 == ret) 78 { 79 //Cycle store ten times stronghold 80 if(MEAN_NUM > temphum_typedef.th_num) 81 { 82 temphum_typedef.th_bufs[temphum_typedef.th_num][0] = curTem; 83 temphum_typedef.th_bufs[temphum_typedef.th_num][1] = curHum; 84 85 temphum_typedef.th_num++; 86 } 87 else 88 { 89 temphum_typedef.th_num = 0; 90 91 temphum_typedef.th_bufs[temphum_typedef.th_num][0] = curTem; 92 temphum_typedef.th_bufs[temphum_typedef.th_num][1] = curHum; 93 94 temphum_typedef.th_num++; 95 } 96 } 97 else 98 { 99 return 1; 100 } 101 102 if(MEAN_NUM <= temphum_typedef.th_num) 103 { 104 temphum_typedef.th_amount = MEAN_NUM; 105 } 106 107 if(0 == temphum_typedef.th_amount) 108 { 109 //Calculate Before ten the mean 110 for(cur_i = 0; cur_i < temphum_typedef.th_num; cur_i++) 111 { 112 temMeans += temphum_typedef.th_bufs[cur_i][0]; 113 hum_means += temphum_typedef.th_bufs[cur_i][1]; 114 } 115 116 temMeans = temMeans / temphum_typedef.th_num; 117 hum_means = hum_means / temphum_typedef.th_num; 118 119 *temperature = temMeans; 120 *humidity = hum_means; 121 } 122 else if(MEAN_NUM == temphum_typedef.th_amount) 123 { 124 //Calculate After ten times the mean 125 for(cur_i = 0; cur_i < temphum_typedef.th_amount; cur_i++) 126 { 127 temMeans += temphum_typedef.th_bufs[cur_i][0]; 128 hum_means += temphum_typedef.th_bufs[cur_i][1]; 129 } 130 131 temMeans = temMeans / temphum_typedef.th_amount; 132 hum_means = hum_means / temphum_typedef.th_amount; 133 134 *temperature = (uint8_t)temMeans; 135 *humidity = (uint8_t)hum_means; 136 } 137 138 return 0; 139 }
這就是我們讀溫濕度的主要代碼,大家可以看一下,我們是不是對40位的bit位的數據做了處理呢?后面我們會結合OLED顯示屏來實際測試一下,這里就先不做測試了。
說完了DHT11,我們再來了解一下SSD1306,其實這是一款OLED屏幕的控制芯片,目前在淘寶等常見的0.96吋小OLED顯示屏幕大多就是使用這款芯片的,當然還有一款功能一樣的替代芯片叫SHT1106,不過兩者指令兼容,所以會一款,另一款也就會了。這款主控芯片是香港晶門科技公司的,目前在一般小制作或者小創意上用這款主控的顯示屏幕還是比較多的,我也一直在使用,這款芯片最大只能控制128x64個像素點,所以有時候我們常說的OLED12864,多也是使用這塊主控的屏幕,當然這家公司也生產很多可以控制更多像素點的主控芯片,有興趣的可以去官網了解一下:
同樣的,我們先來了解一下,遺憾的是沒有找到官方的中文數據手冊,又怕某些翻譯的不太准確,所以這里就直接貼英文數據手冊的截圖了:
其中我們做開發需要關注的就是上面提到的兩種通信方式了,可以看到是支持IIC和SPI通信方式的,還支持亮度調節,我這里其實最近重新擼了一下驅動,之前也是一直拿來別人寫好的直接用,最近比較想自己動手擼一下,於是也借鑒別人寫的,自己看着數據手冊重新寫了一下,當然過程沒那么順利,我這里是使用IIC通信的,但是由於ESP8266沒有硬件IIC,這刷新效果也是有點感人,其實最重要的也是采用了全局刷新方式,沒有寫局部刷新的驅動,就導致每次修改某一個地方,都會把所有的像素點重新寫一遍~自然這FPS就降下來了,后面再考慮優化吧,但是通過整個過程也是對工作方式有了更深的了解,呃呃呃,又講遠了。
我們再來看一下主要特性:
我們再來看一下尺寸,可以說是非常薄了,我們是不可能用手焊接的,一般買也是買別人把芯片跟顯示屏封裝好的模組的:
可以看到厚度只有0.3mm,這里給大家看一下跟屏幕封裝好的模組,這也是前不久做一款STM32 Mini開發板的顯示擴展板剩下的:
按理說接下來我們應該講一下如何去驅動這款芯片了,但是相比於溫濕度傳感器來說,就有點復雜了,而且也比較難講清楚,不過這里推薦大家看一下杜洋老師對這款芯片的視頻講解,戳看片觀看:
分了好幾集,上面卡片是第一集,大家可以看一下,個人認為講的還是比較不錯的。
下面我們來看一下我這擼的驅動代碼的,目前支持IIC通信和中英文顯示,由於手頭沒有SPI接口的屏幕,也沒法測試,所以有機會再更新SPI通信方式的。
1 /* 2 * MIT License 3 * 4 * Copyright (c) 2018 imliubo 5 * 6 * Github https://github.com/imliubo 7 * Website https://www.makingfun.xyz 8 * Zhihu https://www.zhihu.com/people/MAKINGFUNXYZ 9 * 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 * of this software and associated documentation files (the "Software"), to deal 12 * in the Software without restriction, including without limitation the rights 13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 * copies of the Software, and to permit persons to whom the Software is 15 * furnished to do so, subject to the following conditions: 16 * 17 * The above copyright notice and this permission notice shall be included in all 18 * copies or substantial portions of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 * SOFTWARE. 27 */ 28 #include "modules/ssd1306.h" 29 30 #if defined(SSD1306_USE_I2C) 31 int ICACHE_FLASH_ATTR 32 HAL_I2C_Mem_Write( uint16_t DevAddress, uint16_t MemAddress, uint8_t pData, uint16_t Size ) 33 { 34 i2c_master_start(); 35 i2c_master_writeByte( DevAddress ); 36 if ( !i2c_master_checkAck() ) 37 { 38 i2c_master_stop(); 39 return(0); 40 } 41 42 i2c_master_writeByte( MemAddress ); 43 if ( !i2c_master_checkAck() ) 44 { 45 i2c_master_stop(); 46 return(0); 47 } 48 49 i2c_master_writeByte( pData ); 50 if ( !i2c_master_checkAck() ) 51 { 52 i2c_master_stop(); 53 return(0); 54 } 55 56 i2c_master_stop(); 57 return(1); 58 } 59 60 61 void ssd1306_Reset( void ) 62 { 63 /* for I2C - do nothing */ 64 } 65 66 67 /* Send a byte to the command register */ 68 void ssd1306_WriteCommand( uint8_t byte ) 69 { 70 HAL_I2C_Mem_Write( SSD1306_I2C_ADDR, 0x00, byte, 1 ); 71 } 72 73 74 /* Send data */ 75 void ssd1306_WriteData( uint8_t buffer, size_t buff_size ) 76 { 77 HAL_I2C_Mem_Write( SSD1306_I2C_ADDR, 0x40, buffer, buff_size ); 78 } 79 80 #elif defined(SSD1306_USE_SPI) 81 82 #else 83 #error "You should define SSD1306_USE_SPI or SSD1306_USE_I2C macro" 84 #endif 85 86 87 /* Screenbuffer */ 88 static uint8_t SSD1306_Buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; 89 90 /* Screen object */ 91 static SSD1306_t SSD1306; 92 93 /* Initialize the oled screen */ 94 void ssd1306_Init( void ) 95 { 96 /* Reset OLED */ 97 ssd1306_Reset(); 98 99 /* Wait for the screen to boot */ 100 os_delay_us( 60000 ); 101 os_delay_us( 40000 ); 102 103 /* // Init OLED */ 104 ssd1306_WriteCommand( 0xAE ); /* display off */ 105 106 ssd1306_WriteCommand( 0x20 ); /* Set Memory Addressing Mode */ 107 ssd1306_WriteCommand( 0x10 ); /* 108 * 00,Horizontal Addressing Mode; 01,Vertical Addressing Mode; 109 * 10,Page Addressing Mode (RESET); 11,Invalid 110 */ 111 112 ssd1306_WriteCommand( 0xB0 ); /* Set Page Start Address for Page Addressing Mode,0-7 */ 113 114 #ifdef SSD1306_MIRROR_VERT 115 ssd1306_WriteCommand( 0xC0 ); /* Mirror vertically */ 116 #else 117 ssd1306_WriteCommand( 0xC8 ); /* Set COM Output Scan Direction */ 118 #endif 119 120 ssd1306_WriteCommand( 0x00 ); /* ---set low column address */ 121 ssd1306_WriteCommand( 0x10 ); /* ---set high column address */ 122 123 ssd1306_WriteCommand( 0x40 ); /* --set start line address - CHECK */ 124 125 ssd1306_WriteCommand( 0x81 ); /* --set contrast control register - CHECK */ 126 ssd1306_WriteCommand( 0xFF ); 127 128 #ifdef SSD1306_MIRROR_HORIZ 129 ssd1306_WriteCommand( 0xA0 ); /* Mirror horizontally */ 130 #else 131 ssd1306_WriteCommand( 0xA1 ); /*--set segment re-map 0 to 127 - CHECK */ 132 #endif 133 134 #ifdef SSD1306_INVERSE_COLOR 135 ssd1306_WriteCommand( 0xA7 ); /* --set inverse color */ 136 #else 137 ssd1306_WriteCommand( 0xA6 ); /* --set normal color */ 138 #endif 139 140 ssd1306_WriteCommand( 0xA8 ); /* --set multiplex ratio(1 to 64) - CHECK */ 141 ssd1306_WriteCommand( 0x3F ); /* */ 142 143 ssd1306_WriteCommand( 0xA4 ); /* 0xa4,Output follows RAM content;0xa5,Output ignores RAM content */ 144 145 ssd1306_WriteCommand( 0xD3 ); /* -set display offset - CHECK */ 146 ssd1306_WriteCommand( 0x00 ); /* -not offset */ 147 148 ssd1306_WriteCommand( 0xD5 ); /* --set display clock divide ratio/oscillator frequency */ 149 ssd1306_WriteCommand( 0xF0 ); /* --set divide ratio */ 150 151 ssd1306_WriteCommand( 0xD9 ); /* --set pre-charge period */ 152 ssd1306_WriteCommand( 0x22 ); /* */ 153 154 ssd1306_WriteCommand( 0xDA ); /* --set com pins hardware configuration - CHECK */ 155 ssd1306_WriteCommand( 0x12 ); 156 157 ssd1306_WriteCommand( 0xDB ); /* --set vcomh */ 158 ssd1306_WriteCommand( 0x20 ); /* 0x20,0.77xVcc */ 159 160 ssd1306_WriteCommand( 0x8D ); /* --set DC-DC enable */ 161 ssd1306_WriteCommand( 0x14 ); /* */ 162 ssd1306_WriteCommand( 0xAF ); /* --turn on SSD1306 panel */ 163 164 165 /* Clear screen */ 166 ssd1306_Fill( Black ); 167 168 /* Flush buffer to screen */ 169 ssd1306_UpdateScreen(); 170 171 /* Set default values for screen object */ 172 SSD1306.CurrentX = 0; 173 SSD1306.CurrentY = 0; 174 175 SSD1306.Initialized = 1; 176 } 177 178 179 /* Fill the whole screen with the given color */ 180 void ssd1306_Fill( SSD1306_COLOR color ) 181 { 182 /* Set memory */ 183 uint32_t i; 184 185 for ( i = 0; i < sizeof(SSD1306_Buffer); i++ ) 186 { 187 SSD1306_Buffer[i] = (color == Black) ? 0x00 : 0xFF; 188 } 189 } 190 191 192 /* Write the screenbuffer with changed to the screen */ 193 void ssd1306_UpdateScreen( void ) 194 { 195 uint8_t i, j; 196 for ( i = 0; i < 8; i++ ) 197 { 198 ssd1306_WriteCommand( 0xB0 + i ); 199 ssd1306_WriteCommand( 0x00 ); 200 ssd1306_WriteCommand( 0x10 ); 201 for ( int j = 0; j < 128; j++ ) 202 { 203 /* code */ 204 ssd1306_WriteData( SSD1306_Buffer[j + SSD1306_WIDTH * i], SSD1306_WIDTH ); 205 } 206 } 207 } 208 209 210 /* 211 * Draw one pixel in the screenbuffer 212 * X => X Coordinate 213 * Y => Y Coordinate 214 * color => Pixel color 215 */ 216 void ssd1306_DrawPixel( uint8_t x, uint8_t y, SSD1306_COLOR color ) 217 { 218 if ( x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT ) 219 { 220 /* Don't write outside the buffer */ 221 return; 222 } 223 224 /* Check if pixel should be inverted */ 225 if ( SSD1306.Inverted ) 226 { 227 color = (SSD1306_COLOR) !color; 228 } 229 230 /* Draw in the right color */ 231 if ( color == White ) 232 { 233 SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8); 234 } else { 235 SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8) ); 236 } 237 } 238 239 240 /* 241 * Draw 1 char to the screen buffer 242 * ch => char om weg te schrijven 243 * Font => Font waarmee we gaan schrijven 244 * color => Black or White 245 */ 246 char ssd1306_WriteChar( char ch, FontDef Font, SSD1306_COLOR color ) 247 { 248 uint32_t i, b, j; 249 250 /* Check remaining space on current line */ 251 if ( SSD1306_WIDTH <= (SSD1306.CurrentX + Font.FontWidth) || 252 SSD1306_HEIGHT <= (SSD1306.CurrentY + Font.FontHeight) ) 253 { 254 /* Not enough space on current line */ 255 return(0); 256 } 257 258 /* Use the font to write */ 259 for ( i = 0; i < Font.FontHeight; i++ ) 260 { 261 os_printf("Font: %d\n",ch); 262 b = Font.data[(ch - 32) * Font.FontHeight + i]; 263 for ( j = 0; j < Font.FontWidth; j++ ) 264 { 265 if ( (b << j) & 0x8000 ) 266 { 267 ssd1306_DrawPixel( SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) color ); 268 } else { 269 ssd1306_DrawPixel( SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) !color ); 270 } 271 } 272 } 273 274 /* The current space is now taken */ 275 SSD1306.CurrentX += Font.FontWidth; 276 277 /* Return written char for validation */ 278 return(ch); 279 } 280 281 /* 282 * Draw 1 Chinese char to the screen buffer 283 * ch => char 284 * color => Black or White 285 */ 286 char ssd1306_WriteZhChar( signed char ch[], SSD1306_COLOR color ){ 287 288 289 uint32_t b, j, k, data[32]; 290 291 /* Check remaining space on current line */ 292 if ( SSD1306_WIDTH <= (SSD1306.CurrentX + 16) || 293 SSD1306_HEIGHT <= (SSD1306.CurrentY + 16) ) 294 { 295 /* Not enough space on current line */ 296 return(0); 297 } 298 299 for (int i = 0; i < 20; ++i) 300 { 301 if ((ZhFont16x16[i].Index[0] == ch[0]) && (ZhFont16x16[i].Index[1] == ch[1]) && (ZhFont16x16[i].Index[2] == ch[2]) ) 302 { 303 for (int z = 0; z < ZH_CN_HEIGHT_WIDTH; z++) 304 { 305 data[z] = ZhFont16x16[i].Msk[z]; 306 307 } 308 } 309 } 310 311 for ( int i = 0; i < ZH_CN_HEIGHT_WIDTH; i++ ) 312 { 313 314 b = data[i]; 315 for ( j = 0; j < ZH_CN_HEIGHT_WIDTH; j++ ) 316 { 317 //os_printf("Font: %d\n",Font.FontWidth); 318 if ( (b << j) & 0x8000 ) 319 { 320 ssd1306_DrawPixel( SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) color ); 321 } else { 322 ssd1306_DrawPixel( SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) !color ); 323 } 324 } 325 } 326 327 /* The current space is now taken */ 328 SSD1306.CurrentX += ZH_CN_HEIGHT_WIDTH; 329 330 /* Return written char for validation */ 331 return(*ch); 332 } 333 334 335 /* Write full string to screenbuffer */ 336 char ssd1306_WriteString( char* str, FontDef Font, SSD1306_COLOR color ) 337 { 338 /* Write until null-byte */ 339 while ( *str ) 340 { 341 if ( ssd1306_WriteChar( *str, Font, color ) != *str ) 342 { 343 /* Char could not be written */ 344 345 return(*str); 346 } 347 348 /* Next char */ 349 str++; 350 } 351 352 /* Everything ok */ 353 return(*str); 354 } 355 356 /* Write full Chinese string to screenbuffer */ 357 char ssd1306_WriteZhString( signed char *str, SSD1306_COLOR color ){ 358 359 /* Write until null-byte */ 360 while ( *str ) 361 { 362 ssd1306_WriteZhChar( (signed char *)str, color ); 363 /* Next char */ 364 str = str + 3; 365 366 } 367 /* Everything ok */ 368 return(*str); 369 370 } 371 372 373 /* Position the cursor */ 374 void ssd1306_SetCursor( uint8_t x, uint8_t y ) 375 { 376 SSD1306.CurrentX = x; 377 SSD1306.CurrentY = y; 378 }
為了漢字顯示的和諧統一,目前僅支持16*16點陣的宋體,漢字顯示效果如下:
缺點:
- 不支持中英文混顯,就是寫中文和英文都有單獨的函數,如果想在一行中顯示中英文就可能需要分開寫。
- 刷新速度慢,其實這里也不全是全局刷新的原因,也是ESP8266沒有硬件IIC,只能使用模擬IICd的原因。
最后我們結合一下DHT11和SSD1306做一個桌面溫濕度顯示儀,主要代碼:
1 uint8 temp,humd; 2 os_timer_t DHT11_Read_timer; 3 4 void ICACHE_FLASH_ATTR DHT11_Read(void) 5 { 6 char temp_buffer[10],humd_buffer[10]; 7 os_timer_disarm(&DHT11_Read_timer);//取消定時器 8 dh11Read(&temp,&humd); 9 os_printf("Temp: %d'C Humd: %d%\n",temp,humd); 10 os_sprintf(temp_buffer,":%d'",temp); 11 os_sprintf(humd_buffer,":%d%",humd); 12 ssd1306_SetCursor(0, 40); 13 ssd1306_WriteZhString("溫度",White); 14 ssd1306_SetCursor(30, 44); 15 ssd1306_WriteString(temp_buffer,Font_7x10,White); 16 ssd1306_SetCursor(64, 40); 17 ssd1306_WriteZhString("濕度",White); 18 ssd1306_SetCursor(94, 44); 19 ssd1306_WriteString(humd_buffer,Font_7x10,White); 20 ssd1306_UpdateScreen(); 21 os_timer_arm(&DHT11_Read_timer, 5000, true);//使能定時器 22 } 23 24 void user_init(void) 25 { 26 uart_init(115200, 115200); 27 28 dh11Init(); 29 30 i2c_master_gpio_init(); 31 32 ssd1306_Init(); 33 34 ssd1306_Fill(Black); 35 ssd1306_SetCursor(8, 0); 36 ssd1306_WriteZhString("神奇物聯網之旅",White); 37 ssd1306_SetCursor(20, 16); 38 ssd1306_WriteString("IAMLIUBO",Font_11x18,White); 39 ssd1306_UpdateScreen(); 40 41 os_timer_disarm(&DHT11_Read_timer);//取消定時器 42 os_timer_setfn(&DHT11_Read_timer, (os_timer_func_t *) DHT11_Read,NULL);//定時回調函數 43 os_timer_arm(&DHT11_Read_timer, 5000, true);//使能定時器,設置時間為1s 44 45 }
我們直接看一下效果:
最后附上我的Github倉庫,一些小的Demo沒有寫相應的文章,但是不斷更新,建議大家Star或者watch一下,以便及時獲得新的例程~
歡迎大家去我的倉庫點個star,有問題或者Bug可以提交issues,我看到后會第一時間回復,如果您對我的代碼有改進意見,也歡迎fork后提交PR,我會及時采納大家的意見。
夜深了,晚安。
QQ交流群:592587184