前言

前一篇已經順利成功在 Arduino Uno 上安裝了 Flash FORTH 語言,這一篇可以開始來應用囉。所以我們先來搞定用 Flash FORTH 自由控制 Arduino Uno 上的 Digital I/O 埠吧!

大家對 Arduino Uno 的喜愛是因為它的開發環境 Ardunio IDE 的簡潔,好用。也因為如此, Arduino Uno 的 IDE 也隱藏了太多底層對硬體 Atmega328 微處理器控制的細節。透過利用 Flash FORTH 來一步步建構出所有的控制,可以讓我們在不用組合語言的情況下,利用 FORTH 語言的即時交談性的特性,讓我們更能掌握 AVR 系列微處理跟內部硬體的特性。同時完全100% 解放出 Arduino IDE 為了簡單易用所犧牲掉的硬體控制。

所以來開始好好玩一下吧,計畫來發一系列的文章。先是 Digital I/O,然後 SPI,I2C,最後是內建的 ADC,PWM ... 等等。

 

Arduino Uno Pin 腳命名規則

先送上大家應該耳熟能詳, Arduino Uno Pin 腳的定義

arduino uno pins.png

 

字有點太小了,把右邊那排 Pin 針腳位圖放大如下

Arduino Uno Pins Detail.png

可以看得很清楚了,印刷線路板上,Pin針的定義從 Pin0 定義到 Pin13.

Arduino 的 IDE 就是利用這個定義來控制個腳位的輸出的。例如下面的範例,控制 Pin13 腳位輸出,點亮 LED

arduino ide example.png

然後大家一定很納悶的,那一堆標黃色的,從 PD0到PD7,從 PB0到PB6,然後從PC0到PC5的這些標示,這是什麼鬼啊!

哦,這是從 AVR 微處理的視角的一個標示。

Arduino Uno 所用的 ATmega328 微處理器,總共有 24個腳針,分成 Port B, Port C, Port D 三個埠。

所以

PB0 = Port B 的 Pin0, PB1 = Port B 的 Pin1, PB2 = Port B 的 Pin2 ... etc.

PC0 = Port C 的 Pin0, PC1 = Port C 的 Pin1, PC2 = Port C 的 Pin2 ... etc.

PD0 = Port D 的 Pin0, PD1 = Port D 的 Pin1, PD2 = Port D 的 Pin2 ... etc.

 

為什麼要這麼麻煩呢? 原來啊,在微處理器裡面,只有暫存器這種東西。 ATmega328 裡面,這些 Pin 腳的輸入跟輸出完全是透過每個埠 Port B, C, D各三個的八位元暫存器來控制的。所以自然以微處理器的視角,用埠的代碼跟此埠的第幾根腳來標示會比較清楚,可以馬上可以知道這根腳是對應到暫存器的哪個位置。

 

FORTH 指令語法設計

仿效 Arduino IDE 的語法,我們來設計我們 FORTH 語言的語法吧。

首先每個 Digital I/O 的腳位要先指明方向,看是要當作輸入,還是輸出。所以希望能這樣

Pin13 >OUTPUT      這個指令像上面 Arduino IDE 的 pinMode(13, OUTPUT) 一樣,指定 Pin13 為輸出

Pin13 <INPUT        這個指令跟 Arduino IDE 的 pinMode(13, INPUT) 一樣,指定 Pin13 為輸出

Pin13 ->High      這個指令像上面 Arduino IDE 的 digitalWrite(13, HIGH) 一樣,指定 Pin13 輸出 HIGH

Pin13 ->Low        這個指令像上面 Arduino IDE 的 digitalWrite(13, LOW) 一樣,指定 Pin13 輸出 LOW

Pin13 PinRead      這個指令跟 Arduino IDE 的 digitalRead(13) 一樣,讀出 Pin13 的訊號, 0=LOW,其他=High

 

此外,也可以使用微處理器的視角來標定 Pin 的腳位

