前言

其實是從零件盒翻到一顆一年前買的 BMP180,這個很普遍的大氣壓力感測器。想說,好吧,來玩玩這顆感測器吧,順便豐富一下我們 Flash FORTH 的函式庫吧。

利用人家已經寫好的函式庫,然後 #include 進來用,這太 Low 了啦。來練練功,試看看只靠 Data Sheet 裡的資料,能否順利寫出底層可以跟 BMP180 溝通的程式碼呢?就這樣抱著試看看的心態,(鍵盤)東敲敲,西打打的,最後還是順利寫出來囉,蠻有趣的。

其實,以前就對那種利用氣壓計來做天氣預測的電子晴雨計非常著迷。只要有機會出國的話,總是會買個帶回來,放在客廳。出門前總是會多看幾眼,瞧瞧它的預測,看是要下雨了,還是會是個大晴天來當個參考。當然,現在網路時代,即時氣象預測應該都比這種老古董準確多囉。但,客廳擺個電子溫度計晴雨計的個人小氣象台,瞬間質感就增加很多。☺️

沒想到現在可以自己做了呢, DIY Maker 萬歲!

 

BMP180

BMP180 溫度大氣壓力感測器,是最新 BOSCH 所推出的溫度氣壓傳感器,用來代替舊型的 BMP085。

其性能非常高,主要鎖定在智慧手機,平板電腦和運動設備等高級移動設備的應用。它主要遵循舊型的 BMP085 但增加許多改進,如較小的尺寸和數位匯流排的擴展。
超低功耗低至3μA,絕對精度最低可以達到0.03hPa,使BMP180成為移動設備節能的領導者。BMP180的獨特之處在於其獨立於電源電壓的非常穩定的行為(性能)。其採用強大的8-pin陶瓷無引線晶片承載(LCC)超薄封裝,可以通過I2C匯流排直接與各種微處理器相連接。


主要特點:

  • 壓力範圍:300~1100hPa(海拔9000米~-500米)
  • 電源電壓:1.8V~3.6V(VDDA),1.62V~3.6V(VDDD)
  • LCC8封裝:無鉛陶瓷載體封裝(LCC)
  • 尺寸:3.6mmx3.8×0.93mm
  • 低功耗:5μA,在標準模式
  • 高精度:低功耗模式下,解析度為0.06hPa(0.5米)
  • 高線性模式下,解析度為0.03hPa(0.25米),且具有溫度補償
  • 含溫度輸出
  • I2C介面
  • MSL 1反應時間:7.5ms
  • 待機電流:0.1μA
  • 無需外部時鐘電路


典型應用:

  • 室內室外導航,GPS 精確導航輔助
  • 休閒、體育和醫療健康等監測
  • 天氣預報
  • 垂直速度指示(上升/下沉速度)
  • 風扇功率控制

HipstamaticPhoto-650561598.226563.jpg

HipstamaticPhoto-650561629.396473.jpg

 

 

硬體接線

最近學到的技巧,兩台 I2C 裝置要對連的話,只要簡單 SDA 接 SDA,SCL 接 SCL 就可以囉,不需要上拉電阻。然後這個 BMP180,它的 I2C 的 SlaveID 是 0x77

要注意的 BMP180 是吃 3.3V 的電源的,所以 Vcc 要接到 Arduino Uno 3.3V 那裡,不要錯接到 5V 囉。

內部方塊圖

BMP180 block.png

 

IMG_7004.png

 

FORTH 程式解說

看圖說故事囉,看 Datasheet 寫程式碼!一切的一切,從這個原廠的 Datasheet 開始

BMP180 很壞的地方是,它提供了很多校正常數。只讀出感測器的溫度和壓力的數值是不準確的,需要利用這些 BMP180 內部的校正常數,透過演算法的計算,才能算出正確的溫度和壓力的數值。 BMP180 內部並沒有 MCU,所以這麼複雜的計算它是無能為力的。也就是說,沒有免費的午餐,你要幫它在你自己的 MCU 裡實作這些演算法才能正確得到最終溫度跟壓力的數值。

Datasheet 提供了詳細的流程跟C語言整數運算的算式和範例。你要一步一步的照這些整數運算的算式,逐步演算後才會得到正確的溫度和壓力的數值。但是,詳細的校正常數的校正原理,Bosch 並沒有公開。所以所有的算式只能照著 Datasheet 去實做啦,而這些公式怎麼來的,這一切都是秘密!

