設(shè)計本官方網(wǎng)站下載如何設(shè)計網(wǎng)站
通訊協(xié)議之路主要分為兩部分,第一部分從理論上面講解各類協(xié)議的通訊原理以及通訊格式,第二部分從具體運用上講解各類通訊協(xié)議的具體應(yīng)用方法。
后續(xù)文章會同時發(fā)表在個人博客(jason1016.club)、CSDN;視頻會發(fā)布在bilibili(UID:399951374)
本文前綴:
通訊協(xié)議專欄:通訊協(xié)議_JASON丶LI的博客-CSDN博客
UART理論部分:
一、具體實踐方案選擇
同樣的對于SPI也具有軟件模擬和硬件外設(shè)配置的兩種方案,此外也同樣可以采用DMA轉(zhuǎn)運數(shù)據(jù)、中斷處理數(shù)據(jù)、輪詢處理數(shù)據(jù)這三種方案。
軟件SPI和硬件SPI之間的關(guān)系是,軟件SPI是對硬件SPI的一種軟件實現(xiàn)。軟件SPI可以在沒有硬件SPI模塊的情況下實現(xiàn)SPI通信,但由于軟件實現(xiàn)的限制,軟件SPI的速度和可靠性可能不如硬件SPI。在一些資源受限的系統(tǒng)中,軟件SPI是一種常用的替代方案。
軟件模擬
按照SPI傳輸?shù)臅r序與模式,通過對SCK、SS、MOSI、MISO這四個進行高低電平的時序配置是實現(xiàn)SPI通訊協(xié)議的模擬。
硬件模式
硬件模式直接配置單片機的SPI外設(shè),使用其封裝的庫進行協(xié)議通信,不需要像軟件一樣一步步配置其時序,硬件SPI的工作狀態(tài)主要通過讀其SPI內(nèi)部寄存器進行判斷。其信息讀取的4種模式按照參考下表。
NSS管腳與片選
NSS管腳及我們熟知的片選信號,作為主設(shè)備NSS管腳為高電平,從設(shè)備NSS管腳為低電平。當NSS管腳為低電平時,該spi設(shè)備被選中,可以和主設(shè)備進行通信。在stm32中,每個spi控制器的NSS信號引腳都具有兩種功能,即輸入和輸出。所謂的輸入就是NSS管腳的信號給自己。所謂的輸出就是將NSS的信號送出去,給從機。
NSS分為內(nèi)部管腳和外部管腳,通過設(shè)置spi_cr1寄存器的ssm位和ssi位都為1可以設(shè)置NSS管腳為軟件輸入模式且內(nèi)部管腳提供的電平為高電平,其中SSM位為使能軟件輸入位。SSI位為設(shè)置內(nèi)部管腳電平位。同理通過設(shè)置SSM和SSI位1和0則此時的NSS管腳為軟件輸入模式但內(nèi)部管腳提供的電平為0。若從設(shè)備是一個其他的帶有spi接口的芯片,并不能選擇NSS管腳的方式,則可以有兩種辦法,一種是將NSS管腳直接接低電平。另一種就是通過主設(shè)備的任何一個gpio口去輸出低電平選中從設(shè)備。
二、開發(fā)實踐
標準庫
軟件SPI
SPI_Software.c
#include "stm32f10x.h" // Device header
#include "SPI_Software.h"void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);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);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);MySPI_W_SS(1);MySPI_W_SCK(0);
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}MySPI_W_SCK(0);}return ByteReceive;
}//SPI寫應(yīng)該Byte函數(shù)
void SPI_WriteByte(uint8_t Byte)
{uint8_t i;for(i = 0;i < 8;i++){//SCK從低電平到高電平(上升沿)時傳輸數(shù)據(jù)MySPI_W_SCK(0);if(Byte & 0x80) //取出最高為,每次只能傳輸一個bit的數(shù)據(jù){MySPI_W_MOSI(1);}else{MySPI_W_MOSI(0);}Byte <<= 1;MySPI_W_SCK(1);}MySPI_W_SCK(0);
}//SPI讀一個Byte函數(shù)
uint8_t SPI_ReadByte(void)
{uint8_t i,Byte;MySPI_W_SCK(0);for(i = 0;i < 8;i++){MySPI_W_SCK(1);Byte <<= 1;if(MySPI_R_MISO()){Byte ++;}MySPI_W_SCK(0);}return Byte;
}
SPI_Software.h
#ifndef __SPISOFTWARE_H
#define __SPISOFTWARE_H#include "stm32f10x.h" // Device headervoid MySPI_W_SS(uint8_t BitValue);void MySPI_W_SCK(uint8_t BitValue);void MySPI_W_MOSI(uint8_t BitValue);uint8_t MySPI_R_MISO(void);void MySPI_Init(void);void SPI_WriteByte(uint8_t Byte);uint8_t SPI_ReadByte(void);#endif
SPI_Control.c
#include "SPI_Control.h"
#include "SPI_Software.h"//設(shè)備為:25AA010A//EEPROM開啟寫使能函數(shù)
void EEPROM_Write_ENABLE(void)
{//MySPI_W_SS(1);MySPI_W_SS(0);SPI_WriteByte(EEPROM_Address_ENABLE);MySPI_W_SS(1);
}//EEPROM關(guān)閉寫使能函數(shù)
void EEPROM_Write_DISABLE(void)
{//MySPI_W_SS(1);MySPI_W_SS(0);SPI_WriteByte(EEPROM_Address_DISABLE);MySPI_W_SS(1);
}//從EEPROM中讀取數(shù)據(jù)
uint8_t EEPROM_Read(uint8_t HW_Address,uint8_t SW_Address)
{uint8_t date = 0;//MySPI_W_SS(1);MySPI_W_SS(0);SPI_WriteByte(HW_Address);SPI_WriteByte(SW_Address);date = SPI_ReadByte();MySPI_W_SS(1);return date;
}//往EEPROM中寫數(shù)據(jù)函數(shù)
void EEPROM_Write(uint8_t HW_Address,uint8_t SW_Address,uint8_t date)
{//HW_Address:EEPROM硬件地址//SW_Address: EEPROM的軟件地址,即寫出內(nèi)存的地址uint8_t status = 0x01; EEPROM_Write_ENABLE(); //開啟寫使能//MySPI_W_SS(1);MySPI_W_SS(0);SPI_WriteByte(HW_Address);SPI_WriteByte(SW_Address);SPI_WriteByte(date);MySPI_W_SS(1);//讀取EEPROM狀態(tài)寄存器的最低為,當狀態(tài)寄存器的最低位為1表示還未寫完while(1){//MySPI_W_SS(1);MySPI_W_SS(0);SPI_WriteByte(EEPROM_Address_REGISTER);status = SPI_ReadByte();if((status & 0x01) == 0){break;}MySPI_W_SS(1);}EEPROM_Write_DISABLE(); //關(guān)閉寫使能
}
SPI_Control.h
#ifndef __SPICONTROL_H
#define __SPICONTROL_H#include "stm32f10x.h" // Device header#define EEPROM_Address_W 0x02 //從指定地址開始寫
#define EEPROM_Address_R 0X03 //從指定地址開始讀
#define EEPROM_Address_ENABLE 0x06 //開啟寫使能命令
#define EEPROM_Address_DISABLE 0x04 //關(guān)閉寫使能命令
#define EEPROM_Address_REGISTER 0x05 //讀取寄存器的狀態(tài)(狀態(tài)寄存器的值)void EEPROM_Write_ENABLE(void);void EEPROM_Write_DISABLE(void);uint8_t EEPROM_Read(uint8_t HW_Address,uint8_t SW_Address);void EEPROM_Write(uint8_t HW_Address,uint8_t SW_Address,uint8_t date);#endif
main.c
#include "stm32f10x.h" // Device header
#include <string.h>
#include "delay.h"
#include "sys.h"
#include "led.h"
#include "OLED.h"
#include "key.h"
#include "SPI_Control.h"
#include "SPI_Software.h"uint8_t RxData;
extern uint8_t num;int main(void)
{led_Init();Key_Init();OLED_Init();MySPI_Init();
// num = EEPROM_Read(EEPROM_Address_R,0x00);while(1){OLED_ShowNum(1,1,num,2);EEPROM_Write(EEPROM_Address_W,0x00,num);led_turn(GPIOB, GPIO_Pin_0);Delay_ms(1000);}
}
硬件SPI
HAL庫
- 有主機模式全雙工/半雙工——Full-Duplex Master
- 從機模式全雙工/半雙工——Ful-Duplex Slave
- 只接收主機模式/只接收從機模式——Half-Duplex Master
- 只發(fā)送主機模式——Half-Duplex Slave
SPI發(fā)送和接收輪詢、中斷、DMA三種模式操作函數(shù)
/* IO operation functions ****************************************************/
/******* Blocking mode: Polling */
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);/******* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_EnableListen_IT(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_DisableListen_IT(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_Master_Abort_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress);/******* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
?硬件模式:
SPI的proteus硬件模式仍在調(diào)試,后續(xù)會持續(xù)更新