STM32筆記(16):在SSD1306 OLED顯示紅綠燈狀態

為了方便之後的實做顯示結果,本篇將在初步瞭解 GPIO 的作法後,使用 I2C 介面,將結果的值顯示在 0.96 吋的 OLED 顯示器上。這個顯示器的型號是 SSD1306,找到了一個生產顯示器廠商提供的驅動程式,獨立產生一個 oled.c 及 oled.h(定義顯示字型的 oledfont.h 及定義圖形的 bmp.h 檔)。

[設定include路徑及程式]

上一篇使用 LED 作為交通燈號,僅使用 GPIO 的引腳設定為 on/off,就可以控制 LED 燈亮或滅,這篇開始會將每種硬體(如 OLED、LCD1602...等)的驅動函式放在 Hardware 目錄中。首先在專案的根路徑下,新增一個 Hardware 資料夾,與 System、User 同一層目錄。

開啟 Keil 5 主程式,在左方樹狀目錄的 Target 按右鍵,出現選單視窗,選擇「Optios for Target...」或上方工具列的魔法棒(如下圖紅色框處),開啟選項設定視窗,如下圖:
點選「C/C++」頁簽,在下方點選 include 路徑的圖示,增加一個 \Hardware 的路徑,如下圖:
再增加一個 Group,名為 Hardware,將未來新增的 .c 檔案,都增加進來,方法請參考前一篇文章,或按下下圖上方紅色處圖示,來增加 Group 及 .c 檔案。

[STM32F103x的I2C]

I2C 是一種使用多主從架構的串列通訊協議,只需要兩條線:數據線(SDA)和時鐘線(SCL)即可進行通訊。I2C模組接收和發送資料,將資料從串列轉換成並行,或平行轉換成串列。

I2C 總線有三種傳輸速率可供選擇:標準模式(可達100 kbit/s)、快速模式(可達400 kbit/s)和高速模式(可達3.4 Mbit/s),STM32 僅使用標準和快速兩種模式。STM32F103x 的 I2C 總線通信接口,其內部結構如下圖:
① 引腳:I2C 的硬體架構只需要兩個引腳(SDA和SCL),SMBA 線用於 SMBUS 的警告訊號,I2C 通訊用不到。

② 時鐘訊號:SCL 的時鐘訊號,透過設定時鐘控制暫存器(ClockControl Register,CCR)控制,設定 SCL 的頻率,可選擇「標準(100 kbit/s)/快速(400 kbit/s)」模式。

③ 資料收發:I2C 的 SDA 信號主要連接到資料移位暫存器(Data Shift Register,DSR)上,DSR的資料來源及目標是資料暫存器(Data Register,DR),傳送資料時,傳送的位元組寫入 DR 暫存器,硬體會把 DR 中的位元組搬到 DSR 中,然後在時鐘訊號的配合下,把 DSR 最高位的資料放到傳輸線 SDA 上,並對 DSR 進行移位元運算。接收資料時,資料控制器(Data Control)根據時鐘訊號,把 SDA 線上的高低電平轉換為「1」或「0」的資料,寫到 DSR 的最低位,同時 DSR 移位元運算,當接收完一個位元組的 8 位元資料後,把 DSR 中的資料搬到DR暫存器中。

④ 控制邏輯:控制邏輯負責協調整個 I2C 外設,控制邏輯可改變「控制暫存器(Control Register 1,CR1)和(Control Register 2,CR2)」來達成觸發起始和停止訊號,做出ACK響應,設定外設時脈頻率,開啟DMA和中斷的功能。外設工作時,控制邏輯會根據外設的工作狀態修改「狀態暫存器(Status Register 1,SR1)和(Status Register 2,SR2)」,只要讀取這些暫存器相關的暫存器位,就可以知道當前匯流排是否被佔用,本機是主裝置還是從裝置,資料是否傳送完畢等 I2C 的工作狀態。

除了上述簡單的說明外,要使用 STM32 開發板控制 I2C 接收/傳送資料,還有很多內容需要瞭解,之後再以實做來補充。



