STM32 SPI通信(軟件)

一、SPI簡介

  • SPI(Serial Peripheral Interface)是由Motorola公司開發的一種通用數據總線
  • 四根通信線:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)
  • 同步,全雙工
  • 支持總線掛載多設備(一主多從)

SCK:提供時鐘信號,數據位的輸出和輸入,都是在SCK的上升沿或下降沿進行的。?

MOSI:主機輸出從機輸入,主機配置為輸出。從機配置為輸入。??

MISO:主機輸入從機輸出

SS:從機選擇線。有幾個從機就有幾條SS

二、硬件電路

  • 所有SPI設備的SCK、MOSI、MISO分別連在一起
  • 主機另外引出多條SS控制線,分別接到各從機的SS引腳
  • 輸出引腳配置為推挽輸出,輸入引腳配置為浮空或上拉輸入

解釋:

  • 因為有三個從機,所以有三個SS線,一共就是6跟線。因為都是單端型號,所以所有的設備還需要共地。從機沒有獨立供電的話,主機還需要額外引出電源正極VCC,給從機供電。?
  • SS線是低電平有效,同一時間只能置一個SS為低電平,只能選中一個從機,同時未選中的從機的MISO引腳,為高阻態,否則三個輸出同時進入主機的輸入會產生沖突
  • 對于輸出,選擇推挽輸出,使得上升沿、下降沿非常迅速

上圖是SPI內部移位示意圖

步驟:SPI高位先行,每來一個時鐘,移位寄存器都會向左進行移位。

假設現在主機要將10101010發給從機,從機要將01010101發給主機。

首先向左移位

?移出去的數據會在輸出數據寄存器

此時MOSI為高電平1,MISO為低電平0。這就是第一個時鐘上升沿的結果?。那么在第一個時鐘的下降沿,寄存器里的數據被分別采樣輸入到對應要去的最低位。

這就是第一個時鐘結束后的現象。

三、SPI時序基本單元

  • 起始條件:SS從高電平切換到低電平
  • 終止條件:SS從低電平切換到高電平

住:這里一共有四種模式,由CPOL和CPHA變換1、0來選擇。是為了適配更多的設備而設置的。傳輸流程看上面的硬件電路部分。

四、W25Q64簡介

注:上面有橫線的都是低電平有效

電路框圖:

  • (1)和(2)描述的是存儲器規劃示意圖,(2)被劃分為若干個塊“Block”,其中每一塊再劃分為若干個扇區(1)“Sector”。對于每個扇區又可以分為很多頁? “Page” 。?
  • 在(2)里面,以64KB為一個基本單元,分了128塊(因為一共8MB)。? ?
  • 在(1)里面,是對于塊的更細的劃分,以4KB為一份,分了16份。
  • 在寫入數據時,還會有個更細的劃分Page,256字節為一份。
  • (7)為SPI控制邏輯,接收指令和數據等
  • (8)狀態寄存器,表示芯片是否處于忙狀態、是否寫使能、是否寫保護
  • (9)寫控制邏輯,配合WP引腳實現硬件寫保護
  • (4)高電壓生成器,配合Flash編程,Flash要實現掉電不丟失,就需要高電壓來刺激。
  • (5)頁地址鎖存/計數器、(6)字節地址鎖存/計數器,用來指定地址,讀寫操作
  • (3)256字節的RAM存儲器,由于SPI寫入頻率非常高,所有寫入數據先到RAM緩存區里,再寫入Flash(2)里面。寫入時會給狀態寄存器(8)的BUSY位置1

五、Flash操作注意事項

寫入操作時:

  • 寫入操作前,必須先進行寫使能
  • 每個數據位只能由1改寫為0,不能由0改寫為1
  • (由于上一條)寫入數據前必須先擦除,擦除后,所有數據位變為1
  • 擦除必須按最小擦除單元進行(一個扇區)
  • 連續寫入多字節時,最多寫入一頁的數據(256字節),超過頁尾位置的數據,會回到頁首覆蓋寫入
  • 寫入操作結束后(寫到RAM),芯片進入忙狀態(當前在RAM轉Flash,或者在擦除當中),不響應新的讀寫操作

