STM32上SPI+DMA实现大批量读取flash数据

2023-05-16

最近做项目需要使用SPI+DMA,为了做实验感受DMA传输数据块,本人以SPI+DMA来读取flash中的数据。网上有很多例程是spi直接读取flash,无法提高性能。因为只是简单的实验SPI的DMA功能,所以在写数据时并没有考虑页写一些制约,只是简单的将1k大小的数据写入flash,然后用DMA读出这1K大小的数据,相信SPI和DMA的配置大家都很熟悉了,本人在此不在强调,只是说几点注意点的:

(1)DMA关于SPI通道的选择,在stm32中,SPI1_RX读请求是DMA通道2,SPI1_TX发送请求是DMA通道3。刚开始我在配置通道的时候没仔细看,看的是SPI/I2S2_RX这个请求,把通道配置成了通道四和通道五,结果一直无法出来结果。所以这个是第一个要注意的。


(2)设置发送和接收缓冲区,并且对发送缓冲区初始化,本例中我设定发送和接收缓冲区大小是1K,可以根据自己需要设定,本帖子起抛砖引玉作用。

uint32_t Tx_Buffer[256];
uint32_t Rx_Buffer[256];

对发送缓冲区初始化:

for(i = 0; i< 256; i++)
{
Tx_Buffer[i] = i;
}

下面是对DMA的初始化,本例中没有用到中断。

void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;

/*开启时钟*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);


DMA_DeInit(DMA1_Channel2);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Rx_Buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 256;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel2, &DMA_InitStructure);

   
    DMA_DeInit(DMA1_Channel3);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Tx_Buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 256;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel3, &DMA_InitStructure);

DMA_Cmd (DMA1_Channel2,ENABLE);
DMA_Cmd (DMA1_Channel3,ENABLE);

}

在初始化函数中先不给使能SPI的DMA读请求或者写请求,下面是在main函数中的程序,写之前先擦除,在这里再说明下,我只有在对flash里的数据进行读写操作是才使用DMA,而一些命令的发送接收不用DMA,因为DMA是对数据块进行操作的,小量的数据没必要使用DMA.

下面具体介绍main函数中的函数

void SPI_DMA_PageWrite(u32 WriteAddr)
{
  /* Enable the write access to the FLASH */
  SPI_FLASH_WriteEnable();


  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();
  /* Send "Write to Memory " instruction */
  SPI_FLASH_SendByte(W25X_PageProgram);
  /* Send WriteAddr high nibble address byte to write to */
  SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  /* Send WriteAddr medium nibble address byte to write to */
  SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  /* Send WriteAddr low nibble address byte to write to */
  SPI_FLASH_SendByte(WriteAddr & 0xFF);


SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);

while(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET);

  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();

}
先发送写数据命令,然后发送写数据地址,命令和地址发送完后,我们就要写入数据了,此时我们要是使能SPI的DMA发送请求,即函数SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); 就会启动DMA,为了防止DMA没有发送完数据就释放总线,我加入了函数while(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET);,来保证DMA传输完成。最后拉高片选线。


void SPI_DMA_BufferRead(u32 ReadAddr)
{
  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();


  /* Send "Read from Memory " instruction */
  SPI_FLASH_SendByte(W25X_ReadData);


  /* Send ReadAddr high nibble address byte to read from */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /* Send ReadAddr medium nibble address byte to read from */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /* Send ReadAddr low nibble address byte to read from */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);


SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_RxOnly;
SPI_Init(SPI1, &SPI_InitStructure);


//SPI_FLASH_SendByte(0xff);
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);

while(DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET);

  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();
}

首先发送地址和读命令,在这里重要说明一下,刚开始我把SPI配置成全双工模式,因为牵扯到要获取flash地址的操作,但是我们在用SPI以DMA读flash数据的时候,就不能使用全双工模式了,在全双工模式下,我们读取flash的时候需要一直发送一个无效数据0xff,来使电平发生变化,这样就限制了DMA的性能。所以在用DMA读flash数据的时候,我们把SPI模式配置成只读模式,如上面程序中的样子,这个时候就可以直接读取数据,而不需要在发送无效数据0xff,大大提高了性能。SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_RxOnly;
SPI_Init(SPI1, &SPI_InitStructure);配置为只读模式,SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);使能SPI_DMA读请求。同理为了防止DMA未传输完片选线就拉高,用while(DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET);来保证。

uint32_t Tx_Buffer[256];
uint32_t Rx_Buffer[256];

整个程序思路就是我将发送缓冲区Tx_Buffer中的数据用DMA方式写入flash,用DMA方式读出数据保存到接收缓冲区Rx_Buffer。

