STM32F407标准库下SPI-Slave-DMA收发

stm32f407对外高速传输的方法有usb、spi。但是usb通讯需要软件实现usb协议栈,太耗费计算资源了。而SPI是一个功能完整的硬件外设,使用DMA进行收发基本不占用cpu计算资源。

FT4222H是一款采用紧凑型32引脚QFN封装的高速/全速USB2.0至四路SPI/I2C器件控制器。四线SPI模式下传输速率高达53.8Mbps

f407虽然不支持4线spi,但是和ft4222h组合,最大40MHz SPI时钟频率,可以轻松实现和电脑之间高速数据传输。

一些说明

本文代码用于与FT4222H进行SPI通讯,其中F407作为slave设备,ft4222h作为master设备。

ft4222h通过FT4222_SPIMaster_Init方法 设置CPOL=1,CPHA=1。

1
ft42Status = FT4222_SPIMaster_Init(ftHandle, FT4222_SPIMode.SPI_IO_SINGLE, FT4222_SPIClock.CLK_DIV_2, FT4222_SPICPOL.CLK_IDLE_HIGH, FT4222_SPICPHA.CLK_TRAILING, 0x01);

ft4222h的SPI时序图如下

所以,f407端设置了

1
2
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

因为ft4222h的片选信号是低电平,所以407端片选引脚设置了上升沿中断。每当出发上升沿中断,代表spi通讯完毕。根据情况重置dma、spi状态,增加稳定性。

引脚定义

MISO—–PA6
MOSI—–PA7
SCK —–PB3
NSS —–PA4

相关代码

bsp_spi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#include "bsp_spi.h"

#define TX_BUFFER_SIZE 12
#define RX_BUFFER_SIZE 32


uint8_t txBuffer[TX_BUFFER_SIZE] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36,0x37};
uint8_t rxBuffer[RX_BUFFER_SIZE];
uint8_t SPI_DMA_Rx_flag = 0;
uint8_t SPI_DMA_Tx_flag = 0;
uint8_t SPI_IDLE_flag=0;
static void DMA_Configuration(void);
void SPI_FT4222H_Config(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;

// 使能GPIO时钟、SPI时钟、DMS时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

// 设置引脚复用
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); // MISO
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); // MOSI
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1); // SCK

// 配置SCK引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);

// 配置MISO引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 配置MOSI引脚
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 配置NS引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 配置SPI参数
SPI_StructInit(&SPI_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 0;
SPI_Init(SPI1, &SPI_InitStructure);

// 使能 SYSCFG 时钟 ,使用GPIO外部中断时必须使能SYSCFG时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
// 连接 EXTI 中断源 到key1引脚
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource4);
// 配置GPIO中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
// 配置抢占优先级:
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 配置子优先级:
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
// 使能中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 选择 EXTI 中断源
EXTI_InitStructure.EXTI_Line = EXTI_Line4;
// 中断模式
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
// 上升沿触发
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
// 使能中断/事件线
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);

/* 使能 FLASH_SPI */
SPI_Cmd(SPI1, ENABLE);
// 初始化DMA
DMA_Configuration();
}
static void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
// 发送DMA通道配置
DMA_DeInit(DMA2_Stream3);
DMA_InitStructure.DMA_Channel = DMA_Channel_3; // DMA通道
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&SPI1->DR); // 外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)txBuffer; // 内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // dma传输方向
DMA_InitStructure.DMA_BufferSize = TX_BUFFER_SIZE; // 设置DMA在传输时缓冲区的长度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 设置DMA的外设递增模式,一个外设
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 设置DMA的内存递增模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据字长
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; // 内存数据字长
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 设置DMA的传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // 设置DMA的优先级别
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // 指定如果FIFO模式或直接模式将用于指定的流 : 不使能FIFO模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; // 指定了FIFO阈值水平
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; // 指定的Burst转移配置内存传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 指定的Burst转移配置外围转移
DMA_Init(DMA2_Stream3, &DMA_InitStructure); // 配置DMA的通道

DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIF3); // 清除标志位
DMA_Cmd(DMA2_Stream3, DISABLE); // 关闭DMA
DMA_ITConfig(DMA2_Stream3, DMA_IT_TC, ENABLE); // 使能DMA中断

NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

// 接收DMA通道配置
DMA_DeInit(DMA2_Stream2);
DMA_InitStructure.DMA_Channel = DMA_Channel_3; // DMA通道
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&SPI1->DR); // 外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)rxBuffer; // 内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // dma传输方向
DMA_InitStructure.DMA_BufferSize = RX_BUFFER_SIZE; // 设置DMA在传输时缓冲区的长度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 设置DMA的外设递增模式,一个外设
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 设置DMA的内存递增模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据字长
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; // 内存数据字长
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 设置DMA的传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // 设置DMA的优先级别
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // 指定如果FIFO模式或直接模式将用于指定的流 : 不使能FIFO模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; // 指定了FIFO阈值水平
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; // 指定的Burst转移配置内存传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 指定的Burst转移配置外围转移
DMA_Init(DMA2_Stream2, &DMA_InitStructure); // 配置DMA的通道

DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2); // 清除标志位
DMA_Cmd(DMA2_Stream2, DISABLE); // 关闭DMA
DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE); // 使能DMA中断

NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

SPI_DMACmd(SPI1, SPI_DMAReq_Tx, ENABLE); // 采用DMA方式发送
SPI_DMACmd(SPI1, SPI_DMAReq_Rx, ENABLE); // 采用DMA方式发送

/*
注销这里,由片选信号控制dma开关
DMA_Cmd(DMA2_Stream2, ENABLE); // RX
DMA_Cmd(DMA2_Stream3, ENABLE); // TX
*/
}

void EXTI4_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line4) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line4);

DMA2_Stream2->CR &= ~(uint32_t)DMA_SxCR_EN;
DMA2_Stream3->CR &= ~(uint32_t)DMA_SxCR_EN;
SPI1->DR;
SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);
if ((SPI1->SR & 0x80) != 0)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
SPI1->CR2 |= SPI_DMAReq_Tx;
SPI1->CR2 |= SPI_DMAReq_Rx;
}
else
{
SPI1->CR1 |= SPI_CR1_SPE;
DMA2_Stream2->CR &= ~(uint32_t)DMA_SxCR_EN;
DMA2_Stream3->CR &= ~(uint32_t)DMA_SxCR_EN;
DMA2_Stream2->NDTR = RX_BUFFER_SIZE;
DMA2_Stream3->NDTR = TX_BUFFER_SIZE;
DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIF3);
DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
DMA2_Stream2->CR |= (uint32_t)DMA_SxCR_EN;
DMA2_Stream3->CR |= (uint32_t)DMA_SxCR_EN;
}
SPI_IDLE_flag = 1;
}
}

// DMA接收中断
void DMA2_Stream2_IRQHandler(void)
{
if (DMA_GetITStatus(DMA2_Stream2, DMA_IT_TCIF2) != RESET)
{
DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2); // 清除标志位
SPI_DMA_Rx_flag = 1;
}
}
// DMA发送中断
void DMA2_Stream3_IRQHandler(void)
{
if (DMA_GetITStatus(DMA2_Stream3, DMA_IT_TCIF3) != RESET)
{
DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIF3); // 清除标志位
SPI_DMA_Tx_flag = 1;
}
}
bsp_spi.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef __BSP_SPI_H
#define __BSP_SPI_H
#include "stm32f4xx.h"


extern uint8_t SPI_DMA_Rx_flag ;
extern uint8_t SPI_DMA_Tx_flag ;
extern uint8_t SPI_IDLE_flag;
void SPI_FT4222H_Config(void);



#endif

代码使用

main.c
1
2
3
4
5
6
7
8
9
10
11
12
int main(void)
{
....
SPI_FT4222H_Config();
while (1)
{
if(SPI_IDLE_flag)
{
//spi通讯完毕,对发送缓冲区和接收缓冲区进行处理....
}
}
}

代码下载