bit6 PortB  >OUTPUT      ( PB6 = Pin13)   等同   Pin13 >OUTPUT

bit6 PortB  <INPUT        ( PB6 = Pin13)   等同   Pin13 <INPUT

bit6 PortB  ->High        ( PB6 = Pin13)   等同   Pin13 ->High

bit6 PortB  ->Low          ( PB6 = Pin13)   等同   Pin13 ->Low

bit6 PortB  PinRead     ( PB6 = Pin13)   等同   Pin13 PinRead

 

最後,希望能夠有個可以自由定義 Pin 腳名稱的指令,這樣用起來就會更方便囉。

例如

bit6 PortB  Pin_Def LED1  將 PB6 (Pin13) 的 Pin腳定義為 LED1

Pin12       Pin_Def LED2  將 Pin12 的 Pin腳定義為 LED2

 

然後

LED1 >OUTPUT

LED2 >OUTPUT

LED1 ->High     

LED2 ->Low     

這樣可讀性是不是變得非常之高啊! 💪

 

 

Atmega328 digital I/O 控制

對於 Atmega328 主要有三個 Port,每個 Port 有三個暫存器來控制其所屬的 Digit I/O 主要狀態。  (請參閱 Atmega328 官方 Datasheet p.60)

Port B

PORTB (0x25) - The Port B Data Register,  控制輸出狀態: bit = 1 為 High, bit = 0 為 Low

DDRB (0x24) - The Port B Data Direction Register, 控制方向: bit = 1 為輸出, bit = 0 為輸入

PINB (0x23) - The Port B Input Pin Address, 當為輸入時 Digital I/O 的狀況: bit = 1 為 High, bit = 0 為 Low

 

Port C

PORTC (0x28) - The Port B Data Register,  控制輸出狀態: bit = 1 為 High, bit = 0 為 Low

DDRC (0x27) - The Port B Data Direction Register, 控制方向: bit = 1 為輸出, bit = 0 為輸入

PINC (0x26) - The Port B Input Pin Address, 當為輸入時 Digital I/O 的狀況: bit = 1 為 High, bit = 0 為 Low

 

Port D

PORTD (0x2B) - The Port B Data Register,  控制輸出狀態: bit = 1 為 High, bit = 0 為 Low

DDRD (0x2A) - The Port B Data Direction Register, 控制方向: bit = 1 為輸出, bit = 0 為輸入

PIND (0x29) - The Port B Input Pin Address, 當為輸入時 Digital I/O 的狀況: bit = 1 為 High, bit = 0 為 Low

 

 

Flash FORTH位元存取指令 mset, mclr, mtst

Flash FORTH 提供了三個很核心的位元 bit 存取的指令,解說如下

mset ( mask addr -- )

這個指令堆疊上需要兩個參數,第一個是 mask 數字,第二個是記憶體的位址。

它的動作是,會從記憶體的位址裡取出一個八位元的資料,跟 mask 數字做 OR 運算後再存回去!

舉例,假如我們用一個 %0001.0000 的 mask 來當參數, 而 addr 記憶體位址裡的資料是 %abcd.efgh ,透過 OR 運算後 d 的那個位元因為跟 1 做 OR 運算,所以不管它原來是不是0,最後一定會是1。所以記憶體裡的資料會變成 %abc1.efgh。

很明顯的,這個指令可以透過 mask (位元1 的地方) 來指定要將哪幾個 bit 設成 1 。 所以指令全名叫做 mask set

 

mclr ( mask addr --)

一樣的參數。它的動作是,先將 mask 反向 ,然後從記憶體的位址裡取出一個八位元的資料,跟反向後的 mask 數字做 AND 運算後再存回去!

因為 mask 反向,原來是位元1的變成0了,0的變成1。透過 AND 操作,所以只要反向後 mask 的位元是1 的地方資料就通過,位元0的地方資料就變成0

所以這個指令可以透過 mask (位元1 的地方) 來指定要將哪幾個 bit 設成 0 。 所以指令全名叫做 mask clear

 

