概述
背景: 在我的毕业设计中需要单片机将采集到的数据上传到服务器,同时需要接收来自服务器的一些天气信息,我的单片机的型号是 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 的一个半串口太坑了
代码的下载链接在这里
最后
以上就是陶醉黑米为你收集整理的20200305--毕业设计--ESP8266(micropython) 与单片机(Stm32F407)之间通过SPI进行双向数据传输的一种方案,单片机从机模式 ESP8266 主机的全部内容,希望文章能够帮你解决20200305--毕业设计--ESP8266(micropython) 与单片机(Stm32F407)之间通过SPI进行双向数据传输的一种方案,单片机从机模式 ESP8266 主机所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复