STM32 I2C總線通信與SPI總線通信專題講解

2023-07-27 17:06:39 來源:舊巷聞書
總線介紹:I2C(Inter-Integrated Circuit)總線(也稱IIC或I2C)是由PHILIPS公司開發的兩線式串行總線(單雙工),用于連接微控制器及其外圍設備,在這兩根線上可以掛很多設備,同一時刻只能有一個節點處于主機模式,其他節點處于從機模式,總線上數據的傳送都由主機發起。I2C總線沒有片選信號線,所以需要通過協議來找到對應操作的芯片。是微電子通信控制領域廣泛采用的一種總線標準。它是同步通信的一種特殊形式,具有接口線少,控制方式簡單,期間封裝形式少,通信速率高等優點。總線特征:

1.兩條總線線路:一條串行數據SDA,一條串行時鐘線SCL(主從設備使用同一時鐘,屬于同步通信)來完成數據的傳輸及外圍器件的擴展

2.I2C總線上的每一個設備都可以作為主設備或者從設備,而且每一個設備都會對應一個唯一的地址,通常是7位,有時候是10位

3.I2C總線數據傳輸速率在標準模式下可達100kbit/s,快速模式下可達400kbit/s,高速模式下可達3.4Mbit/s。在開發配置的時候,最好檢查從設備的傳輸速率從而對主設備(一般是MCU)進行相應的配置。一般通過I2C總線接口可編程時鐘來實現傳輸速率的調整,同時也跟所接的上拉電阻的阻值有關。


(資料圖片)

4.I2C總線上的主設備與從設備之間以字節(8位)為單位進行單雙工的數據傳輸。

拓撲結構——總線型

I2C 總線在物理連接上分別由SDA(串行數據線)和SCL(串行時鐘線)及上拉電阻組成,SCL由主機發出,SCL越快,通訊速率越快。通信原理是通過對SCL和SDA線高低電平時序的控制來產生I2C總線協議所需要的信號進行數據的傳遞。在總線空閑狀態時,這兩根線一般被上面所接的上拉電阻拉高,保持著高電平。

I2C總線協議

1.I2C協議規定: 總線上數據的傳輸必須以一個起始信號作為開始條件,以一個結束信號作為傳輸的停止條件。起始和結束信號總是由主設備產生。

2.空閑狀態:SCL和SDA都保持著高電平。

3.起始信號: 當SCL為高電平而SDA由高到低的跳變,表示產生一個起始條件,所有的從設備都能感受到這個跳變,做好準備等待被選擇。

4.結束信號:當SCL為高而SDA由低到高的跳變,表示產生一個 停止條件

5.數據傳輸:數據傳輸以字節為單位 , 主設備在SCL線上產生每個時鐘脈沖的過程中將在SDA線上傳輸一個數據位,數據在時鐘的高電平被采樣這時候采集到是1就是1,是0就是0,所以在傳輸數據時,當時鐘處于高電平時一定要保持穩定,時鐘處于低電平時可以變換數據。(高電平采樣,低電平變換)一個字節按數據位從高位到低位的順序進行傳輸。主設備在傳輸有效數據之前 要先指定從設備的地址,一般為7位,然后再發生數據傳輸的方向位, 0表示主設備向從設備寫數據,1表示主設備向從設備讀數據。主從設備以字節為單位(8位)進行數據傳輸,開始傳輸數據時把從設備地址加上方向位組成一個8位的字節進行發送并接收一個應答。

6.應答信號:接收數據的器件在接收到 8bit 數據后,向發送數據的器件發出低電平的應答信號,表示已收到數據。這個信號可以是主控器件發出,也可以是從動器件發出。總之,由接收數據的器件發出。

a.主設備向從設備寫數據:

b.主設備讀從設備的數據:

c.主設備讀從設備的某個寄存器:讀設備的寄存器首先應該對該設備發送寫命令,很多設備都可以看成是一段內存,所以寫命令寫給從設備,指明要讀取哪個地址(寄存器)的數據,接下來才是真正的讀數據。不同的從設備是由區別的,在驅動I2C從設備時應當查明設備的時序圖,又怎樣的要求,不同的時序對應了不同的命令。

STM32F4-I2C控制器特性