所以,照 Data Sheet

第一步,讀出所有校正常數

step1 read calibration data.png

校正常數一共 11 個單整數的數值,(AC1, AC2..., AC6, B1, B2, MB, MC, MD)

我們可以用之前所開發的 I2C 的指令 regsRead ( start-addr n -- data1 data2 ... datan) 一次讀完,送到 FORTH 的參數堆疊來。不過因為 Arduino Uno 的記憶體很有限,所以 Falsh FORTH 的堆疊無法太大,經過測試,這樣會垮掉的。

所以可以一次用 regRead ( addr -- data) 讀一個暫存器的值,這樣比較慢,但比較保險。或是依舊使用 regsRead,但一次讀少一點,避免參數堆疊跨掉。

這裡我們依舊採用 regsRead 一次讀多個參數,但分開兩次讀取。

 

FORTH 有兩種變數的使用方式,

第一種是經典的 variable [Name] ,使用的時候會回傳變數的位址,然後用 @ 取出數值,或 ! 存入數值。

第二種是比較新的,有點可變常數的意涵的 XXX value [Name],使用的時候會不回傳變數的位址了,而是直接是變數的值。然後可以用 XXX to [Name] 用 to 把新數值存入變數中。

這裡校正常數看不出來有需要得到它們變數位址的需要,所以採用第二種方法來定義校正常數。

為了測試用,所以一開始的值是 Datasheet 裡面的範例數值,這樣也好方便測試所編寫的數學公式程式碼有無錯誤。

   408  value AC1
   -72  value AC2
-14383  value AC3
 32741  value AC4
 32757  value AC5
 23153  value AC6
  6190  value B1
     4  value B2
-32768  value MB
 -8711  value MC
  2868  value MD

I2C 一次只能傳一個 byte的數值,且Datasheet 寫了, MSB first。所以 regsRead 讀的時候, MSB 會先,然後才是 LSB。利用下列程式碼將這兩個 byte 變成完整 16bit 的 FORTH 單整數數值。

: >num ( msb lsb -- num)
   swap 8 lshift or
;

就 msb 左移 8 bits 後跟 lsb OR運算一下就是囉。

 

前面說了,這裡我們依舊採用 regsRead 一次讀多個參數,但分開兩次讀取。

第一次從 0xAA 開始,總共讀 12 bytes。 (所以是 AC1, AC2, AC3..., AC6)

第二次從 0xB6 開始,總共讀 10 bytes。 (所以是 AC1, AC2, AC3..., AC6)

 

: calib!  ( --)     \ read calibration data
   $aa 12 regsRead
   >num to AC6
   >num to AC5
   >num to AC4
   >num to AC3
   >num to AC2
   >num to AC1
   
   $b6 10 regsRead
   >num to MD
   >num to MC
   >num to MB
   >num to B2
   >num to B1
;

就讀出 2bytes 後, >num 合併成一個單整數,然後存入對應變數中,提供後面計算使用。

 

第二步,讀出未校正溫度值 UT

temp raed.png

 

: rawT@ ( -- temp.raw)   \ read raw temperature data
   $2e $f4 regWrite
   5 ms
   $f6   2 regsRead
   >num
;

看圖說故事,先在 0xF4 暫存器寫入 0x2E 觸發溫度開始轉換後,等 4.5 ms (這裡我們等 5 ms) 後於暫存器 0xF6 (MSB) 0xF7 (LSB) 讀出結果。

一樣,用 >num 將兩個 bytes 的結果合併成一個單整數後完成,這個數字 Data Sheet 稱它為 UT。

 

第三步,讀出未校正壓力值 UP

pressure read.png

 

: rawP@ ( -- pressure.raw) \ read raw pressure data
   $34 $f4 regWrite
   50 ms
   $f6   2 regsRead
   >num 0  
;

寫入 0x34 資料至暫存器 0xF4 觸發壓力感測器轉換。 要注意的,這裡有個 OSS 過取樣的參數,BMP180 支援過取樣(Over-Sampling) 的技術來增加數值的精確度。如果要使用的話, OSS 參數需左移6bits 後合併至 0x34 。

這裡為了簡單,都不使用 Over-Sampling,所以 OSS = 0 。