因为程序比较简单,并没有考虑到flash写入时的页面大小限制等,但是读写操作已经掌握了,剩下的就是完善了。希望本帖子能够帮助大家,有问题的地方还望指出来,大家共同进步哈!!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32上SPI+DMA实现大批量读取flash数据 的相关文章

  • 为什么系统进入到Checking Media Presence

    你按联想热启键来开机 xff0c 也就是那个还原键来开机 然后你选择BIOS Setup回车 选择Boot这项的boot mode把UEFI改成legacy support和boot priority把UEFI改成legacy 然后保存退出
  • MacBook Air响一声白屏故障情况说明及解决

    情况说明 xff1a 2013款的MacBook Air安装Windows 7系统 xff0c 结果导致开机响一声就白屏 xff0c 按option无选项出现 xff0c 其他各种组合按键尝试都无效 百度搜索 xff0c 有人说是屏幕故障
  • 如何在Eclipse中打开现有项目(高手免入)

    如果我们现在已经有了Java项目 xff08 网上下载的或者从别的电脑上拷贝过来的 xff09 xff0c 我们都知道 xff0c Eclipse和其他的编程软件不一样 xff0c 不能够通过直接双击某个文件的方式来打开 xff0c 那么我
  • svn is not a working copy 怎么解决

    确定当目录下是否含有 svn文件夹 如果没有就重新啊checkout xff0c 或者在上一层目录或下一层目录查找 xff0c 有则可执行 svn commit m 34 更新部分代码 34
  • CSS3设置Border边框是内边框还是外边框

    CSS3可以设置边框是向内还是向外 xff0c 如果要设置为内边框使用 1 box sizing border box 外边框 1 box sizing content box
  • If this view is optional add '@Nullable' (fields) or '@Optional' (methods) ...

    lt 在出错的activity中 xff0c 对应布局文件中加入 gt lt 不能缺少 xff0c 缺少后出现If this view is optional add 39 64 Nullable 39 fields or 39 64 Op
  • MTK Camera(OV13850) 驱动移植

    一 驱动源码包结构 拿到的驱动源码包解压后得到hal和kernel两个目录文件 xff0c 源码目录结构如下所示 13850 6592 driver 10 28 7z hal camera AE PLineTable ov13850mipi
  • 查看路由器WAN口IP是否为公网ip指南

    查看路由器WAN口IP是否为公网ip指南 吴捷 一 xff0e 公网ip和私网ip ip地址分类中常用的有A B C类 xff0c 每类IP中都规划了一段私网IP xff0c 除了这些私网外的IP都是公网IP 分类IP地址范围适用用户A1
  • [iOS] WKWebView 于JavaScript传值

    如果在项目中采用WKWebView的方法加载网页 OC向JS传值方法总结 xff1a 1 OC gt JS 传数组的方法 xff1a NSString arrStr 61 self arr componentsJoinedByString
  • iOS日历中的日程生成VCalendar 2.0(.vcs)格式的字符串和解析

    获取 VCalendar2 0 的格式字符串 43 NSString getVCalendar20StrWithEvents NSArray lt EKEvent gt events NSString vcalendar 61 NSStri
  • 数传电台术语详解

    数传电台 xff08 data radio xff09 是指借助DSP 技术和无线电技术实现的高性能专业数据传输电台 数传电台的使用从最早的按键电码 电报 模拟电台加无线MODEM xff0c 发展到目前的数字电台和DSP 软件无线电 xf
  • 无线数传电台的发展趋势

    摘自 专业无线通信 传媒 无线数传电台作为一种最简洁的通行方式 xff0c 具有很长的历史 xff0c 其基本的特征是通联方便 简捷 xff0c 同时也存在着封闭性强的特点 xff0c 正是由于上述原因 xff0c 无线数传电台产品的生命期
  • wget 提交post请求

    格式 xff1a wget post data 34 item1 61 value1 amp item2 61 value2 34 http xxx xxx com 示例 xff1a wget post data 34 username 6
  • 欧拉角、轴角与四元数

    1 欧拉角 欧拉角使用最简单的x y z值来分别表示在x xff0c y xff0c z轴上的旋转角度 xff0c 其取值为0 360 或者0 2pi xff09 xff0c 一般使用roll xff0c pitch xff0c yaw来表
  • Linux 系统投屏显示

    最近使用电脑跑Linux时需要用到显示器投屏 xff0c 于是快乐的拿上我的笔记本连上了投影仪 emmm 然鹅并没有什么卵用 果断问度娘 xff0c 看到好多人说使用xrandr命令设置 xff0c 于是便上手试了下 xff0c 看到我运行
  • C++ 判断一个 int 型整数是否为 2 的 N 次方(幂次)

    判断一个整数是否为2的幂次方法有以下几种 xff1a 1 循环除2 这是最简单最好理解的方式 对于一个数如果是2的幂次 xff0c 则其肯定可以被2一直整除直到其值为1 所以可以通过一个while循环判断 xff1a void judge
  • Dijkstra算法图文详解

    Dijkstra算法算是贪心思想实现的 xff0c 首先把起点到所有点的距离存下来找个最短的 xff0c 然后松弛一次再找出最短的 xff0c 所谓的松弛操作就是 xff0c 遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近 xff
  • 需求中如何画用例图

    UML用例图 用例图主要用来图示化系统的主事件流程 xff0c 它主要用来描述客户的需求 xff0c 即用户希望系统具备的完成一定功能的动作 xff0c 通俗地理解用例就是软件的功能模块 xff0c 所以是设计系统分析阶段的起点 xff0c
  • 反光板导航SLAM(二)VEnus代码浅析

    上一章简单介绍了VEnus中几个主要函数的作用 xff0c 这里详细展开看一下每个函数的具体思路 xff0c 通过研究具体的代码我们可以简单了解VEnus中对于反光柱定位的具体流程 1 IntensityExtraction Extract
  • TEB算法详解(TebLocalPlannerROS::computeVelocityCommands(3))

    第一章主要分析了teb算法的准备条件 xff0c 包括获取当前位姿与速度 对全局路径的裁减以获取局部路径等以及局部地图的获取等 第二章主要讲述了在获取前置条件后 xff0c 如何根据前置条件进行位姿优化 xff0c teb的路径优化主要是调

随机推荐