軟件模擬I2C時序:由于直接控制 GPIO 引腳電平產生通訊時序時,需要由 CPU控制每個時刻的引腳狀態,所以稱之為“軟件模擬協議”方式。我們知道,驅動I2C設備只需要兩根管腳,即使單片機上沒有I2C控制器,根據協議控制每根管腳每一時刻的電平狀態,一根模擬數據線,一根模擬時鐘線,就可以驅動從設備,相對而言效率低,但是可以實現控制驅動。STM32內部具備專門的I2C控制器,使用時只需對其進行相應的配置即可。

硬件控制產生I2C時序:STM32 的 I2C 片上外設專門負責實現 I2C 通訊協議,只要配置好該外設,它就會自動根據協議要求產生通訊信號,收發數據并緩存起來,CPU只要檢測該外設的狀態和訪問數據寄存器,就能完成數據收發。這種由硬件外設處理I2C協議的方式減輕了 CPU 的工作,且使軟件設計更加簡單。

控制器功能:配置主從模式(一般都把STM32當作主機使用,作為從機時應當對其賦一個地址),通過配置其內部的寄存器產生一些中斷和錯誤信號,配置通信速率位標準模式、快速模式、超快速模式等

STM32芯片有3組I2C外設,可以同時進行3組I2C傳輸。它們的I2C通訊信號引出到不同的GPIO引腳上,使用時必須配置到這些指定的引腳。

EEPROM(AT24CXX)存儲芯片介紹

一個典型的I2C接口的從設備,專門用于存儲數據的芯片。EEPROM (Electrically ErasableProgrammable readonly memory),帶電可擦可編程只讀存儲器,一種掉電后數據不丟失的存儲芯片。EEPROM可以在電腦上或專用設備上擦除已有信息,重新編程。

EEPROM常用來存儲一些配置信息,以便系統重新上電的時候加載之,容量不會很高。EEPOM 芯片最常用的通訊方式就是I2C協議。XX表示容量,常用值為01、02、04、16、32、64等,單位Kbit。一般的存儲芯片都具有寫保護功能,對WP管腳加一個高電平就開啟了寫保護功能,就無法往芯片內寫數據了。在開發中通常將該管腳接地,確保能夠寫數據

典型24CXX芯片引腳如下:

例:24C65的設備地址為7位,高4位恒定為1010,低3位取決于A0-A2的電平狀態,般主機在讀寫24CXX都是把設備地址連同讀寫位組合成一個字節一起發送。

24C65的電氣連線如下,根據電氣連線可知,A0-A2均接地,因此讀地址為1010 0001,即0xA1;寫地址為10100000,即0xA0 ,且WP接地,用戶隨時可向芯片內部寫入數據。

24C65寫時序:首先發送一個起始信號,接著發送從設備地址以及方向位,收到應答后,向從設備發送要寫的存儲區域的首地址,24C65的存儲地址是16位,先發送高8位,收到應答后再發送低8位,再次收到應答后開始寫數據。64Kbit大小位8K字節,需要13位即可表示,所以高3位固定定為0,如下圖。

這里是BYTE WRITE,一次寫一個字節,此芯片還支持PAGE WRITE,一次寫一頁,也就是8個字節,如果想寫更多,可設置一個for循環實現。

24C65 讀時序與寫時序基本相同,只不過在讀之前要發送再發送重復開始位進行讀操作。

I2C讀寫EEPROM實例

由電氣原理圖可知SCL和SDA分別接入了PB6和PB7管腳,讀地址為1010 0001,即0xA1;寫地址為10100000,即0xA0

步驟:

1.配置RCC

2.配置PB6和PB7管腳

3.配置I2C協議參數

4.編寫代碼

