本帖最后由 Afanx 于 2023-1-7 14:52 编辑
N32G45x手册虽然说DMA支持三种模式:支持内存到内存、内存到外设和外设到内存三种传输类型。
但也可以实现外设到外设的传输效果。
比如使用SPI1读取Flash数据,再通过SPI2发送给SPI屏幕。中间不需要SRAM缓存,读取后直接发送。
DMA只需要将MemAddr配置为SPI2->DAT地址就行:
DMA_InitStruct.PeriphAddr = (uint32_t)(&SPI1->DAT);
DMA_InitStruct.MemAddr = (uint32_t)(&SPI2->DAT);
SPI1与SPI2都是主机,SPI1使用2个DMA通道分别是Tx和Rx,Tx用于产生时序。
SPI2作为主机,但是“被动”发送,由SPI1的Rx DMA填入数据,自动发送出去。
以下是DMA配置部分代码:
void Test(void)
{
uint32_t nullDat = 0xFFFFFFFF;
DMA_InitType DMA_InitStruct;
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA1, ENABLE);
DMA_DeInit(DMA1_CH2);
DMA_DeInit(DMA1_CH3);
/* DMA1_CH3,SPI1_Tx,用来产生时序,读取数据 */
/* 这里读取100个数据停止,配置Normal模式 */
DMA_InitStruct.PeriphAddr = (uint32_t)(&SPI1->DAT);
DMA_InitStruct.MemAddr = (uint32_t)(&nullDat);
DMA_InitStruct.Direction = DMA_DIR_PERIPH_DST;
DMA_InitStruct.BufSize = 100;
DMA_InitStruct.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStruct.DMA_MemoryInc = DMA_MEM_INC_DISABLE;
DMA_InitStruct.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
DMA_InitStruct.MemDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.CircularMode = DMA_MODE_NORMAL;
DMA_InitStruct.Mem2Mem = DMA_M2M_DISABLE;
DMA_InitStruct.Priority = DMA_PRIORITY_HIGH;
DMA_Init(DMA1_CH3, &DMA_InitStruct);
DMA_RequestRemap(DMA1_REMAP_SPI1_TX, DMA1, DMA1_CH3, ENABLE); // Config DMA Request
/* DMA1_CH2,SPI1_Rx,用于接收数据,存放到SPI2数据寄存器中,自动发送出去 */
/* 只要接收到数据就存入SPI2->DAT,配置circural模式 */
DMA_InitStruct.PeriphAddr = (uint32_t)(&SPI1->DAT);
DMA_InitStruct.MemAddr = (uint32_t)(&SPI2->DAT);
DMA_InitStruct.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStruct.BufSize = 1;
DMA_InitStruct.CircularMode = DMA_MODE_CIRCULAR;
DMA_Init(DMA1_CH2, &DMA_InitStruct);
DMA_RequestRemap(DMA1_REMAP_SPI1_RX, DMA1, DMA1_CH2, ENABLE); // Config DMA Request
/* 开启片选 */
GPIO_WriteBit(GPIOA, GPIO_PIN_4, Bit_RESET); // Enable SPI1 CS
GPIO_WriteBit(GPIOB, GPIO_PIN_12, Bit_RESET); // Enable SPI2 CS
/* 使能外设请求 */
SPI_I2S_EnableDma(SPI1, SPI_I2S_DMA_RX, ENABLE); // Enable Request
SPI_I2S_EnableDma(SPI1, SPI_I2S_DMA_TX, ENABLE); // Enable Request
/* 打开DMA(先打开接收,后打开发送) */
DMA_EnableChannel(DMA1_CH2, ENABLE); // Enable DMA , SPI1 Rx
DMA_EnableChannel(DMA1_CH3, ENABLE); // Enable DMA , SPI1 Tx
/* 等待传输完成 */
DelayMs(10);
/* 关闭片选 */
GPIO_WriteBit(GPIOA, GPIO_PIN_4, Bit_SET); // Disable SPI1 CS
GPIO_WriteBit(GPIOB, GPIO_PIN_12, Bit_SET); // Disable SPI2 CS
}
传输效果:
注意,发送速度>=接收速度,因此使用不同外设需要调整好波特率。
从上面看来,其实对于DMA来说,不区分外设和内存,都是“地址”,只区分“源地址”和“目标地址”。
只要地址合法,可以实现任意地址到任意地址的传输,甚至包括外挂存储器件的内存映射地址。
按照这种思路,DMA的工作原理也很简单,可以看成一个简单的数据泵:给一个“请求信号”搬一个数据。
这个请求信号也不局限于当前操作的外设,也可以是其他外设产生的信号。比如使用DMA搬运SPI数据,但请求可以来自定时器。
如果将原来的请求更改为下面一条代码:
// DMA_RequestRemap(DMA1_REMAP_SPI1_TX, DMA1, DMA1_CH3, ENABLE); // 请求来自SPI DAT 发送缓存为空
DMA_RequestRemap(DMA1_REMAP_TIM1_UP, DMA1, DMA1_CH3, ENABLE); // 请求来自TIM Update事件
同时开启对应外设的DMA请求:
// SPI_I2S_EnableDma(SPI1, SPI_I2S_DMA_TX, ENABLE); // 打开 SPI 的DMA请求
TIM_EnableDma(TIM1, TIM_DMA_UPDATE, ENABLE); //打开 TIM1 的DMA请求
此时,会根据TIM Update事件(10us),产生一次DMA操作。达到下面的效果:
|