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