STM32 SPI DMA 的使用

发布者:Huanle最新更新时间:2016-06-03 来源: eefocus关键字:STM32  SPI  DMA 手机看文章 扫描二维码
随时随地手机看文章
一是想总结一下SPI总线的特点与注意点,二是总结一下SPI DMA的使用

一、SPI信号线说明

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

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

二、原理

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

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

STM32 SPI  DMA 的使用
 
三、NSS说明与注意点
 
STM32 SPI  DMA 的使用
 

  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核心的干预下,在后台完成数据传输。在传输数据的过程中,主处理器可以执行其它任务,只有在整个数据块传输结束后,需要处理这些数据时才会中断主处理器的操作。它可以在对系统性能产生较小影响的情况下,实现大量数据的传输。

STM32 SPI  DMA 的使用

 五、SPI_DMA的通信过程

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

 

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

六、相关代码

这里使用的是SPI1

SPI_DMA配置

 1 /*******************************************************************************
 2 * Function Name  : SPI1_DMA_Configuration
 3 * Description    : 配置SPI1_RX的DMA通道2,SPI1_TX的DMA通道3
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 * Attention             : 
 8 *******************************************************************************/
 9 void SPI1_DMA_Configuration( void )
10 {
11     DMA_InitTypeDef DMA_InitStructure;
12     
13     /* DMA1 Channel2 (triggered by SPI1 Rx event) Config */
14   DMA_DeInit(DMA1_Channel2);  
15   DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr;                          //设置 SPI1 发送外设(0x4001300C) 地址(目的地址)
16   DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff;                    //设置 SRAM 存储地址(目的地址)
17   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                //传输方向 外设-内存
18   DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize;                         //设置 SPI1 发送长度
19   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
20   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
21   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
22   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
23   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
24   DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
25   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
26   DMA_Init(DMA1_Channel2, &DMA_InitStructure);
27   
28   DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
29   /* Enable SPI1 DMA RX request */
30   SPI1->CR2 = 1<<0;                                                                 //接收缓冲区DMA使能
31   DMA_Cmd(DMA1_Channel2, ENABLE);
32     
33     
34     /* DMA1 Channel3 (triggered by SPI1 Tx event) Config */
35   DMA_DeInit(DMA1_Channel3);  
36   DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr;                          //设置  接收外设(0x4001300C) 地址(源地址)
37   DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff;                    //设置 SRAM 存储地址(源地址)
38   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                                //传输方向 内存-外设
39   DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize;                           //设置 SPI1 接收长度
40   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                  //外设地址增量(不变)
41   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                           //内存地址增量(变化)
42   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;           //外设传输宽度(字节)
43   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                   //内存传输宽度(字节)
44   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                     //传输方式,一次传输完停止,不重新加载
45   DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                           //中断方式-高(三级)
46   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                      //内存到内存方式禁止
47   DMA_Init(DMA1_Channel3, &DMA_InitStructure);
48   
49   DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);                                   //开启 DMA1_Channel3 传输完成中断
50   DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE);                                   //开启 DMA1_Channel3 传输错误中断
51   /* Enable SPI1 DMA TX request */
52   SPI1->CR2 = 1<<1;                                                                //发送缓冲区DMA使能
53   DMA_Cmd(DMA1_Channel3, DISABLE);                                                  //开启 DMA 通道 DMA1_Channel3
54 }

SPI发送

 1 /*******************************************************************************
 2 * Function Name  : SPI1_Send
 3 * Description    : SPI1的DMA方式发送
 4 * Input          : SPI1_TX_Buff[SPI1_SendBufferSize]
 5 * Output         : None
 6 * Return         : None
 7 * Attention             : 关闭DMA通道3之前必须等待TXE为1,等待忙标志为0
 8 *******************************************************************************/
 9 void SPI1_Send( u8 *buff, u32 len )