觸發壓力轉換後,需要等待轉換完畢後在 0xF6, 0xF7, 0xF8 取出轉換後的結果。 BMP180 為了彈性,設計了很多模式。每種模式得到的精確度跟轉換時間的不一樣。這裡為了保險,採用了最大的等待時間,不會超過 50 ms ,所以等待 50 ms 後來取值。

未校正的壓力值被稱為 UP ,為一個 24 bits 的數字,所以需要一個 32 bits 的雙整數來容納它。

所以是 0xF6 (MSB) 左移 16位元,0xF7 (LSB) 左移 8位元,再跟 0xF8 (XLSB) 合併成 24位元的數字,但因為我們不用 Over-Sampling,所以 OSS =0 , 8 - OSS = 8 - 0 = 8

所以24位元的數字要右移8位元。這結果就是 MSB.LSM 一個 16位元的單整數囉。 所以用 >num 來合併就可以囉。而 0xF8 (XLSB) 可以直接丟掉不用。

最後為了彈性,補個 0 讓 UP 還是一個 FORTH 系統裡面不帶符號的雙整數。

 

第四步,根據校正參數,由 UT 計算出正確的溫度值

temp calc.png

呵呵,好玩的來囉。

來好好練習一下 FORTH 的固定整數運算的技巧囉。

這裡一切先求有,先不考慮最佳化。 Data Sheet 的這些公式都是以 C 語言為基礎的。C語言這類的語言都是以變數為主來運作的語言,跟我們 FORTH 是以堆疊為主的語言很不一樣,但也只能將就了。

所以這裡看圖說故事, Data Sheet 怎麼說,我們就怎麼做,可讀性就只能放棄囉。

所以

: X1 ( UT -- X1)
   AC6 -  AC5  32768  u*/mod nip
;

就 X1 = (UT - AC6) * AC5 / 2^15

給定 UT 把 X1 算出來。 2^15 = 32768 ,很直接的 UT 和 AC6 相減後 AC5/32768 的一個不帶符號的比例乘法 u*/mod 就搞定囉!

 

: X2 ( X1 -- X2)
   MD +   MC 2048 rot */
;

就 X2 = MC * 2^11 / (X1 + MD)

給定 X1 算出 X2 來。 2^11 = 2048 ,也是很直接的 MC 來跟 2048/(X1+MD) 來個帶符號的比例乘法 */ 就搞定囉!

 

: B5 ( UT -- B5)
   X1  dup X2  +
;

就 B5 = X1 + X2

給定 UT 算出 X1,複製給定 X1 算出 X2,然後 X1 跟X2 相加!

 

: >Celsius ( UT -- Celsius) \ unit: in 0.1C
   B5 8 + 16 /
;

就 T = (B5 + 8) / 2^4 ,最終結果,校正過後的真實攝氏溫度。

給定 UT 算出 B5 加 8 後除以 16 就是最終結果!

 

第五步,根據校正參數,由 UT 及 UP 計算出正確的壓力值

pressure calculation.png

最複雜的來了,來根據 UT 及 UP 及校正常數,逐步算出正確的壓力值吧!

: B6 ( B5 -- B6)
   4000 -
;

就 B6 = B5 - 4000 ,簡單明暸!

 

: X1.1 ( B6 -- X1)
   dup 4096 */   B2  2048  */
;

就 X1 = (B6 * B6 / 2^12) * B2 / 2^11

給定 B6 ,複製一下跟 4096 來個 */ 的比例運算 ( 2^12 = 4096)

然後結果來跟 B2/2048 再來一次 */ 的比例運算就是囉 (2^11 = 2048)

這裡要先抱怨一下的,一般的 FORTH 系統,正常是允許重複定義指令的。也就是說,當已經有一個指令被定義在 FORTH 的字典中,這時候假如再重複定義一個相同名字的指令,這是可以被允許的。只是已經被編譯過的不受影響,但新的指令如果再被用到的話,新的會蓋過舊的指令。

然後我們 Flash FORTH 的開發者,不知道哪根筋不對了。這麼優良的傳統居然給它屏棄掉了,已經定義過的指令不允許再次重新定義過。

哎哎哎,只好用很憋扭的名字,將 X1 用 X1.1 來代替囉! 

 

: X2.1 ( B6 -- X2)
   AC2 2048 */
;

就 X2 = AC2 * B6 /2^11 ,一次 */ 比例運算搞定!

 

: X3.1 ( B6 -- X3)
   dup X1.1  swap X2.1  +
;

