20200305--畢業設計--ESP8266(micropython) 與單片機(Stm32F407)之間通過SPI進行雙向數據傳輸的一種方案,單片機從機模式 ESP8266 主機

背景: 在我的畢業設計中需要單片機將採集到的數據上傳到服務器,同時需要接收來自服務器的一些天氣信息,我的單片機的型號是 Stm32F407;  ESP8266 刷入了 micropython 的固件,使用python進行開發; 協議是 SPI協議 ESP8266 主機  Stm32F4作爲 從機

 單片機側使用的是 Stm32F407 的硬件 SPI  + DMA 接收發送。    ESP8266 側使用的也是硬件SPI1 ;  通訊速度應該可以跑滿 單片機這一側的極限速度(42MHz),但是我在測試時候發現數據在40M 就很不穩定了(邏輯分析儀測試,可能是導線有點長)   最終就選擇了4MHz.。

整體的這個實現的機制就是在單片機裏面設置好 SPI的從機模式  + DMA收發  DMA使用循環模式(自動重複覆蓋內存) 然後讓ESP8266 側也是開闢相同大小的 空間,讀取發送同步進行;通過控制 單片機側的開啓時間進而實現 兩側的內存的同步(近似的同步  有點類似於鏡像)   底層的着四塊空間 兩兩相互可以實現單向映射。

程序實現的介紹

 

單片機程序的下載地址在這裏

ESP8266

import network
#import simple
import time
import json
import machine
from machine import UART,SPI,Pin
machine.freq(160000000)  # 提高主頻
#import esps
#esp.osdebug(None) 
CS = Pin(16, Pin.OUT) #片選引腳
#spi = SPI(baudrate=10000000, polarity=1, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12))  #軟件模擬
spi = SPI(1, baudrate=4000000, polarity=1, phase=1) #硬件實現

send_buf = bytearray(60)    #創建兩個數組大小是 60個byte
recv_buf = bytearray(60)
for i in range(60):
  send_buf[i] =i          # 賦值   實際應用中我們應該是放自己想要傳遞的數據
cnt =0
print('ok')
while True:
  cnt +=1
  CS.value(0) #
  spi.write_readinto(send_buf,recv_buf) 
  CS.value(1)
  print(recv_buf)
  time.sleep_ms(200)
  print(cnt)

這個是python的代碼實現  沒啥特殊的  很簡單 就是 發送的時候同步進行讀取  兩個同時進行。

比較難實現的是單片機側的程序  我們需要配置 SPI  然後 SPI 配置兩個DMA的數據流。

聲明: 我下面的代碼是在其他網友的代碼的基礎上修改出來的,在這裏向原作者致敬



uint8_t SPI_RX_BUFFER[RX_LEN]= {0,}; 
uint8_t SPI_TX_BUFFER[TX_LEN]= {0x1,0x2,0x3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22}; 





/**
  *	@breif  The spi gpio init function.
  * @param  None
  * @retval None
  */  
static void _gpio_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1); //  CS  Òý½Å  Èí¼þÄ£Äâ
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);

	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}