10 {
11     DMA1_Channel3->CPAR = SPI1_DR_Addr; //外设地址
12     DMA1_Channel3->CMAR = (u32) buff; //mem地址
13     DMA1_Channel3->CNDTR = len ; //传输长度
14     DMA1_Channel3->CCR = (0 << 14)  // 非存储器到存储器模式
15             (2 << 12)  // 通道优先级高
16             (0 << 11)  // 存储器数据宽度8bit
17             (0 << 10)  // 存储器数据宽度8bit
18             (0 <<  9)  // 外设数据宽度8bit
19             (0 <<  8)  // 外设数据宽度8bit
20             (1 <<  7)  // 存储器地址增量模式
21             (0 <<  6)  // 外设地址增量模式(不增)
22             (0 <<  5)  // 非循环模式
23             (1 <<  4)  // 从存储器读
24             (1 <<  3)  // 允许传输错误中断
25             (0 <<  2)  // 允许半传输中断
26             (1 <<  1)  // 允许传输完成中断
27             (1);        // 通道开启
28 }

SPI接收

 1 /*******************************************************************************
 2 * Function Name  : SPI1_Recive
 3 * Description    : SPI1的DMA方式接收
 4 * Input          : None
 5 * Output         : SPI1_RX_Buff[SPI1_ReciveBufferSize]
 6 * Return         : None
 7 * Attention             : 必须要先关闭通道2,然后再配置通道2的参数
 8 *******************************************************************************/
 9 void SPI1_Recive( u8 *buff, u32 len )
10 {
11     DMA1_Channel2->CCR &= ~( 1 << 0 );          //关闭DMA通道2
12     
13     DMA1_Channel2->CPAR = SPI1_DR_Addr; //外设地址
14     DMA1_Channel2->CMAR = (uint32_t)buff; //mem地址
15     DMA1_Channel2->CNDTR = len ; //传输长度
16     DMA1_Channel2->CCR = (0 << 14)  // 非存储器到存储器模式
17             (2 << 12)  // 通道优先级高
18             (0 << 11)  // 存储器数据宽度8bit
19             (0 << 10)  // 存储器数据宽度8bit
20             (0 <<  9)  // 外设数据宽度8bit
21             (0 <<  8)  // 外设数据宽度8bit
22             (1 <<  7)  // 存储器地址增量模式
23             (0 <<  6)  // 外设地址增量模式(不增)
24             (0 <<  5)  // 非循环模式
25             (0 <<  4)  // 传输方向 外设-内存
26             (0 <<  3)  // 允许传输错误中断
27             (0 <<  2)  // 允许半传输中断
28             (1 <<  1)  // 允许传输完成中断
29             (1);        // 通道开启
30 }

 

关键字:STM32  SPI  DMA 引用地址:STM32 SPI DMA 的使用

上一篇:STM32串口DMA超时接收方法,可大大节约CPU时间
下一篇:STM32使用DMA加串口空闲中断接收数据

推荐阅读最新更新时间:2024-03-16 14:55