//mian.c#include "main.h"#include "stm32f4xx_hal.h"#include "i2c.h"#include "usart.h"#include "gpio.h"#define ReadAddr   0xA1#define WriteAddr  0xA0uint8_t Wbuf[20] = "EEPROM TEST OK!";uint8_t Rbuf[20] = {0};/*********  24C65寫數據函數*****************************/void  Eeprom_Write(uint16_t MemAddr, uint8_t *Wbuf, uint16_t len ){        while(len--){        //I2C_MEMADD_SIZE_16BIT表示存儲單元大小        //默認為兩個參數,分別是I2C_MEMADD_SIZE_16BIT和I2C_MEMADD_SIZE_8BIT        //由于24C65的存儲地址是16位的        //所以我們選擇I2C_MEMADD_SIZE_16BIT        //1表示一次寫一個字節                while(HAL_I2C_Mem_Write(&hi2c1, WriteAddr, MemAddr, I2C_MEMADD_SIZE_16BIT, Wbuf, 1, 100) != HAL_OK){};MemAddr++;Wbuf++;}}/*********  24C65讀數據函數*****************************/void  Eeprom_Read(uint16_t MemAddr, uint8_t *Rbuf, uint16_t len ){        //可以連續讀,所以無需循環        while(HAL_I2C_Mem_Read(&hi2c1, ReadAddr, MemAddr, I2C_MEMADD_SIZE_16BIT, Rbuf, len, 100) != HAL_OK );}int mian(){      MX_GPIO_Init();      MX_I2C1_Init();      MX_USART1_UART_Init();      printf("this is i2c eeprom testn");      Eeprom_Write(0, Wbuf, sizeof(Wbuf) );      HAL_Delay(500);      Eeprom_Read(0 , Rbuf, sizeof(Rbuf));      printf("READ:  %sn", Rbuf);      while(){      }}
STM32 SPI總線通信專題講解

SPI接口是Motorola 首先提出的全雙工三線同步串行外圍接口,采用主從模式(Master Slave)架構;支持多slave模式應用,一般僅支持單Master。時鐘由Master控制,在時鐘移位脈沖下,數據按位傳輸,是高位在前還是低位在前是可以配置的,配置時根據從設備的通信進行相應配置,一般是高位在前,低位在后(MSB first)。SPI接口有2根單向數據線,為全雙工通信,目前應用中的數據速率可達幾Mbps的水平。

SPI總線被廣泛地使用在FLASH、ADC、LCD等設備與MCU間,要求通訊速率較高的場合。

SPI接口共有4根信號線,分別是:設備選擇線、時鐘線、串行輸出數據線、串行輸入數據線。

(1)MOSI:主器件數據輸出,從器件數據輸入,連接從機的MOSI,與串口不同,串口需要反著連接(Rx-----Tx)

(2)MISO:主器件數據輸入,從器件數據輸出,連接從機的MISO

(3)SCLK :時鐘信號,由主器件產生

(4)/SS:從器件使能信號,由主器件控制(片選),一般情況下為地電平選中設備,高電平釋放設備。

SPI總線協議

1.數據交換邏輯:主機和從機都包含一個串行移位寄存器,主機通過向它的SPI串行寄存器寫入一個字節發起一次傳輸。寄存器通過MOSI信號線將字節傳送給從機,從機也將自己的移位寄存器中的內容通過MISO信號線返回給主機。這樣兩個移位寄存器中的內容就被交換了。從機的寫操作和讀操作時同步完成的,因此SPI成為一個很有效的協議。

如果主機只想寫不想讀,只需把數據放在數據寄存器,SPI控制器會自動傳給外設,同時忽略掉外設傳過來的數據即可;如果主機只想讀不想寫,主機寫給外設一個空字符或者隨便寫一個數據,外設就會把數據傳過來,不管是只讀還是只寫,主機與外設的讀和寫都h會發生且同時進行。

2.起始信號: NSS信號線由高變低,是SPI通訊的起始信號。

3.結束信號:NSS信號由低變高,是SPI通訊的停止信號。

4.數據傳輸:SPI使用MOSI及MISO信號線來傳輸數據,使用SCK信號線進行數據同步。MOSI及MISO數據線在SCK的每個時鐘周期傳輸一位數據,按位傳輸,且數據輸入輸出是同時進行的。SPI每次數據傳輸可以 8 位或 16 位為單位,每次傳輸的單位數不受限制,要么是8位,要么是16位,可以配置。

SPI的4種通信模式

在SPI操作中,最重要的兩項設置就是時鐘極性(CPOL)和時鐘相位(CPHA)這兩項即是主從設備間數據采樣的約定方式。由CPOL及CPHA的不同狀態,SPI分成了四種模式,主機與從機需要工作在相同的模式下才可以正常通訊,因此通常主機要按照從機支持的模式去設置。同樣在配置時一定要弄明白從機支持什么通信模式進行相應的配置。

1.時鐘極性CPOL : 設置時鐘空閑時的電平:

a.當CPOL= 0 ,SCK引腳在空閑狀態保持低電平;

