其實各種協議是很重要的,這篇文章就當做我對spi協議的一個整理吧。
必要的spi簡介:
https://www.cnblogs.com/zengsf/p/7221207.html?utm_source=itdadao&utm_medium=referral
前幾天在網上看到一段關於oled的程序
不過那段程序是用的io口模擬spi來控制oled模塊的
我在想stm32本身就有spi為何要用io口來模擬spi協議呢
所以就想自己試着寫一寫。
首先第一部分是關於stm32的spi引腳:
http://www.eeworld.com.cn/mcu/2015/0615/article_20333.html
SPI1->CS ------ PA4
SPI1->MISO ------ PA6
SPI2->CS ------ PB12
SPI2->CLK ------ PB13
SPI2->MISO ------ PB14
SPI2->MOSI ------ PB15
SPI3->CS ------ PA15
SPI3->CLK ------ PB3
SPI3->MISO ------ PB4
SPI3->MOSI ------ PB5
對於SPI ,需要打開相關RCC時鍾
主模式下
CLK 配置成復用推挽輸出
MOSI 配置成復用推挽輸出
MISO 配置成富哦那個或帶上拉輸入
CS若采用硬件則配置成推挽輸出,若采用軟件模式,則采用普通IO推挽輸出即可。

引腳配置好了,我們下面進行spi模式的配置。下面的圖片僅供參考,具體問題還需具體分析。spi的極性和相位為4中,我們還需要根據實際情況去查看。
配置好了之后,我們就開始寫應用了,也就是收發函數,收發函數把數據通過配置好的底層發出去。
對於應用函數,我們應該設置好形參,形參主要是用來保存協議來往的數據的。
這個協議的收發函數有兩種(因為這個協議是雙工的):讀寫分開的函數,讀寫一起的。
讀寫分開的函數:
void SPI_Ecah_Buffer_Send(u8* pBuffer, u16 NumByteToRead) //發送
{
for(int i = 0; i < NumByteToRead; i++)
{
SPI_Conmunication_SendByte(*pBuffer);
pBuffer++;
}
}
void SPI_Buffer_Receive(u8* pBuffer, u16 NumByteToRead) //接收
{
while (NumByteToRead--)
{
*pBuffer = SPI_Conmunication_SendByte (Dummy_Byte);
pBuffer++;
}
}
void SPI_Ecah_Buffer_Send(u8* str , u8* pBuffer, u16 NumByteToRead)
{
for(int i = 0; i < NumByteToRead; i++)
{
*str = SPI_Conmunication_SendByte(*pBuffer);
pBuffer++;
str++;
}
}
OLED引腳介紹: 這個是oled模塊上的幾個引腳。我們要把它和spi對應起來。
CS:OLED片選信號
RST:OLED復位端口
DC: 命令/數據選擇端口(0:讀寫命令, 1: 讀寫數據)
SCLK(D0):串口時鍾線
SDIN(D1): 串口數據線CS————GPIOD3; spi的片選
RST————GPIOD4; 復位(spi里沒有)
DC—————GPIOD5; 表示寫數據還是命令。(spi里沒有)
D0——————GPIOD6; spi時鍾線
D1——————GPIOD7; spi MOSI,代表oled從這里接收數據,假設單片機是主機,oled屏是從機。
* Mode :O:寫命令 1:寫數據
* data :數據/命令
*
*/
void SPI_Write(char data, int Mode)
{
int i = 0;
if(Mode) //這個是用來區分命令,還是數據的。
{
OLED_DC(1); //DC引腳輸入高,表示寫數據
}
else
{
OLED_DC(0); //DC引腳輸入低,表示寫命令
}
OLED_CS(0); //CS引腳輸入低,片選使能 這里符合spi協議,低電平是選中。
for(i = 0; i < 8; i++) //從這句話,我們能判斷出,這個spi協議是,8位的, spi分為8幀和16幀
{
OLED_D0(0); //D0引腳輸入低
if(data & 0x80) //判斷傳輸的數據最高位為1還是0 從這里判斷是先傳輸高位, spi協議有先傳高位或先傳低位。
{
OLED_D1(1); //D1引腳輸入高
}
else
{
OLED_D1(0); //D1引腳輸入低
}
OLED_D0(1); //D1引腳輸入高 //先准備好數據,然后在讓時鍾有一個上升沿,也就是上升沿的時候讀取數據。
data <<= 1; //將數據左移一位
}
OLED_DC(0); //DC引腳輸入低
OLED_CS(1); //CS引腳輸入高,片選失能, //這個可能是為了防止干擾,
}
CS————GPIOD3; spi的片選
RST————GPIOD4; 復位(spi里沒有)
DC—————GPIOD5; 表示寫數據還是命令。(spi里沒有)
D0——————GPIOD6; spi時鍾線
SPI1->CS ------ PA4----CS
SPI1->MISO ------ PA6----- D1

第一個是全雙工,其實沒有必要,因為oled屏好像不會返回數據給stm32
第二行是從機,我感覺單片機應該還是主機的好
第三個8幀,這個應該不用改
第四個,這個應該也是低電平,空閑時刻D0 ,,,,,,候選項:SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0)
第五個,感覺應該是第一個跳變沿被采集,,,,,,, 候選項:SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)
第六個,cs片選引腳為軟件模式
第七個, 這個是波特率,分頻為8,可以但是這個時鍾默認應該是36M的。
第八個,這個的確是先傳高字節
第九個,這個是CRC校驗,實際上是這個賦值是7是沒有意義的,至於為什么?
https://blog.csdn.net/kobesdu/article/details/50972273
SPI_CRCPolynomial :這是 SPI 的 CRC 校驗中的多項式,若我們使用 CRC 校驗
時,就使用這個成員的參數(多項式)來計算 CRC 的值。由於本實驗的 Flash 不支持 CRC
校驗,所以我們向這個結構體成員賦值為7 實際上是沒有意義的。
配置完這些結構體成員后,我們要調用SPI_Init() 函數把這些參數寫入寄存器中,實現
SPI 的初始化,然后調用
* Mode :O:寫命令 1:寫數據
* data :數據/命令
*
*/
void SPI_Write(char data, int Mode)
{
int i = 0;
if(Mode) //這個是用來區分命令,還是數據的。
{
OLED_DC(1); //DC引腳輸入高,表示寫數據
}
else
{
OLED_DC(0); //DC引腳輸入低,表示寫命令
}
OLED_CS(0); //CS引腳輸入低,片選使能 這里符合spi協議,低電平是選中。
OLED_DC(0); //DC引腳輸入低
OLED_CS(1); //CS引腳輸入高,片選失能, //這個可能是為了防止干擾,
}