查看: 3798|回复: 3

STM32 SPI DMA 的使用【转】

[复制链接]
  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2015-11-26 08:55:27 | 显示全部楼层 |阅读模式
    分享到:
    004hdRpazy6Kz9hfKWe0a&690.jpg
    一是想总结一下SPI总线的特点与注意点,二是总结一下SPI DMA的使用


    一、SPI信号线说明


      通常SPI通过4个引脚与外部器件相连:


    · MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
    · MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
    · SCK:串口时钟,作为主设备的输出,从设备的输入
    · NSS:从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。

    二、原理


      MOSI脚相互连接,MISO脚相互连接。这样,数据在主和从之间串行地传输(MSB位在前)。
      通信总是由主设备发起。主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚回传数据。这意味全双工通信的数据输出和数据输入是用同一个时钟信号同步的;时钟信号由主设备通过SCK脚提供。
    004hdRpazy6Kz96SSgF31&690.jpg

    三、NSS说明与注意点


    004hdRpazy6Kz98WDEca8&690.jpg
      NSS分为内部引脚和外部引脚。
      NSS外部引脚可以作为输入信号或者输出信号,输入信号一般用作硬件方式从机的片选,而输出信号一般用于主SPI去片选与之相连的从SPI。

      NSS从设备选择有两种模式:


    1、软件模式


      可以通过设置SPI_CR1寄存器的SSM位来使能这种模式,当它为1时,NSS引脚上的电平由SSI决定。在这种模式下NSS外部引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动。 


    2、硬件模式


    两种方式:


    (1)对于主SPI,NSS可以直接接高电平,对于从SPI,可以直接接低电平。  


    (2)当STM32F10xxx工作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时主机的NSS讲作为输出信号,引脚信号被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从SPI设备。


      此时两个的NSS信号线可以接个上拉电阻直连。


    四、DMA说明


      DMA是AMBA的先进高性能总线(AHB)上的设备,它有2个AHB端口:一个是从端口,用于配置DMA,另一个是主端口,使得DMA可以在不同的从设备之间传输数据。


      DMA的作用是在没有Cortex-M3核心的干预下,在后台完成数据传输。在传输数据的过程中,主处理器可以执行其它任务,只有在整个数据块传输结束后,需要处理这些数据时才会中断主处理器的操作。它可以在对系统性能产生较小影响的情况下,实现大量数据的传输。
    004hdRpazy6Kz9bzUcv1f&690.jpg

    五、SPI_DMA的通信过程


    · 设置外设地址
    · 设置存储器地址
    · 设置传输数据量
    · 设置通道的配置信息
    · 使能DMA通道,启动传输



    ·  发送时,在每次TXE被设置为’1’时发出DMA请求,DMA控制器则写数据至SPI_DR寄存器,TXE标志因此而被清除。
    ·  接收时,在每次RXNE被设置为’1’时发出DMA请求,DMA控制器则从SPI_DR寄存器读出数据,RXNE标志因此而被清除。

    六、相关代码


    这里使用的是SPI1


    SPI_DMA配置
    1. <span style="color: rgb(70, 70, 70); font-family: Verdana; font-size: medium; line-height: 24px;">void SPI1_DMA_Configuration( void )</span>
    2. <div><font color="#464646" face="Verdana" size="3">{</font></div><div><font color="#464646" face="Verdana" size="3">    DMA_InitTypeDef DMA_InitStructure;</font></div><div><font color="#464646" face="Verdana" size="3">    DMA_DeInit(DMA1_Channel2);  </font></div><div><font color="#464646" face="Verdana" size="3">    DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr;     //设置 SPI1 发送外设(0x4001300C) 地址(目的地址)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff;    //设置 SRAM 存储地址(目的地址)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;       //传输方向 外设-内存</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize;   //设置 SPI1 发送长度</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_Init(DMA1_Channel2, &DMA_InitStructure);</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);   </font></div><div><font color="#464646" face="Verdana" size="3">   SPI1->CR2 |= 1<<0;                                        //接收缓冲区DMA使能</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_Cmd(DMA1_Channel2, ENABLE);</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_DeInit(DMA1_Channel3);  </font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr;     //设置  接收外设(0x4001300C) 地址(源地址)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff;      //设置 SRAM 存储地址(源地址)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;    //传输方向 内存-外设</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize;   //设置 SPI1 接收长度</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    //外设地址增量(不变)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;    //内存地址增量(变化)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //外设传输宽度(字节)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  //内存传输宽度(字节)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;     //传输方式,一次传输完停止,不重新加载</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;   //中断方式-高(三级)</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;     //内存到内存方式禁止</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_Init(DMA1_Channel3, &DMA_InitStructure);   </font></div><div><font color="#464646" face="Verdana" size="3">   DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);   //开启 DMA1_Channel3 传输完成中断</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE);    //开启 DMA1_Channel3 传输错误中断</font></div><div><font color="#464646" face="Verdana" size="3">   SPI1->CR2 |= 1<<1;                                           //发送缓冲区DMA使能</font></div><div><font color="#464646" face="Verdana" size="3">   DMA_Cmd(DMA1_Channel3, DISABLE);                //开启 DMA 通道 DMA1_Channel3</font></div><div><font color="#464646" face="Verdana" size="3">}</font></div>
    复制代码
    SPI发送

    1. <div><font color="#464646" face="Verdana" size="3">void SPI1_Send( u8 *buff, u32 len )</font></div><div><font color="#464646" face="Verdana" size="3">{</font></div><div><font color="#464646" face="Verdana" size="3">     DMA1_Channel3->CPAR = SPI1_DR_Addr; //外设地址</font></div><div><font color="#464646" face="Verdana" size="3">     DMA1_Channel3->CMAR = (u32) buff; //mem地址</font></div><div><font color="#464646" face="Verdana" size="3">     DMA1_Channel3->CNDTR = len ; //传输长度</font></div><div><font color="#464646" face="Verdana" size="3">     DMA1_Channel3->CCR = (0 << 14) | // 非存储器到存储器模式</font></div><div><font color="#464646" face="Verdana" size="3">            (2 << 12) | // 通道优先级高</font></div><div><font color="#464646" face="Verdana" size="3">            (0 << 11) | // 存储器数据宽度8bit</font></div><div><font color="#464646" face="Verdana" size="3">            (0 << 10) | // 存储器数据宽度8bit</font></div><div><font color="#464646" face="Verdana" size="3">            (0 <<  9) | // 外设数据宽度8bit</font></div><div><font color="#464646" face="Verdana" size="3">            (0 <<  8) | // 外设数据宽度8bit</font></div><div><font color="#464646" face="Verdana" size="3">            (1 <<  7) | // 存储器地址增量模式</font></div><div><font color="#464646" face="Verdana" size="3">            (0 <<  6) | // 外设地址增量模式(不增)</font></div><div><font color="#464646" face="Verdana" size="3">            (0 <<  5) | // 非循环模式</font></div><div><font color="#464646" face="Verdana" size="3">            (1 <<  4) | // 从存储器读</font></div><div><font color="#464646" face="Verdana" size="3">            (1 <<  3) | // 允许传输错误中断</font></div><div><font color="#464646" face="Verdana" size="3">            (0 <<  2) | // 允许半传输中断</font></div><div><font color="#464646" face="Verdana" size="3">            (1 <<  1) | // 允许传输完成中断</font></div><div><font color="#464646" face="Verdana" size="3">            (1);        // 通道开启</font></div><div><font color="#464646" face="Verdana" size="3">}</font></div>
    复制代码


    SPI接收
    1. <div><font color="#464646" face="Verdana" size="3">void SPI1_Recive( u8 *buff, u32 len )</font></div><div><font color="#464646" face="Verdana" size="3">{</font></div><div><font color="#464646" face="Verdana" size="3">   DMA1_Channel2->CCR &= ~( 1 << 0 );          //关闭DMA通道2     </font></div><div><font color="#464646" face="Verdana" size="3">   DMA1_Channel2->CPAR = SPI1_DR_Addr; //外设地址</font></div><div><font color="#464646" face="Verdana" size="3">   DMA1_Channel2->CMAR = (uint32_t)buff; //mem地址</font></div><div><font color="#464646" face="Verdana" size="3">   DMA1_Channel2->CNDTR = len ; //传输长度</font></div><div><font color="#464646" face="Verdana" size="3">   DMA1_Channel2->CCR = (0 << 14) | // 非存储器到存储器模式</font></div><div><font color="#464646" face="Verdana" size="3">          (2 << 12) | // 通道优先级高</font></div><div><font color="#464646" face="Verdana" size="3">          (0 << 11) | // 存储器数据宽度8bit</font></div><div><font color="#464646" face="Verdana" size="3">          (0 << 10) | // 存储器数据宽度8bit</font></div><div><font color="#464646" face="Verdana" size="3">          (0 <<  9) | // 外设数据宽度8bit</font></div><div><font color="#464646" face="Verdana" size="3">          (0 <<  8) | // 外设数据宽度8bit</font></div><div><font color="#464646" face="Verdana" size="3">          (1 <<  7) | // 存储器地址增量模式</font></div><div><font color="#464646" face="Verdana" size="3">          (0 <<  6) | // 外设地址增量模式(不增)</font></div><div><font color="#464646" face="Verdana" size="3">          (0 <<  5) | // 非循环模式</font></div><div><font color="#464646" face="Verdana" size="3">          (0 <<  4) | // 传输方向 外设-内存</font></div><div><font color="#464646" face="Verdana" size="3">          (0 <<  3) | // 允许传输错误中断</font></div><div><font color="#464646" face="Verdana" size="3">          (0 <<  2) | // 允许半传输中断</font></div><div><font color="#464646" face="Verdana" size="3">          (1 <<  1) | // 允许传输完成中断</font></div><div><font color="#464646" face="Verdana" size="3">          (1);        // 通道开启</font></div><div><font color="#464646" face="Verdana" size="3"> }</font></div>
    复制代码
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2015-11-26 08:59
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2015-11-26 09:15:42 | 显示全部楼层
    代码乱了。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-3-27 11:54
  • 签到天数: 309 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2015-12-21 21:10:26 | 显示全部楼层
    看代码要疯了,哈哈哈
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2020-1-23 13:44
  • 签到天数: 243 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2015-12-23 14:50:55 | 显示全部楼层
    看花眼睛了。。多谢分享
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-11-25 09:33 , Processed in 0.164657 second(s), 22 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.