mtst ( mask addr -- result)

位元測試的指令,還是一樣的參數。它的動作是,從記憶體的位址取出一個八位元的資料,跟 mask 做 AND 後將結果放上堆疊。因為是 AND,所以 mask 上 bit 是1位置的就可以通過。用這樣來選取我們需要的 bit 資料出來。然後 FORTH 裡面通常用 0 來代表邏輯 false, 不等於零代表邏輯真。 所以這個指令可以拿來取出記憶體或暫存器裡資料的某個 bit ,來做真值判斷觸發我們的條件式。 

 

Digital I/O FORTH 程式碼

有了上面三個重要指令後就簡單了,例如我們想把 Pin13 方向設成輸出,並將輸出設成 High 方法如下

對於 Pin13 

當輸出的時候,由位於 位址#37 的 PortB 暫存器的 bit 5 的數值來控制輸出, 0 = Low, 1 = High

由位於 位址#36 的 DDRB 暫存器的 bit 5 的數值來控制方向,看是要輸出還是要讀入, 0 = 讀入, 1 = 輸出

當輸入的時候,由位於 位址#35 的 PinB 暫存器的 bit 5 的數值來儲存硬體所讀入的結果, 0 = Low, 1 = High

所以很明顯的

要讓 Pin13 的 Digital I/O 方向變成輸出時,指令如下

%00010000 #36 mset    將 #36 位址 (DDRB) 的第5位元設成1

當是輸出時,要求 Pin13 輸出 High

%00010000 #37 mset    將 #37 位址 (PORTB) 的第5位元設成1

 

如前面所設計的語法,把 mask 定義成 bit0, bit1, ... bit7 的常數,再把 PortB, PortC, PortD 的位址也定義成常數。 這樣就可以很簡潔的用兩個參數來標定所有的 Pin 腳

例如 bit5 PortB  兩個參數標定 Pin13

所以控制 Pin13 輸出的指令定義如下

: ->High ( mask port --) mset ;

: ->Low  ( mask port --) mclr ;

bit5 PortB ->High   讓 Pin13 輸出 High

bit5 PortB ->Low     讓 Pin13 輸出 Low

 

PortB, PortC, PortD 暫存器位址減一,就得到方向暫存器 DDR,所以方向的指令定義如下

: >OUTPUT ( mask port --)   1- mset ;    將Digital I/O方向設為輸出

: <INPUT( mask port --)     1- mclr ;    將Digital I/O方向設為輸入

bit5 PortB >OUTPUT   將 Pin13 Digital I/O設為輸出

bit5 PortB <INPUT    將 Pin13 Digital I/O設為輸入

 

PortB, PortC, PortD 暫存器位址減二,就得到狀態暫存器 PIN,所以狀態讀取的指令定義如下

: PinRead ( mask port -- status)   2- mtst.  ;   

bit5 PortB PinRead   讀取 Pin13 Digital I/O 輸入的狀態

 

再更近一步,發揮 FORTH 語言的特色,來用 create-does> 進一步精煉吧!

來定義個可以自由定義 Pin 腳名稱的指令吧,當給定由此指令所定義的特定名字後,會自動將 mask port 這兩個參數放上堆疊。然後後面接著 >OUTPUT/<INPUT/PinHigh/PinLow/PinRead 這些動詞,語法就更漂亮囉!

 

: Pin_Def ( mask Port "Name" --)
   create swap c, c,              用後面所接著的名字創造新詞,將堆疊上的 mask, port 資料編入字典 
   does>  dup  c@        ( mask)  執行時取出資料區的第一個資料 mask 放上堆疊  
          swap char+ c@  ( Port)  將位址往後位移 1byte 後取出 port 資料放上堆疊

;

所以很漂亮的, Pin13 其實是用這個指令定義出來的

bit5 PortB  Pin_Def Pin13

 

