STM32F030 Nucleo-多樣的SPI通信之Master標(biāo)準(zhǔn)模式-SPIFlash讀寫
我個(gè)人認(rèn)為,學(xué)習(xí)單片機(jī),在硬件上和驅(qū)動(dòng)上,我們無非學(xué)習(xí)這么幾個(gè)東西:
1.能靈活的操作GPIO端口
2.理解單片機(jī)各個(gè)引腳的功能和作用和外圍最小系統(tǒng)的設(shè)計(jì)
3.各種通信協(xié)議比如:并口協(xié)議(LCD1602等),UART/USART協(xié)議,IIC協(xié)議,485通信協(xié)議,SPI通信協(xié)議,IIS音頻流傳輸協(xié)議,CAN通信協(xié)議,單總線通信協(xié)議(DS18B20等),三總線協(xié)議(DS1302等)等等通信協(xié)議。
4.ADC/DAC 模數(shù)/數(shù)模轉(zhuǎn)換
5.單片機(jī)內(nèi)部提供的一些運(yùn)輸協(xié)議或者特殊功能,比如DMA,RTC,定時(shí)器Timer等,這些功能的操作有單片機(jī)本身確定,經(jīng)過協(xié)議相同目標(biāo)相同,但是操作等可能各家單片機(jī)不盡相同;咱只需要理解它完成的是什么功能/任務(wù)就好。
通過學(xué)習(xí)各種協(xié)議、一些基本的操作和所使用的這款單片機(jī)的內(nèi)核功能或者特殊功能(與內(nèi)核和廠商設(shè)計(jì)相關(guān)),就基本上能利用單片機(jī)與外設(shè)或者其他MCU或CPU實(shí)現(xiàn)通信交流了,都交流上了!那自然就是有關(guān)系了!哈哈哈!那至于想搞什么關(guān)系(男女關(guān)系還是朋友關(guān)系
關(guān)系都搞通了!那么借錢(讀取)或者還錢(寫入)就有機(jī)會(huì)啦!!!
那么其實(shí)問題就來了!有人會(huì)疑問,不是還有算法啊!數(shù)據(jù)結(jié)構(gòu)等等這些嗎??其實(shí)這是一個(gè)顯而易見的問題:
建議:
1.在我們寫代碼的時(shí)候,最好將我們的代碼封裝起來,分成與硬件相關(guān)的代碼(層)和與硬件無關(guān)的層。
2.與硬件無關(guān)的層可能有繼續(xù)劃分,比如分一些總線層或者書中間調(diào)度層等等,這些層和我們的產(chǎn)品功能實(shí)現(xiàn)可能沒有太大關(guān)系,但是卻為我們的功能進(jìn)行調(diào)度或者作為基礎(chǔ),是的我們的代碼的可移植性更高。
3.當(dāng)然,萬事沒有絕對,在RAM和ROM/Flash緊缺的情況下,用上面1和2點(diǎn)的方法顯然是不合適的,因?yàn)樵鄣腞OM/Flash一不小心就裝不了咱的代碼了,那就沒得玩了,何況還有RAM的情況也是這樣,所以這時(shí)候咱就盡量的減少代碼,優(yōu)化代碼,這樣可能就會(huì)使得與硬件相關(guān)的代碼和功能代碼混合在一起了,這樣的代碼的可移植性是非常低的,但是沒辦法嘛!這是一種解決方案!哈哈!
4.基于上面三點(diǎn)的建議,做一下總結(jié)。首先說明一下實(shí)際的情況,絕大部分的初學(xué)者管你內(nèi)存多大,他們的代碼都采用的是第3種方法,這可以說是一種不好的習(xí)慣,建議還是盡量的模仿使用1和2中方法學(xué)習(xí),當(dāng)然也不是那么簡單的,這需要一個(gè)長久的學(xué)習(xí)和閱讀一些好的開源項(xiàng)目代碼,比如一些實(shí)時(shí)操作系統(tǒng)源碼(UCosII、FreeRTOS,小型用于SD卡或者SPIFlash等的文件系統(tǒng)源碼等等),這樣理解開源項(xiàng)目的架構(gòu),才能自己慢慢學(xué)習(xí)構(gòu)建架構(gòu)的,這是一個(gè)比較漫長的過程,需要有心人在學(xué)習(xí)的過程中慢慢的關(guān)注。
OK!廢話了半天!言歸正傳!這里咱要學(xué)習(xí)的是SPI通信,身邊剛好有SPIFlash,咋就用咱的STM32F030 Nucleo板卡作為主機(jī)與它通信吧。
首先呢,有必要先介紹一下SPI通信協(xié)議。
SPI通信總線協(xié)議最開始是由Motorola(摩托羅拉)提出或者發(fā)明的。她提供了三線制全雙工同步串行外圍接口,采用主從模式架構(gòu)。在一同一個(gè)通信系統(tǒng)中,支持多從機(jī)單一主機(jī)。也就是說,每一個(gè)SPI總線通信上只存在一個(gè)主機(jī)。
它的通信方式是:通信時(shí)鐘CLK有主機(jī)控制,數(shù)據(jù)在時(shí)鐘脈沖下按位傳輸,先傳高位再傳低位。()這一點(diǎn)非常重要!在單片機(jī)不支持硬件SPI通信時(shí),與具有SPI通信接口的外設(shè)傳感器等通信就必須使用MCU的I/O口模擬SPI時(shí)序與之進(jìn)行數(shù)據(jù)通信,從而實(shí)現(xiàn)功能。
還有一個(gè)非常好的好處就是,通信速率能達(dá)到幾M到十幾M,哈!好快。
好!基本上幾句話就知道SPI的基本信息了!那么,干啥呢??當(dāng)然是。。。。。。。。分析分析通信過程了哇!哈哈!
硬件通信接口:
CLK-------CLOCK Signal;時(shí)鐘線
MISO-----Master Input Slave Output;主機(jī)輸入從機(jī)輸出數(shù)據(jù)線
MOSI-----Master Output Slave Input;主機(jī)輸出從機(jī)輸入數(shù)據(jù)線
NSS-------Slave Select pin;從設(shè)備使能線
咦!咋覺得不太對呢?上面明明說是三線制啊!咋的這里有四根線呢(CLK時(shí)鐘線、MISO數(shù)據(jù)線,MOSI數(shù)據(jù)線和NSS片選線)?
其實(shí)是這樣的,前面介紹SPI的時(shí)候,說是三線制,主要是說數(shù)據(jù)通信所需要的線,即CLK、MISO、MOSI這三根線是SPI通信的基礎(chǔ),而NSS片選線有時(shí)候在特定情況下是不必要的(這個(gè)下一篇文章中我會(huì)詳細(xì)介紹),所以,問題就這樣愉快的解決了。哈哈!這種感覺是不是很爽!!嘿嘿。
當(dāng)然,這其實(shí)只是SPI通信硬件接口的一部分,而且是最基礎(chǔ)的部分。那么現(xiàn)在就STM32F030的硬件SPI資源進(jìn)行講解。打開參考手冊《STM32F030x468C and STM32F070x6B advanced ARM?-based 32-bit MCUs.pdf》
打開到SPI通信的講解章節(jié),如下:
這就是SPI的簡介了!哈哈!好!不廢話了!繼續(xù)往下:
這里就說明了這款芯片的SPI的特點(diǎn)和功能,再往下:
注意紅線框出的部分,這就是我們這款單片機(jī)擁有的SPI外設(shè)資源和支持的功能資源。非常重要!!!
講到資源,那我們還必須要牢記這款芯片的資源對應(yīng)的單片機(jī)引腳。這個(gè)要從Datasheet來看了。如下:
哈!看見沒!這就是這款芯片的資源了,特別關(guān)注紅框標(biāo)注的地方,而且還要注意哪些復(fù)用是我們的STM32F030 Nucleo板卡的主控MCU STM32F030R8T8才有的,這一點(diǎn)非常重要!別寫半天引腳的配置都不對。這是一件很悲催很丟臉的事。
OK!咱來看看我們的單片機(jī)的SPI內(nèi)臟,如下:
有必要先說明一下:FIFO:First Input First Output,即先進(jìn)先出隊(duì)列!哈哈!還記得在學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)是的隊(duì)列嗎??就是這個(gè)了!嘿嘿!
發(fā)送數(shù)據(jù):當(dāng)不使用Tx FIFO緩沖區(qū)時(shí),直接就是將1Byte數(shù)據(jù)從MOSI上傳移位寄存器,然后移位寄存器自己根據(jù)時(shí)鐘CLK的邊沿將數(shù)據(jù)按位移動(dòng)到IO口,然后傳送出去;當(dāng)使用Tx FIFO的時(shí)候,咱們就直接將一堆數(shù)據(jù)放在Tx FIFO中,然后使能傳輸,它就自己講數(shù)按照SPI協(xié)議發(fā)送出去了(如紅色線)。
接收數(shù)據(jù):當(dāng)不使用Rx FIFO緩沖區(qū)的時(shí)候,MCU根據(jù)時(shí)鐘CLK的邊沿對MISO引腳進(jìn)行數(shù)據(jù)采集,然后放在移位寄存器上,當(dāng)采集滿8bit了,時(shí)鐘就好停下來啦!然后就是讀取數(shù)據(jù)出來啦!哈哈!當(dāng)使用Rx FIFO時(shí),就將數(shù)據(jù)放大Rx FIFO中,然后當(dāng)FIFO滿的時(shí)候,產(chǎn)生標(biāo)志或者中斷,然后就將數(shù)據(jù)搬到內(nèi)存中就好嘍!嘿嘿!(如藍(lán)色線)。
對于時(shí)鐘線和NSS,當(dāng)然是分別作為產(chǎn)生數(shù)據(jù)傳輸是所需要的時(shí)鐘脈沖和片選了哈!
那么其實(shí)在這個(gè)SPI“內(nèi)臟”中,還包含了CRC校驗(yàn)控制器和同學(xué)控制器,顧名思義肯定是用來校驗(yàn)和控制SPI通了的拉!嘿嘿!
那么粉色框是啥意思呢??最下面那個(gè)一看就知道這是波特率了!嘿嘿!就是控制速度的拉!兩個(gè)設(shè)備要通信,肯定是要站在同一個(gè)水平上交流嘛!如果一個(gè)快一個(gè)慢,那就沒法交流了!嘿嘿!那上面的粉色框是啥呢??RXONLY就是只讀嘍!但是其實(shí)參考手冊上都有表述,先不糾結(jié)這個(gè)。咱要關(guān)注的應(yīng)該是:CPOL和CPHA。這兩東西到底是啥呢??其實(shí)呢CPOL表示SPI通信的時(shí)鐘極性,即表示時(shí)鐘線在空閑時(shí)的電平狀態(tài);CPHA表示SPI通信的時(shí)鐘相位,即表示在時(shí)鐘線脈沖的什么時(shí)候?qū)?shù)據(jù)進(jìn)行捕捉和輸出??梢耘渲贸鰞煞NSPI通信協(xié)議(它對SPI通信影響極大)。
CPOL和CPHA的配置表述:
CPOL:CPOL= 0;時(shí)鐘線空閑狀態(tài)時(shí)為低電平;CPOL=1;時(shí)鐘線空閑狀態(tài)時(shí)為高電平。
CPHA:CPHA=0;在時(shí)鐘線的第一個(gè)跳變沿(上升沿/下降沿)數(shù)據(jù)被采集;CPHA=1;在時(shí)鐘線的第二個(gè)跳變沿(上升沿/下降沿)數(shù)據(jù)被采集。
注:在進(jìn)行SPI通信時(shí),通過一總線上的所有設(shè)備必須保證CPOL和CPHA的一致性;這是非常重要的,反正意思就是在同一水平上進(jìn)行交流。
得到以上消息之后,咱組合一下就有如下情況了,其實(shí)就是2的2次方等于4種SPI通信模式,如下:
(1)SPI0:CPOL=0,CPHA=0
(2)SPI1:CPOL=0,CPHA=1
(3)SPI2:CPOL=1,CPHA=0
(4)SPI3:CPOL=1,CPHA=1
嘿嘿!以上就是SPI通信的四種模式了!也可以理解成SPI通信的四種協(xié)議!還有就是至于SPI0~SPI3到底表示的是啥就不要糾結(jié)了(有些人不知道咋的,就是要糾結(jié)這東西,然后然后連自己都分不清楚這到底是啥)!咱至于記住CPOL和CPHA分別在不同狀態(tài)下表示的是啥就夠了!然后組合!哈哈哈哈!你懂的。
OK!對于SPI總線通信的基本說明就到這了!當(dāng)然了,還有時(shí)序的說明,但是呢,這個(gè)比較長!就不一個(gè)個(gè)分析了,看看SPI通信協(xié)議更靠譜!嘿嘿!我會(huì)附上我認(rèn)為比較好的SPI協(xié)議文檔進(jìn)行參考。
下面咱要繼續(xù)挖SPI了!
SPI主機(jī)-從機(jī)全雙工通信:
看見了沒!現(xiàn)在知道為毛是三線了吧?上面已經(jīng)表示出了主機(jī)和從機(jī)之間的接口連接方式,有時(shí)候只需要一直講NSS處于一個(gè)低電平狀態(tài),就可以一直正常通信了。至于發(fā)送數(shù)據(jù)那就是CLK,NISO,MOSI的事了。
SPI主機(jī)-從機(jī)半雙工通信:
看見了沒有??這樣也是可以的!這就有點(diǎn)像IIC了,但是,但是協(xié)議是不一樣的!主機(jī)的MOSI與從機(jī)的MISO接口相連!既然是半雙工,那么就意味著它不能同時(shí)發(fā)送和接收數(shù)據(jù)了,所以,主機(jī)應(yīng)該是先發(fā)完數(shù)據(jù),然后再讀取數(shù)據(jù),呵呵!!!這樣的通信方式,到目前為止,俺遇到的傳感器等等設(shè)備也是沒用過哇。不過不管它!既然支持,那么有機(jī)會(huì)就要使用它!嘿嘿!
SPI主機(jī)-從機(jī)單工通信:
在參考手冊中有如下圖的一段話:
講述了SPI可以工作在單工模式下雨設(shè)備通信!和它的配置!那么說明時(shí)候用到這種配置的通信呢??不知道各位有木有用過74HC595,74HC573等等這些鎖存器呢或者是SPILCD/OLED呢?沒錯(cuò)!對于這些設(shè)備或者芯片,單片機(jī)沒有什么數(shù)據(jù)要從他們那里讀取的!只需要向他們寫數(shù)據(jù)就好!所以在這種情況下就需要單工SPI通信了!哈哈!
看下圖:
接口就如上了!嘿嘿!但是!但是一定要看下面的標(biāo)注1、2、3和Note啊,別傻乎乎的出問題了。
SPI主機(jī)-多從機(jī)通信:
看見上圖沒?知道NSS是干啥的了吧?SPI的多機(jī)通信就是這樣了!特別注意粉色框的NSS的接法和用法!很重要哦!其他的一切都是按照SPI標(biāo)準(zhǔn)協(xié)議來通信的!就不多廢話了。嘿嘿!
OK!SPI的基本的協(xié)議!就是這樣了!哈哈!在參考手冊上還分別對CLK、NSS、RxFIFO/TxFIFO、DMA等等的用法!這個(gè)就自己去看了!基本的協(xié)議將清楚了!那就應(yīng)該做做實(shí)驗(yàn),謝謝代碼了。嘿嘿!
還有一點(diǎn)解釋就是,為毛不講解寄存器呢??嘿嘿!我將反問道,為毛樣講寄存器呢?難道我會(huì)將的有手冊的好,有手冊的權(quán)威??這是不可能的!而且,學(xué)到了這一步,對怎么看寄存器資料介紹和操作寄存器應(yīng)該是沒問題了的!嘿嘿!
我剛好有64M的SPIFlash,嘿嘿!這個(gè)可不能浪費(fèi)嘍!哈哈!那咱就先玩Flash吧!用它來體驗(yàn)一下SPI通信!嘿嘿!
Flash型號:W25Q64 華邦公司的產(chǎn)品嘍!
Flash大小:64M Bit == 8M Byte,空間可是好大哦!有8M Byte
主控MCU:STM32F030R8T6
咱就通過MCU的硬件SPI資源與SPIFlash進(jìn)行通信,實(shí)現(xiàn)數(shù)據(jù)的存儲(chǔ)和讀取。
要干下面這么幾件事:
1.查閱W25Q64 數(shù)據(jù)手冊Datasheet,獲取其SPI的時(shí)鐘相位和極性。這可是通信的關(guān)鍵哦!
看見沒!SPIFlash支持兩種SPI標(biāo)準(zhǔn)通信模式,分別是
SPI0:CPOL=0,CPHA=0
SPI3:CPOL=1,CPHA=1
OK!想要的信息得到了!
2.選擇和配置MCU SPI資源,在這里咱就用SPI1了!如下圖:
有的選擇!這是好事啊!意味著有備胎哇!哈哈哈!那么我們到底該用哪一組呢?查看了一下Nucleo板卡的原理圖,得知PA5引腳是用來驅(qū)動(dòng)發(fā)光二極管LD2的,那就意味著如果咱選用第1組,那么LD2就沒得玩了!這樣就不好玩了!所以咋還是選擇第二組吧!嘿嘿!記住資源選擇的思路哦!!!一切都要考慮到!
咦!是不是感覺有哪里不對??第二組為毛沒有硬件NSS資源呢??這個(gè)咋玩啊??嘿嘿!其實(shí)那,在這里咱是和SPIFlash通信,那么就是以為著STM32F030是作為主機(jī)Master來使用的,所以,嘿嘿!NSS任意選擇一個(gè)GPIO口即可。哈哈!在這里咱根據(jù)Nucleo板卡的布局和考慮到功能的復(fù)用等等,選擇了GPIOA10口作為NSS了。
3.硬件連接,如下圖:
下面就該貼程序了!嘿嘿!
SPI的初始化配置:
解釋:
1.打開SPI1的時(shí)鐘嘍!不理解為毛是APB2的話,沒關(guān)系,兩種方法(1)看時(shí)鐘樹(2)查看stm32f10x_rcc.c文件!找到RCC_APB2Periph_SPI1即可!嘿嘿!
2.打開GPIO端口的復(fù)用功能,這個(gè)從函數(shù)源碼來講應(yīng)該是很好理解的!函數(shù)源碼參數(shù)說明如下:
問題就來了!你咋知道我們所用的資源對應(yīng)的到時(shí)是哪一個(gè)GPIO_AF_x?比日說SPI1從函數(shù)說明來說可能是GPIO_AF_0也可能是GPIO_AF_1,這個(gè)咋玩呢??還是一句話,在參考手冊或者數(shù)據(jù)手冊Datasheet上找,肯定能找到。沒錯(cuò),就是在Datasheet上找到了!如下圖:
看見了沒??對應(yīng)的是AF0啊啊啊啊啊啊!所以就是GPIO_AF_0。嘿嘿!多么簡單的領(lǐng)悟哇!
3.就是硬件SPI的基本配置了,嘿嘿!不管他!反正,咱只要配置成:全雙工通信(主要參數(shù)宏的2Lines(兩線)是指兩根數(shù)據(jù)線)、數(shù)據(jù)長度8Bit、CPOL=1,CPHA=1(用模式3)、NSS由軟件控制、波特率預(yù)分頻值為4分頻(關(guān)于速率也要參看SPIFlash手冊,這樣才能正確通信)、指定數(shù)據(jù)傳輸從MSB位開始(這個(gè)一定要參看參考手冊)、指定CRC多項(xiàng)式計(jì)算因子為7(這個(gè)在參考手冊中也有詳細(xì)說明)、最后指定SPI為Master主模式。這樣就完成配置了!嘿嘿!
4.啟動(dòng)NSS為軟件管理模式,配置FIFO的接收閥值為事件生成的1/4,最后就使能SPI1控制器了。
哈哈!配置過程就這樣完了!
既然配置完成了!咱就發(fā)送和接收數(shù)據(jù)嘍!這才是最終的目標(biāo)嘛!哈哈!那就往下看嘍!
上面就是讀寫函數(shù)了!四句話解決!是不是很簡潔?嘿嘿!肯定是下班等待發(fā)送標(biāo)志空閑,然后再SPI_SendData8()發(fā)送數(shù)據(jù)啦!因?yàn)槭侨p工的嘛,所以每發(fā)送出去1bit數(shù)據(jù),MCU也會(huì)接收到1bit的數(shù)據(jù),只是這些事咱不用考慮,因?yàn)镸CU的SPI控制器已經(jīng)給咱們干了。哈哈!所以發(fā)送了之后,就要該讀取了!但是總得等待接收標(biāo)志位空閑吧!然后讀取就返回了!所以,對于SPI的通信,發(fā)送數(shù)據(jù)和讀取數(shù)據(jù)就是同一個(gè)函數(shù)實(shí)現(xiàn)嘍!但是,有一點(diǎn)要注意哦,SPI通信接收的數(shù)據(jù)是上一次的數(shù)據(jù),啥意思呢??比如,咱發(fā)送一個(gè)命令(0x08),spi_data = SPI_SendData8(SPI1,0x08);這是返回的spi_data不是命令0x08所返回的結(jié)果數(shù)據(jù),所以想要獲取命令0x08的返回?cái)?shù)據(jù),就要緊接著spi_data = SPI_SendData8(SPI1,0xff);這次才是命令0x08所返回的數(shù)據(jù),這一點(diǎn)尤其要注意。
OK!既然配置好了!也可以發(fā)送和接收數(shù)據(jù)了!那SPI的講解也就完了!嘿嘿!至于其他部分的關(guān)于SPIFlash的代碼,這就沒什么好說的了!見我附的例程吧!具體的實(shí)現(xiàn)就要參考SPIFlash的datasheet了。
解釋:在這里雖然與SPIFlash通信,但是對SPIFlash的講解并不大!其實(shí)呢,對于SPIFlash的講解又是一大堆!而且我覺得沒必要!因?yàn)檫@與SPI通信無關(guān)了,而且SPIFlash的Datasheet將的非常權(quán)威了,我們只需要知道SPIFlash是存儲(chǔ)數(shù)據(jù)的存儲(chǔ)器,我們用它的目的就是為了存儲(chǔ)數(shù)據(jù)或者存放Demo。這就夠 ,至于它的指令,等等每一種SPIFlash都是不盡相同的!沒法講。