STM32F103的SPI2+DMA接收数据

需求:STM32F103作为从设备,通过SPI接收大量数据;

 

方案:1.STM32的SPI通过中断接收(占用CPU资源,且长数据易丢失)

         2.STM32通过SPI+DMA实现大数据接收(占用资源少)

 

 

本次采用第二种方案实现

 

时序图(见STM32F10XXX参考手册第471页):

主要思路:

1.      配置SPI外设

SPI2配置:双线、只收、禁止CRC、16位数据、仅接收禁止发送、NSS由硬件控制、高位开始传输、配置为从设备、时钟悬空低电平、第1个时钟采集数据、关闭I2S

2.      配置DMA外设;

DMA配置:外设到内存、非循环模式、允许传输完成中断

3.      配置SPI与DMA的连接;

使能SPI2->CR2第0位

需要编写的文件:SPI.C  SPI.H  DMA.C  DMA.H文件

 

 

1.      SPI.H

#ifndef __SPI_H

#define __SPI_H    

#include "sys.h"    //没有这个文件就屏蔽掉,定义时就不能写u16,按规范定义;

 

void SPI2_Config(void);   //SPI2初始化函数

#endif

 

2.      SPI.C文件

 

#include "spi.h"                    

void SPI2_Config(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

SPI_InitTypeDef  SPI_InitStructure;

 

//挂载对应时钟

RCC->APB1ENR  |=  1<<14;        //SPI2

RCC->APB2ENR  |=  1<<3;         //GPIOB

 

 //SPI2的GPIO配置

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15|GPIO_Pin_12;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);

 

//也可以使用库函数定义,主要一定是双线模式不然接收数据为0X0000;

SPI2->CR1  = 0X00;                  //复位SPI2->CR1控制寄存器

SPI2->CR1 &= ~(1<<15);              //双线模式

SPI2->CR1 &= ~(1<<14);               //只收模式

SPI2->CR1 &= ~(1<<13);               //禁止CRC

SPI2->CR1 |= 1<<11;                    //16位数据模式

SPI2->CR1 |= 1<<10;                  //仅接收禁止发送

SPI2->CR1 &= ~(1<<9);                //NSS由硬件控制

SPI2->CR1 &= ~(1<<7);                //MSB传输

SPI2->CR1 &= ~(1<<2);             //配置为从设备

SPI2->CR1 |=  0<<1;               //时钟悬空低

SPI2->CR1 |=  0<<0;               //第1个时钟采样

//可不配置以下两句话,I2SCFGR默认复位为0X00

SPI2->I2SCFGR|=0<<11;             //选择SPI模式

SPI2->I2SCFGR|=0<<10;             //关闭I2S模式

 

//要关闭SPI2接收中断,允许产生DMA中断,这是SPI与DMA连接的桥梁

SPI2->CR2  =0X00;           //¸SPI控制寄存器2配置

SPI2->CR2 |= 0<<6;

SPI2->CR2 |= 0<<7;

SPI2->CR2 |= 0<<1;

SPI2->CR2 |= 1<<0;                 //允许DMA接收数据

}     

 

3.      DMA.H

 

#ifndef __DMA_H

#define __DMA_H 

#include "sys.h"

 

#define SPI2_DR_addr 0x4000380C  //宏定义外设寄存器地址为SPI2->DR=0x4000380c

//其它外设查看STM32F1xx数据手册

#define DMA1_MEM_LEN 52          //定义接收数据长度

extern u16 receive_dma[52];          //全局申明接收缓存数组,可在其它文件中调用

extern u8 DMA_RX_FLAG;            //声明接收完成标志位

void dma_init(void);                   //初始化                                    

#endif

 

 

4.      DMA,C

 

 

#include "dma.h"

u16 receive_dma[52];

u8 DMA_RX_FLAG=0;

void dma_init(void)    //DMA初始化

{

DMA_InitTypeDef DMA_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

 

//具体含义查看STM32F1的说明书中关于这些寄存器的描述

//也可用库函数写

DMA1_Channel4->CCR   = 0x00000000;                    

       DMA1_Channel4->CPAR=SPI2_DR_addr;                                  DMA1_Channel4->CMAR = (u32)&receive_dma;            

DMA1_Channel4->CNDTR =  DMA1_MEM_LEN;                 

DMA1_Channel4->CCR   |= 0<<4;

DMA1_Channel4->CCR|=0<<5;                          

DMA1_Channel4->CCR|=0<<6;            

DMA1_Channel4->CCR|=1<<7;                          DMA1_Channel4->CCR|=2<<8;                           DMA1_Channel4->CCR|=1<<10;                          DMA1_Channel4->CCR|=1<<12;                         DMA1_Channel4->CCR |= 0<<14;

                        

DMA1_Channel4->CCR |= 1<<1;                   

DMA1_Channel4->CCR  |= 1<<2;                                                          

DMA1_Channel4->CCR  |= 0<<3;                               

 

DMA1_Channel4->CCR|=1<<0;   

           

 NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;

 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

 NVIC_Init(&NVIC_InitStructure);

}

 

void DMA1_Channel4_IRQHandler(void)

{

 if((DMA1->ISR & 0x00002000) == 0x00002000)

 {

DMA_RX_FLAG=1;

SPI2->CR1 &= ~(1<<6);              

DMA1_Channel4->CCR  &= ~(1<<0);            

DMA1->IFCR |=1<<12 ;                       

 }

}

 

 

5.      主函数(仅写出相关部分)

本部分是判断接收到一组数据后,将数据打印出来,然后再打开DMA与SPI中断

       if(DMA_RX_FLAG==1){  

              BackLeakageSpotAnaysis();  //将数据打印或则做其它处理

              DMA_RX_FLAG=0;

              DMA1_Channel4->CNDTR = 52;

              DMA1_Channel4->CMAR  = (u32)&receive_dma;   //从写接收缓存器地址

              DMA1_Channel4->CCR  |= 1<<0;                //使能DMA

              SPI2->CR1 |= 1<<6;                          //使能SPI2

       }

posted @ 2019-09-12 10:22  ctu19  阅读(7502)  评论(0编辑  收藏  举报