這裡要留意的, Flash FORTH 是特別為 AVR 微處理器所設計的。這個微處理器有個特色,它採用哈佛架構,在處理器內部指令微碼跟資料是分開的。跟一般 FORTH 指令跟資料都混在一起有所不同。同時此微處理器同時有 Flash, EEPROM, RAM 三種儲存結構。

所以 Flash FORTH 裡面的 here, allot, , c, 這幾個指令跟一般的 FORTH 有所不同,是可以在 FLASH, EEPROM, RAM 這三個媒介裡運作。

Flash FORTH 提供三個指令來讓你選擇這三個指令要在哪個媒介運作。

flash 這個指令指明接下來在 flash 裡面運作,所以假如你的資料要編入 flash 記憶體裡儲存,可以利用這個指令

eeprom 這個指明要在 eeprom 裡面運作,所以假如你的資料要編入 eeprom 記憶體裡儲存,可以利用這個指令

最後是預設的 ram,假如你的資料編到這裡的話,保證下次再開機,資料就不見囉。

這裡我希望所有 Pin 腳的定義是隨著程式碼一起放在 flash 的,所以在開始所有 Arduino Uno 的 Pin 腳定義前,先用 flash 指令指名後續的資料,請燒錄至 flash 之中。

flash

bit0 PortD  Pin_Def Pin0
bit1 PortD  Pin_Def Pin1
bit2 PortD  Pin_Def Pin2
bit3 PortD  Pin_Def Pin3

...

這樣就可以建立起所有 Pin 腳的定義,永遠存在系統中隨時使用。

FORTH 真的也是很好的描述語言呢,假設你完全不知道 Pin_Def 的程式碼,但光看字面的定義,你也可以很清楚的看到它們在「描述」 PortD bit0 就是 Pin0, PortD bit1 就是 Pin1, PortD bit2 就是 Pin2 ...

就資訊而言,跟上面那張 Arduino Uno Pin腳的腳位圖表,是等價的。

 

3位7段顯示器

接下來來實戰演練,實際測試囉。就拿我之前的 Arduino 三位LED七段顯示器應用吧。一模一樣的電路,只是之前是用 Arduino IDE 的 C 語言來撰寫,現在改用 FORTH 囉!來看結果有沒有一樣啊!

電路圖如下,至於運作的細節請移駕至原 BLOG文,這裏就不詳述囉。

final circuit.png

 

實際組起來後的照片

IMG_5109.png

Arduino Uno

IMG_5111.png

 

七段顯示器的定義還是要詳述一下的,每一段 LED 位置名稱代碼如下

7 seg LED symbol.png

 

要讓某一段亮起來,上面三個 Pin 選三位 LED 中的哪一位,下面 a, b, c, d, ... g, dp 選擇讓哪一個燈管亮。看燈管是共陰極還是共陽極決定流動的方向。然後讓一邊一個是 Source 提供電壓電流,一個是 Sink 接地,承載電流,這樣就會亮囉!

 

3 digit 7 seg LED.png

 

FORTH程式解說

這個程式其實我們也是在建一個可以自由定義字型,點亮 LED 的函式庫啦。

七段顯示器加上小數點剛好8個,剛好可以用一個 byte 的資料來定義字型。所以字型表就這樣定義囉。

字型 = dp.a.b.c.d.e.f.g   dp 是 MSB,  g 是 LSB ,當其為1的時候代表要點亮,當其為0的時候代表要熄滅。

所以

數字 1 要點亮 b, c. 其字型資料就是 %00110000

數字 2 要點亮 a,b,g,e,d 其字型資料就是 %01101101

...

全部字型資料如下

create CharData

