實現目標
1、掌握SPI總線基礎知識;
2、會使用軟件模擬SPI總線和STM32硬件SPI總線;
3、?學會STM32CubeMX軟件關于SPI的配置;
4、掌握OLED顯示屏驅動;
5、具體目標:(1)用STM32硬件SPI驅動OLED顯示“你好哇,王小波!”;(2)用軟件模擬SPI總線驅動OLED顯示“你好哇,王小波!”(只提供代碼)。
一、SPI概述
1.1 SPI簡介
????????SPI是串行外設接口(Serial Peripheral Interface)的縮寫,是美國摩托羅拉公司(Motorola)最先推出的一種同步串行傳輸規范,也是一種單片機外設芯片串行擴展接口,是一種高速、全雙工、同步通信總線。它被廣泛地使用在 ADC、LCD 等設備與 MCU 間,要求通訊速率較高的場合。
????????SPI有主、從兩種模式(一主一從,一主多從),通常由一個主模塊和一個或多個從模塊組成(SPI不支持多主機),主模塊選擇一個從模塊進行同步通信,從而完成數據的交換。提供時鐘的為主設備(Master),接收時鐘的設備為從設備(Slave),SPI接口的讀寫操作,都是由主設備發起,當存在多個從設備時,通過各自的片選信號進行管理。
?
1.2 SPI通信原理
SPI總線通信原理很簡單,需要至少4根線,單向傳輸時3根線,它們是MISO(主設備數據輸入)、MOSI(主設備數據輸出)、SCLK(時鐘)和CS/SS(片選):
MOSI(Master Out Slave In):主設備 --->從設備(主設備發送,從設備接收)
MISO(Master In Slave Out):從設備 --->主設備(主設備接收,從設備發送)
SCLK(Serial Clock):傳輸時鐘信號,用于主從設備的同步
CS? ? ? (chip select):片選信號,用于選擇從設備
SPI主機與從機通訊實際上時兩者的移位寄存器交換數據的過程,在時鐘信號SCL的推動下,主機發送一位,從機也發送一位,兩者互換數據,交換8次之后,便完成了一個字節的傳輸。
?
SPI數據通信的流程可以分為以下幾步:
1、主設備發起信號,將CS/SS拉低,啟動通信。
2、主設備通過發送時鐘信號,來告訴從設備進行寫數據或者讀數據操作(采集時機可能是時鐘信號的上升沿(從低到高)或下降沿(從高到低),因為SPI有四種模式,后面會講到),它將立即讀取數據線上的信號,這樣就得到了一位數據(1bit)。
3、主機(Master)將要發送的數據寫到發送數據緩存區(Menory),緩存區經過移位寄存器(緩存長度不一定,看單片機配置),串行移位寄存器通過MOSI信號線將字節一位一位的移出去傳送給從機,同時MISO接口接收到的數據經過移位寄存器一位一位的移到接收緩存區。
4、從機(Slave)也將自己的串行移位寄存器(緩存長度不一定,看單片機配置)中的內容通過MISO信號線返回給主機。同時通過MOSI信號線接收主機發送的數據,這樣,兩個移位寄存器中的內容就被交換。
例如,下圖示例中簡單模擬SPI通信流程,主機拉低NSS片選信號,啟動通信,并且產生時鐘信號,上升沿觸發邊沿信號,主機在MOSI線路一位一位發送數據0X53,在MISO線路一位一位接收數據0X46,如下圖所示:
????????說明:SPI只有主模式和從模式之分,沒有讀和寫的說法,外設的寫操作和讀操作是同步完成的。若只進行寫操作,主機只需忽略接收到的字節(虛擬數據);反之,若主機要讀取從機的一個字節,就必須發送一個空字節來引發從機的傳輸。也就是說,你發一個數據必然會收到一個數據;你要收一個數據必須也要先發一個數據
1.3 通信特性
1.3.1、設備選擇
????????SPI是單主設備(Single Master)通信協議,只有一支主設備能發起通信,當SPI主設備想讀/寫從設備時,它首先拉低從設備對應的SS線(SS是低電平有效)。接著開始發送工作脈沖到時鐘線上,在相應的脈沖時間上,主設備把信號發到MOSI實現“寫”,同時可對MISO采樣而實現“讀”。如下圖所示:
1.3.2、設備時鐘
SPI時鐘特點主要包括:時鐘速率、時鐘極性和時鐘相位三方面。
(1) 時鐘速率
????????SPI總線上的主設備必須在通信開始時候配置并生成相應的時鐘信號。從理論上講,只要實際可行,時鐘速率就可以是你想要的任何速率,當然這個速率受限于每個系統能提供多大的系統時鐘頻率,以及最大的SPI傳輸速率。
(2)時鐘極性
????????根據硬件制造商的命名規則不同,時鐘極性通常寫為CKP或CPOL。時鐘極性和相位共同決定讀取數據的方式,比如信號上升沿讀取數據還是信號下降沿讀取數據。
????????CKP可以配置為1或0。這意味著你可以根據需要將時鐘的默認狀態(IDLE)設置為高或低。極性反轉可以通過簡單的邏輯逆變器實現。你必須參考設備的數據手冊才能正確設置CKP和CKE。
CKP = 0:時鐘空閑IDLE為低電平 0;
CKP = 1:時鐘空閑IDLE為高電平1。
(3)時鐘相位
????????根據硬件制造商的不同,時鐘相位通常寫為CKE或CPHA。顧名思義,時鐘相位/邊沿,也就是采集數據時是在時鐘信號的具體相位或者邊沿;
CKE = 0:在時鐘信號SCK的第一個跳變沿采樣;
CKE = 1:在時鐘信號SCK的第二個跳變沿采樣。
1.3.3? SPI的四種模式
????????根據SPI的時鐘極性和時鐘相位特性可以設置4種不同的SPI通信操作模式,它們的區別是定義了在時鐘脈沖的哪條邊沿轉換(toggles)輸出信號,哪條邊沿采樣輸入信號,還有時鐘脈沖的穩定電平值(就是時鐘信號無效時是高還是低),詳情如下所示:
? ? ? ? ? ? CPOL=0 | ? ? ? ? ? ? CPOL=1 | |
CPHA=0 | 空閑態時,SCK處于低電平,數據采樣是在第1個邊沿,也就是SCK由低電平到高電平的跳變,所以數據采樣是在上升沿(準備數據),(發送數據)數據發送是在下降沿。 | 當空閑態時,SCK處于低電平,數據發送是在第2個邊沿,也就是SCK由低電平到高電平的跳變,所以數據采樣是在下降沿,數據發送是在上升沿。 |
CPHA=1 | 當空閑態時,SCK處于高電平,數據采集是在第1個邊沿,也就是SCK由高電平到低電平的跳變,所以數據采集是在下降沿,數據發送是在上升沿。 | 當空閑態時,SCK處于高電平,數據發送是在第2個邊沿,也就是SCK由高電平到低電平的跳變,所以數據采集是在上升沿,數據發送是在下降沿。 |
(1)相位(CPHA):表示時鐘線空閑時的狀態;
(2)極性(CPOL):表示設備從數據線采樣的時刻;(3)四種模式編號:[00]:Mode0 、[01]:Mode1、[10]:Mode2??、[11]:Mode3?
黑線為采樣數據的時刻,藍線為SCK時鐘信號
舉個例子,下圖是SPI Mode0讀/寫時序,可以看出SCK空閑狀態為低電平,主機數據在第一個跳變沿被從機采樣,數據輸出同理。
下圖是SPI Mode3讀/寫時序,SCK空閑狀態為高電平,主機數據在第二個跳變沿被從機采樣,數據輸出同理。
1.4、多從機模式
多從機模式有兩種:多片選和菊花鏈片選
1.4.1多片選
通常,每個從機都需要一條單獨的SS線。如果要和特定的從機進行通訊,可以將相應的NSS
信號線拉低,并保持其他SS信號線的狀態為高電平;如果同時將兩個SS信號線拉低,則可能會出現亂碼,因為從機可能都試圖在同一條MISO
線上傳輸數據,最終導致接收數據亂碼。
1.4.2菊花鏈片選
菊花鏈的最大缺點是信號串行傳輸,一旦數據鏈路中的某設備發生故障的時候,它下面優先級較低的設備就不可能得到服務了。另一方面,距離主機越遠的從機,獲得服務的優先級越低,所以需要安排好從機的優先級,并且設置總線檢測器,如果某個從機超時,則對該從機進行短路,防止單個從機損壞造成整個鏈路崩潰的情況。
1.5 SPI優缺點
1.5.1 優點
- 高速數據傳輸速率;
- 沒有像I2C這樣復雜的從設備尋址系統;
- 分離的MISO和MOSI信號線,全雙工通信;
- 極其靈活的數據傳輸,不限于8位,它可以是任意大小的字;
- 非常簡單的硬件結構。從站不需要唯一地址(與I2C不同)。從機使用主機時鐘,不需要精密時鐘振蕩器/晶振(與UART不同)。不需要收發器(與CAN不同)。
1.5.2 缺點
- 使用四根信號線(I2C和UART使用兩根信號線);
- 無法確認是否已成功接收數據(I2C擁有此功能);
- 沒有任何形式的錯誤檢查,如UART中的奇偶校驗位;
- 只允許一個主設備;
- 沒有硬件從機應答信號(主機可能在不知情的情況下無處發送);
- 沒有定義硬件級別的錯誤檢查協議;
- 與RS-232和CAN總線相比,只能支持非常短的距離;
二、STM32的SPI總線
2.1 STM32 的SPI
1.STM32內部集成了硬件SPI收發電路,可以由硬件自動執行時鐘生成、數據收發等功能,減輕CPU的負擔
2.可配置8位/16位數據幀、高位先行/低位先行
3.時鐘頻率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)
4.支持多主機模型、主或從操作
5.可精簡為半雙工/單工通信
6.支持DMA
7.兼容I2S協議
2.2 STM32? SPI框圖
2.3 SPI外設對應的引腳
不同型號的芯片基本都有3個SPI外設,其中SPI2,SPI3支持I2S通信因為I2S與SPI協議類似,所以他們共用一套邏輯就是上面的SPI框圖。
三、OLED概述
3.1OLED簡介
????????OLED,即有機發光二極管(Organic Light-Emitting Diode),又稱為有機電激光顯示(Organic Electroluminesence Display, OELD)。OLED由于同時具備自發光,不需背光源、對比度高、厚度薄、視角廣、反應速度快、可用于撓曲性面板、使用溫度范圍廣、構造及制程較簡單等優異之特性,被認為是下一代的平面顯示器新興應用技術。
? ? ? ? OLED顯示技術具有自發光的特性,采用非常薄的有機材料涂層和玻璃基板,當有電流通過時,這些有機材料就會發光,而且OLED顯示屏幕可視角度大,并且能夠節省電能,從2003年開始這種顯示設備在MP3播放器上得到了應用。
? ? ? ? LCD都需要背光,而OLED不需要,因為它是自發光的。這樣同樣的顯示,OLED效果要來得好一些。以目前的技術,OLED的尺寸還難以大型化,但是分辨率確可以做到很高。
7針OLED引腳
1.GND -- 電源地,接在板子上任意GND的引腳2.VCC -- 電源正(3.3V~5V)接在板子上任意VCC引腳
3.D0 -- 對應的是時鐘線(SCK或者SCLK)
4.D1 -- 對應的是數據線,也就是 MISO (主入從出) 或者 MOSI(主出從入)
5.RES -- 復位線,最開始是低電平,先拉低再拉高,可選擇任意空IO口
6.DC -- 數據/命令控制線,高電平為1是數據線,低電平為0是命令線,可選擇任意空IO口
7.CS -- 片選信號線,低電平有效,不用可以接地,用來選擇從機的
四、原理圖設計
五、STM32CubeMX 配置
5.1.SPI 配置(D0、D1)
(1)Mode: 模式
Full-Duplex Master 主機全雙工模式
Full-Duplex Slave 從機全雙工模式
Half-Duplex Master 主機半雙工模式
Half-Duplex Slave 從機半雙工模式
Receive Only Master 只接收主機模式
Receive Only Slave 只接收從機模式
Transmit Only Master 只發送從機模式? ?
(2)硬件NSS信號
- 不使能
- NSS輸入信號
- NSS輸出信號
如果片選引腳沒有連接硬件NSS信號,則需要選擇軟件片選
(3)基本參數
Frame Format幀格式:Motorola摩托羅拉 或 TI
Data size數據大小:8位 或 16位
First Bit:MSB/LSB先行
(4)時鐘參數波特率分配因子:
CPOL時鐘極性:low or high
CPHA時鐘相位:1 edge or 2 edge
(5)高級參數CRC循環校驗:使能/失能
NSS信號類型:軟件
?5.2 其他管腳配置(RES、CS、DC)
PA1、PA4、PA6,配置為普通IO口,gpio_output
六、取模軟件的應用(PCtoLCD2002完美版)
1. 打開軟件,模式為(字符模式)
2.?點擊菜單欄【選項】,打開字模選項
3.勾選【陰碼點陣】、逐行式取模、逆向、C51格式。
4.輸入“你好哇,王小波!”,進行取模。
軟件下載地址:https://wwe.lanzoui.com/iIBEzs77lcf?
七、程序設計
7.1修改驅動函數
中景園官方驅動代碼(標準庫):https://download.csdn.net/download/luojuan198780/89319982
將中景園原版的?OLED_WR_Byte函數內容修改為含SPI發送函數:HAL_SPI_Transmit,兩個函數如下:
//中景園原版
void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{ u8 i; if(cmd)OLED_DC_Set();else OLED_DC_Clr(); OLED_CS_Clr();for(i=0;i<8;i++){ OLED_SCL_Clr();if(dat&0x80)OLED_SDA_Set();else OLED_SDA_Clr();OLED_SCL_Set();dat<<=1; } OLED_CS_Set();OLED_DC_Set(); }//修改后帶HAL_SPI_Transmit的函數
void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{ uint8_t *data = &dat; if(cmd)OLED_DC_Set();elseOLED_DC_Clr();OLED_CS_Clr();HAL_SPI_Transmit(&hspi1, (uint8_t *)data, 1, 200); OLED_CS_Set();OLED_DC_Set();
}
7.2修改 SPI的其它管腳
打開oled.h文件將CS? DC RES? 標準庫置高置低函數修改為HAL庫函數
#define OLED_CS_Clr() HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_RESET)//RES
#define OLED_CS_Set() HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_SET)#define OLED_DC_Clr() HAL_GPIO_WritePin(DC_GPIO_Port,DC_Pin,GPIO_PIN_RESET)//DC
#define OLED_DC_Set() HAL_GPIO_WritePin(DC_GPIO_Port,DC_Pin,GPIO_PIN_SET)#define OLED_RES_Clr() HAL_GPIO_WritePin(RES_GPIO_Port,RES_Pin,GPIO_PIN_RESET)//RES
#define OLED_RES_Set() HAL_GPIO_WritePin(RES_GPIO_Port,RES_Pin,GPIO_PIN_SET)
7.3 增加? u8/u16/u32??,并包含各個頭文件
標準庫是u8/u16/u32,HAL庫是uint8_t/uint16_t/uint32_t .可以用下面方法實現轉化,也可以直接ctr+h? 替換。
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
7.4 添加中文字庫
? ?在oledfont.h文件的? Hzk1[][32]={} 數組中添加下列“你好哇,王小波!”字模
{0x00,0x80,0x60,0xF8,0x07,0x40,0x20,0x18,0x0F,0x08,0xC8,0x08,0x08,0x28,0x18,0x00,0x01,0x00,0x00,0xFF,0x00,0x10,0x0C,0x03,0x40,0x80,0x7F,0x00,0x01,0x06,0x18,0x00},/*"你",11*/
{0x10,0x10,0xF0,0x1F,0x10,0xF0,0x00,0x80,0x82,0x82,0xE2,0x92,0x8A,0x86,0x80,0x00,0x40,0x22,0x15,0x08,0x16,0x61,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00},/*"好",12*/
{0x00,0xFC,0x04,0x04,0xFC,0x00,0x40,0x44,0x44,0x44,0x7F,0x44,0x44,0x44,0x40,0x00,0x00,0x0F,0x04,0x04,0x0F,0x00,0x40,0x44,0x44,0x44,0x7F,0x44,0x44,0x44,0x40,0x00},/*"哇",13*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",14*/
{0x00,0x02,0x82,0x82,0x82,0x82,0x82,0xFE,0x82,0x82,0x82,0x82,0x82,0x02,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00},/*"王",15*/
{0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x20,0x40,0x80,0x00,0x00,0x08,0x04,0x03,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x01,0x0E,0x00},/*"小",16*/
{0x10,0x60,0x02,0x0C,0xC0,0x00,0xF8,0x88,0x88,0x88,0xFF,0x88,0x88,0xA8,0x18,0x00,0x04,0x04,0x7C,0x03,0x80,0x60,0x1F,0x80,0x43,0x2C,0x10,0x28,0x46,0x81,0x80,0x00},/*"波",17*/
{0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",18*/
7.5 延時函數替換
將驅動函數中的delay_ms延時函數替換為HAL_Delay 函數。刪除頭文件#include "delay.h"與
#include "sys.h"
HAL_Delay(200);//delay_ms(200);
7.6 OLED初始化,顯示設計
/* USER CODE BEGIN 2 */OLED_Init();//oled初始化OLED_ColorTurn(0);//0正常顯示,1 反色顯示OLED_DisplayTurn(0);//0正常顯示 1 屏幕翻轉顯示/* USER CODE END 2 */while (1){OLED_ShowChinese(0,16,11,16,1);//你OLED_ShowChinese(16,16,12,16,1);//好OLED_ShowChinese(32,16,13,16,1);//哇OLED_ShowChinese(48,16,14,16,1);//,OLED_ShowChinese(64,16,15,16,1);//王OLED_ShowChinese(80,16,16,16,1);//小OLED_ShowChinese(96,16,17,16,1);//波 OLED_ShowChinese(112,16,18,16,1);//! OLED_Refresh();HAL_Delay(500);OLED_Clear();}
八、實驗效果
總結
最終版程序(HAL硬件SPI):https://gitee.com/luojaun/qianrushi/tree/master/
最終版程序(HAL軟件模擬SPI):https://gitee.com/luojaun/qianrushi