static void spi_dma_init(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	/* ??DMA2?? */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
	
	/* DMA RX config */
	DMA_InitStructure.DMA_Channel = DMA_Channel_3;                                     // DMA  ͨµÀ
	DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_ADDR;                   //  ÍâÉèµØÖ·
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPI_RX_BUFFER;     // ½ÓÊÕ»º³åÇø£¨ÄÚ´æÖеÄÓÐÒ»¸öÊý×飩
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                    //DMA ´«Êä·½Ïò
	DMA_InitStructure.DMA_BufferSize = 100;                                                     //  DMA ´«ÊäµÄÊýÁ¿    Õâ¸öºóÆÚ»¹¿ÉÒÔÔÙ¸Ä
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;             //  ÍâÉèµØÖ·×ÔÔö  È¡Ïû
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                    // ÄÚ´æµØÖ·×ÔÔö  ʹÄÜ
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     //  ´«ÊäµÄ µ¥Î» £¨byte  8bit£©
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;// ´«ÊäµÄ µ¥Î» £¨byte  8bit£©
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                   //  ÆÕͨģʽ  ´«ÊäÍê³ÉÒ»´Î¾Í×Ô¶¯½áÊø
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                              // ÓÅÏȼ¶ ÖеÈ
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                   //²»Ê¹Óà FIFO
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;             //
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;              //
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;        //
	DMA_Init(DMA2_Stream2, &DMA_InitStructure);                                            //³õʼ»¯
	                                                                                                                   //
	/* DMA TX Config */                                                                                      //
	DMA_InitStructure.DMA_Channel = DMA_Channel_3;                                     // DMA  ͨµÀ
	DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_ADDR;                   //   ÍâÉèµØÖ· 
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPI_TX_BUFFER;     // ½ÓÊÕ»º³åÇø£¨ÄÚ´æÖеÄÓÐÒ»¸öÊý×飩
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                    // DMA ´«Êä·½Ïò 
	DMA_InitStructure.DMA_BufferSize = 100;                                                     //  DMA ´«ÊäµÄÊýÁ¿    Õâ¸öºóÆÚ»¹¿ÉÒÔÔÙ¸Ä
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;             //  ÍâÉèµØÖ·×ÔÔö  È¡Ïû
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                    // ÄÚ´æµØÖ·×ÔÔö  ʹÄÜ
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     //   ´«ÊäµÄ µ¥Î» £¨byte  8bit£© 
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;// ´«ÊäµÄ µ¥Î» £¨byte  8bit£©
//	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                   //   ÆÕͨģʽ  ´«ÊäÍê³ÉÒ»´Î¾Í×Ô¶¯½áÊø 
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                   //   ÆÕͨģʽ  ´«ÊäÍê³ÉÒ»´Î¾Í×Ô¶¯½áÊø 
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                              // ÓÅÏȼ¶ ÖеÈ
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                   //
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;             //
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;              //
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;        //
	DMA_Init(DMA2_Stream3, &DMA_InitStructure);                                            //³õʼ»¯
}



/**
  *	@breif  The spi init function.
  * @param  None
  * @retval None
  */  
void bsp_spi_init(void)
{
	SPI_InitTypeDef SPI_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	_gpio_init();
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE);   
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE);  

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // Ë«ÏßÈ«Ë«¹¤
	SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;                            // Ö÷»úģʽ
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                       //8bit  λ¿í
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                            //SCK  ¿ÕÏÐʱÖÓΪ¸ßµçƽ
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                         // µÚ¶þ¸öʱÖÓ±ßÔµ  ²¶»ñ
	SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;                                //NSSʹÓõÄÊÇÈí¼þÄ£Äâ
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; // SPI ʱÖӵķÖƵϵÊý 
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                        //  MSB £¨ ¸ßλÔÚÇ°£©
	SPI_InitStructure.SPI_CRCPolynomial = 7;                             
	SPI_Init(SPI1, &SPI_InitStructure);

	SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);  
	SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
	spi_dma_init();
	SPI_Cmd(SPI1, ENABLE);
	
	SPI_I2S_ClearITPendingBit(SPI1, SPI_I2S_IT_RXNE);
  SPI1_SetSpeed(SPI_BaudRatePrescaler_2);    ///  µ÷ÕûËÙ¶È   ËÙ¶ÈÈ«¿ª  ʵ²â´óÔ¼ÊÇ 42M Hz
}



/* ?????DMA?? */

/**
  *	@breif  The spi dma trans function.
  * @param  rx_buf -- the point of rx buffer
  * @param  tx_buf -- the point of tx buffer
  * @elngth length -- the size of data.
  * @retval None
  */  
