串口DMA

转载:https://www.cnblogs.com/viggogao/p/11175101.html

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;



typedef struct
{
	uint32_t periph_addr;       /*!< peripheral base address */
	uint32_t periph_width;      /*!< transfer data size of peripheral */
	uint32_t memory_addr;       /*!< memory base address */
	uint32_t memory_width;      /*!< transfer data size of memory */
	uint32_t number;            /*!< channel transfer number */
	uint32_t priority;          /*!< channel priority level */
	uint8_t periph_inc;         /*!< peripheral increasing mode */
	uint8_t memory_inc;         /*!< memory increasing mode */
	uint8_t direction;          /*!< channel data transfer direction */

} dma_parameter_struct;

大神将DMA形象的比喻成
快递员将快递投递在快递柜中,等待我(收货人)从快递柜中取快递-----DMA就是缓冲器,就是快递柜。
我在手机上下单寄快递,选择哪家快递,哪一个快递柜,存放什么物品,将快递进行寄出去。

DMA方式

例如:
(1)数据搬运,你要告诉CPU你搬的数据的源地址,目的地址,然后启动,完成一个字节搬运,要浪费很多个CPU 时钟,如果有多个字节,以上动作需要重复N次,此时,CPU完全被这些动作占用了。
(2)如果使用DMA,你只需要告诉DMA寄存器,你的源地址,目的地址,数据长度,动作类型(复制,异或等等),启动以后就可以丢给DMA处理,解放CPU了,差不多1个CLOCK,就能完成一个字节的操作。
所以,相比起来,如果数据不是很少的话,使用DMA能大大节约系统资源与时间。

DMA request pending
pending(挂起):网络处于挂起状态,指发送的请求是“进行中”的状态,但还没有接到服务端的响应。(这类似于debug模式下加断点,请求被阻止住一样)

STM32中DMA对应的通道图

DMA1

DMA2

编程

串口用DMA方式发送和接收,分以下几步:
1)串口初始化
2)DMA初始化
3)发送数据
4)接收数据

1)串口初始化

#define  DMASIZE 1024

// 配置串口一的发送和接收的GPIO口功能,以及中断
static void _uart1_gpio_init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA   |
					 RCC_APB2Periph_USART1  |
						 RCC_APB2Periph_AFIO, ENABLE) ;

	GPIOA->CRH&=0XFFFFF00F;
	GPIOA->CRH|=0X000008B0;//IO状态设置 10pin_上拉输入  9pin_推挽输出

	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	/* Configure USART1 Rx as input floating */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	/* Configure USART1 Tx as alternate function push-pull */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);


	/* Enable the USART1 Interrupt */
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	USART_ClearFlag(USART1, USART_FLAG_TC); /* 清发送完成标志,Transmission Complete flag */

	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);// 采用空闲中断,目的是在产生空闲中断时,说明接收或者发送已经结束,此时可以读取DMA中的数据了。
	//USART_ITConfig(USART1, USART_IT_TC, ENABLE);
	//USART_ITConfig(USART1, USART_IT_FE, ENABLE);
}
// 设置对应串口的波特率
static void _uart_setbaudrate(USART_TypeDef* USARTx,u32 value)
{
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate =value;
	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(USARTx, &USART_InitStructure);
	USART_Cmd(USARTx, ENABLE);
}

2)初始化DMA

u8 sendbuf[1024];
u8 receivebuf[1024];
static void _uart1_dma_configuration()
{
	DMA_InitTypeDef DMA_InitStructure;

	/* DMA1 Channel6 (triggered by USART1 Rx event) Config */
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 ,
						ENABLE);

	/* DMA1 Channel5 (triggered by USART1 Rx event) Config */
	DMA_DeInit(DMA1_Channel5);
	DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;// 初始化外设地址,相当于“哪家快递”
	DMA_InitStructure.DMA_MemoryBaseAddr =(u32)receivebuf;// 内存地址,相当于几号柜
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据来源,即为收快递
	DMA_InitStructure.DMA_BufferSize = DMASIZE ;// 缓存容量,即柜子大小
	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_InitStructure.DMA_Mode = DMA_Mode_Normal;// 正常模式,即满了就不在接收了,而不是循环存储
	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;// 优先级很高,对应快递就是加急
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 内存与外设通信,而非内存到内存
	DMA_Init(DMA1_Channel5, &DMA_InitStructure);// 把参数初始化,即拟好与快递公司的协议

	DMA_Cmd(DMA1_Channel5, ENABLE);// 启动DMA,即与快递公司签订合同,正式生效

	/* DMA1 Channel4 (triggered by USART1 Tx event) Config */
	DMA_DeInit(DMA1_Channel4);
	DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;  // 外设地址,串口1, 即发件的快递
	DMA_InitStructure.DMA_MemoryBaseAddr =(u32)sendbuf;// 发送内存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;// 外设为传送数据目的地,即发送数据,即快递是发件
	DMA_InitStructure.DMA_BufferSize = 0;  //发送长度为0,即未有快递需要发送
	DMA_Init(DMA1_Channel4, &DMA_InitStructure);//初始化

	USART_ITConfig(USART1, USART_IT_TC, ENABLE);// 使能串口发送完成中断
	USART_DMACmd(USART1, USART_DMAReq_Tx|USART_DMAReq_Rx, ENABLE);// 使能DMA串口发送和接受请求
}

3)数据发送

流程:串口数据发送,全部数据发送完毕之后,会产生一个发送中断,所以,发送数据分为两部分
A.发送数据
B.中断处理

posted on 2021-02-26 13:37  夏天师妹  阅读(152)  评论(0编辑  收藏  举报

导航