%01111110 c,    ( 0, aa bb cc dd ee ff) 
%00110000 c,    ( 1, bb cc)
%01101101 c,    ( 2, aa bb gg ee dd)
%01111001 c,    ( 3, aa bb gg cc dd)
%00110011 c,    ( 4, ff gg bb cc)
%01011011 c,    ( 5, aa ff gg cc dd)
%01011111 c,    ( 6, aa ff ee dd cc gg)
%01110000 c,    ( 7, aa bb cc)
%01111111 c,    ( 8, aa bb cc dd ee ff gg)
%01110011 c,    ( 9, aa bb cc ff gg)
%00000001 c,    ( -, gg)

 

定義個小指令來存取這些資料

: CharData@ ( index -- data)   chars CharData +    c@  ;

給個 index, CharData@ 會算出資料的正確位址,並用 c@ 取出 1byte 的資料。

資料順序是經過設計的, index = 0 時剛好取到0的資料, index=9 時剛好取到9 , index=10 以上都當特殊字元。

 

字型資料有了,再來就是 Digital I/O 囉。

為了方便跟可讀性,逐一定義所有 pin 腳。同時因為這是應用程式,所以這些資料我決定是燒在在 EEPROM 裡面,前面加一行 eeprom 要求後面的資料是要放到 EEPROM裡面的

eeprom

\ 7 Segments Dsiplay control pins

Pin12 Pin_Def digit0    ( digit 0 on/off control pin)
Pin9  Pin_Def digit1    ( digit 1 on/off control pin)
Pin8  Pin_Def digit2    ( digit 2 on/off control pin)
Pin11 Pin_Def aa       
Pin7  Pin_Def bb
Pin5  Pin_Def cc
Pin3  Pin_Def dd
Pin2  Pin_Def ee
Pin10 Pin_Def ff
Pin6  Pin_Def gg
Pin4  Pin_Def dpp

 

要點亮某個LED 需要兩個 Digital I/O 的合作,一個是 High 提供電壓,一個是 Low 形成電位差並承接電流。

根據三位七段顯示器的電路,定義 digital1, digital2, digital3 就是每一位數的總電流流入,或流出的Pin腳。定義 aa, bb, cc,dd, ee, ff, gg, dpp 就是分別流入或流出a,b,c,d,e,f,g,dp LED的 Pin 腳

舉例,要點亮第一位的7段顯示器中的 a 就是要 digital1 跟 aa 這兩個 Pin 腳的搭配,一個High,一個Low。至於誰 High 誰 Low 要看 LED 的方向,看是共陰極還是共陽極。方向對了,電流流通 LED 就亮囉。

所以,

假如是共陽極3位七段顯示器

digit1 ->High       aa ->Low      下這兩個指令後 a 段 LED 就會亮囉

假如是共陰極

digit1 ->Low        aa ->High      下這兩個指令後 a 段 LED 就會亮囉

 

為了方便用迴圈索引操作,這這些放到陣列裡面吧

陣列定義指令

: pins_def   ( define control pin array )
   create                                    建個Pin腳位陣列的詞頭,但不預留空間
   does>  ( index -- mask port)              陣列資料的取出,給定 index,取出對應的資料
      swap 2* chars +  ( addr)               陣列的資料是一對的 mask 跟 port ,計算它們的位址
      dup c@     swap char+  c@              取出 mask 然後再取出 port
;

陣列元素編入指令

: pin,  ( mask port --)  swap  c, c,  ;      將 mask 跟 port 編入(或燒入)指定記憶體

 

七段顯示器八個LED的控制腳位陣列,給 index 就可以取出對應的腳位值

pins_def SegsPin   ( Segments off/on Pin array)
dpp pin, aa pin, bb pin, cc pin, dd pin, ee pin, ff pin, gg pin,

 

三位七段顯示器每一位的控制腳位陣列,給 index 就可以取出對應的腳位值

pins_def DigitsPin ( Digit on/off Pin array)
digit0 pin,  digit1 pin,  digit2 pin,

 

因為三位都共用相同的8段LED控制腳位,所以一次只能顯示一位,否則其他兩位都會顯示出相同的字型。所以要顯示某一位時,其他兩位必須關掉。

要顯示第一位時: digit0: ON, digit1: OFF, digit2: OFF