b.當CPOL= 1 ,SCK引腳在空閑狀態保持高電平。

2.時鐘相位CPHA :設置數據采樣時的時鐘沿:

a.當 CPHA=0時,MOSI或 MISO 數據線上的信號將會在 SCK時鐘線的奇數邊沿被采樣

b.當 CPHA=1時, MOSI或 MISO 數據線上的信號將會在 SCK時鐘線的偶數邊沿被采樣

STM32F4-SPI控制器特性

1.通訊引腳:

STM32F4芯片最多支持6個SPI外設控制器,它們的SPI通訊信號引出到不同的GPIO引腳上,使用時必須配置到這些指定的引腳,以《STM32F4xx規格書》為準。f407只有SPI1、SPI2、SPI3。

其中SPI1、SPI4、SPI5、SPI6是APB2上的設備,最高通信速率達42Mbtis/s,SPI2、SPI3是APB1上的設備,最高通信速率為21Mbits/s。其它功能上沒有差異。

2.時鐘控制邏輯:

SCK線的時鐘信號,由波特率發生器根據“控制寄存器CR1”中的BR[0:2]位控制,該位是對f pclk 時鐘的分頻因子,對f pclk 的分頻結果就是SCK引腳的輸出時鐘頻率。

其中的fpclk 頻率是指SPI所在的APB總線頻率,APB1為fpclk1 ,APB2為fpckl2

3.數據控制邏輯:

STM32F4的MOSI及MISO都連接到數據移位寄存器上,數據移位寄存器的數據來源來源于接收緩沖區及發送緩沖區。

a.通過寫SPI的“數據寄存器DR”把數據填充到發送緩沖區中。

b.通過讀“數據寄存器DR”,可以獲取接收緩沖區中的內容。

c.其中數據幀長度可以通過“控制寄存器CR1”的“DFF位”配置成8位及16位模式;配置“LSBFIRST位”可選擇MSB先行(高位在前)還是LSB先行(低位在前)。

4.整體控制邏輯:

a.整體控制邏輯負責協調整個SPI外設,控制邏輯的工作模式根據“控制寄存器(CR1/CR2)”的參數而改變,基本的控制參數包括前面提到的SPI模式、波特率、LSB先行、主從模式、單雙向模式(同時發送和接收、只發送關掉接收、只接收關掉發送)等等。

b.在外設工作時,控制邏輯會根據外設的工作狀態修改“狀態寄存器(SR)”,只要讀取狀態寄存器相關的寄存器位,就可以了解SPI的工作狀態了。除此之外,控制邏輯還根據要求,負責控制產生SPI中斷信號、DMA請求及控制NSS信號線。

c.實際應用中,一般不使用STM32 SPI外設的標準NSS信號線,而是更簡單地使用普通的GPIO,軟件控制它的電平輸出,從而產生通訊起始和停止信號。

串行FLASH_W25X16簡介

FLSAH 存儲器又稱閃存,它與EEPROM都是掉電后數據不丟失的存儲器,但FLASH存儲器容量普遍大于 EEPROM,現在基本取代了它的地位。我們生活中常用的 U盤、SD卡、SSD 固態硬盤以及我們 STM32 芯片內部用于存儲程序的設備,都是 FLASH 類型的存儲器。在存儲控制上,最主要的區別是FLASH 芯片只能一大片一大片地擦寫,而EEPROM可以單個字節擦寫。

W25X16有8192個可編程頁,每頁256字節。用“頁編程指令”每次就可以編程256個字節。用扇區擦除指令每次可以擦除16頁,即一個扇區包含16頁,用塊擦除指令每次可以擦除256頁,用整片擦除指令即可以擦除整個芯片。W25X16有512個可擦除扇區或32個可擦除塊。

1.W25X16的硬件連線如下:

CS: 片選引腳,低電平有效,連接到STM32-PH2管腳

SO: 連接到STM32-PB4管腳(MISO)

SI: 連接到STM32-PB5管腳(MOSI)

CLK: 連接到STM32-PA5管腳(CLK)

WP: 寫保護管腳,低電平有效,有效時禁止寫入數據。接電源未使用

HOLD: HOLD 引腳可用于暫停通訊,該引腳為低電平時,通訊暫停,未使用

2.W25X16控制指令:

