打印

【转】STM32 SPI DMA 的使用

[复制链接]
2013|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、SPI信号线说明

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

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

沙发
monkeypony|  楼主 | 2015-8-30 22:43 | 只看该作者
二、原理

  MOSI脚相互连接,MISO脚相互连接。这样,数据在主和从之间串行地传输(MSB位在前)。

  通信总是由主设备发起。主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚回传数据。这意味全双工通信的数据输出和数据输入是用同一个时钟信号同步的;时钟信号由主设备通过SCK脚提供。

使用特权

评论回复
板凳
monkeypony|  楼主 | 2015-8-30 22:44 | 只看该作者
DMA说明

DMA是AMBA的先进高性能总线(AHB)上的设备,它有2个AHB端口:

一个是从端口,用于配置DMA,另一个是主端口,使得DMA可以在不同的从设备之间传输数据。

DMA的作用是在没有Cortex-M3核心的干预下,在后台完成数据传输。在传输数据的过程中,

主处理器可以执行其它任务,只有在整个数据块传输结束后,需要处理这些数据时才会中断主处理器的操作。

它可以在对系统性能产生较小影响的情况下,实现大量数据的传输。

使用特权

评论回复
地板
monkeypony|  楼主 | 2015-8-30 22:44 | 只看该作者
SPI_DMA的通信过程

设置外设地址
设置存储器地址
设置传输数据量
设置通道的配置信息
使能DMA通道,启动传输
发送时,在每次TXE被设置为’1’时发出DMA请求,DMA控制器则写数据至SPI_DR寄存器,TXE标志因此而被清除。
接收时,在每次RXNE被设置为’1’时发出DMA请求,DMA控制器则从SPI_DR寄存器读出数据,RXNE标志因此而被清除。

使用特权

评论回复
5
monkeypony|  楼主 | 2015-8-30 22:44 | 只看该作者
相关代码  这里使用的是SPI1 SPI_DMA配置
/*******************************************************************************
* Function Name  : SPI1_DMA_Configuration
* Description    : 配置SPI1_RX的DMA通道2,SPI1_TX的DMA通道3
* Input          : None
* Output         : None
* Return         : None
* Attention             :
*******************************************************************************/
void SPI1_DMA_Configuration( void )
{
  DMA_InitTypeDef DMA_InitStructure;
   
    /* DMA1 Channel2 (triggered by SPI1 Rx event) Config */
  DMA_DeInit(DMA1_Channel2);  
  DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr;                          //设置 SPI1 发送外设(0x4001300C) 地址(目的地址)
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff;                    //设置 SRAM 存储地址(目的地址)
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                //传输方向 外设-内存
  DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize;                         //设置 SPI1 发送长度
  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_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
  /* Enable SPI1 DMA RX request */
  SPI1->CR2 |= 1<<0;                                                                 //接收缓冲区DMA使能
  DMA_Cmd(DMA1_Channel2, ENABLE);
   
   
    /* DMA1 Channel3 (triggered by SPI1 Tx event) Config */
  DMA_DeInit(DMA1_Channel3);  
  DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr;                          //设置  接收外设(0x4001300C) 地址(源地址)
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff;                    //设置 SRAM 存储地址(源地址)
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                                //传输方向 内存-外设
  DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize;                           //设置 SPI1 接收长度
  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_Channel3, &DMA_InitStructure);
  
  DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);                                   //开启 DMA1_Channel3 传输完成中断
  DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE);                                   //开启 DMA1_Channel3 传输错误中断
  /* Enable SPI1 DMA TX request */
  SPI1->CR2 |= 1<<1;                                                                //发送缓冲区DMA使能
  DMA_Cmd(DMA1_Channel3, DISABLE);                                                  //开启 DMA 通道 DMA1_Channel3
}

使用特权