[材料]

  • STM32F103C8T6 主板 x 1
  • OLED SSD1306 顯示器 x1
  • 麵包板 x1
  • LED 紅、黃、綠 各 1 個
  • STLINK V2 模擬下載器 x 1
  • 連接線 x N 條

[接線與電路圖]

LED 正極(長腳)都接到 3.3v,另一腳,分別接在 STM32F103 的 PA0、PA1 及 PA2。STM32F103x 連接 SSD1306 如下表:
STM32F103xSSD1306 OLED
3.3vVDD
GNDGND
B8SCK/SCL
B9SDA

[程式]

主程式一開始先對 OLED 進行初始化,再設定三個引腳 GPIO 連接三個 LED燈,進入迴圈循環時,紅綠燈亮時各等待三秒,黃燈亮時等待一秒,主程式 main.c 如下:
/**********************************************************
 * @file main.c
 * @date 2022/3/27  
 * @brief traffic light with OLED
 *        	VDD 	- 3.3V
 *       	GND 	- GND
 * 	  	SCL/SCK - PB8    // IIC時鐘信號
 *	  	SDA 	- PB9    // IIC資料信號
 **********************************************************/

#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "bmp.h"

GPIO_InitTypeDef GPIO_InitStruct;    // 定義一個GPIO_InitTypeDef類型的結構體

int main (void)
{
	delay_init();	    	 //延時函數初始化	  
	NVIC_Configuration(); 	 //設置 NVIC 中斷分組2:2位搶佔優先順序,2位回應優先順序 	
	OLED_Init();		 //初始化OLED  
	OLED_Clear(0);           //清除螢幕

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //開啟GPIOA的外設時鐘
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;  //選擇要控制的GPIOA引腳
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;   		  //設置引腳速率為2MHz
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;   		  //設置引腳為通用推輓輸出模式
	GPIO_Init(GPIOA, &GPIO_InitStruct);  				  //調用庫函數,初始化GPIOA5引腳

	// 將引腳設為高電位,此時三個燈熄滅
	GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2);	

	OLED_Init();
		
	OLED_ShowString(1, 2, "Welcome...  ",16);
	OLED_ShowString(1, 4, "Stop Light Proj",16);
	delay_s(3);

	OLED_Clear(0);
	
	while (1)
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_0);	// 點亮紅燈	
		OLED_ShowString(6, 3, "Red    ",16); 
		delay_s(3);				// 停 3 秒

		GPIO_SetBits(GPIOA, GPIO_Pin_0);	// 熄滅紅燈
		GPIO_ResetBits(GPIOA, GPIO_Pin_1);	// 點亮黃燈	
		OLED_ShowString(6, 3, "Yellow",16);
		delay_s(1);

		GPIO_SetBits(GPIOA, GPIO_Pin_1);	// 熄滅黃燈
		GPIO_ResetBits(GPIOA, GPIO_Pin_2);	// 點亮綠燈	
		OLED_ShowString(6, 3, "Green ",16);		
		delay_s(3);
		
		GPIO_SetBits(GPIOA, GPIO_Pin_2);	// 熄滅綠燈
	}
}

由於引用的 OLED 等相關程式數量較多,我將程式放在 Github:2. LED Traffic light with OLED,有興趣的朋友可以下載來試試。

[編譯錯誤#268]

程式寫完在第一次編譯時,出現一個錯誤的提示:
oled.c(45): error: #268: declaration may not appear after executable statement in block
找到網路上的解決方法:ARMCC: Error #268: Declaration may not appear after Exec
原因是宣告不能在執行語句後面,也就是說變數的宣告需寫在使用語句的前面,否則會導致程序在編譯時找不到這個變數,造成錯誤。解決方法是在選項(下圖左上紅框處圖示)中勾選「C99」(下圖下方方框處),讓編譯時,可以將宣告和語句混用,就像在 C 語言中一樣。如下圖:

[結果]


[參考資料]


Post a Comment

較新的 較舊