就 X3 = X1 + X2

 

: B3 ( B6 -- B3) 
   X3.1 AC1 4 * + 2+ 4 /
;

就 B3 = (((AC1*4 + X3) <<OSS) + 2) / 4

這裡 OSS = 0 ,因為我們不使用 Over-Sampling。 所以不用左移任何位元。

所以公式為 B3 = (AC1*4 + X3 + 2) / 4

...

中間一堆算式,都是用類似的手法轉成 FORTH 語言程式碼,就不介紹囉。

挑比較重要的,

: B7 ( d.UP B6 -- d.p0)
  >r  r@ B3  0  d-
  d2*   50000 ud*
  r> B4  ud/mod  rot drop
;

就看起來很複雜的 B7 = ((unsigned long)UP - B3) * (50000 >>OSS) if (B7<0x80000000) { p = (B7*2)/B4} else { p=(B7/B4)*2}

這段的 C語言程式碼,把 UP 轉型成不帶符號長整數 (等於FORTH 雙整數) 進行運算,後面的 if 判斷是怕數字太大而溢位,所以先乘後除,改成先除後乘,只是這樣而已。

這裡,主角 UP 開始出場了。

要注意啊, UP 是個雙整數(兩個單整數組成,共 32bits) 的喲。所以這裡是很好的機會,來練習一下經典 FORTH 裡面雙整數的算數運算。

首先堆疊裡有一個 d.UP 雙整數,跟 B6 單整數。 >r 先把礙眼的 B6 推到返回堆疊裡面暫存一下。

r@ B3 給定 B6 把 B3 算出來。 放個 0 把B3單整數變成不帶符號的雙整數。

d- ( d1 d2 -- d1-d2) 是個雙整數減法的指令, 所以就是 d.UP - d.B6

d2* ( d -- 2*d) 是個雙整數乘上2的指令,所以就是 (d.UP - d.B6)*2

ud* ( ud u -- ud*u) 這個在 FORTH 裡面,我們叫做混合算數的指令。就一個不帶符號雙整數ud和一個不帶符號單整數u的相乘,結果是不帶符號的雙整數ud'=ud*u

所以 50000 ud* 後就是 (d.UP - d.B6)*2*50000

r> B4 ,給定 B6 算出 B4  

ud/mod ( ud u -- ur udq) 又是一個混合算數的指令,就一個不帶符號雙整數ud和一個不帶符號單整數u的相除,結果是不帶符號的單整數餘數 ur ,及一個依舊是不帶符號雙整數的商 udq

所以 r> B4  ud/mod  rot drop   其實就是將前面雙整數的結果除以 B4,只留商,將餘數丟掉。所以就是 (d.UP - d.B6)*2*50000/B4,這個就是由B7算出來的 p 啦,是個雙整數的結果。

 

最後

: >Pressure ( d.UP UT -- d.Pressure)
   B5 B6 B7          ( d.p0)
   2dup  X1.3  >r    ( d.p0 R:x1)
   2dup  X2.3  r>    ( d.p0 x2 x1)
   swap -  3791 +  16 /
   m+ 
;

串連全部啦,給定 UP UT 然後算出正確壓力值囉!

就 p = p + (X1 + X2 + 3791 ) / 2^4

相信前面的解說之下,現在這段你應該是看得懂的!要提醒的,原來前面X2是要乘上 -7357 的負的數值的。這裡用了小技巧,前面乘上 7357 正的值,而在這裡,用相減的方式而不是相加來彌補,這樣可以盡量用到不帶符號雙整數的最大位數。

就這樣,所有計算完畢,正確壓力值就出來囉。

 

: BMP180@ ( -- Pa Celsius)
   rawT@ >r
   rawP@  r@ >Pressure
          r> >Celsius
;

透過 I2C 觸發 BMP180 工作,取出溫度跟壓力,然後透過校正常數計算出正確壓力跟溫度的數值。

 

: main ( --)
   init
   
   begin
     BMP180@
     ." Temperature = " 0 <# # [char] . hold #s #> type ." C , "
     ." Pressure = "    d.   ." Pa"   cr
   again
;

主測試程式碼,不斷觸發 BMP180 工作,取出正確壓力,溫度數值後列印。
 

測試結果,

還不錯的啦!溫度 (29.5C) 跟小米的溫度計所量到的 (29.3C) 相比較,誤差只有 0.2C 左右。