讀取操作時:

  • 直接調用讀取時序,無需使能,無需額外操作,沒有頁的限制,讀取操作結束后不會進入忙狀態,但不能在忙狀態時讀取

六、代碼部分

MySPI.c

#include "stm32f10x.h"                  // Device header/*引腳配置層*//*** 函    數:SPI寫SS引腳電平* 參    數:BitValue 協議層傳入的當前需要寫入SS的電平,范圍0~1* 返 回 值:無* 注意事項:此函數需要用戶實現內容,當BitValue為0時,需要置SS為低電平,當BitValue為1時,需要置SS為高電平*/
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		//根據BitValue,設置SS引腳的電平
}/*** 函    數:SPI寫SCK引腳電平* 參    數:BitValue 協議層傳入的當前需要寫入SCK的電平,范圍0~1* 返 回 值:無* 注意事項:此函數需要用戶實現內容,當BitValue為0時,需要置SCK為低電平,當BitValue為1時,需要置SCK為高電平*/
void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);		//根據BitValue,設置SCK引腳的電平
}/*** 函    數:SPI寫MOSI引腳電平* 參    數:BitValue 協議層傳入的當前需要寫入MOSI的電平,范圍0~1* 返 回 值:無* 注意事項:此函數需要用戶實現內容,當BitValue為0時,需要置MOSI為低電平,當BitValue為1時,需要置MOSI為高電平*/
void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);		//根據BitValue,設置MOSI引腳的電平,BitValue要實現非0即1的特性
}/*** 函    數:I2C讀MISO引腳電平* 參    數:無* 返 回 值:協議層需要得到的當前MISO的電平,范圍0~1* 注意事項:此函數需要用戶實現內容,當前MISO為低電平時,返回0,當前MISO為高電平時,返回1*/
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);			//讀取MISO電平并返回
}/*** 函    數:SPI初始化* 參    數:無* 返 回 值:無* 注意事項:此函數需要用戶實現內容,實現SS、SCK、MOSI和MISO引腳的初始化*/
void MySPI_Init(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//開啟GPIOA的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//將PA4、PA5和PA7引腳初始化為推挽輸出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//將PA6引腳初始化為上拉輸入/*設置默認電平*/MySPI_W_SS(1);											//SS默認高電平MySPI_W_SCK(0);											//SCK默認低電平
}/*協議層*//*** 函    數:SPI起始* 參    數:無* 返 回 值:無*/
void MySPI_Start(void)
{MySPI_W_SS(0);				//拉低SS,開始時序
}/*** 函    數:SPI終止* 參    數:無* 返 回 值:無*/
void MySPI_Stop(void)
{MySPI_W_SS(1);				//拉高SS,終止時序
}/*** 函    數:SPI交換傳輸一個字節,使用SPI模式0* 參    數:ByteSend 要發送的一個字節* 返 回 值:接收的一個字節*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;					//定義接收的數據,并賦初值0x00,此處必須賦初值0x00,后面會用到for (i = 0; i < 8; i ++)						//循環8次,依次交換每一位數據{/*兩個!可以對數據進行兩次邏輯取反,作用是把非0值統一轉換為1,即:!!(0) = 0,!!(非0) = 1*/MySPI_W_MOSI(!!(ByteSend & (0x80 >> i)));	//使用掩碼的方式取出ByteSend的指定一位數據并寫入到MOSI線MySPI_W_SCK(1);								//拉高SCK,上升沿移出數據if (MySPI_R_MISO()){ByteReceive |= (0x80 >> i);}	//讀取MISO數據,并存儲到Byte變量//當MISO為1時,置變量指定位為1,當MISO為0時,不做處理,指定位為默認的初值0MySPI_W_SCK(0);								//拉低SCK,下降沿移入數據}return ByteReceive;								//返回接收到的一個字節數據
}