要顯示第二位時: digit0: OFF, digit1: ON, digit2: OFF

要顯示第三位時: digit0: OFF, digit1: OFF, digit2: ON

轉成指令變這樣

: digitSelect ( i --)                            選擇顯示第i位    
   \ off all other 2 digits, on the selected one
   
   3 for  
          dup r@ =    ( same digit?)             迴圈 index 跟 i相同
          if      r@ DigitsPin  ->High           給它 PinLow
          else    r@ DigitsPin  ->Low      then  不是 i 的給它 PinLow
     next  drop
;     

 

這裏要提醒的 Flash FORTH 在 AVR 的處理器上,為了速度,用的是  n for ... next  只有一個引數,最早由丁陳老師在 eFORTH 上所提出來非常精簡的結構,而不是一般大家所習慣的 n1 n2 DO ... LOOP 結構。

這個 n for ... loop ,是倒數的,不是正數的。目前迴圈所跑到的圈數放在返回堆疊的頂端,所以直接用 r@ 取出,而不是一般的 i。

 

接下來就容易理解囉,顯示整個字型的指令

: Decode&Light ( data ---)               給定字型資料,然後依序點亮 LED
   \ light 7 segments LED according data
 
   8 for                                 重複8次
       dup %00000001 and                 遮罩,取出字型資料的 LSB bit
       if     r@ SegsPin ->Low           非零的話就 PinLow
       else   r@ SegsPin ->High    then  是零的話就 PinHigh
  
       2/   ( shift right)               所有位元右移一位
   
     next   drop
;

 

根據字型資料表,點亮特定位7段顯示器的指令,順便控制小數點要不要亮

: LightChar ( digit index dot? --)   digit=位,index=字型資料位置,dot?=要不要小數點
   \ digit = 0, 1, 2
   \ index = 0..10 of CharData
   
   rot digitSelect                   控制一下要顯示的那一位

   swap  CharData@   swap            取出字型資料
   if   %10000000   or  then         要小數點的話,合併到字型的第一位元
       
   Decode&Light                      點亮它們
   
;

 

再進階一點,給個 0...999 的數字,會自動完整地將數字顯示在七段顯示器上

要強調的,因為硬體電路的限制,三位七段顯示器一次只能顯示一個位的數字,若是強迫要「同時」顯示三位的數字,三個位的數字都會一模一樣。所以只能用掃描的方式,利用人類眼睛視覺暫留的特性,雖然一次只能點亮一個位的數字,但快速的輪流點亮三個時,眼睛就會誤以為三個數字是一起同時點亮的。

LightNum 用掃描的方式,利用 LightChar 輪流點亮數字的每一位,人眼分辨不出來,就會以為是一個完整的數字。

 

: LightNum ( n --)               給一個 0...999 的數字
  \ light a number with range 0...999

   10 u/mod  ( r q)              用10來除,取餘數跟商
   2 rot -1 LightChar   5 ms     餘數就是第2位要顯示的值,用 LightChr 顯示後停個 5 ms
   
   10 u/mod                      商繼續用10來除,取餘數跟商
   1 rot 0  LightChar   5 ms     餘數就是第1位要顯示的值,用 LightChr 顯示後停個 5 ms
   
   0 swap 0 LightChar   5 ms     最後的商就是第0要顯示的值,用 LightChr 顯示後停個 5 ms
;

 

 

測試程式

這次所有的 Digital I/O 都是輸出,所以一開始設定一下 Digital I/O 的方向

: init
   digit1  >OUTPUT
   digit2  >OUTPUT
   digit3  >OUTPUT
   aa      >OUTPUT
   bb      >OUTPUT
   cc      >OUTPUT
   dd      >OUTPUT
   ee      >OUTPUT
   ff      >OUTPUT
   gg      >OUTPUT
   dpp     >OUTPUT
;

 

第一個程式,單獨測試每一位的七段顯示器,讓它以特定的字型表來輪流點亮。