壓力 (996.3hPa) 跟 Apple Watch 所量測到的 (992.5 hPa) 相比較,大概很穩定的相差約 4 hPa 左右。真的是不錯的呀,可以拿來進一步製作家庭小氣象台,晴雨計的囉!

IMG_7005D.png

 

測試影片

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Frank Lin(@ohiyooo)分享的貼文

 

 

 

原始程式碼

\
\  BMP180 Pressure Sensor
\    Frank Lin 2021.08.08
\

marker --i2c--

decimal

\
\  ARduino I2C 
\


\ Registers

$b8 constant TWBR
$b9 constant TWSR
$bb constant TWDR
$bc constant TWCR


\ Bits in the Control Register 

%10000000 constant mTWINT   \ Interrupt Flag
%01000000 constant mTWEA    \ Enable Acknowledge
%00100000 constant mTWSTA   \ Start Condition
%00010000 constant mTWSTO   \ Stop Condition
%00001000 constant mTWWC    \ Write Collition
%00000100 constant mTWEN    \ Enable
%00000001 constant mTWIE    \ Interrupt Enable

variable ACK?


: i2cInit ( -- )     \ Set frequency to 100kHz 
  %11 TWSR mclr      \ prescale = 1
  [ Fcy #100 / #16 - 2/ ] literal TWBR c!
  mTWEN TWCR mset
;

: DoneWait ( -- )  \ Wait for operation to complete 
  begin TWCR c@ mTWINT and until
;

: <Start| ( -- )   \ Send start condition
  [ mTWINT mTWEN or mTWSTA or ] literal  TWCR c!
  DoneWait
;


: |Restart| ( -- ) \ Send repeated start 
  <Start|     
;


: |Stop> ( -- )   \ Send stop condition
  [ mTWINT mTWEN or mTWSTO or ] literal  TWCR c!
;

 

: |Tx|  ( c--)   \ send 1 byte to bus
  DoneWait
  
  TWDR c!
  [ mTWINT mTWEN or ] literal  TWCR c!
  DoneWait
  
  TWSR c@ $f8 and $18 xor 0=   \ SLA + W
  if  true ACK? !   exit then  \ true if ACK
  
  TWSR c@ $f8 and $28 xor 0=   \ data byte
  if  true ACK? !   exit then  \ true if ACK
  
  TWSR c@ $f8 and $40 xor 0=   \ SLA + R
  if  true ACK? !   exit then  \ true if ACK
  
  false ACK? !    \ false if NACK
;  


: |Rx-ACK| ( -- c)
  [ mTWINT mTWEN or mTWEA or ] literal TWCR c! 
  DoneWait  TWDR c@
;  


: |Rx-NACK| ( -- c)
  [ mTWINT mTWEN or ] literal TWCR c! 
  DoneWait  TWDR c@
;


: |Addr-WR| ( 7-bit-addr --)   \ ask slave for writing
  1 lshift 1 invert and
  |Tx|
;


: |Addr-RD| ( 7-bit-addr --)   \ ask slve for reading
  1 lshift 1 or 
  |Tx|
;


: ?Response  ( --)  \ ACK check
   ACK? @  abort" I2C: No response from Slave!"  
;


: i2cPing? ( 7-bit-addr -- f )  \ ping i2c slave
   <Start| |Addr-RD| 
   ACK? @ 
   if |Rx-NACK| drop true  
   else              false   then
;


: scanner ( --)  \ scan all i2c slave
  base @ hex i2cInit
  cr #5 spaces $10 for r@ 2 u.r next
  $80 
  for r@ $0f and $f =
      if cr r@ $f0 and #2 u.r [char] : emit space
      then
      r@ $7 $78 within
      if  r@ i2cPing?
          if    r@ #2 u.r
          else ." -- "   then
      else  #3 spaces  then
  next
  cr base !
;


\
\ I2C extension
\

variable slaveid         \ i2c address


: regWrite ( data reg --)
    <Start|   
    slaveid @ |Addr-WR|  ?Response
              |Tx|       ( reg)
              |Tx|       ( data)
    |Stop>
;


: regRead ( reg -- data)
    <Start|
   slaveid @ |Addr-WR|  ?Response
             |Tx|       ( reg)
             |Restart| 
   slaveid @ |Addr-RD| 
             |Rx-NACK| 
    |Stop>
;

 

: regsWrite ( d1 d2 d3 ... dn reg n --)
   <Start|
   slaveid @ |Addr-WR| ?Response 
       swap  |Tx|  ( reg)
       for
             |Tx|  ( data)
       next
     |Stop>
;


: regsRead ( reg n -- d1 d2 ... dn)
   <Start|
  slaveid @ |Addr-WR| ?Response
      swap  |Tx|             ( reg)
            |Restart|  
  slaveid @ |Addr-RD|
   1-
   for 
     |Rx-ACK|
   next
   |Rx-NACK| 
   |Stop>
;     

 


\ Math extension


: */  ( n1 n2 n3 --- n1*n2/n3)
    >r  2dup xor >r   ( n1 n2 R: sign1,2 n3)
    abs swap abs swap
    r> r>             ( u1 u2 sign1,2 n3 )
    tuck xor          ( u1 u2 n3  sign )
    >r
    abs u*/mod nip  r> ?negate
;


marker --b180--

 

\ BMP180

   408  value AC1
   -72  value AC2
-14383  value AC3
 32741  value AC4
 32757  value AC5
 23153  value AC6
  6190  value B1
     4  value B2
-32768  value MB
 -8711  value MC
  2868  value MD


: >num ( msb lsb -- num)
   swap 8 lshift or
;


: calib!  ( --)     \ read calibration data
   $aa 12 regsRead
   >num to AC6
   >num to AC5
   >num to AC4
   >num to AC3
   >num to AC2
   >num is AC1
   
   $b6 10 regsRead
   >num to MD
   >num to MC
   >num to MB
   >num to B2
   >num to B1
;

: init
   $77 slaveid !
   i2cInit 10 ms
   calib!
;


: rawT@ ( -- temp.raw)   \ read raw temperature data
   $2e $f4 regWrite
   5 ms
   $f6   2 regsRead
   >num
;

  
: rawP@ ( -- pressure.raw) \ read raw pressure data
   $34 $f4 regWrite
   50 ms
   $f6   2 regsRead
   >num 0  
;

  

\ Calculation for temperature


: X1 ( UT -- X1)
   AC6 -  AC5  32768  u*/mod nip
;


: X2 ( X1 -- X2)
   MD +   MC 2048 rot */
;

: B5 ( UT -- B5)
   X1  dup X2  +
;

: >Celsius ( UT -- Celsius) \ unit: in 0.1C
   B5 8 + 16 /
;

 

\ Calculation for pressure


: B6 ( B5 -- B6)
   4000 -
;

: X1.1 ( B6 -- X1)
   dup 4096 */   B2  2048  */
;

: X2.1 ( B6 -- X2)
   AC2 2048 */
;

: X3.1 ( B6 -- X3)
   dup X1.1  swap X2.1  +
;

: B3 ( B6 -- B3) 
   X3.1 AC1 4 * + 2+ 4 /
;

: X1.2 ( B6 -- X1)
   AC3 8192 */
;

: X2.2 ( B6 -- X2)
   dup 4096 */   B1  32767  */  2/
;

: X3.2 ( B6 -- X3)
   dup X1.2  swap X2.2  + 2+ 4 /
;

: B4 ( B6 -- B4)
   X3.2  32768 +   AC4   32768  u*/mod nip
;


: B7 ( d.UP B6 -- d.p0)
  >r  r@ B3  0  d-
  d2*   50000 ud*
  r> B4  ud/mod  rot drop
;
  
  

: X1.3 ( d.p0 -- x1 )
  256 um/mod nip  0  over ud*
  3038 ud*  65535 um/mod nip
;


: X2.3 ( d.p0 -- -x2 )
   7357 ud*  65535 um/mod nip
;


: >Pressure ( d.UP UT -- d.Pressure)
   B5 B6 B7          ( d.p0)
   2dup  X1.3  >r    ( d.p0 R:x1)
   2dup  X2.3  r>    ( d.p0 x2 x1)
   swap -  3791 +  16 /
   m+ 
;


: BMP180@ ( -- Pa Celsius)
   rawT@ >r
   rawP@  r@ >Pressure
          r> >Celsius
;


: main ( --)
   init
   
   begin
     BMP180@
     ." Temperature = " 0 <# # [char] . hold #s #> type ." C , "
     ." Pressure = "    d.   ." Pa"   cr
   again
;

 

 

 

arrow
arrow

    ohiyooo2 發表在 痞客邦 留言(0) 人氣()