平台:STM32F103
功能:SPI工作在主模式时用DMA方式接收数据,在需要从SPI FLASH中读出大量数据时,可以在读数据的同时释放CPU的占用,提高系统利用效率
DMA不可以自动帮忙发送时钟信号,但是如果让spi发送数据并且使用DMA方式,spi可以帮忙自动发送时钟信号。基于这样的理论, 直接上代码(参考:http://blog.sina.com.cn/s/blog_9d8d362b0101cukb.html)
#if (ON == SPI_DMA_ON)
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
void SPI_DMA_Config(u32 MBAr)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
//接收DMA配置
DMA_Cmd(SPI_RX_DMA_CH, DISABLE); //DMA关闭后才能配置
DMA_DeInit(SPI_RX_DMA_CH); //将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&SPI_FLASH_NUM->DR);//DMA外设ADC基地址
DMA_InitStructure.DMA_MemoryBaseAddr = MBAr; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,外设->内存
DMA_InitStructure.DMA_BufferSize = 0; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
SPI_I2S_ReceiveData(SPI_FLASH_NUM); //空读一次数据
/*传输完成中断使能*/
DMA_ITConfig(SPI_RX_DMA_CH, DMA_IT_TC, ENABLE);
DMA_Init(SPI_RX_DMA_CH, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
//发送DMA配置
DMA_Cmd(SPI_TX_DMA_CH, DISABLE); //DMA关闭后才能配置
DMA_DeInit(SPI_TX_DMA_CH); //将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设
DMA_InitStructure.DMA_BufferSize = 0; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //内存地址寄存器不用递增,因为只是为了发送时钟
DMA_Init(SPI_TX_DMA_CH, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
}
//开启一次DMA传输
void SPI_DMA_Enable(u16 len)
{
DMA_Cmd(SPI_RX_DMA_CH, DISABLE ); //关闭 指示的通道
DMA_SetCurrDataCounter(SPI_RX_DMA_CH,len); //DMA通道的DMA缓存的大小
DMA_Cmd(SPI_RX_DMA_CH, ENABLE);
DMA_Cmd(SPI_TX_DMA_CH, DISABLE ); //关闭 指示的通道
DMA_SetCurrDataCounter(SPI_TX_DMA_CH,len); //DMA通道的DMA缓存的大小
DMA_Cmd(SPI_TX_DMA_CH, ENABLE); //开启DMA传输
}
void SPI_FLASH_Data_Read_By_DMA(uint8_t *s,const uint32_t Add,const uint16_t Date_Length)
{
uint32_t Add_24;
uint32_t n;
uint8_t Shift_Bit,Temp;
//重新配置DMA通道
while(DMA_GetCurrDataCounter(SPI_RX_DMA_CH)!=0); //等待上一次通道传输完成
SPI_DMA_Config((u32)s);
ErrorFlashStatus = ERROR_OK; //开始一次新的读操作,错误状态复位
/*SPI_FLASH_NUM的片选有效*/
SPI_FLASH_Enable;
/*地址有效位数为24,屏蔽高12位*/
Add_24 = Add & 0x00FFFFFF;
/*发送读数据指令*/
SPI_FLASH_Byte_Send(READ);
/*24位地址要分三次发送*/
for(n=3;n>0;n--)
{
Shift_Bit = (n-1)*8;
Temp = (uint8_t)(0xFF & (Add_24 >> Shift_Bit));
SPI_FLASH_Byte_Send(Temp);
}
/*开始读数据*/
SPI_DMA_Enable(Date_Length);
/*结束读操作,SPI_FLASH_NUM片选无效,放在DAM中断中完成*/
}
/*
* 函数名:void DMA1_Channel1_IRQHandler(void)
* 描述 :DMA1中断服务函数
* 输入 :无
* 输出 :无
*/
void DMA1_Channel2_IRQHandler(void)
{
/*结束读操作,SPI_FLASH_NUM片选无效*/
SPI_FLASH_Disable;
/* 清除 DMA1 Channel2 transfer complete 中断标志 */
DMA_ClearITPendingBit(DMA1_IT_TC2);
DMA_Cmd(SPI_RX_DMA_CH, DISABLE );
}
#endif