W25Q64.c

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"/*** 函    數:W25Q64初始化* 參    數:無* 返 回 值:無*/
void W25Q64_Init(void)
{MySPI_Init();					//先初始化底層的SPI
}/*** 函    數:W25Q64讀取ID號* 參    數:MID 工廠ID,使用輸出參數的形式返回* 參    數:DID 設備ID,使用輸出參數的形式返回* 返 回 值:無*/
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_JEDEC_ID);			//交換發送讀取ID的指令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交換接收MID,通過輸出參數返回*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交換接收DID高8位*DID <<= 8;									//高8位移到高位*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//或上交換接收DID的低8位,通過輸出參數返回MySPI_Stop();								//SPI終止
}/*** 函    數:W25Q64寫使能* 參    數:無* 返 回 值:無*/
void W25Q64_WriteEnable(void)
{MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_WRITE_ENABLE);		//交換發送寫使能的指令MySPI_Stop();								//SPI終止
}/*** 函    數:W25Q64等待忙* 參    數:無* 返 回 值:無*/
void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);				//交換發送讀狀態寄存器1的指令Timeout = 100000;							//給定超時計數時間while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//循環等待忙標志位{Timeout --;								//等待時,計數值自減if (Timeout == 0)						//自減到0后,等待超時{/*超時的錯誤處理代碼,可以添加到此處*/break;								//跳出等待,不等了}}MySPI_Stop();								//SPI終止
}/*** 函    數:W25Q64頁編程* 參    數:Address 頁編程的起始地址,范圍:0x000000~0x7FFFFF* 參    數:DataArray	用于寫入數據的數組* 參    數:Count 要寫入數據的數量,范圍:0~256* 返 回 值:無* 注意事項:寫入的地址范圍不能跨頁*/
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();						//寫使能MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_PAGE_PROGRAM);		//交換發送頁編程的指令MySPI_SwapByte(Address >> 16);				//交換發送地址23~16位MySPI_SwapByte(Address >> 8);				//交換發送地址15~8位MySPI_SwapByte(Address);					//交換發送地址7~0位for (i = 0; i < Count; i ++)				//循環Count次{MySPI_SwapByte(DataArray[i]);			//依次在起始地址后寫入數據}MySPI_Stop();								//SPI終止W25Q64_WaitBusy();							//等待忙
}/*** 函    數:W25Q64扇區擦除(4KB)* 參    數:Address 指定扇區的地址,范圍:0x000000~0x7FFFFF* 返 回 值:無*/
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();						//寫使能MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);	//交換發送扇區擦除的指令MySPI_SwapByte(Address >> 16);				//交換發送地址23~16位MySPI_SwapByte(Address >> 8);				//交換發送地址15~8位MySPI_SwapByte(Address);					//交換發送地址7~0位MySPI_Stop();								//SPI終止W25Q64_WaitBusy();							//等待忙
}/*** 函    數:W25Q64讀取數據* 參    數:Address 讀取數據的起始地址,范圍:0x000000~0x7FFFFF* 參    數:DataArray 用于接收讀取數據的數組,通過輸出參數返回* 參    數:Count 要讀取數據的數量,范圍:0~0x800000* 返 回 值:無*/
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_READ_DATA);			//交換發送讀取數據的指令MySPI_SwapByte(Address >> 16);				//交換發送地址23~16位MySPI_SwapByte(Address >> 8);				//交換發送地址15~8位MySPI_SwapByte(Address);					//交換發送地址7~0位for (i = 0; i < Count; i ++)				//循環Count次{DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//依次在起始地址后讀取數據}MySPI_Stop();								//SPI終止
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/907104.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/907104.shtml
英文地址,請注明出處:http://en.pswp.cn/news/907104.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Kotlin Native與C/C++高效互操作:技術原理與性能優化指南