我們需要了解如何對FLASH芯片進行讀寫。FLASH 芯片自定義了很多指令,我們通過控制 STM32利用 SPI總線向 FLASH 芯片發送指令,FLASH芯片收到后就會執行相應的操作。

而這些指令,對主機端(STM32)來說,只是它遵守最基本的 SPI通訊協議發送出的數據,但在設備端(FLASH 芯片)把這些數據解釋成不同的意義,所以才成為指令。

a.讀制造商/設備ID(90):該指令通常在調試程序的時候用到,判斷SPI通信是否正常。該指令通過主器件拉低/CS片選使能器件開始傳輸,首先通過DI線傳輸“90H”指令,接著傳輸000000H的24位地址(A23-A0),之后從器件會通過DO線返回制造商ID(EFH)和設備ID。(注:SPI為數據交換通信,主器件在發送“90H”指令時也會接收到一個字節FFH,但此數據為無效數據)

b.寫使能命令(06H):在向 FLASH 芯片存儲矩陣寫入數據前,首先要使能寫操作,通過“Write Enable”命令即可寫使能。

c.扇區擦除(20H):由于 FLASH 存儲器的特性決定了它只能把原來為“1”的數據位改寫成“0”,而原來為“0”的數據位不能直接改寫為“1”。所以這里涉及到數據“擦除”的概念。

在寫入前,必須要對目標存儲矩陣進行擦除操作,把矩陣中的數據位擦除為“1”,在數據寫入的時候,如果要存儲數據“1”,那就不修改存儲矩陣 ,在要存儲數據“0”時,才更改該位。

d.讀狀態寄存器(05H):FLASH 芯片向內部存儲矩陣寫入數據需要消耗一定的時間,并不是在總線通訊結束的一瞬間完成的,所以在寫操作后需要確認FLASH芯片“空閑”。我們只需要讀取FLASH芯片內部的狀態寄存器SRP的S0即可(當這個位為“1”時,表明 FLASH芯片處于忙碌狀態,它可能正在對內部的存儲矩陣進行“擦除”或“數據寫入”的操作)

e.讀數據(03H):讀數據指令可從存儲器依次一個或多個數據字節,該指令通過主器件拉低/CS電平使能設備開始傳輸,然后傳輸“03H”指令,接著通過DI管腳傳輸24位芯片存儲地址,從器件接到地址后,尋址存儲器中的數據通過DO引腳輸出。每傳輸一個字節地址自動遞增,所以只要時鐘繼續傳輸,可以不斷讀取存儲器中的數據。

f.寫數據——頁編程(02H):頁編程指令可以在已擦除的存儲單元中寫入256個字節。該指令先拉低/CS引腳電平,接著傳輸“02H”指令和24位地址。后面接著傳輸至少一個數據字節,最多256字節。

注:當數據寫到一個新的扇區的時候,需要重新發起一個頁編程信號才能繼續寫入數據。

STM32 SPI_FLASH基本配置和操作

根據如下的硬件連線圖進行配置

步驟:

1.使能時鐘RCC

2.使能SPI1,配置相應管腳

3.配置SPI協議

4.編碼

