需求: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
}