void spi_trans(uint8_t *rx_buf,     
			   uint8_t *tx_buf,    
			   uint16_t length)    
{

	DMA_Cmd(DMA2_Stream2, DISABLE);                                                         //  ¹Ø±Õ DMA 
	DMA_Cmd(DMA2_Stream3, DISABLE);                                                         //¹Ø±Õ DMA 
                                                                                                                      //
	DMA_SetCurrDataCounter(DMA2_Stream2, (uint16_t)length);                          //  ÉèÖà DMAµÄ´«Êä²ÎÊý 
	DMA_SetCurrDataCounter(DMA2_Stream3, (uint16_t)length);                          // ÉèÖà DMAµÄ´«Êä²ÎÊý 
	                                                                                                                    //
	DMA2_Stream2->M0AR = (uint32_t)rx_buf;                                                     //  ÉèÖà ÄÚ´æµÄÆðʼµØÖ·
	DMA2_Stream3->M0AR = (uint32_t)tx_buf;                                                     //   ÉèÖà ÄÚ´æµÄÆðʼµØÖ·
                                                                                                                      //
	SPI1->DR;                                                                                                    //
	                                                                                                                    //  ¼Ù×°¶ÁÈ¡Çå³þ ±ê־λ 
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);            //   µÈ´ýÏÈÇ°µÄ·¢ËÍÍê±Ï
	                                                                                                                    //
	DMA_Cmd(DMA2_Stream2, ENABLE);                                                           //  ´ò¿ª DMA  ¿ªÊ¼·¢ËÍ
	DMA_Cmd(DMA2_Stream3, ENABLE);                                                           //  ´ò¿ª DMA  ¿ªÊ¼½ÓÊÕ
	                                                                                                                    //
//	while( DMA_GetFlagStatus(DMA2_Stream3, DMA_FLAG_TCIF3) == RESET);   //  ×èÈûʽ  ÔÚÕâÀïµÈ´ýÊý¾Ý·¢ËÍÍê
//	while( DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_TCIF2) == RESET);   //   ÕâÀï·¢ËÍËٶȿÉÒԷdz£¿é  ×èÈûÒ²¿ÉÒÔ 
//	                                                                                                                    //
//	DMA_Cmd(DMA2_Stream2, DISABLE);                                                         // ·¢ËÍÍê  ¹Ø±Õ DMA
//	DMA_Cmd(DMA2_Stream3, DISABLE);	                                                        //
//	                                                                                                                    //
//	DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);                                    //   Çå³þ±ê־λ
//	DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_TCIF3);                                    //
}

這個是主體代碼

這一部分是 整體的初始化函數  主要是 SPI協議相關的參數的設置 以及 一些設置。

這裏是一些GPIO的 複用設置   這裏要注意  就是 CS引腳  要選擇硬件 CS引腳

DMA 初始化前半部分  這是 接收DMA的 部分

這一部分是  發送DMA 的程序   相比較於原作者的代碼 我的代碼修改了 DMA的傳輸模式 換成了  循環模式

  最後一部分的代碼是  開啓DMA傳輸的函數 

再從機模式下  我們設置的時鐘都不起作用  時鐘是主機提供的,  所以這邊可以不管時鐘的設置 

 

在這裏我的程序中使用的 IO 口是 PA4 PA5 PA6 PA7

這是 SPI.h 的頭文件

 

 

 

void bsp_spi_init(void);
void spi_trans_read(uint8_t *rx_buf,
					uint8_t *tx_data,
					uint16_t length);
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler);


#define RX_LEN 		    	(uint8_t)60
#define TX_LEN          (uint8_t)60

#define SPI1_DR_ADDR    (uint32_t)0x4001300C



extern uint8_t SPI_RX_BUFFER[RX_LEN];
extern uint8_t SPI_TX_BUFFER[TX_LEN]; 

還有一點小的心得就是 我們要注意初始化的時機,就是爲了防止出現錯位的情況 我們需要在 CS 引腳 是高電平的時候 開啓傳輸

ESP8266 那一側不是DMA 所以這個的波形不是很緊湊   不過速度也還可以  比串口肯定是快多了

10M的速度也是非常穩定的   不過 用不到那麼快的速度。  而且我是用導線連接的  干擾很大 

20 M 的速度時候 CS 引腳就會收到很大的干擾

剩下的就是我們去  自己定義想要傳輸的數據了   準備好的數據放在內存中就可以了  等待 主機發起一次傳輸  雙方就把數據進行了一次的交換。  再次吐槽 ESP8266 的一個半串口太坑了 

 

 

代碼的下載鏈接在這裏  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章