//main.c#include "w25x16.h"uint8_t RD_Buffer[5000] = {0};uint8_t WR_Buffer[5000] = "SPI FLASH WRITE TESTn";int main(){    uint16_t FLASH_ID = 0;    uint32_t i;    MX_GPIO_Init();    MX_SPI1_Init();    FLASH_ID = sFLASH_ReadID();    /******測試擦除******/    sFLASH_EraseSector(4096*0);    //sFLASH_EraseSector(4096*1);    sFLASH_ReadBuffer(RD_Buffer,0,4096);    printf("讀數據開始n");    for(i=0; i< 4096; i++)    {    printf("%x ",RD_Buffer[i]);    }    printf("讀數據結束n");    /******測試寫操作1*****/    //寫之前都需要擦除扇區    sFLASH_EraseSector(4096*0);    sFLASH_WritePage(WR_Buffer,0, 20);    sFLASH_ReadBuffer(RD_Buffer,0,20);    printf("READ DATA: %sn",RD_Buffer);    /******測試寫操作2*****/    //寫之前都需要擦除扇區    sFLASH_EraseSector(4096*0);    sFLASH_EraseSector(4096*1);                                for(i=0; i< 4096; i++)    {    WR_Buffer[i] = 0x55;    }    sFLASH_WriteBuffer(WR_Buffer,4090, 1000);    sFLASH_ReadBuffer(RD_Buffer,4090,1000);    for(i=0; i< 1000; i++)    {    printf("%x ",RD_Buffer[i]);    }    /*****************/    while(){}}
//w25x16.h#ifndef __W25X16_H#define __W25X16_H#include "stm32f4xx_hal.h" //使用宏定義芯片指令#define W25X_ManufactDeviceID  0x90  /* Read identification */#define sFLASH_CMD_WREN        0x06/* Write enable instruction */#define sFLASH_CMD_RDSR        0x05/* Read Status Register instruction  */#define sFLASH_CMD_SE          0x20/* Sector Erase instruction */#define sFLASH_CMD_WRITE       0x02  /* Write to Memory instruction */#define sFLASH_CMD_READ        0x03/* Read from Memory instruction */#define sFLASH_DUMMY_BYTE      0x00 //空字節,用于只讀傳回來的數據#define sFLASH_BUSY_FLAG       0x01#define sFLASH_SPI_PAGESIZE    0x100/* 選中芯片,拉低信號 */#define sFLASH_CS_LOW()       HAL_GPIO_WritePin(GPIOH,GPIO_PIN_2,GPIO_PIN_RESET)/* 釋放芯片,拉高信號 */#define sFLASH_CS_HIGH()      HAL_GPIO_WritePin(GPIOH,GPIO_PIN_2,GPIO_PIN_SET)//定義函數uint8_t sFLASH_SendByte(uint8_t byte);uint16_t sFLASH_ReadID(void);void sFLASH_EraseSector(uint32_t SectorAddr);void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite);void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite);void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint32_t NumByteToRead);#endif
//w25x16.c#include "w25x16.h"extern SPI_HandleTypeDef hspi1;/*讀寫一個字節函數,因為SPI讀和寫同時完成*//*發送數據一定會接收到一個數據*/uint8_t sFLASH_SendByte(uint8_t byte){    uint8_t TX_DATA = byte;    uint8_t RX_DATA = 0;    HAL_SPI_TransmitReceive(&hspi1, &TX_DATA ,&RX_DATA , 1, 1000);    return RX_DATA;}/*等待擦除或者寫數據完成*/void sFLASH_WaitForEnd(void){    uint8_t sr_value = 0;    sFLASH_CS_LOW();    sFLASH_SendByte(sFLASH_CMD_RDSR);            //讀S0的值,為1表示忙碌,為0表示停止              do{                //發一個空字節,得到S0的值                             sr_value = sFLASH_SendByte(sFLASH_DUMMY_BYTE);    }while( sr_value & sFLASH_BUSY_FLAG);    sFLASH_CS_HIGH();}void sFLASH_WriteEnable(void){    sFLASH_CS_LOW();    sFLASH_SendByte(sFLASH_CMD_WREN);    sFLASH_CS_HIGH();}/*讀設備ID*/ uint16_t sFLASH_ReadID(void){    uint16_t FLASH_ID;    uint8_t temp0,temp1;    sFLASH_CS_LOW();    sFLASH_SendByte(W25X_ManufactDeviceID);    //讀設備指令后要發24位地址,所以要發三次    sFLASH_SendByte(sFLASH_DUMMY_BYTE);    sFLASH_SendByte(sFLASH_DUMMY_BYTE);    sFLASH_SendByte(sFLASH_DUMMY_BYTE);    //制造商ID    temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);    //設備商ID            temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);    sFLASH_CS_HIGH();    FLASH_ID = (temp0 < < 8) | temp1;    return FLASH_ID;} //擦除扇區,擦除為1,因為只能由1變為0 ,不能0變1void sFLASH_EraseSector(uint32_t SectorAddr){    //SectorAddr表示擦除第幾個扇區    sFLASH_WriteEnable();  //開啟寫使能    sFLASH_CS_LOW();//拉低,片選    //擦除命令    sFLASH_SendByte(sFLASH_CMD_SE);            //傳24位地址            //傳送高8位,將中8位和低8位一共16位移出去,得到高8位                       sFLASH_SendByte( (SectorAddr >>16) & 0xff);       sFLASH_SendByte( (SectorAddr >>8) & 0xff);    //傳送中8位    sFLASH_SendByte( (SectorAddr >>0) & 0xff);    //傳送低8位    sFLASH_CS_HIGH();    /*讀狀態寄存器,等待擦除完成*/    sFLASH_WaitForEnd();} //讀數據 //讀命令和讀地址發送后,芯片內部會自動不斷遞增讀數據void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint32_t NumByteToRead){    sFLASH_CS_LOW();    sFLASH_SendByte(sFLASH_CMD_READ);    sFLASH_SendByte( (ReadAddr >>16) & 0xff);   //傳送高8位    sFLASH_SendByte( (ReadAddr >>8) & 0xff);    //傳送中8位    sFLASH_SendByte( (ReadAddr >>0) & 0xff);    //傳送低8位    while(NumByteToRead--)    {    * pBuffer = sFLASH_SendByte(sFLASH_DUMMY_BYTE);    pBuffer++;    }    sFLASH_CS_HIGH();}//寫一頁最多只能寫256個字節,一個扇區16頁,一個塊16個扇區                               void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite){    if(NumByteToWrite > sFLASH_SPI_PAGESIZE )    {    NumByteToWrite = sFLASH_SPI_PAGESIZE;    printf("寫數據量過大,超過一頁大小n");    }    sFLASH_WriteEnable();  //開啟寫使能    sFLASH_CS_LOW();    sFLASH_SendByte(sFLASH_CMD_WRITE);    sFLASH_SendByte( (WriteAddr >>16) & 0xff);   //傳送高8位    sFLASH_SendByte( (WriteAddr >>8) & 0xff);     //傳送中8位    sFLASH_SendByte( (WriteAddr >>0) & 0xff);      //傳送低8位    while(NumByteToWrite--)    {    sFLASH_SendByte(* pBuffer);    pBuffer++;    }    sFLASH_CS_HIGH();    /*擦除和寫數據都涉及到寫動作,一定要等待完成*/    sFLASH_WaitForEnd();}//寫任意地址、任意長度void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite){    uint16_t NumOfPage, NumOfBytes, count, offset;    //求WriteAddr在某一頁的位置            offset = WriteAddr % sFLASH_SPI_PAGESIZE;    //求某一頁剩余的大小     count = sFLASH_SPI_PAGESIZE - offset;    /*處理頁不對齊的情況,防止頁內覆蓋*/    //先把某一頁剩下的部分寫掉,之后的就能新頁的起始處開始寫        /*offset有值表示需要頁對齊,如果要寫的字節數小于某一頁剩余的部分,那就無需對齊*/                       /*這兩個條件必須同時滿足*/    if(offset && (NumByteToWrite > count ))    {    sFLASH_WritePage(pBuffer,WriteAddr,count);    NumByteToWrite -= count;//去掉已經寫了的,從新頁開始    pBuffer += count;    WriteAddr += count;    }    /*最多可分多少頁*/    NumOfPage = NumByteToWrite / sFLASH_SPI_PAGESIZE;    /*剩余多少字節*/    NumOfBytes = NumByteToWrite % sFLASH_SPI_PAGESIZE;    if(NumOfPage)    {        while(NumOfPage--)        {             //每一頁都發起頁編程             sFLASH_WritePage(pBuffer,WriteAddr,sFLASH_SPI_PAGESIZE);             pBuffer += sFLASH_SPI_PAGESIZE;     WriteAddr += sFLASH_SPI_PAGESIZE;         }    }    if(NumOfBytes)    {    sFLASH_WritePage(pBuffer,WriteAddr,NumOfBytes);    }}
為什么會有兩種寫操作函數,是因為這里的寫操作有兩個特點:

1.無法突破頁限制,超過一頁需要重新發起頁編程信號。另外如果要寫的數據大于剩余一頁剩余的容量,那么超出的數據會寫到當前頁起始地址出。例如,初始輸入的寫地址為200,而要寫的數據大小為100,那么要寫的前56個字節會從地址200開始依次寫入,剩下的44個字節會從當前頁的0地址開始依次寫入,這很有可能覆蓋之前的數據。

2.無法突破扇區的限制,當數據寫到一個新的扇區的時候,需要重新發起一個頁編程信號才能繼續寫入數據。

標簽:

上一篇:怎么通過紅外傳感器與DS18B20來認識單總線?
下一篇:最后一頁