- 總線介紹: I2C(Inter-Integrated Circuit)總線(也稱(chēng)IIC或I2C)是由PHILIPS公司開(kāi)發(fā)的兩線式串行總線(單雙工),用于連接微控制器及其外圍設(shè)備,在這兩根線上可以掛很多設(shè)備,同一時(shí)刻只能有一個(gè)節(jié)點(diǎn)處于主機(jī)模式,其他節(jié)點(diǎn)處于從機(jī)模式,總線上數(shù)據(jù)的傳送都由主機(jī)發(fā)起。I2C總線沒(méi)有片選信號(hào)線,所以需要通過(guò)協(xié)議來(lái)找到對(duì)應(yīng)操作的芯片。是微電子通信控制領(lǐng)域廣泛采用的一種總線標(biāo)準(zhǔn)。它是同步通信的一種特殊形式,具有接口線少,控制方式簡(jiǎn)單,期間封裝形式少,通信速率高等優(yōu)點(diǎn)。
- 總線特征:
1.兩條總線線路:一條串行數(shù)據(jù)SDA,一條串行時(shí)鐘線SCL(主從設(shè)備使用同一時(shí)鐘,屬于同步通信)來(lái)完成數(shù)據(jù)的傳輸及外圍器件的擴(kuò)展
2.I2C總線上的每一個(gè)設(shè)備都可以作為主設(shè)備或者從設(shè)備,而且每一個(gè)設(shè)備都會(huì)對(duì)應(yīng)一個(gè)唯一的地址,通常是7位,有時(shí)候是10位
3.I2C總線數(shù)據(jù)傳輸速率在標(biāo)準(zhǔn)模式下可達(dá)100kbit/s,快速模式下可達(dá)400kbit/s,高速模式下可達(dá)3.4Mbit/s。在開(kāi)發(fā)配置的時(shí)候,最好檢查從設(shè)備的傳輸速率從而對(duì)主設(shè)備(一般是MCU)進(jìn)行相應(yīng)的配置。一般通過(guò)I2C總線接口可編程時(shí)鐘來(lái)實(shí)現(xiàn)傳輸速率的調(diào)整,同時(shí)也跟所接的上拉電阻的阻值有關(guān)。
4.I2C總線上的主設(shè)備與從設(shè)備之間以字節(jié)(8位)為單位進(jìn)行單雙工的數(shù)據(jù)傳輸。
- 拓?fù)浣Y(jié)構(gòu)——總線型
I2C 總線在物理連接上分別由SDA(串行數(shù)據(jù)線)和SCL(串行時(shí)鐘線)及上拉電阻組成,SCL由主機(jī)發(fā)出,SCL越快,通訊速率越快。通信原理是通過(guò)對(duì)SCL和SDA線高低電平時(shí)序的控制來(lái)產(chǎn)生I2C總線協(xié)議所需要的信號(hào)進(jìn)行數(shù)據(jù)的傳遞。在總線空閑狀態(tài)時(shí),這兩根線一般被上面所接的上拉電阻拉高,保持著高電平。
- I2C總線協(xié)議
1.I2C協(xié)議規(guī)定: 總線上數(shù)據(jù)的傳輸必須以一個(gè)起始信號(hào)作為開(kāi)始條件,以一個(gè)結(jié)束信號(hào)作為傳輸?shù)耐V箺l件。起始和結(jié)束信號(hào)總是由主設(shè)備產(chǎn)生。
2.空閑狀態(tài):SCL和SDA都保持著高電平。
3.起始信號(hào): 當(dāng)SCL為高電平而SDA由高到低的跳變,表示產(chǎn)生一個(gè)起始條件,所有的從設(shè)備都能感受到這個(gè)跳變,做好準(zhǔn)備等待被選擇。
4.結(jié)束信號(hào):當(dāng)SCL為高而SDA由低到高的跳變,表示產(chǎn)生一個(gè) 停止條件
5.數(shù)據(jù)傳輸:數(shù)據(jù)傳輸以字節(jié)為單位 , 主設(shè)備在SCL線上產(chǎn)生每個(gè)時(shí)鐘脈沖的過(guò)程中將在SDA線上傳輸一個(gè)數(shù)據(jù)位,數(shù)據(jù)在時(shí)鐘的高電平被采樣這時(shí)候采集到是1就是1,是0就是0,所以在傳輸數(shù)據(jù)時(shí),當(dāng)時(shí)鐘處于高電平時(shí)一定要保持穩(wěn)定,時(shí)鐘處于低電平時(shí)可以變換數(shù)據(jù)。(高電平采樣,低電平變換)一個(gè)字節(jié)按數(shù)據(jù)位從高位到低位的順序進(jìn)行傳輸。主設(shè)備在傳輸有效數(shù)據(jù)之前 要先指定從設(shè)備的地址,一般為7位,然后再發(fā)生數(shù)據(jù)傳輸?shù)姆较蛭唬?0表示主設(shè)備向從設(shè)備寫(xiě)數(shù)據(jù),1表示主設(shè)備向從設(shè)備讀數(shù)據(jù)。主從設(shè)備以字節(jié)為單位(8位)進(jìn)行數(shù)據(jù)傳輸,開(kāi)始傳輸數(shù)據(jù)時(shí)把從設(shè)備地址加上方向位組成一個(gè)8位的字節(jié)進(jìn)行發(fā)送并接收一個(gè)應(yīng)答。
6.應(yīng)答信號(hào):接收數(shù)據(jù)的器件在接收到 8bit 數(shù)據(jù)后,向發(fā)送數(shù)據(jù)的器件發(fā)出低電平的應(yīng)答信號(hào),表示已收到數(shù)據(jù)。這個(gè)信號(hào)可以是主控器件發(fā)出,也可以是從動(dòng)器件發(fā)出。總之,由接收數(shù)據(jù)的器件發(fā)出。
a.主設(shè)備向從設(shè)備寫(xiě)數(shù)據(jù):
b.主設(shè)備讀從設(shè)備的數(shù)據(jù):
c.主設(shè)備讀從設(shè)備的某個(gè)寄存器:讀設(shè)備的寄存器首先應(yīng)該對(duì)該設(shè)備發(fā)送寫(xiě)命令,很多設(shè)備都可以看成是一段內(nèi)存,所以寫(xiě)命令寫(xiě)給從設(shè)備,指明要讀取哪個(gè)地址(寄存器)的數(shù)據(jù),接下來(lái)才是真正的讀數(shù)據(jù)。不同的從設(shè)備是由區(qū)別的,在驅(qū)動(dòng)I2C從設(shè)備時(shí)應(yīng)當(dāng)查明設(shè)備的時(shí)序圖,又怎樣的要求,不同的時(shí)序?qū)?yīng)了不同的命令。
- STM32F4-I2C控制器特性
軟件模擬I2C時(shí)序:由于直接控制 GPIO 引腳電平產(chǎn)生通訊時(shí)序時(shí),需要由 CPU 控制每個(gè)時(shí)刻的引腳狀態(tài),所以稱(chēng)之為“軟件模擬協(xié)議”方式。我們知道,驅(qū)動(dòng)I2C設(shè)備只需要兩根管腳,即使單片機(jī)上沒(méi)有I2C控制器,根據(jù)協(xié)議控制每根管腳每一時(shí)刻的電平狀態(tài),一根模擬數(shù)據(jù)線,一根模擬時(shí)鐘線,就可以驅(qū)動(dòng)從設(shè)備,相對(duì)而言效率低,但是可以實(shí)現(xiàn)控制驅(qū)動(dòng)。STM32內(nèi)部具備專(zhuān)門(mén)的I2C控制器,使用時(shí)只需對(duì)其進(jìn)行相應(yīng)的配置即可。
硬件控制產(chǎn)生I2C時(shí)序:STM32 的 I2C 片上外設(shè)專(zhuān)門(mén)負(fù)責(zé)實(shí)現(xiàn) I2C 通訊協(xié)議,只要配置好該外設(shè),它就會(huì)自動(dòng)根據(jù)協(xié)議要求產(chǎn)生通訊信號(hào),收發(fā)數(shù)據(jù)并緩存起來(lái),CPU只要檢測(cè)該外設(shè)的狀態(tài)和訪問(wèn)數(shù)據(jù)寄存器,就能完成數(shù)據(jù)收發(fā)。這種由硬件外設(shè)處理I2C協(xié)議的方式減輕了 CPU 的工作,且使軟件設(shè)計(jì)更加簡(jiǎn)單。
控制器功能:配置主從模式(一般都把STM32當(dāng)作主機(jī)使用,作為從機(jī)時(shí)應(yīng)當(dāng)對(duì)其賦一個(gè)地址),通過(guò)配置其內(nèi)部的寄存器產(chǎn)生一些中斷和錯(cuò)誤信號(hào),配置通信速率位標(biāo)準(zhǔn)模式、快速模式、超快速模式等
STM32芯片有3組I2C外設(shè),可以同時(shí)進(jìn)行3組I2C傳輸。它們的I2C通訊信號(hào)引出到不同的GPIO引腳上,使用時(shí)必須配置到這些指定的引腳。
- EEPROM(AT24CXX)存儲(chǔ)芯片介紹
一個(gè)典型的I2C接口的從設(shè)備,專(zhuān)門(mén)用于存儲(chǔ)數(shù)據(jù)的芯片。EEPROM (Electrically ErasableProgrammable read only memory),帶電可擦可編程只讀存儲(chǔ)器,一種掉電后數(shù)據(jù)不丟失的存儲(chǔ)芯片。EEPROM可以在電腦上或?qū)S迷O(shè)備上擦除已有信息,重新編程。
EEPROM常用來(lái)存儲(chǔ)一些配置信息,以便系統(tǒng)重新上電的時(shí)候加載之,容量不會(huì)很高。EEPOM 芯片最常用的通訊方式就是I2C協(xié)議。XX表示容量,常用值為01、02、04、16、32、64等,單位Kbit。一般的存儲(chǔ)芯片都具有寫(xiě)保護(hù)功能,對(duì)WP管腳加一個(gè)高電平就開(kāi)啟了寫(xiě)保護(hù)功能,就無(wú)法往芯片內(nèi)寫(xiě)數(shù)據(jù)了。在開(kāi)發(fā)中通常將該管腳接地,確保能夠?qū)憯?shù)據(jù)
典型24CXX芯片引腳如下:
例:24C65的設(shè)備地址為7位,高4位恒定為1010,低3位取決于A0-A2的電平狀態(tài),般主機(jī)在讀寫(xiě)24CXX都是把設(shè)備地址連同讀寫(xiě)位組合成一個(gè)字節(jié)一起發(fā)送。
24C65的電氣連線如下,根據(jù)電氣連線可知,A0-A2均接地,因此讀地址為1010 0001,即0xA1;寫(xiě)地址為10100000,即0xA0 ,且WP接地,用戶(hù)隨時(shí)可向芯片內(nèi)部寫(xiě)入數(shù)據(jù)。
24C65寫(xiě)時(shí)序:首先發(fā)送一個(gè)起始信號(hào),接著發(fā)送從設(shè)備地址以及方向位,收到應(yīng)答后,向從設(shè)備發(fā)送要寫(xiě)的存儲(chǔ)區(qū)域的首地址,24C65的存儲(chǔ)地址是16位,先發(fā)送高8位,收到應(yīng)答后再發(fā)送低8位,再次收到應(yīng)答后開(kāi)始寫(xiě)數(shù)據(jù)。64Kbit大小位8K字節(jié),需要13位即可表示,所以高3位固定定為0,如下圖。
這里是BYTE WRITE,一次寫(xiě)一個(gè)字節(jié),此芯片還支持PAGE WRITE,一次寫(xiě)一頁(yè),也就是8?jìng)€(gè)字節(jié),如果想寫(xiě)更多,可設(shè)置一個(gè)for循環(huán)實(shí)現(xiàn)。
24C65 讀時(shí)序與寫(xiě)時(shí)序基本相同,只不過(guò)在讀之前要發(fā)送再發(fā)送重復(fù)開(kāi)始位進(jìn)行讀操作。
- I2C讀寫(xiě)EEPROM實(shí)例
由電氣原理圖可知SCL和SDA分別接入了PB6和PB7管腳,讀地址為1010 0001,即0xA1;寫(xiě)地址為10100000,即0xA0
步驟:
1.配置RCC
2.配置PB6和PB7管腳
3.配置I2C協(xié)議參數(shù)
4.編寫(xiě)代碼
//mian.c
#include "main.h"
#include "stm32f4xx_hal.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#define ReadAddr 0xA1
#define WriteAddr 0xA0
uint8_t Wbuf[20] = "EEPROM TEST OK!";
uint8_t Rbuf[20] = {0};
/********* 24C65寫(xiě)數(shù)據(jù)函數(shù)*****************************/
void Eeprom_Write(uint16_t MemAddr, uint8_t *Wbuf, uint16_t len ){
while(len--){
//I2C_MEMADD_SIZE_16BIT表示存儲(chǔ)單元大小
//默認(rèn)為兩個(gè)參數(shù),分別是I2C_MEMADD_SIZE_16BIT和I2C_MEMADD_SIZE_8BIT
//由于24C65的存儲(chǔ)地址是16位的
//所以我們選擇I2C_MEMADD_SIZE_16BIT
//1表示一次寫(xiě)一個(gè)字節(jié)
while(HAL_I2C_Mem_Write(&hi2c1, WriteAddr, MemAddr, I2C_MEMADD_SIZE_16BIT, Wbuf, 1, 100) != HAL_OK){};
MemAddr++;
Wbuf++;
}
}
/********* 24C65讀數(shù)據(jù)函數(shù)*****************************/
void Eeprom_Read(uint16_t MemAddr, uint8_t *Rbuf, uint16_t len ){
//可以連續(xù)讀,所以無(wú)需循環(huán)
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總線通信專(zhuān)題講解
SPI接口是Motorola 首先提出的全雙工三線同步串行外圍接口,采用主從模式(Master Slave)架構(gòu);支持多slave模式應(yīng)用,一般僅支持單Master。時(shí)鐘由Master控制,在時(shí)鐘移位脈沖下,數(shù)據(jù)按位傳輸,是高位在前還是低位在前是可以配置的,配置時(shí)根據(jù)從設(shè)備的通信進(jìn)行相應(yīng)配置,一般是高位在前,低位在后(MSB first)。SPI接口有2根單向數(shù)據(jù)線,為全雙工通信,目前應(yīng)用中的數(shù)據(jù)速率可達(dá)幾Mbps的水平。
SPI總線被廣泛地使用在FLASH、ADC、LCD等設(shè)備與MCU間,要求通訊速率較高的場(chǎng)合。
SPI接口共有4根信號(hào)線,分別是:設(shè)備選擇線、時(shí)鐘線、串行輸出數(shù)據(jù)線、串行輸入數(shù)據(jù)線。
(1)MOSI:主器件數(shù)據(jù)輸出,從器件數(shù)據(jù)輸入,連接從機(jī)的MOSI,與串口不同,串口需要反著連接(Rx-----Tx)
(2)MISO:主器件數(shù)據(jù)輸入,從器件數(shù)據(jù)輸出,連接從機(jī)的MISO
(3)SCLK :時(shí)鐘信號(hào),由主器件產(chǎn)生
(4)/SS:從器件使能信號(hào),由主器件控制(片選),一般情況下為地電平選中設(shè)備,高電平釋放設(shè)備。
- SPI總線協(xié)議
1.數(shù)據(jù)交換邏輯:主機(jī)和從機(jī)都包含一個(gè)串行移位寄存器,主機(jī)通過(guò)向它的SPI串行寄存器寫(xiě)入一個(gè)字節(jié)發(fā)起一次傳輸。寄存器通過(guò)MOSI信號(hào)線將字節(jié)傳送給從機(jī),從機(jī)也將自己的移位寄存器中的內(nèi)容通過(guò)MISO信號(hào)線返回給主機(jī)。這樣兩個(gè)移位寄存器中的內(nèi)容就被交換了。從機(jī)的寫(xiě)操作和讀操作時(shí)同步完成的,因此SPI成為一個(gè)很有效的協(xié)議。
如果主機(jī)只想寫(xiě)不想讀,只需把數(shù)據(jù)放在數(shù)據(jù)寄存器,SPI控制器會(huì)自動(dòng)傳給外設(shè),同時(shí)忽略掉外設(shè)傳過(guò)來(lái)的數(shù)據(jù)即可;如果主機(jī)只想讀不想寫(xiě),主機(jī)寫(xiě)給外設(shè)一個(gè)空字符或者隨便寫(xiě)一個(gè)數(shù)據(jù),外設(shè)就會(huì)把數(shù)據(jù)傳過(guò)來(lái),不管是只讀還是只寫(xiě),主機(jī)與外設(shè)的讀和寫(xiě)都h會(huì)發(fā)生且同時(shí)進(jìn)行。
2.起始信號(hào): NSS信號(hào)線由高變低,是SPI通訊的起始信號(hào)。
3.結(jié)束信號(hào):NSS信號(hào)由低變高,是SPI通訊的停止信號(hào)。
4.數(shù)據(jù)傳輸:SPI使用MOSI及MISO信號(hào)線來(lái)傳輸數(shù)據(jù),使用SCK信號(hào)線進(jìn)行數(shù)據(jù)同步。MOSI及MISO數(shù)據(jù)線在SCK的每個(gè)時(shí)鐘周期傳輸一位數(shù)據(jù),按位傳輸,且數(shù)據(jù)輸入輸出是同時(shí)進(jìn)行的。SPI每次數(shù)據(jù)傳輸可以 8 位或 16 位為單位,每次傳輸?shù)膯挝粩?shù)不受限制,要么是8位,要么是16位,可以配置。
- SPI的4種通信模式
在SPI操作中,最重要的兩項(xiàng)設(shè)置就是時(shí)鐘極性(CPOL)和時(shí)鐘相位(CPHA)這兩項(xiàng)即是主從設(shè)備間數(shù)據(jù)采樣的約定方式。由CPOL及CPHA的不同狀態(tài),SPI分成了四種模式,主機(jī)與從機(jī)需要工作在相同的模式下才可以正常通訊,因此通常主機(jī)要按照從機(jī)支持的模式去設(shè)置。同樣在配置時(shí)一定要弄明白從機(jī)支持什么通信模式進(jìn)行相應(yīng)的配置。
1.時(shí)鐘極性CPOL : 設(shè)置時(shí)鐘空閑時(shí)的電平:
a.當(dāng)CPOL= 0 ,SCK引腳在空閑狀態(tài)保持低電平;
b.當(dāng)CPOL= 1 ,SCK引腳在空閑狀態(tài)保持高電平。
2.時(shí)鐘相位CPHA :設(shè)置數(shù)據(jù)采樣時(shí)的時(shí)鐘沿:
a.當(dāng) CPHA=0時(shí),MOSI或 MISO 數(shù)據(jù)線上的信號(hào)將會(huì)在 SCK時(shí)鐘線的奇數(shù)邊沿被采樣
b.當(dāng) CPHA=1時(shí), MOSI或 MISO 數(shù)據(jù)線上的信號(hào)將會(huì)在 SCK時(shí)鐘線的偶數(shù)邊沿被采樣
- STM32F4-SPI控制器特性
1.通訊引腳:
STM32F4芯片最多支持6個(gè)SPI外設(shè)控制器,它們的SPI通訊信號(hào)引出到不同的GPIO引腳上,使用時(shí)必須配置到這些指定的引腳,以《STM32F4xx規(guī)格書(shū)》為準(zhǔn)。f407只有SPI1、SPI2、SPI3。
其中SPI1、SPI4、SPI5、SPI6是APB2上的設(shè)備,最高通信速率達(dá)42Mbtis/s,SPI2、SPI3是APB1上的設(shè)備,最高通信速率為21Mbits/s。其它功能上沒(méi)有差異。
2.時(shí)鐘控制邏輯:
SCK線的時(shí)鐘信號(hào),由波特率發(fā)生器根據(jù)“控制寄存器CR1”中的BR[0:2]位控制,該位是對(duì)f pclk 時(shí)鐘的分頻因子,對(duì)f pclk 的分頻結(jié)果就是SCK引腳的輸出時(shí)鐘頻率。
其中的fpclk 頻率是指SPI所在的APB總線頻率,APB1為fpclk1 ,APB2為fpckl2
3.數(shù)據(jù)控制邏輯:
STM32F4的MOSI及MISO都連接到數(shù)據(jù)移位寄存器上,數(shù)據(jù)移位寄存器的數(shù)據(jù)來(lái)源來(lái)源于接收緩沖區(qū)及發(fā)送緩沖區(qū)。
a.通過(guò)寫(xiě)SPI的“數(shù)據(jù)寄存器DR”把數(shù)據(jù)填充到發(fā)送緩沖區(qū)中。
b.通過(guò)讀“數(shù)據(jù)寄存器DR”,可以獲取接收緩沖區(qū)中的內(nèi)容。
c.其中數(shù)據(jù)幀長(zhǎng)度可以通過(guò)“控制寄存器CR1”的“DFF位”配置成8位及16位模式;配置“LSBFIRST位”可選擇MSB先行(高位在前)還是LSB先行(低位在前)。
4.整體控制邏輯:
a.整體控制邏輯負(fù)責(zé)協(xié)調(diào)整個(gè)SPI外設(shè),控制邏輯的工作模式根據(jù)“控制寄存器(CR1/CR2)”的參數(shù)而改變,基本的控制參數(shù)包括前面提到的SPI模式、波特率、LSB先行、主從模式、單雙向模式(同時(shí)發(fā)送和接收、只發(fā)送關(guān)掉接收、只接收關(guān)掉發(fā)送)等等。
b.在外設(shè)工作時(shí),控制邏輯會(huì)根據(jù)外設(shè)的工作狀態(tài)修改“狀態(tài)寄存器(SR)”,只要讀取狀態(tài)寄存器相關(guān)的寄存器位,就可以了解SPI的工作狀態(tài)了。除此之外,控制邏輯還根據(jù)要求,負(fù)責(zé)控制產(chǎn)生SPI中斷信號(hào)、DMA請(qǐng)求及控制NSS信號(hào)線。
c.實(shí)際應(yīng)用中,一般不使用STM32 SPI外設(shè)的標(biāo)準(zhǔn)NSS信號(hào)線,而是更簡(jiǎn)單地使用普通的GPIO,軟件控制它的電平輸出,從而產(chǎn)生通訊起始和停止信號(hào)。
- 串行FLASH_W25X16簡(jiǎn)介
FLSAH 存儲(chǔ)器又稱(chēng)閃存,它與EEPROM都是掉電后數(shù)據(jù)不丟失的存儲(chǔ)器,但FLASH存儲(chǔ)器容量普遍大于 EEPROM,現(xiàn)在基本取代了它的地位。我們生活中常用的 U盤(pán)、SD卡、SSD 固態(tài)硬盤(pán)以及我們 STM32 芯片內(nèi)部用于存儲(chǔ)程序的設(shè)備,都是 FLASH 類(lèi)型的存儲(chǔ)器。在存儲(chǔ)控制上,最主要的區(qū)別是FLASH 芯片只能一大片一大片地擦寫(xiě),而EEPROM可以單個(gè)字節(jié)擦寫(xiě)。
W25X16有8192個(gè)可編程頁(yè),每頁(yè)256字節(jié)。用“頁(yè)編程指令”每次就可以編程256個(gè)字節(jié)。用扇區(qū)擦除指令每次可以擦除16頁(yè),即一個(gè)扇區(qū)包含16頁(yè),用塊擦除指令每次可以擦除256頁(yè),用整片擦除指令即可以擦除整個(gè)芯片。W25X16有512個(gè)可擦除扇區(qū)或32個(gè)可擦除塊。
1.W25X16的硬件連線如下:
CS: 片選引腳,低電平有效,連接到STM32-PH2管腳
SO: 連接到STM32-PB4管腳(MISO)
SI: 連接到STM32-PB5管腳(MOSI)
CLK: 連接到STM32-PA5管腳(CLK)
WP: 寫(xiě)保護(hù)管腳,低電平有效,有效時(shí)禁止寫(xiě)入數(shù)據(jù)。接電源未使用
HOLD: HOLD 引腳可用于暫停通訊,該引腳為低電平時(shí),通訊暫停,未使用
2.W25X16控制指令:
我們需要了解如何對(duì)FLASH芯片進(jìn)行讀寫(xiě)。FLASH 芯片自定義了很多指令,我們通過(guò)控制 STM32利用 SPI總線向 FLASH 芯片發(fā)送指令,F(xiàn)LASH芯片收到后就會(huì)執(zhí)行相應(yīng)的操作。
而這些指令,對(duì)主機(jī)端(STM32)來(lái)說(shuō),只是它遵守最基本的 SPI通訊協(xié)議發(fā)送出的數(shù)據(jù),但在設(shè)備端(FLASH 芯片)把這些數(shù)據(jù)解釋成不同的意義,所以才成為指令。
a.讀制造商/設(shè)備ID(90):該指令通常在調(diào)試程序的時(shí)候用到,判斷SPI通信是否正常。該指令通過(guò)主器件拉低/CS片選使能器件開(kāi)始傳輸,首先通過(guò)DI線傳輸“90H”指令,接著傳輸000000H的24位地址(A23-A0),之后從器件會(huì)通過(guò)DO線返回制造商ID(EFH)和設(shè)備ID。(注:SPI為數(shù)據(jù)交換通信,主器件在發(fā)送“90H”指令時(shí)也會(huì)接收到一個(gè)字節(jié)FFH,但此數(shù)據(jù)為無(wú)效數(shù)據(jù))
b.寫(xiě)使能命令(06H):在向 FLASH 芯片存儲(chǔ)矩陣寫(xiě)入數(shù)據(jù)前,首先要使能寫(xiě)操作,通過(guò)“Write Enable”命令即可寫(xiě)使能。
c.扇區(qū)擦除(20H):由于 FLASH 存儲(chǔ)器的特性決定了它只能把原來(lái)為“1”的數(shù)據(jù)位改寫(xiě)成“0”,而原來(lái)為“0”的數(shù)據(jù)位不能直接改寫(xiě)為“1”。所以這里涉及到數(shù)據(jù)“擦除”的概念。
在寫(xiě)入前,必須要對(duì)目標(biāo)存儲(chǔ)矩陣進(jìn)行擦除操作,把矩陣中的數(shù)據(jù)位擦除為“1”,在數(shù)據(jù)寫(xiě)入的時(shí)候,如果要存儲(chǔ)數(shù)據(jù)“1”,那就不修改存儲(chǔ)矩陣 ,在要存儲(chǔ)數(shù)據(jù)“0”時(shí),才更改該位。
d.讀狀態(tài)寄存器(05H):FLASH 芯片向內(nèi)部存儲(chǔ)矩陣寫(xiě)入數(shù)據(jù)需要消耗一定的時(shí)間,并不是在總線通訊結(jié)束的一瞬間完成的,所以在寫(xiě)操作后需要確認(rèn)FLASH芯片“空閑”。我們只需要讀取FLASH芯片內(nèi)部的狀態(tài)寄存器SRP的S0即可(當(dāng)這個(gè)位為“1”時(shí),表明 FLASH芯片處于忙碌狀態(tài),它可能正在對(duì)內(nèi)部的存儲(chǔ)矩陣進(jìn)行“擦除”或“數(shù)據(jù)寫(xiě)入”的操作)
e.讀數(shù)據(jù)(03H):讀數(shù)據(jù)指令可從存儲(chǔ)器依次一個(gè)或多個(gè)數(shù)據(jù)字節(jié),該指令通過(guò)主器件拉低/CS電平使能設(shè)備開(kāi)始傳輸,然后傳輸“03H”指令,接著通過(guò)DI管腳傳輸24位芯片存儲(chǔ)地址,從器件接到地址后,尋址存儲(chǔ)器中的數(shù)據(jù)通過(guò)DO引腳輸出。每傳輸一個(gè)字節(jié)地址自動(dòng)遞增,所以只要時(shí)鐘繼續(xù)傳輸,可以不斷讀取存儲(chǔ)器中的數(shù)據(jù)。
f.寫(xiě)數(shù)據(jù)——頁(yè)編程(02H):頁(yè)編程指令可以在已擦除的存儲(chǔ)單元中寫(xiě)入256個(gè)字節(jié)。該指令先拉低/CS引腳電平,接著傳輸“02H”指令和24位地址。后面接著傳輸至少一個(gè)數(shù)據(jù)字節(jié),最多256字節(jié)。
注:當(dāng)數(shù)據(jù)寫(xiě)到一個(gè)新的扇區(qū)的時(shí)候,需要重新發(fā)起一個(gè)頁(yè)編程信號(hào)才能繼續(xù)寫(xiě)入數(shù)據(jù)。
- STM32 SPI_FLASH基本配置和操作
根據(jù)如下的硬件連線圖進(jìn)行配置
步驟:
1.使能時(shí)鐘RCC
2.使能SPI1,配置相應(yīng)管腳
3.配置SPI協(xié)議
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();
/******測(cè)試擦除******/
sFLASH_EraseSector(4096*0);
//sFLASH_EraseSector(4096*1);
sFLASH_ReadBuffer(RD_Buffer,0,4096);
printf("讀數(shù)據(jù)開(kāi)始n");
for(i=0; i< 4096; i++)
{
printf("%x ",RD_Buffer[i]);
}
printf("讀數(shù)據(jù)結(jié)束n");
/******測(cè)試寫(xiě)操作1*****/
//寫(xiě)之前都需要擦除扇區(qū)
sFLASH_EraseSector(4096*0);
sFLASH_WritePage(WR_Buffer,0, 20);
sFLASH_ReadBuffer(RD_Buffer,0,20);
printf("READ DATA: %sn",RD_Buffer);
/******測(cè)試寫(xiě)操作2*****/
//寫(xiě)之前都需要擦除扇區(qū)
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 //空字節(jié),用于只讀傳回來(lái)的數(shù)據(jù)
#define sFLASH_BUSY_FLAG 0x01
#define sFLASH_SPI_PAGESIZE 0x100
/* 選中芯片,拉低信號(hào) */
#define sFLASH_CS_LOW() HAL_GPIO_WritePin(GPIOH,GPIO_PIN_2,GPIO_PIN_RESET)
/* 釋放芯片,拉高信號(hào) */
#define sFLASH_CS_HIGH() HAL_GPIO_WritePin(GPIOH,GPIO_PIN_2,GPIO_PIN_SET)
//定義函數(shù)
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;
/*讀寫(xiě)一個(gè)字節(jié)函數(shù),因?yàn)镾PI讀和寫(xiě)同時(shí)完成*/
/*發(fā)送數(shù)據(jù)一定會(huì)接收到一個(gè)數(shù)據(jù)*/
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;
}
/*等待擦除或者寫(xiě)數(shù)據(jù)完成*/
void sFLASH_WaitForEnd(void)
{
uint8_t sr_value = 0;
sFLASH_CS_LOW();
sFLASH_SendByte(sFLASH_CMD_RDSR);
//讀S0的值,為1表示忙碌,為0表示停止
do{
//發(fā)一個(gè)空字節(jié),得到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();
}
/*讀設(shè)備ID*/
uint16_t sFLASH_ReadID(void)
{
uint16_t FLASH_ID;
uint8_t temp0,temp1;
sFLASH_CS_LOW();
sFLASH_SendByte(W25X_ManufactDeviceID);
//讀設(shè)備指令后要發(fā)24位地址,所以要發(fā)三次
sFLASH_SendByte(sFLASH_DUMMY_BYTE);
sFLASH_SendByte(sFLASH_DUMMY_BYTE);
sFLASH_SendByte(sFLASH_DUMMY_BYTE);
//制造商ID
temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
//設(shè)備商ID
temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
sFLASH_CS_HIGH();
FLASH_ID = (temp0 < < 8) | temp1;
return FLASH_ID;
}
//擦除扇區(qū),擦除為1,因?yàn)橹荒苡?變?yōu)? ,不能0變1
void sFLASH_EraseSector(uint32_t SectorAddr)
{
//SectorAddr表示擦除第幾個(gè)扇區(qū)
sFLASH_WriteEnable(); //開(kāi)啟寫(xiě)使能
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();
/*讀狀態(tài)寄存器,等待擦除完成*/
sFLASH_WaitForEnd();
}
//讀數(shù)據(jù)
//讀命令和讀地址發(fā)送后,芯片內(nèi)部會(huì)自動(dòng)不斷遞增讀數(shù)據(jù)
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();
}
//寫(xiě)一頁(yè)最多只能寫(xiě)256個(gè)字節(jié),一個(gè)扇區(qū)16頁(yè),一個(gè)塊16個(gè)扇區(qū)
void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite)
{
if(NumByteToWrite > sFLASH_SPI_PAGESIZE )
{
NumByteToWrite = sFLASH_SPI_PAGESIZE;
printf("寫(xiě)數(shù)據(jù)量過(guò)大,超過(guò)一頁(yè)大小n");
}
sFLASH_WriteEnable(); //開(kāi)啟寫(xiě)使能
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();
/*擦除和寫(xiě)數(shù)據(jù)都涉及到寫(xiě)動(dòng)作,一定要等待完成*/
sFLASH_WaitForEnd();
}
//寫(xiě)任意地址、任意長(zhǎng)度
void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite)
{
uint16_t NumOfPage, NumOfBytes, count, offset;
//求WriteAddr在某一頁(yè)的位置
offset = WriteAddr % sFLASH_SPI_PAGESIZE;
//求某一頁(yè)剩余的大小
count = sFLASH_SPI_PAGESIZE - offset;
/*處理頁(yè)不對(duì)齊的情況,防止頁(yè)內(nèi)覆蓋*/
//先把某一頁(yè)剩下的部分寫(xiě)掉,之后的就能新頁(yè)的起始處開(kāi)始寫(xiě) /*offset有值表示需要頁(yè)對(duì)齊,如果要寫(xiě)的字節(jié)數(shù)小于某一頁(yè)剩余的部分,那就無(wú)需對(duì)齊*/
/*這兩個(gè)條件必須同時(shí)滿足*/
if(offset && (NumByteToWrite > count ))
{
sFLASH_WritePage(pBuffer,WriteAddr,count);
NumByteToWrite -= count;//去掉已經(jīng)寫(xiě)了的,從新頁(yè)開(kāi)始
pBuffer += count;
WriteAddr += count;
}
/*最多可分多少頁(yè)*/
NumOfPage = NumByteToWrite / sFLASH_SPI_PAGESIZE;
/*剩余多少字節(jié)*/
NumOfBytes = NumByteToWrite % sFLASH_SPI_PAGESIZE;
if(NumOfPage)
{
while(NumOfPage--)
{
//每一頁(yè)都發(fā)起頁(yè)編程
sFLASH_WritePage(pBuffer,WriteAddr,sFLASH_SPI_PAGESIZE);
pBuffer += sFLASH_SPI_PAGESIZE;
WriteAddr += sFLASH_SPI_PAGESIZE;
}
}
if(NumOfBytes)
{
sFLASH_WritePage(pBuffer,WriteAddr,NumOfBytes);
}
}
- 為什么會(huì)有兩種寫(xiě)操作函數(shù),是因?yàn)檫@里的寫(xiě)操作有兩個(gè)特點(diǎn):
1.無(wú)法突破頁(yè)限制,超過(guò)一頁(yè)需要重新發(fā)起頁(yè)編程信號(hào)。另外如果要寫(xiě)的數(shù)據(jù)大于剩余一頁(yè)剩余的容量,那么超出的數(shù)據(jù)會(huì)寫(xiě)到當(dāng)前頁(yè)起始地址出。例如,初始輸入的寫(xiě)地址為200,而要寫(xiě)的數(shù)據(jù)大小為100,那么要寫(xiě)的前56個(gè)字節(jié)會(huì)從地址200開(kāi)始依次寫(xiě)入,剩下的44個(gè)字節(jié)會(huì)從當(dāng)前頁(yè)的0地址開(kāi)始依次寫(xiě)入,這很有可能覆蓋之前的數(shù)據(jù)。
2.無(wú)法突破扇區(qū)的限制,當(dāng)數(shù)據(jù)寫(xiě)到一個(gè)新的扇區(qū)的時(shí)候,需要重新發(fā)起一個(gè)頁(yè)編程信號(hào)才能繼續(xù)寫(xiě)入數(shù)據(jù)。
評(píng)論
查看更多