就字型表裡頭的資料,從0一直掃到最後10 共11個,然後三位七段顯示器每一位都給它顯示一下! 每顯示一筆資料,停個 500 ms 繼續

: test
   init
   3 for r@
      11 for   dup r@ -1 LightChar    500 ms      next  
         drop
     next
;

IMG_5112D.png

實際測試影片

 

 

 

 

第二個程式,利用視覺暫態的原理,快速輪流點亮三位的七段顯示器,讓使用者認為同時有三位七段顯示器同時點亮。來做個數字的計數吧,從0數到100。

 

: CountTest  ( --)   
   init
   1000 for  r@ LightNum  10 ms    next
;

 

實際測試影片

 

 

 

 

 

完整程式碼列表

Part 1 - Arduino Uno Digital I/O

 

\
\  Flash FORTH  Arduino Uno  digital I/O
\
\      Frank Lin 2020.10.15
\

--uno_dio--

marker --uno_dio--

\
\ bit masks
\


%00000001 constant bit0
%00000010 constant bit1
%00000100 constant bit2
%00001000 constant bit3
%00010000 constant bit4
%00100000 constant bit5
%01000000 constant bit6
%10000000 constant bit7


\
\ Atmega328 Digital I/O Registers
\

#37 constant PortB
\ #36 constant DDRB  (-1)
\ #35 constant PinB  (-2)

#40 constant PortC
\ #39 constant DDRC
\ #38 constant PinC

#43 constant PortD
\ #42 constant DDRD
\ #41 constant PinD

 

: Pin_Def ( mask Port "Name" --)
   create swap c, c,
   does>  dup  c@        ( mask)
          swap char+ c@  ( Port)
;


\
\  Arduino Uno with ATmega328 Pin definations
\


flash

bit0 PortD  Pin_Def Pin0
bit1 PortD  Pin_Def Pin1
bit2 PortD  Pin_Def Pin2
bit3 PortD  Pin_Def Pin3
bit4 PortD  Pin_Def Pin4
bit5 PortD  Pin_Def Pin5
bit6 PortD  Pin_Def Pin6
bit7 PortD  Pin_Def Pin7

bit0 PortB  Pin_Def Pin8
bit1 PortB  Pin_Def Pin9
bit2 PortB  Pin_Def Pin10
bit3 PortB  Pin_Def Pin11
bit4 PortB  Pin_Def Pin12
bit5 PortB  Pin_Def Pin13

bit0 PortC  Pin_Def Pin14
bit1 PortC  Pin_Def Pin15
bit2 PortC  Pin_Def Pin16
bit3 PortC  Pin_Def Pin17
bit4 PortC  Pin_Def Pin18
bit5 PortC  Pin_Def Pin19


\
\ UART
\

Pin0        Pin_Def RxD
Pin1        Pin_Def TxD


\
\ SPI
\

Pin10       Pin_Def SS
Pin11       Pin_Def MOSI
Pin12       Pin_Def MISO
Pin13       Pin_Def SCK

 

\
\ I2C
\

Pin18      Pin_Def SDA
Pin19      Pin_Def SCL

 

\
\   Digital I/O Access Codes
\


: >OUTPUT ( mask port --)   \ set the direction of digital I/O to output
   1-     ( DDR register)
   mset   ( set to High = OUTPUT)
;

: <INPUT  ( mask port --)   \ set the direction of digital I/O to input
   1-     ( DDR register)
   mclr   ( set to Low = INPUT)
;

: ->High  ( mask port --)   \ put digital I/O to High
   mset
;

: ->Low   ( mask port --)   \ put digital I/O to Low
   mclr
;

: PinRead ( mask port -- status)   \ read the state of digital I/O, 0=false
   2-     ( Pin register)
   mtst   ( read the state)
;

 

 

 

Part 2 - 七段顯示器控制

 

\
\  3 digits 7 Segment Display test on Arduino UNO
\
\     Frank Lin 2020.10.16
\

 

