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