STM32之DMA
STM32F103RBT6 只有1 個(gè)DMA控制器,DMA1 ,下面我們就針對(duì)DMA1 進(jìn)行介紹。
從外設(shè)(TIMx、ADC、SPIx 、I2Cx 和USARTx )產(chǎn)生的DMA請(qǐng)求,通過(guò)邏輯或輸入到DMA控制器,這就意味著同時(shí)只能有一個(gè)請(qǐng)求有效。外設(shè)的DMA請(qǐng)求,可以通過(guò)設(shè)置相應(yīng)的外設(shè)寄存器中的控制位,被獨(dú)立地開(kāi)啟或關(guān)閉。
- u16DMA1_MEM_LEN;//保存DMA每次數(shù)據(jù)傳送的長(zhǎng)度
- //DMA1的各通道配置
- //這里的傳輸形式是固定的,這點(diǎn)要根據(jù)不同的情況來(lái)修改
- //從存儲(chǔ)器->外設(shè)模式/8位數(shù)據(jù)寬度/存儲(chǔ)器增量模式
- //DMA_CHx:DMA通道CHx
- //cpar:外設(shè)地址
- //cmar:存儲(chǔ)器地址
- //cndtr:數(shù)據(jù)傳輸量
- voidMYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32cpar,u32cmar,u16cndtr)
- {
- RCC->AHBENR|=1<<0;//開(kāi)啟DMA1時(shí)鐘
- delay_ms(1);//等待DMA時(shí)鐘穩(wěn)定
- DMA_CHx->CPAR=cpar;//DMA1外設(shè)地址
- DMA_CHx->CMAR=(u32)cmar;//DMA1,存儲(chǔ)器地址
- DMA1_MEM_LEN=cndtr;//保存DMA傳輸數(shù)據(jù)量
- DMA_CHx->CNDTR=cndtr;//DMA1,傳輸數(shù)據(jù)量
- DMA_CHx->CCR=0X00000000;//復(fù)位
- DMA_CHx->CCR|=1<<4;//從存儲(chǔ)器讀
- DMA_CHx->CCR|=0<<5;//普通模式
- DMA_CHx->CCR|=0<<6;//外設(shè)地址非增量模式
- DMA_CHx->CCR|=1<<7;//存儲(chǔ)器增量模式
- DMA_CHx->CCR|=0<<8;//外設(shè)數(shù)據(jù)寬度為8位
- DMA_CHx->CCR|=0<<10;//存儲(chǔ)器數(shù)據(jù)寬度8位
- DMA_CHx->CCR|=1<<12;//中等優(yōu)先級(jí)
- DMA_CHx->CCR|=0<<14;//非存儲(chǔ)器到存儲(chǔ)器模式
- }
- //開(kāi)啟一次DMA傳輸
- voidMYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
- {
- DMA_CHx->CCR&=~(1<<0);//關(guān)閉DMA傳輸
- DMA_CHx->CNDTR=DMA1_MEM_LEN;//DMA1,傳輸數(shù)據(jù)量
- DMA_CHx->CCR|=1<<0;//開(kāi)啟DMA傳輸
- }
DMA可以傳遞多少數(shù)據(jù)?
本文引用地址:http://m.butianyuan.cn/article/201611/316160.htm直接存儲(chǔ)器存?。―irect Memory Access,DMA)是計(jì)算機(jī)科學(xué)中的一種內(nèi)存訪問(wèn)技術(shù)。它允許某些電腦內(nèi)部的硬體子系統(tǒng)(電腦外設(shè)),可以獨(dú)立地直接讀寫(xiě)系統(tǒng)存儲(chǔ)器,而不需繞道 CPU。在同等程度的CPU負(fù)擔(dān)下,DMA是一種快速的數(shù)據(jù)傳送方式。它允許不同速度的硬件裝置來(lái)溝通,而不需要依于 CPU的大量中斷請(qǐng)求。【摘自Wikipedia】
現(xiàn)在越來(lái)越多的單片機(jī)采用DMA技術(shù),提供外設(shè)和存儲(chǔ)器之間或者存儲(chǔ)器之間的高速數(shù)據(jù)傳輸。當(dāng) CPU 初始化這個(gè)傳輸動(dòng)作,傳輸動(dòng)作本身是由
DMA 控制器和Cortex-M3核共享系統(tǒng)數(shù)據(jù)總線執(zhí)行直接存儲(chǔ)器數(shù)據(jù)傳輸。當(dāng)CPU和DMA同時(shí)訪問(wèn)相同的目標(biāo)(RAM或外設(shè))時(shí),DMA請(qǐng)求可能會(huì)停止 CPU訪問(wèn)系統(tǒng)總線達(dá)若干個(gè)周期,總線仲裁器執(zhí)行循環(huán)調(diào)度,以保證CPU至少可以得到一半的系統(tǒng)總線(存儲(chǔ)器或外設(shè))帶寬。
在發(fā)生一個(gè)事件后,外設(shè)發(fā)送一個(gè)請(qǐng)求信號(hào)到DMA控制器。DMA控制器根據(jù)通道的優(yōu)先權(quán)處理請(qǐng)求。當(dāng)DMA控制器開(kāi)始訪問(wèn)外設(shè)的時(shí)候,DMA控制器立即發(fā)送給外設(shè)一個(gè)應(yīng)答信號(hào)。當(dāng)從DMA控制器得到應(yīng)答信號(hào)時(shí),外設(shè)立即釋放它的請(qǐng)求。一旦外設(shè)釋放了這個(gè)請(qǐng)求,DMA控制器同時(shí)撤銷(xiāo)應(yīng)答信號(hào)。如果發(fā)生更多的請(qǐng)求時(shí),外設(shè)可以啟動(dòng)下次處理。
總之,每個(gè)DMA傳送由3個(gè)操作組成:
1. 從外設(shè)數(shù)據(jù)寄存器或者從DMA_CMARx寄存器指定地址的存儲(chǔ)器單元執(zhí)行加載操作。
2. 存數(shù)據(jù)到外設(shè)數(shù)據(jù)寄存器或者存數(shù)據(jù)到DMA_CMARx寄存器指定地址的存儲(chǔ)器單元。
3. 執(zhí)行一次DMA_CNDTRx寄存器的遞減操作。該寄存器包含未完成的操作數(shù)目。
仲裁器根據(jù)通道請(qǐng)求的優(yōu)先級(jí)來(lái)啟動(dòng)外設(shè)/存儲(chǔ)器的訪問(wèn)。優(yōu)先級(jí)分為兩個(gè)等級(jí):軟件(4個(gè)等級(jí):最高、高、中等、低)、硬件(有較低編號(hào)的通道比擁有較高編號(hào)的通道有較高的優(yōu)先權(quán))。
可以在DMA傳輸過(guò)半、傳輸完成和傳輸錯(cuò)誤時(shí)產(chǎn)生中斷。
STM32中DMA的不同中斷(傳輸完成、半傳輸、傳輸完成)通過(guò)“線或”方式連接至NVIC,需要在中斷例程中進(jìn)行判斷。
進(jìn)行DMA配置前,不要忘了在RCC設(shè)置中使能DMA時(shí)鐘。STM32的DMA控制器掛在AHB總線上。
DMA總共有7個(gè)通道,各個(gè)通道的DMA映射關(guān)系如下:
外設(shè)的事件連接至相應(yīng)DMA通道,每個(gè)通道均可以通過(guò)軟件觸發(fā)實(shí)現(xiàn)存儲(chǔ)器內(nèi)部的DMA數(shù)據(jù)傳輸(M2M模式)
Tips:庫(kù)2.0中函數(shù)RCC_AHBPeriphClockCmd的參數(shù)由“RCC_AHBPeriph_DMA”改成“RCC_AHBPeriph_DMA1”(如果是DMA1控制器的話)。
DMA的傳輸標(biāo)志位(CHTIFx、CTCIFx、CGIFx)由硬件設(shè)置為“1”,但需要軟件清零,在中斷服務(wù)程序中清除。當(dāng)CGIFx(全局中斷標(biāo)志位)清零后,CHTIFx 和 CTCIFx均清零。
過(guò)程:怎樣啟用DMA?首先,眾所周知的是初始化,任何設(shè)備啟用前都要對(duì)其進(jìn)行初始化,要對(duì)模塊初始化,還要先了解該模塊相應(yīng)的結(jié)構(gòu)及其函數(shù),以便正確的設(shè)置;由于DMA較為復(fù)雜,我就只談?wù)凞MA的基本結(jié)構(gòu)和和常用函數(shù),這些都是ST公司提供在庫(kù)函數(shù)中的。
1、 下面代碼是一個(gè)標(biāo)準(zhǔn)DMA設(shè)置,當(dāng)然實(shí)際應(yīng)用中可根據(jù)實(shí)際情況進(jìn)行裁減:
DMA_DeInit(DMA_Channel1);
上面這句是給DMA配置通道,根據(jù)ST提供的資料,STM3210Fx中DMA包含7個(gè)通道(CH1~CH7),也就是說(shuō)可以為外設(shè)或memory提供7座“橋梁”(請(qǐng)?jiān)试S我使用橋梁一詞,我覺(jué)得更容易理解,哈哈,別“拍磚”呀!);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
上面語(yǔ)句中的DMA_InitStructure是一個(gè)DMA結(jié)構(gòu)體,在庫(kù)中有聲明了,當(dāng)然使用時(shí)就要先定義 了;DMA_PeripheralBaseAddr是該結(jié)構(gòu)體中一個(gè)數(shù)據(jù)成員,給DMA一個(gè)起始地址,好比是一個(gè)buffer起始地址,數(shù)據(jù)流程是:外設(shè) 寄存器à DMA_PeripheralBaseAddàmemory中變量空間(或flash中數(shù)據(jù)空間等),ADC1_DR_Address是我定義的一個(gè)地址 變量;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;
上面這句很顯然是DMA要連接在Memory中變量的地址,ADC_ConvertedValue是我自己在memory中定義的一個(gè)變量;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
上面的這句是設(shè)置DMA的傳輸方向,就如前面我所說(shuō)的,DMA可以雙向傳輸,也可以單向傳輸,這里設(shè)置的是單向傳輸,如果需要雙向傳輸:把DMA_DIR_PeripheralSRC改成DMA_DIR_PeripheralDST即可。
DMA_InitStructure.DMA_BufferSize = 2;
上面的這句是設(shè)置DMA在傳輸時(shí)緩沖區(qū)的長(zhǎng)度,前面有定義過(guò)了buffer的起始地址:ADC1_DR_Address ,為了安全性和可靠性,一般需要給buffer定義一個(gè)儲(chǔ)存片區(qū),這個(gè)參數(shù)的單位有三種類(lèi)型:Byte、HalfWord、word,我設(shè)置的2個(gè) half-word(見(jiàn)下面的設(shè)置);32位的MCU中1個(gè)half-word占16 bits。
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
上面的這句是設(shè)置DMA的外設(shè)遞增模式,如果DMA選用的通道(CHx)有多個(gè)外設(shè)連接,需要使用外設(shè)遞增模式:DMA_PeripheralInc_Enable;我的例子里DMA只與ADC1建立了聯(lián)系,所以選用DMA_PeripheralInc_Disable
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
上面的這句是設(shè)置DMA的內(nèi)存遞增模式,DMA訪問(wèn)多個(gè)內(nèi)存參數(shù)時(shí),需要使用DMA_MemoryInc_Enable,當(dāng)DMA只訪問(wèn)一個(gè)內(nèi)存參數(shù)時(shí),可設(shè)置成:DMA_MemoryInc_Disable。
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
上面的這句是設(shè)置DMA在訪問(wèn)時(shí)每次操作的數(shù)據(jù)長(zhǎng)度。有三種數(shù)據(jù)長(zhǎng)度類(lèi)型,前面已經(jīng)講過(guò)了,這里不在敘述。
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
與上面雷同。在此不再說(shuō)明。
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
上面的這句是設(shè)置DMA的傳輸模式:連續(xù)不斷的循環(huán)模式,若只想訪問(wèn)一次后就不要訪問(wèn)了(或按指令操作來(lái)反問(wèn),也就是想要它訪問(wèn)的時(shí)候就訪問(wèn),不要它訪問(wèn)的時(shí)候就停止),可以設(shè)置成通用模式:DMA_Mode_Normal
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
上面的這句是設(shè)置DMA的優(yōu)先級(jí)別:可以分為4級(jí):VeryHigh,High,Medium,Low.
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
上面的這句是設(shè)置DMA的2個(gè)memory中的變量互相訪問(wèn)的
DMA_Init(DMA_Channel1,&DMA_InitStructure);
前面那些都是對(duì)DMA結(jié)構(gòu)體成員的設(shè)置,在次再統(tǒng)一對(duì)DMA整個(gè)模塊做一次初始化,使得DMA各成員與上面的參數(shù)一致。
DMA_Cmd(DMA_Channel1,ENABLE);
哈哈哈!這一句我想我就不羅嗦了,大家一看就明白。
至此,整個(gè)DMA總算設(shè)置好了,但是,DMA通道又是怎樣與外設(shè)聯(lián)系在一起的呢?哈哈,這也是我當(dāng)初最想知道的一個(gè)事情,別急!容我想喝口茶~~~~~~哈哈哈!
要使DMA與外設(shè)建立有效連接,這不是DMA自身的事情,是各個(gè)外設(shè)的事情,每個(gè)外設(shè)都有 一個(gè)xxx_DMACmd(XXXx,Enable )函數(shù),如果使DMA與ADC建立有效聯(lián)系,就使用ADC_DMACmd(ADC1,Enable); (這里我啟用了ADC中的ADC1模塊)。
一個(gè)簡(jiǎn)單的例子 transfer
在V3.1.2庫(kù)的位置
STM32F10x_StdPeriph_Lib_V3.1.2ProjectSTM32F10x_StdPeriph_ExamplesDMAFLASH_RAM
DMA_DeInit(DMA1_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//緩沖區(qū)大小 Number of data to be transferred (0 up to 65535).數(shù)據(jù)傳輸數(shù)目
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//the Peripheral data width
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
//the DMAy Channelx will be used in memory-to-memory transfer
//DMA通道的操作可以在沒(méi)有外設(shè)請(qǐng)求的情況下進(jìn)行,這種操作就是存儲(chǔ)器到存儲(chǔ)器模式。
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel6, ENABLE);
=======================================================================
外設(shè)的DMA請(qǐng)求映像
要使DMA與外設(shè)建立有效連接,這不是DMA自身的事情,是各個(gè)外設(shè)的事情,每個(gè)外設(shè)都有 一個(gè)
xxx_DMACmd(XXXx,Enable )函數(shù),如果使DMA與ADC建立有效聯(lián)系,就使用 ADC_DMACmd
(ADC1,Enable); (這里我啟用了ADC中的ADC1模塊)。
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&AD_Value;
//u16
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &ADC_InitStructure);
//內(nèi)部溫度傳感器
ADC_TempSensorVrefintCmd(ENABLE);
//##############
//################ Channel 10(電位器)
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_13Cycles5);
//###### 內(nèi)部溫度傳感器
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_55Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatu
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
評(píng)論