一、互操作基礎與性能瓶頸分析 1.1 Kotlin Native調用原理 Kotlin Native通過LLVM編譯器生成機器碼,與C/C++的互操作基于以下核心機制: CInterop工具:解析C頭文件生成Kotlin/Native綁定(.klib),自動生成類型映射和包裝函數雙向調用約定: Kotlin調用C:直接通過生成的綁…

云原生安全 SaaS :從基礎到實踐

&#x1f525;「炎碼工坊」技術彈藥已裝填&#xff01; 點擊關注 → 解鎖工業級干貨【工具實測|項目避坑|源碼燃燒指南】 1. 基礎概念 什么是 SaaS&#xff1f; SaaS&#xff08;Software as a Service&#xff0c;軟件即服務&#xff09;是一種基于云計算的軟件交付模式。用…

git clone 提速

git上的項目時間久了 .git文件夾非常大&#xff0c;這時候更新一次項目需要花費很長的時間&#xff0c;解決方法也很簡單&#xff0c;加一個depth參數&#xff0c;命令如下&#xff1a; git clone --depth 1 https://github.com/xxxxxx/xxxxxx指定了 depth 1 的時候&#xff0…

Redis 性能優化:核心技術、技巧與最佳實踐

Redis 作為高性能的內存數據庫,其性能優化是系統設計中的關鍵環節。本文結合 Redis 官方文檔及實踐經驗,從內存管理、延遲優化、CPU 效率、網絡配置等多個維度,系統性地闡述 Redis 性能優化的核心技術與最佳實踐。 通過以下優化手段,Redis 可在高并發、低延遲場景中發揮極致…

PostgreSQL 14 pacemaker 高可用集群

核心架構原理 集群組成&#xff08;典型三節點結構&#xff09;&#xff1a; [Node1] PostgreSQL Pacemaker Corosync pcsd [Node2] PostgreSQL Pacemaker Corosync pcsd [Node3] PostgreSQL Pacemaker Corosync pcsd ? ? ? ← Corosync 多…

影刀Fun叉鳥-2048

文章目錄 僅為自動化演示&#xff0c;實際2048判定邏輯需要更加嚴謹 參考代碼 # 使用提醒: # 1. xbot包提供軟件自動化、數據表格、Excel、日志、AI等功能 # 2. package包提供訪問當前應用數據的功能&#xff0c;如獲取元素、訪問全局變量、獲取資源文件等功能 # 3. 當此模塊作…

Vue3.5 企業級管理系統實戰(二十):角色菜單

本篇聚焦于角色菜單權限分配功能的實現&#xff0c;圍繞“給角色賦予菜單權限”這一核心場景&#xff0c;從接口設計、組件封裝到頁面集成展開完整技術方案的闡述。主要內容包括&#xff1a; 1. 角色權限接口開發&#xff1a;定義獲取角色權限、分配權限等接口&#xff0c;規范…

go實現釘釘三方登錄

釘釘的的官方開發文檔中只給出了java實現三方登錄的&#xff0c;我們準備用go語言來實現 實現網頁方式登錄應用&#xff08;登錄第三方網站&#xff09; - 釘釘開放平臺 首先就是按照文檔進行操作&#xff0c;備注好網站的信息 獲得應用憑證&#xff0c;我們后面會用到 之后…

一、OpenCV的基本操作

目錄 1、OpenCV的模塊 2、OpenCV的基礎操作 2.1圖像的IO操作 2.2繪制幾何圖形 2.3獲取并修改圖像中的像素點 2.4 獲取圖像的屬性 2.5圖像通道的拆分與合并 2.6色彩空間的改變 3、OpenCV的算數操作 3.1圖像的加法 3.2圖像的混合 3.3總結 1、OpenCV的模塊 2、OpenCV的基…

虛擬機配置橋接,遠程工具直接訪問

虛擬機網絡配置 前言windows下安裝linux虛擬機配置網絡1、設置虛擬機網絡模式&#xff1a;橋接模式2、配置網絡參數1、查看本機電腦連接的網絡情況2、打開虛擬機&#xff0c;編輯配置文件3、編輯虛擬網絡 3、測試連通性 前言 好不容易裝上了虛擬機&#xff0c;輸入命令時又發現…