评论回复
6
monkeypony|  楼主 | 2015-8-30 22:45 | 只看该作者
SPI发送
/*******************************************************************************
* Function Name  : SPI1_Send
* Description    : SPI1的DMA方式发送
* Input          : SPI1_TX_Buff[SPI1_SendBufferSize]
* Output         : None
* Return         : None
* Attention             : 关闭DMA通道3之前必须等待TXE为1,等待忙标志为0
*******************************************************************************/
void SPI1_Send( u8 *buff, u32 len )
{
    DMA1_Channel3->CPAR = SPI1_DR_Addr; //外设地址
    DMA1_Channel3->CMAR = (u32) buff; //mem地址
    DMA1_Channel3->CNDTR = len ; //传输长度
    DMA1_Channel3->CCR = (0 << 14) | // 非存储器到存储器模式
            (2 << 12) | // 通道优先级高
            (0 << 11) | // 存储器数据宽度8bit
            (0 << 10) | // 存储器数据宽度8bit
            (0 <<  9) | // 外设数据宽度8bit
            (0 <<  8) | // 外设数据宽度8bit
            (1 <<  7) | // 存储器地址增量模式
            (0 <<  6) | // 外设地址增量模式(不增)
            (0 <<  5) | // 非循环模式
            (1 <<  4) | // 从存储器读
            (1 <<  3) | // 允许传输错误中断
            (0 <<  2) | // 允许半传输中断
            (1 <<  1) | // 允许传输完成中断
            (1);        // 通道开启
}

使用特权

评论回复
7
monkeypony|  楼主 | 2015-8-30 22:45 | 只看该作者
SPI接收
/*******************************************************************************
* Function Name  : SPI1_Recive
* Description    : SPI1的DMA方式接收
* Input          : None
* Output         : SPI1_RX_Buff[SPI1_ReciveBufferSize]
* Return         : None
* Attention             : 必须要先关闭通道2,然后再配置通道2的参数
*******************************************************************************/
void SPI1_Recive( u8 *buff, u32 len )
{
    DMA1_Channel2->CCR &= ~( 1 << 0 );          //关闭DMA通道2
   
    DMA1_Channel2->CPAR = SPI1_DR_Addr; //外设地址
    DMA1_Channel2->CMAR = (uint32_t)buff; //mem地址
    DMA1_Channel2->CNDTR = len ; //传输长度
    DMA1_Channel2->CCR = (0 << 14) | // 非存储器到存储器模式
            (2 << 12) | // 通道优先级高
            (0 << 11) | // 存储器数据宽度8bit
            (0 << 10) | // 存储器数据宽度8bit
            (0 <<  9) | // 外设数据宽度8bit
            (0 <<  8) | // 外设数据宽度8bit
            (1 <<  7) | // 存储器地址增量模式
            (0 <<  6) | // 外设地址增量模式(不增)
            (0 <<  5) | // 非循环模式
            (0 <<  4) | // 传输方向 外设-内存
            (0 <<  3) | // 允许传输错误中断
            (0 <<  2) | // 允许半传输中断
            (1 <<  1) | // 允许传输完成中断
            (1);        // 通道开启
}

使用特权

评论回复
8
蜕变吧| | 2015-8-31 11:20 | 只看该作者
开个DMA这么复杂,那么多移位,晕了

使用特权

评论回复
9
monkeypony|  楼主 | 2015-10-28 13:43 | 只看该作者
蜕变吧 发表于 2015-8-31 11:20
开个DMA这么复杂,那么多移位,晕了

万变不离其中!

使用特权

评论回复
10
smilingangel| | 2015-10-28 19:24 | 只看该作者
通信总是由主设备发起。主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚回传数据。这意味全双工通信的数据输出和数据输入是用同一个时钟信号同步的;时钟信号由主设备通过SCK脚提供。

使用特权

评论回复
11
baimiaocun2015| | 2015-10-28 22:55 | 只看该作者
DMA的作用是在没有Cortex-M3核心的干预下,在后台完成数据传输。在传输数据的过程中,主处理器可以执行其它任务,只有在整个数据块传输结束后,需要处理这些数据时才会中断主处理器的操作。

使用特权

评论回复
12
小葱521| | 2015-10-29 12:17 | 只看该作者
这个 不需要重启吧!
另外 问一下,那个STM32 和GD 是不是通过串口ISP ,是不需要外部REST引脚的?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

133

主题

715

帖子

0

粉丝