七段顯示器的應用在生活中相當常見,可以使用GPIO的控制在不同的位元顯示數字或英文字母,呈現我們所要表達的資料。學習如何控制七段顯示器後,也可以應用在許多的掃描控制中,例如LED矩陣的跑馬燈、米字顯示器等等。
下文將以STM32L053R8Tx為示範,使用意法半導體官方所提供的Nucleo開發板,以四位元的共陽七段顯示器用掃描的方式完成該實驗,使用的IDE為Keil uVision5(MDK-ARM),而STM32CubeMX的版本為6.2.1。
七段顯示器分為共陽與共陰,顧名思義就是共接點(COM)是接到VCC或是GND,決定我們GPIO控制的準位為何,透過下圖介紹一下七段顯示器總共有七個LED,若包含小數點則有八個LED,電路圖如下所示。
因此若為共陽極七段顯示器,控制GPIO時輸出”0”的訊號,則會使該LED亮起,反之則會熄滅。透過控制不同的LED,達到顯示數字與英文字的效果。
電路圖如下,控制的方式也是使用「掃描」,在Lesson3也是使用掃描的方式控制4×4的鍵盤,而四位元的七段顯示器使用同樣的a~dp接腳,並使用各位元的共接點控制是否顯示。
以電路圖為例,當Seg1為”0”,其餘為”1”時,則DP1會將a~dp的資料顯示出來,DP2~DP4則是全暗不顯示,接著使Seg2為”0”,其餘為”1”時,則DP2會將a~dp的資料顯示出來,DP1、DP3、DP4則是全暗不顯示,依此類推則可以控制每位元七段顯示器的顯示狀態。當我們不斷在Seg1~Seg4依序送”0”,則稱為掃描,當我們的掃描速度快於人眼可以辨識,則可以達到視覺暫留的現象,看起來四位元的七段顯示器皆亮起,並顯示不同的資料。
GPIO設定的部分與Lesson3的做法相同,前幾次已經熟悉了STM32CubeMX的使用了,在此因為使用8+4隻腳,可以直接從原本產生的程式碼進行改寫,則不用每隻接腳單獨設定,會較有效率,但若需返回STM32CubeMX重新設定和產生程式碼,在Keil所改寫的部分則會被覆蓋掉。
使用PB0~PB3作為Seg掃描訊號,而PC0~PC7作為Data,透過STM32CubeMX設置PC0、PB0後,即按下產生程式碼。進入Keil後,可以移至MX_GPIO_Init(void)查看。
如上電路圖不論掃描或資料線以Low Active,因此PC0~PC7、PB0~PB3需設置Pull-up的提升電阻。在GPIO_Init中,修改PC0的設置,PIN腳改為0x0f,轉為二進制是0b00001111,所代表的是PB0~PB3都進行Output的設置,且在Output Level預設需輸出為HIGH(SET)。而PC0的設置,PIN腳改為0xff,也需有提升電阻Pull-up,如下所示
調整完GPIO的設定後,點選Application/User/Core資料夾右鍵Add New Item to Group。
並選擇C File,名字可以自己定義,因為主要是鍵盤的副程式,因此Name為SevenSegment,而路徑一定要在Src當中,才可以直接引用。
接著重複上述,新增標頭檔Header File,檔名也是SevenSegment,路徑需在Inc當中。
打開SevenSegment.h,新增#ifndef、#define、#endif,並引用HAL的函式庫,以利下方的參數定義。
接著我們需要定義一些參數,以利我們後面的程式撰寫更簡潔及順利。首先需要定義Blank,使七段顯示器可以完全關閉,及依序讓七段顯示器掃描的Scan參數。
#define ss_scan_blank GPIOB->ODR|=0xf //blank
#define ss_data_blank GPIOC->ODR|=0xff //blank
#define ss_scan(x) GPIOB->ODR&=~(1<<x) //scan
在Lesson3有提過掃描可以用Sub Function,也可以用define,依照個人習慣即可,不影響功能進行。
打開SevenSegment.c,先引用.h file,並宣告顯示的資料編碼。可以透過Debug Mode進行編碼,會更加快速且正確。
進入Debug Mode後打開GPIOB、GPIOC,按下RUN後再按下STOP,即可調整暫存器的內容,將GPIOB掃描的PB0~PB3都取消打勾(輸出Low),則可以透過GPIOC的暫存器調整我們所要的編碼。
以數字0為例,將GPIOC中的OD0~OD5取消勾選,則可以看到七段顯示器顯示數字0,編碼為0xC0,代表我們接下來GPIOC的ODR若為0xC0,則會顯示數字0。在這邊可以有兩種解讀方式可以讓0xC0成立,會影響到我們後續的副程式撰寫。
- 使OD0~OD7為0,並將OD6、OD7寫入1
- 使OD0~OD7為1,並將OD0~OD5清除為0
從上述的方式我們可以獲得顯示0~9的編碼如下0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,因此可以宣告一個陣列用來存放編碼。
const uint8_t ss_code[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
完成編碼後可以來撰寫輸出資料的副程式,首先我們需要先將data線路關閉後,再送新的資料,避免有殘影的現象存留,接著就將要輸出的資料依照查表法在對應的位元輸出。
Data陣列用來存放所要輸出的d0~d3,並透過scan變數去決定要讓GPIOC->ODR輸出哪個位元的資料,決定後依照ss_code的編碼去查表後輸出。
上方有提到編碼的兩種解讀方式,因為我們是共陽極七段顯示器,因此需要使用第二種使OD0~OD7為1,並將OD0~OD5清除為0。所以可以在下方程式碼看到我們反向兩次,可以分成「&=~」及「~」解讀,前者是寫入0,後者是將我們原本的資料做反向,才會取得需要寫入0的位元。
void ss_set(uint8_t scan,uint8_t d0,uint8_t d1,uint8_t d2,uint8_t d3){
uint8_t data[4]={d0,d1,d2,d3};
ss_data_blank;
GPIOC->ODR&=~~ss_code[data[scan]];
}
完成後我們到main.c,在上方匯入SevenSegment.h。
在int main中的程式碼如下,宣告scan變數為掃描的變數,由0~3依序遞加掃描,而啟動時GPIOC及GPIOB皆是遮蔽狀態(HIGH),需先輸出資料,並遮蔽掃描線,接著開啟掃描線,並將scan變數遞加,避免速度過快因此有加一個Delay。
許多人對於輸出資料後須遮蔽掃描線較疑問,預設已經為HIGH,為什麼要再次寫入HIGH。因為下一次回到該行時,GPIOB並非遮蔽狀態,而是上一次的掃瞄狀態哦!
uint8_t scan=0;
while (1)
{
ss_set(scan,0,1,2,3);
ss_scan_blank;
ss_scan(scan);
if(++scan>3)scan=0;
HAL_Delay(1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
完成以上則可以看到七段顯示器依序顯示0、1、2、3。
可以透過修改傳入給ss_set的參數,調整顯示的數字或文字,若要做一個計數器,則可以如下所示,可以看見count以每100ms的速度進行遞增。
uint8_t scan=0;
uint16_t time=0,count=1234;
while (1)
{
ss_set(scan,count/1000,(count/100)%10,(count/10)%10,count%10);
ss_scan_blank;
ss_scan(scan);
if(++scan>3)scan=0;
if(++time>999){
time=0;
if(++count>9999)count=0;
}
HAL_Delay(1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
完成後可以自己嘗試搭配我們Lesson3所教到的4×4鍵盤,顯示鍵值於七段顯示器,也可以嘗試更多的編碼,讓七段顯示器能顯示的資訊內容更多。或配合Lesson1的Button,做七段的位移控制、上數下數等等不少的應用,也歡迎與我們交流或分享。