--7segLED--

marker --7segLED--

 


\
\ 7 Segments Display Pin Definations
\


eeprom

\ 7 Segments Dsiplay control pins

Pin12 Pin_Def digit0    ( digit 0 on/off control pin)
Pin9  Pin_Def digit1    ( digit 1 on/off control pin)
Pin8  Pin_Def digit2    ( digit 2 on/off control pin)
Pin11 Pin_Def aa       
Pin7  Pin_Def bb
Pin5  Pin_Def cc
Pin3  Pin_Def dd
Pin2  Pin_Def ee
Pin10 Pin_Def ff
Pin6  Pin_Def gg
Pin4  Pin_Def dpp

 


\ characters data

\
\  8bits data = dpp.aa.bb.cc.dd.ee.ff.gg
\

 

create CharData

%01111110 c,    ( 0, aa bb cc dd ee ff) 
%00110000 c,    ( 1, bb cc)
%01101101 c,    ( 2, aa bb gg ee dd)
%01111001 c,    ( 3, aa bb gg cc dd)
%00110011 c,    ( 4, ff gg bb cc)
%01011011 c,    ( 5, aa ff gg cc dd)
%01011111 c,    ( 6, aa ff ee dd cc gg)
%01110000 c,    ( 7, aa bb cc)
%01111111 c,    ( 8, aa bb cc dd ee ff gg)
%01110011 c,    ( 9, aa bb cc ff gg)
%00000001 c,    ( -, gg)


: CharData@ ( index -- data)   chars CharData +    c@  ;

 

\
\ pins data
\


: pins_def   ( define control pin array )
   create   
   does>  ( index -- mask port) 
      swap 2* chars +  ( addr)
      dup c@     swap char+  c@   
;
          

: pin,  ( mask port --)  swap  c, c,  ;


pins_def SegsPin   ( Segments off/on Pin array)
dpp pin, aa pin, bb pin, cc pin, dd pin, ee pin, ff pin, gg pin,


pins_def DigitsPin ( Digit on/off Pin array)
digit0 pin,  digit1 pin,  digit2 pin,


ram

: digitSelect ( i --)
   \ off all other 2 digits, on the selected one
   
   3 for  
          dup r@ =    ( same digit?)
          if      r@ DigitsPin ->High   
          else    r@ DigitsPin ->Low      then
     next  drop
;     


: Decode&Light ( data ---)
   \ light 7 segments LED according data
 
   8 for  
       dup %00000001 and
       if     r@ SegsPin ->Low  
       else   r@ SegsPin ->High    then
  
       2/   ( shift right)
   
     next   drop
;


: LightChar ( digit index dot? --)
   \ digit = 0, 1, 2
   \ index = 0..10 of CharData
   
   rot digitSelect

   swap  CharData@   swap      ( char_data dot?)
   if   %10000000   or  then   ( if dot?, merge it to CharData)
       
   Decode&Light
   
;


: LightNum ( n --)
  \ light a number with range 0...999

   10 u/mod  ( r q)
   2 rot -1 LightChar   5 ms   \ digit 2
   
   10 u/mod
   1 rot 0  LightChar   5 ms   \ digit 1
   
   0 swap 0 LightChar   5 ms   \ digit 0
;

: init
   digit1  >OUTPUT
   digit2  >OUTPUT
   digit3  >OUTPUT
   aa      >OUTPUT
   bb      >OUTPUT
   cc      >OUTPUT
   dd      >OUTPUT
   ee      >OUTPUT
   ff      >OUTPUT
   gg      >OUTPUT
   dpp     >OUTPUT
;

 

marker --test--


: test
   init  
   3 for r@
      11 for   dup r@ -1 LightChar    500 ms      next  
         drop
     next
;

 

: CountTest  ( --) 
   init
   1000 for  r@ LightNum  10 ms    next
;

 

 

xxx

 

 

arrow
arrow

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