DMA簡(jiǎn)介
DMA全稱為Direct Memory Access,即直接存儲(chǔ)器訪問。在進(jìn)行DMA傳輸前,CPU將總線控制權(quán)交給DMA,通過共享系統(tǒng)總線,實(shí)現(xiàn)無(wú)需CPU參與的快速數(shù)據(jù)傳輸,能夠直接將數(shù)據(jù)從一個(gè)地址空間復(fù)制到另一個(gè)地址空間,DMA在數(shù)據(jù)傳輸結(jié)束后將總線控制權(quán)交回CPU。
MM32F0140內(nèi)置5路通用DMA,可以管理設(shè)備到存儲(chǔ)器、存儲(chǔ)器到設(shè)備與存儲(chǔ)器到存儲(chǔ)器的數(shù)據(jù)傳輸,每個(gè)通道都有專門的硬件DMA請(qǐng)求邏輯,也可以通過軟件配置來(lái)觸發(fā)每個(gè)通道。
MM32F0140的DMA模塊所支持的外設(shè)類型包括UART、I2C、SPI、ADC與通用、高級(jí)和基礎(chǔ)定時(shí)器,當(dāng)DMA從外設(shè)產(chǎn)生的請(qǐng)求通過邏輯或輸入DMA控制器時(shí),為避免沖突,在一個(gè)通道中,同時(shí)只能有一個(gè)外設(shè)DMA請(qǐng)求有效,詳細(xì)各通道DMA請(qǐng)求如圖1所示。
圖1.各通道DMA外設(shè)請(qǐng)求
DMA配置
DMA的配置涉及到傳輸模式、數(shù)據(jù)寬度、外設(shè)地址、存儲(chǔ)器地址、通道優(yōu)先級(jí)、數(shù)據(jù)傳輸數(shù)量、中斷使能、自動(dòng)重裝載及指針增量。
傳輸模式
存儲(chǔ)器到外設(shè)模式
配置DMA_CCRx寄存器(x由1 ~ 7)的DIR位選擇傳輸方向,該位置1,傳輸方向?yàn)閺拇鎯?chǔ)器讀;DMA_CCRx寄存器的MEM2MEM位置0,關(guān)閉存儲(chǔ)器到存儲(chǔ)器模式。
外設(shè)到存儲(chǔ)器模式
配置DMA_CCRx寄存器的DIR位選擇傳輸方向,該位置0,傳輸方向?yàn)閺耐庠O(shè)讀;DMA_CCRx寄存器的MEM2MEM位置0,關(guān)閉存儲(chǔ)器到存儲(chǔ)器模式。
存儲(chǔ)器到存儲(chǔ)器模式
數(shù)據(jù)傳輸方向?yàn)橥庠O(shè)地址到存儲(chǔ)器地址:DMA_CCRx寄存器的MEM2MEM位置1,使能存儲(chǔ)器到存儲(chǔ)器模式;DIR位置0,從外設(shè)讀。
數(shù)據(jù)傳輸方向?yàn)榇鎯?chǔ)器地址到外設(shè)地址:DMA_CCRx寄存器的MEM2MEM位置1,使能存儲(chǔ)器到存儲(chǔ)器模式;DIR位置1,從存儲(chǔ)器讀。
注意,存儲(chǔ)器到存儲(chǔ)器模式不能與循環(huán)模式同時(shí)使用。
循環(huán)模式
若需要循環(huán)讀寫緩沖區(qū)或進(jìn)行連續(xù)的數(shù)據(jù)傳輸,則可以進(jìn)入循環(huán)模式。配置DMA通道x配置寄存器(DMA_CCRx)中的CIRC位為1,使能循環(huán)模式。在循環(huán)模式下,若DMA傳輸數(shù)量遞減為0,則重新加載先前配置的數(shù)值,繼續(xù)進(jìn)行DMA數(shù)據(jù)傳輸。
自動(dòng)重新加載
DMA通道x配置寄存器(DMA_CCRx)的ARE位控制自動(dòng)重裝載,若ARE位置1,則使能自動(dòng)重裝載傳輸數(shù)量,當(dāng)DMA通道x傳輸數(shù)量寄存器(DMA_CNDTRx)中數(shù)值為0時(shí),會(huì)自動(dòng)將DMA_CNDTRx寄存器中的值加載為之前配置的數(shù)值;若ARE位置0,則禁止自動(dòng)重裝載傳輸數(shù)量。
數(shù)據(jù)寬度
DMA的數(shù)據(jù)寬度配置包含:存儲(chǔ)器數(shù)據(jù)寬度配置與外設(shè)數(shù)據(jù)寬度配置,可獨(dú)立配置為字節(jié)、半字、全字。
存儲(chǔ)器數(shù)據(jù)寬度由DMA通道x配置寄存器(DMA_CCRx)的MSIZE位控制,MSIZE[1:0]為00則數(shù)據(jù)寬度為8bit,MSIZE[1:0]為01則數(shù)據(jù)寬度為16bit,MSIZE[1:0]為10則數(shù)據(jù)寬度為32bit。
外設(shè)數(shù)據(jù)寬度配置由DMA通道x配置寄存器(DMA_CCRx)的PSIZE位控制,可配置為8bit, 16bit或32bit,MSIZE位與PSIZE位如圖2所示。
圖2.數(shù)據(jù)寬度對(duì)應(yīng)位
存儲(chǔ)器/外設(shè)地址
DMA的地址配置包含:存儲(chǔ)器地址的配置與外設(shè)地址的配置。
對(duì)DMA通道x存儲(chǔ)器地址寄存器(DMA_CMARx)進(jìn)行賦值,從而配置存儲(chǔ)器地址,存儲(chǔ)器地址可作為數(shù)據(jù)傳輸?shù)脑椿蚰繕?biāo)。
對(duì)DMA通道x外設(shè)地址寄存器(DMA_CPARx)進(jìn)行賦值,從而配置外設(shè)地址,外設(shè)地址是外設(shè)數(shù)據(jù)寄存器的基地址,作為數(shù)據(jù)傳輸?shù)脑椿蚰繕?biāo)。
源和目標(biāo)地址必須根據(jù)各自配置的數(shù)據(jù)傳輸寬度對(duì)齊。
指針增量
指針增量配置:每次傳輸后指針自動(dòng)向后遞增或保持不變。
操作DMA通道x配置寄存器(DMA_CCRx)的MINC位,若MINC位置1,則DMA配置為存儲(chǔ)器地址遞增模式,存儲(chǔ)器的訪問地址可以按照步長(zhǎng)累加,不需要每次都去設(shè)置訪問地址;若MINC位置0,則每次DMA傳輸固定訪問同一個(gè)地址。
設(shè)置DMA通道x配置寄存器(DMA_CCRx)的PINC位,若PINC位置1,則DMA配置為外設(shè)地址遞增模式,外設(shè)的訪問地址可以按照步長(zhǎng)累加;若PINC位置0,則每次DMA傳輸固定訪問同一個(gè)地址。
外設(shè)或存儲(chǔ)器配置為增量模式時(shí),下一個(gè)要傳輸?shù)牡刂穼⑹乔耙粋€(gè)地址加上步長(zhǎng),步長(zhǎng)取決于所選的數(shù)據(jù)寬度,首個(gè)傳輸?shù)牡刂反娣旁贒MA通道x外設(shè)地址寄存器(DMA_CPARx)/DMA通道x存儲(chǔ)器地址寄存器(DMA_CMARx)中。
優(yōu)先級(jí)
仲裁器根據(jù)通道請(qǐng)求的優(yōu)先級(jí)來(lái)啟動(dòng)外設(shè)或存儲(chǔ)器的訪問,優(yōu)先處理軟件優(yōu)先級(jí)高的請(qǐng)求,當(dāng)軟件優(yōu)先級(jí)相同時(shí),默認(rèn)編號(hào)更低的通道優(yōu)先處理。每個(gè)通道的優(yōu)先級(jí)可在DMA通道x配置寄存器(DMA_CCRx)中的PL位配置,優(yōu)先級(jí)分為四種:最高優(yōu)先級(jí)(PL[1:0]=11)、高優(yōu)先級(jí)(PL[1:0]=10)、中等優(yōu)先級(jí)(PL[1:0]=01)與低優(yōu)先級(jí)(PL[1:0]=00)。
數(shù)據(jù)傳輸數(shù)量
數(shù)據(jù)傳輸數(shù)量的最大值為65535,將數(shù)據(jù)傳輸數(shù)量值賦值到DMA通道x傳輸數(shù)量寄存器(DMA_CNDTRx),每次傳輸后,DMA_CNDTRx遞減,表示剩余多少次DMA傳輸。
當(dāng)數(shù)值遞減為0時(shí),數(shù)據(jù)全部傳輸完畢。若對(duì)應(yīng)通道配置為自動(dòng)重加載模式時(shí),DMA_CNDTRx寄存器中的內(nèi)容將被自動(dòng)重新加載為之前配置時(shí)的數(shù)值。
中斷
每個(gè)通道支持3種事件標(biāo)志:DMA半傳輸(HTIF)、DMA傳輸完成(TCIF)和DMA傳輸出錯(cuò)(TEIF),各通道單獨(dú)的中斷請(qǐng)求由這3種事件標(biāo)志邏輯或起來(lái)??赏ㄟ^配置DMA通道x配置寄存器(DMA_CCRx)的TCIE位為1使能傳輸完成中斷,配置HTIE位為1使能半傳輸中斷,配置TEIE位為1使能傳輸錯(cuò)誤中斷。
實(shí)驗(yàn)
本實(shí)驗(yàn)演示DMA burst搬運(yùn)模式的中斷行為,配置DMA的傳輸模式為存儲(chǔ)器到存儲(chǔ)器模式且傳輸方向?yàn)閺拇鎯?chǔ)器讀,開啟自動(dòng)重裝載,寬度配置為全字,指針遞增,優(yōu)先級(jí)為低優(yōu)先級(jí),設(shè)置兩數(shù)組為DMA數(shù)據(jù)傳輸?shù)刂罚渲脗鬏敂?shù)量為16。DMA配置完成后,進(jìn)行DMA數(shù)據(jù)傳輸,使能DMA通道,當(dāng)產(chǎn)生半傳輸中斷,設(shè)置半傳輸標(biāo)志為true,當(dāng)產(chǎn)生傳輸完成中斷,設(shè)置傳輸完成標(biāo)志為true,清除中斷標(biāo)志位。本實(shí)驗(yàn)可通過串口調(diào)試工具觀察數(shù)據(jù)傳輸現(xiàn)象,當(dāng)DMA傳輸已到一半時(shí)串口打印"half",當(dāng)DMA傳輸完成時(shí)串口打印"done"。
啟用外設(shè)時(shí)鐘 enable_clock()
實(shí)驗(yàn)使用DMA1,并通過UART串口顯示實(shí)驗(yàn)結(jié)果,因此需要啟用DMA與UART的外設(shè)時(shí)鐘。
void enable_clock() { /* Enable UART1 clock. */ RCC->APB2ENR |= RCC_APB2_PERIPH_UART1; /* Enable GPIOA clock. */ RCC->AHB1ENR |= RCC_AHB1_PERIPH_GPIOA; /* Enable DMA1 clock. */ RCC->AHB1ENR |= RCC_AHB1_PERIPH_DMA1; }
配置引腳 pin_init()
實(shí)驗(yàn)現(xiàn)象通過串口顯示,因此配置UART的TX(PA9)與RX(PA10)引腳。
void pin_init() { /* Setup PA9, PA10. */ GPIOA->CRH = ~GPIO_CRH_MODE9_MASK; GPIOA->CRH |= GPIO_PinMode_AF_PushPull; /* PA9 multiplexed push-pull output. */ GPIOA->AFRH = ~GPIO_AFRH_AFR_MASK; GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE9_SHIFT); /* Use AF1. */ GPIOA->CRH = ~GPIO_CRH_MODE10_MASK; GPIOA->CRH |= GPIO_PinMode_In_Floating; /* PA10 floating input. */ GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE10_SHIFT); /* Use AF1. */ }
UART初始化 uart_init()
初始化UART,配置時(shí)鐘頻率、波特率、數(shù)據(jù)長(zhǎng)度、停止位、傳輸模式及是否使用校驗(yàn)。
void uart_init() { /* Clear the corresponding bit to be used. */ UART1->CCR = ~( UART_CCR_PEN_MASK | UART_CCR_PSEL_MASK | UART_CCR_SPB0_MASK | UART_CCR_SPB1_MASK | UART_CCR_CHAR_MASK ); UART1->GCR = ~( UART_GCR_AUTOFLOWEN_MASK | UART_GCR_RXEN_MASK | UART_GCR_TXEN_MASK ); /* WordLength. */ UART1->CCR |= UART_CCR_CHAR_MASK; /* XferMode. */ UART1->GCR |= (UART_XferMode_RxTx << UART_GCR_RXEN_SHIFT); /* Setup baudrate, BOARD_DEBUG_UART_FREQ = 48000000u, BOARD_DEBUG_UART_BAUDRATE = 9600u. */ UART1->BRR = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) / 16u; UART1->FRA = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) % 16u; /* Enable UART1. */ UART1->GCR |= UART_GCR_UARTEN_MASK; }
DMA初始化 dma_init()
操作DMA_CCRx寄存器,配置輸出傳輸模式、傳輸方向、外設(shè)和存儲(chǔ)器的增量模式、外設(shè)和存儲(chǔ)器的數(shù)據(jù)寬度、通道優(yōu)先級(jí)和指針增量;操作DMA_CNDTRx寄存器,配置DMA傳輸數(shù)量,DMA傳輸完成一次,該數(shù)值減1,且在DMA傳輸期間DMA_CNDTRx寄存器不可被寫入;操作DMA_CPARx寄存器,配置外設(shè)寄存器地址,由于本實(shí)驗(yàn)配置DMA傳輸方向?yàn)閺拇鎯?chǔ)器讀,因此該外設(shè)地址在DMA傳輸時(shí)作為目標(biāo)地址;操作DMA_CMARx寄存器,配置數(shù)據(jù)存儲(chǔ)器的地址,DMA傳輸時(shí)從該存儲(chǔ)器地址加載數(shù)據(jù)。
void dma_init() { uint32_t ccr = 0u; ccr |= DMA_CCR_DIR_MASK; /* Data transmission direction is: read from memory. */ | DMA_CCR_MEM2MEM_MASK; /* Xfer mode: memory to memory. */ | DMA_CCR_ARE_MASK; /* Enable automatic reloading. */ | DMA_CCR_PINC(DMA_AddrIncMode_IncAfterXfer) /* DMA_AddrIncMode_IncAfterXfer=1u, peripheral increment mode. */ | DMA_CCR_MINC(DMA_AddrIncMode_IncAfterXfer) /* Memory increment mode. */ | DMA_CCR_PSIZE(DMA_XferWidth_32b) /* DMA_XferWidth_32b = 2u, peripheral size 32 bits. */ | DMA_CCR_MSIZE(DMA_XferWidth_32b) /* Memory size 32 bits. */ | DMA_CCR_PL(DMA_Priority_Low) /* DMA_Priority_Low = 0u, low priority. */ ; DMA1->CH[0].CCR = ccr; /* channel number = 0 refer to DMA1_CH1. */ DMA1->CH[0].CNDTR = DMA_BUFF_COUNT; /* DMA_BUFF_COUNT = 16u, data transmission quantity. */ DMA1->CH[0].CPAR = (uint32_t)app_dma_buff_to; /* Set arry app_dma_buff_to as peripheral address. */ DMA1->CH[0].CMAR = (uint32_t)app_dma_buff_from; /* Set arry app_dma_buff_from as memory address. */ }
使能DMA enable_dma()
操作DMA_CCRx寄存器的ENABLE位,使能通道1,在通道使能后可進(jìn)行DMA傳輸。
void enable_dma() { DMA1->CH[0].CCR |= DMA_CCR_EN_MASK; /* Enable DMA1 channel 1. */ }
使能中斷 enable_interrupt()
操作DMA_CCRx寄存器的TCIE位置1,使能傳輸完成中斷;HTIE位置1,使能半傳輸中斷。
void enable_interrupt() { DMA1->CH[0].CCR |= (DMA_CHN_INT_XFER_HALF_DONE 0xEu); /* DMA half transfer interrupt. */ DMA1->CH[0].CCR |= (DMA_CHN_INT_XFER_DONE 0xEu); /* DMA end of transfer interrupt. */ }
配置NVIC NVIC_EnableIRQ()
使用Cortex-M0 core_cm0.h頭文件中的NVIC_EnableIRQ使能中斷,由于DMA初始化時(shí)配置使用通道1,因此使用DMA1通道1全局中斷DMA1_CH1_IRQn.
NVIC_EnableIRQ(DMA1_CH1_IRQn)
編寫中斷服務(wù)程序DMA1_CH1_IRQHandler()
讀DMA中斷狀態(tài)寄存器(DMA_ISR)獲取當(dāng)前中斷狀態(tài),當(dāng)產(chǎn)生半傳輸中斷,令定義的半傳輸標(biāo)志app_dma_xfer_half_done為true;當(dāng)產(chǎn)生傳輸完成中斷,令定義的傳輸完成標(biāo)志app_dma_xfer_done為true。操作DMA中斷標(biāo)志清除寄存器(DMA_IFCR)對(duì)應(yīng)位置1,清除對(duì)應(yīng)中斷。
void DMA1_CH1_IRQHandler() { uint32_t flags = DMA1->ISR 0xFu; if (flags DMA_CHN_INT_XFER_HALF_DONE) /* DMA half transfer interrupt. */ { app_dma_xfer_half_done = true; } if (flags DMA_CHN_INT_XFER_DONE) /* DMA end of transfer interrupt. */ { app_dma_xfer_done = true; } DMA1->IFCR = (flag 0xFu); /* Clear interrupt. */ }
main()函數(shù)
main()函數(shù)結(jié)合上述操作,初始化DMA,設(shè)置變量app_dma_xfer_done為傳輸完成標(biāo)志,設(shè)置變量app_dma_xfer_half_done為DMA已傳輸一半的標(biāo)志,設(shè)置數(shù)組app_dma_buff_from[]為源,即DMA傳輸時(shí)從該存儲(chǔ)器地址加載數(shù)據(jù),設(shè)置數(shù)組app_dma_buff_to[]為數(shù)據(jù)存儲(chǔ)地址,設(shè)置變量DMA_BUFF_COUNT為數(shù)據(jù)傳輸數(shù)量,傳輸數(shù)量為16,每次傳輸后,該數(shù)值遞減,當(dāng)數(shù)值遞減為0時(shí),數(shù)據(jù)傳輸完畢,配置自動(dòng)重加載模式,數(shù)據(jù)傳輸數(shù)量會(huì)被自動(dòng)重新加載為之前配置時(shí)的數(shù)值。當(dāng)檢測(cè)到半傳輸中斷標(biāo)志產(chǎn)生,串口打印"half",檢測(cè)到傳輸完成中斷標(biāo)志產(chǎn)生,串口打印"done"。本實(shí)驗(yàn)在串口輸入數(shù)據(jù)時(shí)進(jìn)行DMA傳輸,將源地址app_dma_buff_from[]中的數(shù)據(jù)通過DMA傳輸?shù)絘pp_dma_buff_to[]地址中。實(shí)驗(yàn)現(xiàn)象如圖3所示。
int main() { uint8_t c; enable_clock(); pin_init(); uart_init(); printf("rndma_burst_interrupt example.rn"); dma_init(); NVIC_EnableIRQ(DMA1_CH1_IRQn); enable_interrupt(); for (uint32_t i = 0u; i < DMA_BUFF_COUNT; i++) { app_dma_buff_from[i] = i; app_dma_buff_to[i] = 0u; } while (1) { c = getchar(); putchar(c); for (uint32_t i = 0u; i < DMA_BUFF_COUNT; i++) { app_dma_buff_to[i] = 0u; } app_dma_xfer_done = false; app_dma_xfer_half_done = false; enable_dma(); while (!app_dma_xfer_half_done) {} printf("half.rn"); app_dma_xfer_half_done = false; while (!app_dma_xfer_done) {} printf("done.rn"); app_dma_xfer_done = false; } }
圖3.實(shí)驗(yàn)現(xiàn)象
審核編輯:彭菁
-
存儲(chǔ)器
+關(guān)注
關(guān)注
38文章
7430瀏覽量
163514 -
cpu
+關(guān)注
關(guān)注
68文章
10804瀏覽量
210826 -
dma
+關(guān)注
關(guān)注
3文章
556瀏覽量
100344
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論