STM32 使用DMA读写FLASH数据需要注意以下几点:
1.SPI全双工模式下,无论读写FLASH数据均需要同时使能RX/TX DMA。
2.写数据时回读数据应当丢弃,读数据时应当发送0xff来启动SPI读周期。
3.使用DMA时应当使能DMA Stream,然后再使能SPI DMA请求。
以下为测试代码,为省事,没启动串口提供打印信息,可在读出FLASH数据后通过断点查看数据。
#include <stdbool.h>
#include <stm32f4xx_dma.h>
#include <stm32f4xx_gpio.h>
#include <stm32f4xx_spi.h>#define FLASH_PAGE_SIZE 256static volatile int g_rx_flag = 0;
static volatile int g_tx_flag = 0;static void spi_wait_rx(void)
{while(!g_rx_flag);g_rx_flag = 0;
}static void spi_post_rx(void)
{g_rx_flag = 1;
}static void spi_wait_tx(void)
{while(!g_tx_flag);g_tx_flag = 0;
}static void spi_post_tx(void)
{g_tx_flag = 1;
}static void spi_do_delay(uint16_t n)
{ uint16_t t = n;while (-- t);
}uint8_t spi_byte_transfer(uint8_t Dat)
{while ((SPI3->SR & SPI_I2S_FLAG_TXE) == 0);SPI3->DR = Dat;while ((SPI3->SR & SPI_I2S_FLAG_RXNE) == 0);return ((uint8_t)(SPI3->DR));
}uint8_t spi_readbyte(void)
{return spi_byte_transfer(0xA5);
}/* 使用 GPIO6 作为 CS 信号*/
static void spi_chip_select(bool select)
{spi_do_delay(5);if (select == true){GPIOB->BSRRH = GPIO_Pin_6;}else{GPIOB->BSRRL = GPIO_Pin_6;}spi_do_delay(5);
}void flash_wait_write(void)
{uint8_t status = 0;spi_chip_select(true);spi_byte_transfer(0x05);do{status = spi_byte_transfer(0xA5);} while (status & 0x01);spi_chip_select(false);
}void flash_enable_write(void)
{spi_chip_select(true);spi_byte_transfer(0x06);spi_chip_select(false);
}void flash_write_status(uint8_t value)
{flash_enable_write();spi_chip_select(true);spi_byte_transfer(0x01);spi_byte_transfer(value);spi_chip_select(false);flash_wait_write();
}void flash_disable_write(void)
{flash_write_status(0);
}uint32_t flash_read_identify(void)
{uint8_t byte0 = 0, byte1 = 0, byte2 = 0;spi_chip_select(true);spi_byte_transfer(0x9F);byte0 = spi_byte_transfer(0xA5);byte1 = spi_byte_transfer(0xA5);byte2 = spi_byte_transfer(0xA5);spi_chip_select(false);return ((byte0 << 16) | (byte1 << 8) | (byte2 << 0));
}void flash_erase_sector(uint32_t SectorAddr)
{flash_enable_write();spi_chip_select(true);spi_byte_transfer(0x20);spi_byte_transfer((SectorAddr >> 16) & 0xFF);spi_byte_transfer((SectorAddr >> 8) & 0xFF);spi_byte_transfer((SectorAddr >> 0) & 0xFF);spi_chip_select(false);flash_wait_write();
}/*** 启动SPI DMA通道* buffer 缓冲区指针* length 缓冲区数据长度,* 如果 isread = true 表示需要从 flash 中读取多少个字节。* 如果 isread = false 表示需要向 flash 写入多少个字节。* isread true = 读,false = 写* */
void flash_start_dma(uint8_t *buffer, uint32_t length, uint8_t isread)
{uint32_t temp = 0xffffffff;DMA_InitTypeDef DMA_InitStructure;DMA_StructInit(&DMA_InitStructure);DMA_DeInit(DMA1_Stream0);DMA_DeInit(DMA1_Stream7);while(DMA_GetCmdStatus(DMA1_Stream0) != DISABLE);while(DMA_GetCmdStatus(DMA1_Stream7) != DISABLE);/* DMA1 channel 0 stream0 --- 配置 SPI3-RX DMA */DMA_InitStructure.DMA_Channel = DMA_Channel_0;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI3->DR;if (isread == true){/* 读方式下回读数据放到 buffer 中*/DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buffer;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;}else{/* 写方式下回读数据丢弃 */DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&temp;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;}DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;DMA_InitStructure.DMA_BufferSize = length;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA1_Stream0, &DMA_InitStructure);/* DMA1 channel 0 stream7 --- 配置 SPI3-TX DMA */DMA_StructInit(&DMA_InitStructure);DMA_InitStructure.DMA_Channel = DMA_Channel_0;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI3->DR;if (isread == true){/* 读方式下发送 0xff */DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&temp;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;}else{/* 写方式下发送 buffer 数据*/DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buffer;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;}DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;DMA_InitStructure.DMA_BufferSize = length; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_Priority = DMA_Priority_Low;DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA1_Stream7, &DMA_InitStructure);/* 1.开启 DMA 数据流传输完成中断 */DMA_ITConfig(DMA1_Stream0, DMA_IT_TC, ENABLE);DMA_ITConfig(DMA1_Stream7, DMA_IT_TC, ENABLE);/* 2.使能 DMA 数据流 */DMA_Cmd(DMA1_Stream0, ENABLE);DMA_Cmd(DMA1_Stream7, ENABLE);/* 3.使能 SPI DMA 请求 */SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx|SPI_I2S_DMAReq_Tx, ENABLE);/* 5.等待 DMA 传输完成 */spi_wait_tx();spi_wait_rx();
}void DMA1_Stream7_IRQHandler(void)
{if (DMA_GetFlagStatus(DMA1_Stream7, DMA_FLAG_TCIF7) != RESET){/* 1.清中断标志 */DMA_ClearFlag(DMA1_Stream7, DMA_FLAG_TCIF7);/* 2.禁能 DMA 传输完成中断 */DMA_ITConfig(DMA1_Stream7, DMA_IT_TC, DISABLE);/* 3.禁能 SPI TX DMA 请求 */SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, DISABLE);/* 4.禁能 DMA Stream */DMA_Cmd(DMA1_Stream7, DISABLE);/* 5.通知应用程序 TX 传输完成 */spi_post_tx();}
}void DMA1_Stream0_IRQHandler(void)
{if (DMA_GetFlagStatus(DMA1_Stream0, DMA_FLAG_TCIF0) != RESET){/* 1.清中断标志 */DMA_ClearFlag(DMA1_Stream0, DMA_FLAG_TCIF0);/* 2.禁能 DMA 传输完成中断 */DMA_ITConfig(DMA1_Stream0, DMA_IT_TC, DISABLE);/* 3.禁能 SPI TX DMA 请求 */SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx, DISABLE);/* 4.禁能 DMA Stream */DMA_Cmd(DMA1_Stream0, DISABLE); /* 6.通知应用程序 RX 传输完成 */spi_post_rx();}
}void flash_write_page(uint8_t* buffer, uint32_t addr)
{flash_enable_write();spi_chip_select(true);spi_byte_transfer(0x02);spi_byte_transfer((addr >> 16) & 0xFF);spi_byte_transfer((addr >> 8) & 0xFF);spi_byte_transfer((addr >> 0) & 0xFF);flash_start_dma(buffer, FLASH_PAGE_SIZE, false);spi_chip_select(false);flash_wait_write();
}void flash_read_page(uint8_t *buffer, uint32_t addr)
{spi_chip_select(true);spi_byte_transfer(0x03);spi_byte_transfer((addr >> 16) & 0xFF);spi_byte_transfer((addr >> 8) & 0xFF);spi_byte_transfer((addr >> 0) & 0xFF);flash_start_dma(buffer, FLASH_PAGE_SIZE, true);spi_chip_select(false);
}void flash_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);SPI_InitTypeDef SPI_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI3);GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI3);GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI3);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_4;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_Init(GPIOB, &GPIO_InitStructure);SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI3, &SPI_InitStructure);SPI_Cmd(SPI3, ENABLE);NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream7_IRQn;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream0_IRQn;NVIC_Init(&NVIC_InitStructure);spi_chip_select(false);
}uint8_t wbuffer[512];
uint8_t rbuffer[512];int main(void)
{int i = 0;flash_init();/* 初始化测试数据 */for (i = 0; i < 512; i++){wbuffer[i] = (uint8_t)(i+9);}flash_erase_sector(0);flash_read_page(rbuffer, 0); /* 在此处打断点查看数据 */flash_write_page(wbuffer, 0);flash_read_page(rbuffer, 0); /* 在此处打断点查看数据 */while(1);
}