【STM8】SPI通訊


這篇內容有點長,如果有人想透過我的博客學習STM8的SPI,那是我的榮幸

首先我要先說大綱,這樣大家心里比較有底,可以把精力都用在SPI理解上

【SPI初步介紹】:介紹SPI如何接線、名稱解釋、通訊注意事項

【SPI引腳 - 初始化(上)】:相對於STM8,SPI的引腳位置說明,還有引腳的設置,另外還有初始化的部分代碼

【SPI寄存器 - 初始化(下)】:使用寄存器做一些設定,例如波特率、SPI開啟或關閉、SPI中斷、傳輸方式。。。太多了,要看寄存器手冊,我有整理圖片出來,另外還包括完整的初始化代碼

【SPI通訊】:SPI發送數據、SPI輪詢方式接收數據、SPI中斷方式接收數據

 

 

【SPI初步介紹】

下圖是SPI的通訊方式

M:Master(主)

S:Slave(從)

I:Input(輸入)

O:Output(輸出)

MISO:主設備(M)接收(I)數據,從設備(S)輸出(O)數據

MOSI:主設備(M)輸出(O)數據,從設備(S)接收(I)數據

SCK:時鍾訊號

GPIO:普通I/O口

NSS:或叫SS,由外部的高低電平決定自己是主機還是從機,在STM8里,可以由軟件決定,從而省下一個引腳去做別的事情,其他的芯片,就要去看datasheet才知道了

CS:片選(不一定會有),從機決定接收數據的依據,應用在SPI一主多從,就像廣播一樣,『一年二班小朋友起立!』,但一年一班的小朋友也聽到了,不過你不要起立啊,當然,一主一從也有可能用到的

 

上面解釋了各引腳功能,下面解釋SPI的通訊方式:

MOSI:主機發送線路。額外說明,主機想收到從機數據,主機要提供SCK,但是產生SCK的唯一條件,就是主機要發數據出去,哪怕是無意義的數據(偽字節),不管主機想發還是收,都不能省略這條線

MISO:如果不打算接收從機數據,就不要接了,如果想收,作為配套的,SCK這條線就必然要接上了

SCK:可接可不接,不接的情況下,是從機已經規定好接收格式,主機必須按照這個格式發送

NSS:可接可不接,上面解釋過了

CS:可接可不接,上面也解釋過了

 

最后一點,主機和從機的GND要連上,原理我不知道,這經驗是從串口(UART)學來的,任何通訊方式都要共地

 

【SPI引腳 - 初始化(上)】

因為我是用STM8S開發板來研究的,所以內容就局限在這里,鏈接是我的另一篇博客,介紹開發板和一些工具https://www.cnblogs.com/PureHeart/p/10824556.html

下面是引腳圖

 

這就是SPI相關的引腳,文章一開始也說了,某些情況下,可以省略幾個引腳

然后要說設置的部分了

MISO要設置成『弱上拉輸入模式』

MOSI、SCK要設置成『推挽輸出模式』

為什么設置成這樣,原理我不懂,但是能實現就好了不是嗎?

在現今社會里,沒有成功就代表什么都不是,可以糾結原理,但不是必要?

扯遠了,回來說正題,這兩種模式,需要看寄存器手冊,我知道有人是用『庫』來開發

但我自學的時候就學寄存器了

下面這張圖,是GPIO的設定

MISO對應的引腳是PC7,設定為『弱上拉輸入模式』,所以DDR=0,CR1=1,CR2=0

MOSI對應的引腳是PC6,設定為『推挽輸出模式』,所以DDR=1,CR1=1,CR2=0

SCK對應的引腳是PC5,設定為『推挽輸出模式』,所以DDR=1,CR1=1,CR2=0

NSS我不想外部控制,想用軟件的方式選擇是主機還是從機,在后續文章關於SPI寄存器會有說明

有了這份數據,可以寫代碼了,不過先上一張Px_DDR寄存器的圖,關於GPIO的寄存器,我覺得看上面的表格應該就夠了,上圖只是說明對應位置

PC_DDR = 0x60; // 0110 0000
PC_CR1 = 0xe0; // 1110 0000
PC_CR2 = 0x60; // 0110 0000

/* ==================== 分割線 ====================== 上方是寄存器控制,相對來說比較簡潔的寫法 */
/* ==================== 分割線 ====================== 下方是比較針對的寫法,直接控制寄存器內的某一位,兩種寫法選一個即可 */

PC_DDR_DDR5 = 1;    // 配置PC5(SCK)端口為輸出模式
PC_CR1_C15 = 1;     // 配置PC5(SCK)端口為推挽輸出模式
PC_CR2_C25 = 1;     // 配置PC5(SCK)端口為高速率輸出
    
PC_DDR_DDR6 = 1;    // 配置PC6(MOSI)端口為輸出模式
PC_CR1_C16 = 1;     // 配置PC6(MOSI)端口為推挽輸出模式
PC_CR2_C26 = 1;     // 配置PC6(MOSI)端口為高速率輸出
    
PC_DDR_DDR7 = 0;    // 配置PC7(MISO)端口為輸入模式
PC_CR1_C17 = 1;     // 配置PC7(MISO)端口為弱上拉輸入模式
PC_CR2_C27 = 0;     // 禁止PC7(MISO)端口外部中斷

 關於『寄存器』和『寄存器的某一位』,直接用一個例子來解釋比較快