RabbitMQ 概述與安裝

MQ 作用與介紹 MQ 是什么 MQ (message queue),從字面意思看是一個隊列, FIFO 先進先出,只不過里面存放的內容是 消息 消息 可以比較簡單,比如只包含 文本字符串,JSON 等;也可以很復雜,比如 內嵌對象 等 MQ 多用于分布式系統之間進行通信 系統之間的調用通常有兩種方式: 1…

如何在Vue中實現延遲刷新列表:以Element UI的el-switch為例

如何在Vue中實現延遲刷新列表&#xff1a;以Element UI的el-switch為例 在開發過程中&#xff0c;我們經常需要根據用戶操作或接口響應結果來更新頁面數據。本文將以Element UI中的el-switch組件為例&#xff0c;介紹如何在狀態切換后延遲1秒鐘再調用刷新列表的方法&#xff0…

CSS2相關知識點

CSS2相關知識點 CSS的編寫位置樣式種類樣式表的優先級 CSS選擇器CSS基本選擇器通配選擇器元素選擇器類選擇器ID選擇器 復合選擇器HTML元素間的關系交集選擇器并集選擇器后代選擇器子代選擇器兄弟選擇器屬性選擇器偽類選擇器偽元素選擇器 顏色的表示表示方式一&#xff1a;顏色名…

centos yum源,docker源

yum源repo文件&#xff1a; wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repodocker源repo文件&#xff1a; yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安裝docker和docker c…

深入探索AI模型工程:算法三大門派與監督學習的魅力

在當今人工智能蓬勃發展的時代&#xff0c;AI系統正逐漸滲透到我們生活的方方面面。從智能語音助手到自動駕駛汽車&#xff0c;從醫療影像診斷到金融風險預測&#xff0c;AI的應用場景無處不在。然而&#xff0c;構建一個高效、可靠的AI系統并非易事&#xff0c;它需要我們從宏…

[De1CTF 2019]SSRF Me

算是我第一次正兒八經的分析python代碼了 from flask import Flask, request import socket import hashlib import urllib import sys import os import jsonreload(sys) sys.setdefaultencoding(latin1)app Flask(__name__) # 創建一個Flask應用實例 secret_key os.ura…

Halcon 圖像預處理②

非線性圖像分段變化&#xff1a; 先窗體打開圖片 對數非線性變化&#xff1a; 結果圖像的亮度/對比度顯著增加 log_image(Image,LogImag1,e) 參數1&#xff1a;輸入圖像 參數2&#xff1a; 輸出圖像 參數3&#xff1a;底數 log_image(Image,LogImage2,0.1) 圖像結果亮度和…

云原生安全之網絡IP協議:從基礎到實踐指南

&#x1f525;「炎碼工坊」技術彈藥已裝填&#xff01; 點擊關注 → 解鎖工業級干貨【工具實測|項目避坑|源碼燃燒指南】 一、基礎概念 IP協議&#xff08;Internet Protocol&#xff09;是互聯網通信的核心協議族之一&#xff0c;負責在設備間傳遞數據包。其核心特性包括&…

QML學習08Text

Text 1、顏色&#xff08;color&#xff09;2、獲取寬度和高度&#xff08;contentWidth、contentHeight&#xff09;3、字體格式&#xff08;font&#xff09;4、文本樣式&#xff08;textFormat&#xff09;5、超鏈接 1、顏色&#xff08;color&#xff09; //顏色Text {colo…

Python網絡編程深度解析

目錄 一、網絡編程概述 二、TCP與UDP協議詳解 1.TCP協議&#xff1a;可靠傳輸的基石 2.UDP協議&#xff1a;高效但不可靠的傳輸 3. TCP與UDP對比 三、Socket編程模型 1. Socket基礎 2.TCP服務器實現詳解 3. UDP服務器實現詳解 四、進階應用&#xff1a;簡易聊天程序 …