b2b網(wǎng)站建設(shè)技術(shù)百度推廣創(chuàng)意范例
什么是DMA?
DMA(Direct Memory Access,直接存儲(chǔ)器訪問(wèn))
提供在外設(shè)與內(nèi)存、存儲(chǔ)器和存儲(chǔ)器、外設(shè)與外設(shè)之間的高速數(shù)據(jù)傳輸使用。它允許不同速度的硬件裝置來(lái)溝通,而不需要依賴于CPU,在這個(gè)時(shí)間中,CPU對(duì)于內(nèi)存的工作來(lái)說(shuō)就無(wú)法使用。
簡(jiǎn)單描述:就是一個(gè)數(shù)據(jù)搬運(yùn)工!!
DMA的意義
代替 CPU 搬運(yùn)數(shù)據(jù),為 CPU 減負(fù)。
- 數(shù)據(jù)搬運(yùn)的工作比較耗時(shí)間;
- 數(shù)據(jù)搬運(yùn)工作時(shí)效要求高(有數(shù)據(jù)來(lái)就要搬走);
- 沒(méi)啥技術(shù)含量(CPU 節(jié)約出來(lái)的時(shí)間可以處理更重要的事)。
搬運(yùn)什么數(shù)據(jù)?
這里的外設(shè)指的是spi、usart、iic、adc 等基于APB1
、APB2或AHB時(shí)鐘的外設(shè),而這里的存儲(chǔ)器包括自身的閃存(flash)或者內(nèi)存(SRAM)以及外設(shè)的存儲(chǔ)設(shè)備都可以作為訪問(wèn)地源或者目的。
三種搬運(yùn)方式:
- 存儲(chǔ)器→存儲(chǔ)器(例如:復(fù)制某特別大的數(shù)據(jù)buf)
- 存儲(chǔ)器→外設(shè) (例如:將某數(shù)據(jù)buf寫入串口TDR寄存器)
- 外設(shè)→存儲(chǔ)器 (例如:將串口RDR寄存器寫入某數(shù)據(jù)buf)
存儲(chǔ)器→存儲(chǔ)器
存儲(chǔ)器→外設(shè)
外設(shè)→存儲(chǔ)器
DMA 控制器
STM32F103有2個(gè) DMA 控制器,DMA1有7個(gè)通道,DMA2有5個(gè)通道。
一個(gè)通道每次只能搬運(yùn)一個(gè)外設(shè)的數(shù)據(jù)!! 如果同時(shí)有多個(gè)外設(shè)的 DMA 請(qǐng)求,則按照優(yōu)先級(jí)進(jìn)行響應(yīng)。
DMA1有7個(gè)通道:每個(gè)通道都有其能夠搬運(yùn)的外設(shè)
DMA2有5個(gè)通道
DMA及通道的優(yōu)先級(jí)
優(yōu)先級(jí)管理采用軟件+硬件:
-
軟件: 每個(gè)通道的優(yōu)先級(jí)可以在DMA_CCRx寄存器中設(shè)置,有4個(gè)等級(jí) 最高級(jí)>高級(jí)>中級(jí)>低級(jí)
-
硬件: 如果2個(gè)請(qǐng)求,它們的軟件優(yōu)先級(jí)相同,則較低編號(hào)的通道比較高編號(hào)的通道有較高的優(yōu)先權(quán)。 比如:如果軟件優(yōu)先級(jí)相同,通道2優(yōu)先于通道4
DMA傳輸方式
- DMA_Mode_Normal(正常模式)
一次DMA數(shù)據(jù)傳輸完后,停止DMA傳送 ,也就是只傳輸一次 - DMA_Mode_Circular(循環(huán)傳輸模式)
當(dāng)傳輸結(jié)束時(shí),硬件自動(dòng)會(huì)將傳輸數(shù)據(jù)量寄存器進(jìn)行重裝,進(jìn)行下一輪的數(shù)據(jù)傳輸。 也就是多次傳輸模式
指針遞增模式
外設(shè)和存儲(chǔ)器指針在每次傳輸后可以自動(dòng)向后遞增或保持常量。當(dāng)設(shè)置為增量模式時(shí),下一個(gè)要傳輸?shù)牡刂穼⑹乔耙粋€(gè)地址加上增量值。
實(shí)驗(yàn)一、內(nèi)存到內(nèi)存搬運(yùn)
實(shí)驗(yàn)要求
使用DMA的方式將數(shù)組A的內(nèi)容復(fù)制到數(shù)組B中,搬運(yùn)完之后將數(shù)組B的內(nèi)容打印到屏幕。
CubeMX配置
重定向 printf 的話記得將下面這個(gè)勾打開:
用到的庫(kù)函數(shù)
- HAL_DMA_Start
> HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
參數(shù)一:DMA_HandleTypeDef *hdma,DMA通道句柄
參數(shù)二:uint32_t SrcAddress,源內(nèi)存地址
參數(shù)三:uint32_t DstAddress,目標(biāo)內(nèi)存地址
參數(shù)四:uint32_t DataLength,傳輸數(shù)據(jù)長(zhǎng)度。注意:需要乘以sizeof(uint32_t)返回值:HAL_StatusTypeDef,HAL狀態(tài)(OK,busy,ERROR,TIMEOUT)
- __HAL_DMA_GET_FLAG
#define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__) (DMA1->ISR & (__FLAG__))
參數(shù)一:HANDLE,DMA通道句柄
參數(shù)二:FLAG,數(shù)據(jù)傳輸標(biāo)志。DMA_FLAG_TCx表示數(shù)據(jù)傳輸完成標(biāo)志
返回值:FLAG的值(SET/RESET)
代碼實(shí)現(xiàn)
- 開啟數(shù)據(jù)傳輸
- 等待數(shù)據(jù)傳輸完成
- 打印數(shù)組內(nèi)容
#define BUF_SIZE 16// 源數(shù)組uint32_t srcBuf[BUF_SIZE] = {0x00000000,0x11111111,0x22222222,0x33333333,0x44444444,0x55555555,0x66666666,0x77777777,0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF};// 目標(biāo)數(shù)組uint32_t desBuf[BUF_SIZE];int fputc(int ch, FILE *f)
{ unsigned char temp[1]={ch};HAL_UART_Transmit(&huart1,temp,1,0xffff); return ch;
}main函數(shù)里:// 開啟數(shù)據(jù)傳輸HAL_DMA_Start(&hdma_memtomem_dma1_channel1, (uint32_t)srcBuf, (uint32_t)desBuf, sizeof(uint32_t) * BUF_SIZE);// 等待數(shù)據(jù)傳輸完成while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1) == RESET);// 打印數(shù)組內(nèi)容for (i = 0; i < BUF_SIZE; i++)printf("Buf[%d] = %X\r\n", i, desBuf[i]);
實(shí)驗(yàn)二、內(nèi)存到外設(shè)搬運(yùn)
實(shí)驗(yàn)要求:
使用DMA的方式將內(nèi)存數(shù)據(jù)搬運(yùn)到串口1發(fā)送寄存器,同時(shí)閃爍LED1。
CubeMX配置
DMA配置
用到的庫(kù)函數(shù)
HAL_UART_Transmit_DMA
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
參數(shù)一:UART_HandleTypeDef *huart,串口句柄
參數(shù)二:uint8_t *pData,待發(fā)送數(shù)據(jù)首地址
參數(shù)三:uint16_t Size,待發(fā)送數(shù)據(jù)長(zhǎng)度
返回值:HAL_StatusTypeDef,HAL狀態(tài)(OK,busy,ERROR,TIMEOUT)
代碼實(shí)現(xiàn)
- 準(zhǔn)備數(shù)據(jù)
- 將數(shù)據(jù)通過(guò)串口DMA發(fā)送
#define BUF_SIZE 1000// 待發(fā)送的數(shù)據(jù)unsigned char sendBuf[BUF_SIZE];main函數(shù)里// 準(zhǔn)備數(shù)據(jù)for (i = 0; i < BUF_SIZE; i++)sendBuf[i] = 'A';// 將數(shù)據(jù)通過(guò)串口DMA發(fā)送HAL_UART_Transmit_DMA(&huart1, sendBuf, BUF_SIZE);while (1)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);HAL_Delay(100);
}
實(shí)驗(yàn)三、外設(shè)到內(nèi)存搬運(yùn)
實(shí)驗(yàn)要求
使用DMA的方式將串口接收緩存寄存器的值搬運(yùn)到內(nèi)存中,同時(shí)閃爍LED1。
CubeMX配置
DMA配置:
串口中斷配置
用到的庫(kù)函數(shù)
- __HAL_UART_ENABLE
#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U)
== UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) &
UART_IT_MASK)): \
(((__INTERRUPT__) >> 28U) == UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) &
UART_IT_MASK)): \
((__HANDLE__)->Instance->CR3 |=((__INTERRUPT__) & UART_IT_MASK)))
參數(shù)一:HANDLE,串口句柄
參數(shù)二:INTERRUPT,需要使能的中斷
返回值:無(wú)
- HAL_UART_Receive_DMA
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
參數(shù)一:UART_HandleTypeDef *huart,串口句柄
參數(shù)二:uint8_t *pData,接收緩存首地址
參數(shù)三:uint16_t Size,接收緩存長(zhǎng)度
返回值:HAL_StatusTypeDef,HAL狀態(tài)(OK,busy,ERROR,TIMEOUT)
- __HAL_UART_GET_FLAG
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR &
(__FLAG__)) == (__FLAG__))
參數(shù)一:HANDLE,串口句柄
參數(shù)二:FLAG,需要查看的FLAG返回值:FLAG的值
- __HAL_UART_CLEAR_IDLEFLAG
#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
參數(shù)一:HANDLE,串口句柄
返回值:無(wú)
- HAL_UART_DMAStop
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
參數(shù)一:UART_HandleTypeDef *huart,串口句柄
返回值:HAL_StatusTypeDef,HAL狀態(tài)(OK,busy,ERROR,TIMEOUT)
- __HAL_DMA_GET_COUNTER
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)
參數(shù)一:HANDLE,串口句柄
返回值:未傳輸數(shù)據(jù)大小
代碼實(shí)現(xiàn)
如何判斷串口接收是否完成?如何知道串口收到數(shù)據(jù)的長(zhǎng)度?
使用串口空閑中斷(IDLE)!
- 串口空閑時(shí),觸發(fā)空閑中斷;
- 空閑中斷標(biāo)志位由硬件置1,軟件清零
利用串口空閑中斷,可以用如下流程實(shí)現(xiàn)DMA控制的任意長(zhǎng)數(shù)據(jù)接收:
使能IDLE空閑中斷;
使能DMA接收中斷;
收到串口接收中斷,DMA不斷傳輸數(shù)據(jù)到緩沖區(qū);
一幀數(shù)據(jù)接收完畢,串口暫時(shí)空閑,觸發(fā)串口空閑中斷;
在中斷服務(wù)函數(shù)中,清除中斷標(biāo)志位,關(guān)閉DMA傳輸(防止干擾);
計(jì)算剛才收到了多少個(gè)字節(jié)的數(shù)據(jù)。
處理緩沖區(qū)數(shù)據(jù),開啟DMA傳輸,開始下一幀接收。
有三個(gè)文件需要修改:
main.c
uint8_t rcvBuf[BUF_SIZE]; // 接收數(shù)據(jù)緩存數(shù)組uint8_t rcvLen = 0; // 接收一幀數(shù)據(jù)的長(zhǎng)度__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能IDLE空閑中斷HAL_UART_Receive_DMA(&huart1,rcvBuf,100); // 使能DMA接收中斷while (1)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);HAL_Delay(300);
}
main.h
#define BUF_SIZE 100
stm32f1xx_it.c
extern uint8_t rcvBuf[BUF_SIZE];extern uint8_t rcvLen;void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 */if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET)) // 判斷IDLE標(biāo)志位是否被置位{ __HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除標(biāo)志位HAL_UART_DMAStop(&huart1); // 停止DMA傳輸,防止干擾uint8_t temp=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx); rcvLen = BUF_SIZE - temp; //計(jì)算數(shù)據(jù)長(zhǎng)度HAL_UART_Transmit_DMA(&huart1, rcvBuf, rcvLen);//發(fā)送數(shù)據(jù)HAL_UART_Receive_DMA(&huart1, rcvBuf, BUF_SIZE);//開啟DMA}/* USER CODE END USART1_IRQn 1 */}