【所有一年級的學生去操場】【一年一班的同學去操場】

『寄存器』就相當於一年級

『寄存器的某一位』就相當於一年級的某一班

上面也說了,分割線的上方和下方,選擇一個來用即可

 

上面代碼的變量,全部都定義在『iostm8s103F3.h』里面了,當然,這是官方的頭文件里面的內容,不是我自己命名這些變量的

貌似官方沒有SPI范例,但是有Timer、UART之類的范例,隨便拿一個來添加SPI代碼即可

這是官方全部范例的鏈接,提取碼是gszh,需要請自取https://pan.baidu.com/s/1La0LdFQxKl2_AyZXkBkv3w

 

【SPI寄存器 - 初始化(下)】

 

雖然圖片有點多,但是代碼沒幾行的

下面我直接貼上我測試時的代碼,具體情況可能大家都不同,寄存器的幾個位修改一下就好了,我直接配合『初始化-上』的代碼一起貼出來

 其實整個初始化的思路如下

1.打開SPI時鍾

2.引腳配置

3.開啟SPI中斷(如果不需要則不用開)

4.設置SPI_CR1(SPI控制寄存器1)

5.設置SPI_CR2(SPI控制寄存器2)

6.開啟SPI

void Init_SPI(void)
{
    CLK_PCKENR1 |= 0x02; //打開SPI時鍾 
    /*PC6、PC5設置為輸出,最大10MHz*/ 
    //PC_DDR = 0x60; // 0110 0000
    //PC_CR1 = 0xe0; // 1110 0000 
    //PC_CR2 = 0x60; // 0110 0000 
    
    PC_DDR_DDR5 = 1;    // 配置PC5(SCK)端口為輸出模式
    PC_CR1_C15 = 1;     // 配置PC5(SCK)端口為推挽輸出模式
    PC_CR2_C25 = 1;     // 配置PC5(SCK)端口為高速率輸出
    
    PC_DDR_DDR6 = 1;    // 配置PC6(MOSI)端口為輸出模式
    PC_CR1_C16 = 1;     // 配置PC6(MOSI)端口為推挽輸出模式
    PC_CR2_C26 = 1;     // 配置PC6(MOSI)端口為高速率輸出
    
    PC_DDR_DDR7 = 0;    // 配置PC7(MISO)端口為輸入模式
    PC_CR1_C17 = 1;     // 配置PC7(MISO)端口為弱上拉輸入模式
    PC_CR2_C27 = 0;     // 禁止PC7(MISO)端口外部中斷
    
    SPI_ICR_RXIE = 1; // 開啟SPI中斷接收(下方備注)
    
    // [7]先發MSB
    // [6]禁止SPI
    // [5][4][3]f_Master / 2
    // [2]主設備
    // [1]空閑時SCK保持低電平
    // [0]數據采樣從第一個時鍾沿開始
    SPI_CR1 = 0x04; /*MSB、1MHz、主設備、CPOL空閑為低、CPHA第一個時鍾開始*/ 
    
    // [7]雙線單向模式
    // [6]輸入使能(只接收模式)
    // [5]CRC計算禁止
    // [4]下個發送數據來自Tx緩沖
    // [3]保留
    // [2]全雙工(同時收發)
    // [1]使能軟件從設備管理(不需要判斷硬件CS位,節省一個引腳)
    // [0]主模式
    SPI_CR2 = 0x03; /*雙線單向視距傳輸、CRC計算禁止、軟件NSS、主模式*/ 
    
    SPI_CR1_SPE = 1; // 打開SPI
} 

備注:關於中斷的部分,如果想要使用,還必須加上一行代碼『asm("rim");』,通常加在main函數里面的while(1)之前

      這個代碼就像電源的總閘一樣,你房間的電閘打開了,但總閘沒開,結果還是沒有電

      當然有一個相對應的,就是關閉總閘『asm("sim");』

 

【SPI通訊】

終於到最后的環節了,關於發送和接收,接收又分兩種,輪詢和中斷

直接上代碼吧,也沒幾行

/* SPI發送 */
void SPI_sendchar(unsigned char c)
{
    while(!(SPI_SR & 0x02));    // 等待發送緩沖區『為』空
    SPI_DR = c;                  // 將發送的數據寫到數據寄存器
    //while(!(SPI_SR & 0x01));    // 輪詢的方式,等待接收緩沖區『非』空
    //UART1_sendchar(SPI_DR); // 備注
}

/* SPI中斷 */
#pragma vector=SPI_RXNE_vector
__interrupt void SPI_RXNE_IRQHandler(void)
{
    //RxBuf[cnt++]=SPI_DR;
    while(!(SPI_SR & 0x01)); // 輪詢
    UART1_sendchar(SPI_DR); // 備注
}

 備注:我用很迂回的方式來顯示結果,邏輯分析儀抓SPI還沒研究成功,所以我借用了UART(串口)

       輪詢或中斷接收到數據,透過串口把數據發送給『USB轉TTL小板』,在電腦上用串口調試助手顯示出來

 

終於寫完了,謝謝你的觀看,希望對你有幫助


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM