22.1 直接存储器访问
22.1.1 DMA功能框图讲解
DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能
是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,好
像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以
是 SRAM 或者是 FLASH。DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道,
DMA2 有 5 个通道,这里的通道可以理解为传输数据的一种管道。要注意的是 DMA2 只存
在于大容量的单片机中。
Memory:Flash Sram P:外设
DMA2:5个通道 存在于大容量 互联型
仲裁器:处理多个DMA请求
1.软件阶段:DMA_CCRx 通道优先级
2.硬件阶段:通道编号小的优先级大,DMA1的优先级高于DMA2的优先级
22.1.2 DMA相关库函数讲解
typedef struct
{
//外设地址
uint32_t DMA_PeripheralBaseAddr; /*!< Specifies the peripheral base address for DMAy Channelx. */
//存储器地址
uint32_t DMA_MemoryBaseAddr; /*!< Specifies the memory base address for DMAy Channelx. */
//传输方向
uint32_t DMA_DIR; /*!< Specifies if the peripheral is the source or destination.
This parameter can be a value of @ref DMA_data_transfer_direction */
//传输数目
uint32_t DMA_BufferSize; /*!< Specifies the buffer size, in data unit, of the specified Channel.
The data unit is equal to the configuration set in DMA_PeripheralDataSize
or DMA_MemoryDataSize members depending in the transfer direction. */
//外设地址增量模式
uint32_t DMA_PeripheralInc; /*!< Specifies whether the Peripheral address register is incremented or not.
This parameter can be a value of @ref DMA_peripheral_incremented_mode */
//存储器地址增量模式
uint32_t DMA_MemoryInc; /*!< Specifies whether the memory address register is incremented or not.
This parameter can be a value of @ref DMA_memory_incremented_mode */
//外设数据宽度
uint32_t DMA_PeripheralDataSize; /*!< Specifies the Peripheral data width.
This parameter can be a value of @ref DMA_peripheral_data_size */
//存储器数据宽度
uint32_t DMA_MemoryDataSize; /*!< Specifies the Memory data width.
This parameter can be a value of @ref DMA_memory_data_size */
//模式选择
uint32_t DMA_Mode; /*!< Specifies the operation mode of the DMAy Channelx.
This parameter can be a value of @ref DMA_circular_normal_mode.
@note: The circular buffer mode cannot be used if the memory-to-memory
data transfer is configured on the selected Channel */
//通道优先级
uint32_t DMA_Priority; /*!< Specifies the software priority for the DMAy Channelx.
This parameter can be a value of @ref DMA_priority_level */
//存储器到存储器模式
uint32_t DMA_M2M; /*!< Specifies if the DMAy Channelx will be used in memory-to-memory transfer.
This parameter can be a value of @ref DMA_memory_to_memory */
}DMA_InitTypeDef;
外设数据宽度和存储器数据宽度不对应
22.2 存储器到存储器代码讲解
22.2.1 M to M
把内部的FALASH(代码)数据传输到内部的SRAM(变量)
bsp_dma_mtp.h
#ifndef BSP_DMA_MTP_H
#define BSP_DMA_MTP_H
#include "stm32f10x.h"
#include "stdio.h"
// 串口工作参数宏定义
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
// 串口对应的DMA请求通道
#define USART_TX_DMA_CHANNEL DMA1_Channel4
#define USART_TX_FLAG_TC DMA1_FLAG_TC4
// 外设寄存器地址
#define USART_DR_ADDRESS (USART1_BASE+0x04)
// 一次发送的数据量
#define SENDBUFF_SIZE 5000
void USART_Config(void);
void USARTx_DMA_Config(void);
#endif /*"BSP_DMA_MTP_H"*/
bsp_dma_mtp.c
#include "bsp_dma_mtp.h"
uint8_t SendBuff[SENDBUFF_SIZE];
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
//USART->P
void USARTx_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
// 开启DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 设置DMA源地址:串口数据寄存器地址*/
DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
// 内存地址(要传输的变量的指针)
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
// 方向:从内存到外设
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
// 传输大小
DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
// 外设地址不增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// 内存地址自增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// 外设数据单位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
// 内存数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
// DMA模式,一次或者循环模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// 优先级:中
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
// 禁止内存到内存的传输
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
// 配置DMA通道
DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);
// 清除信道标志位
DMA_ClearFlag(USART_TX_FLAG_TC);
// 使能DMA
DMA_Cmd(USART_TX_DMA_CHANNEL,ENABLE);
}
main.c
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_dma_mtp.h"
extern uint8_t SendBuff[SENDBUFF_SIZE];
void Delay(uint32_t count)
{
for(;count!=0; count--);
}
int main()
{
uint16_t i = 0;
//来到这个系统时钟被配置成72M
LED_GPIO_Config();
USART_Config();
for(i = 0;i<SENDBUFF_SIZE;i++)
{
SendBuff[i] = 'P';
}
USARTx_DMA_Config();
USART_DMACmd(DEBUG_USARTx,USART_DMAReq_Tx,ENABLE);
while(1)
{
LED_R(OFF);
//GPIO_SetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN);
Delay(0xFFFFF);
//GPIO_ResetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN);
LED_R(ON);
Delay(0xFFFFF);
}
}