跑马灯+蜂鸣器的位操作实现&什么是STM32中的位操作?
跑马灯+蜂鸣器的位操作实现 代码部分 Led.c部分 #include buzzer.h #include stm32f10x.h #include sys.h void BUZZER_Init(void) { // 使能时钟 RCC- APB2ENR |= RCC_APB2ENR_IOPBEN; // 配置相应引脚的状态 GPIOB- CRH &= ~(GPIO_CRH_MODE8|GPIO_CRH_CNF8); GPIOB- CRH |= GPIO_CRH_MODE8; // 配置相应引脚的初始电平(寄存器操作) //GPIOB- BSRR |=
[单片机]
跑马灯+蜂鸣器的位操作实现&什么是<font color='red'>STM32</font>中的位操作?
STM32 输出比较错误及解决
程序1:TIM2输出比较,产生频率为366.2Hz的方波 #include stm32f10x.h void RCC_configuration(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE); //开启GPIO的时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); } void Led_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; //定义GPIO初始化的结构体 GPIO_Init
[单片机]
玩转STM32(3)使用模板工程
前面学习了安装什么样的软件,这时就已经具备开发嵌入式软件的基本条件。只要把前面的开发板电源使用USB的电源线插入5V的电源,然后把JLink的USB插头插入到开发的电脑,硬件就已经连接好了。如下图这样连接起来: 在这里一定要注意使用电源供电,而不能使用JLink的供电,因为CPU和外围元件的电压会不稳定,导致调试时,CPU可以下载和运行代码,但外围元件工作不正常的现象。这里都是使用USB接头,电源的USB接头可以插入到手机充电器,现在的手机充电器都能提供比较大的功率。同时手机充电器也随处可见,随时可以买得到,使用这样的电源是最方便的。 当你把硬件按这样接好之后,就可以进入软件开发了。但是这时候,你就感觉到迷茫了,到底怎么样
[单片机]
玩转<font color='red'>STM32</font>(3)使用模板工程
STM32学习之路-感觉自己走到了一个天大的坑里了!
先前兴致勃勃的来弄16位并口驱动LCD,本以为就需要改下LCD IC的初始化就行了,没想到弄了这么多天终于发现自己走进了一个深坑了 T T 原因是我的开发板是奋斗V5的, 它确实有MCU外扩IO口, 还支持16位并口驱动,但是!! 感觉它完全是为了迎合FSMC-LCD来设定TFT接口的.. 这是它的原理图.. 再来看看正点原子的 再来看看它的芯片中关于这些IO口的部分 正点原子这个应该是不支持FSMC的.这个就很容易写数据: #define LCD_CS_SET span style= white-space:pre /span GPIOC- BSRR=1 9 //片选端口 PC9
[单片机]
<font color='red'>STM32</font>学习之路-感觉自己走到了一个天大的坑里了!
STM32单片机-PWM波形输出
一、引脚映像与寄存器 1、定时器引脚复用功能映像 ----------------------------------------------------------- 2、定时器 1)定时器介绍 STM32中一共有11个定时器,其中TIM6、TIM7是基本定时器;TIM2、TIM3、TIM4、TIM5是通用定时器;TIM1和TIM8是高级定时器,以及2个看门狗定时器和1个系统嘀嗒定时器SysTick。 其中TIM1和TIM8能够产生3对WM互补输出,常用于三相电机的驱动,时钟由APB2的输出产生; TIM2~TIM5是普通定时器,TIM6和TIM7是基本定时器,其时钟由APB1输出产生。
[单片机]
<font color='red'>STM32</font>单片机-PWM波形输出
STM32和ENC28J60嵌入uip实现web服务器的设计
本设计功能有 1可以在计算机浏览器上,输入设备的的ip地址后,打开需要显示的网页控制界面,比如可以控制LED灯的开关,继电器的闭合。 2可采集数据。比如可以将温度,湿度,等参数获取显示在浏览器的页面上。 而设计实现需要需要一下知识 一、了解网络协议三大协议中的TCP/ip协议。 二、了解TCP/ip协议中应用层中的HTTP协议。 三、TCP/ip协议在单片机上实现的源码有uip和lwip.在这里主要学习uip。 四、了解http协议中的请求相应的方式GET和post。 五、了解ENC28J60硬件网卡。 六、了解HTML网页语言知识。 架构 如下 实物图
[单片机]
<font color='red'>STM32</font>和ENC28J60嵌入uip实现web服务器的设计
利用stm32的lwip TCP/IP协议栈的通信的思路
利用stm32f103vet6作为平台,enc28j60网卡,lwip tcp/ip作为协议栈进行相应的程序编写。 Stm32作为服务器与stm32作为客户端程序编写的基本步骤,思路清理: 1、stm32作为服务器端 当stm32作为服务器端的时候,首先有一点要明确的是端口与ip的确定性,当然,后期需要改变的是,如果服务器地址的变动,就需要相应的做些程序的改变。 程序的步骤如下所示: 1、利用lwip对网卡做相应的初始化工作,例如ip地址,网络掩码,以及网关的操作。 2、对服务器端的相应操作。(包括pcb控制块的获取,结构体的初始化工作。) 3、绑定相应的远程计算机,设置相应的ip地址和端口。 4、监听相应的信
[单片机]
利用<font color='red'>stm32</font>的lwip TCP/IP协议栈的通信的思路
STM32-printf重定向到USART
在使用STM32的过程中,尤其是刚开始学习使用的时候,由于不知道自己的程序写的对不对,就经常需要一点验证的方法,点亮一个LED灯就是最简单的验证方法,但是有的时候还经常需要串口的输出来验证自己的程序是否正确,但是官方提供的函数库中用于串口发送的好像就一个USART_SendData(),通过外设USARTx发送单个数据,对于熟悉C语言的同学来说,这个函数还没有格式输出,当想要输出一个数字,或者字符串的时候,使用起来可能有点麻烦,现在有一个很好的方法就可以使用C语言中的printf()函数,而且使用方法是一样的。如何使用,很简单,我们只需要重新定向printf就可以,将它的数据用STM32的串口进行发送出去就可以了。 首先添加pr
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved