用PWM的方法實(shí)現(xiàn)熒火蟲燈
上次提到要用Timer的PWM功能來實(shí)現(xiàn)熒火蟲燈。當(dāng)然還是找一個(gè)現(xiàn)成的例子來作個(gè)修改,這回要用到的例子在這里。
復(fù)制一份到自己練習(xí)用的文件夾中,建立工程。
從程序中可以知道:
?。?) 使用TIM3
?。?) 定時(shí)器的時(shí)鐘頻率是36MHz.
?。?) PWM信號(hào)的頻率是36KHz,這是通過TIM3的ARR來設(shè)置的。ARR的值是999,因此PWM的頻率是36MHz/(999+1)=36KHz。
?。?) 四個(gè)通道的占空比分別由TIM3_CCR1~TIM3_CCR4來確定,算式是:
?。═IM3_CCR1/ TIM3_ARR)* 100
由此,當(dāng)PWM的頻率是36K時(shí),占空比分辨率接近0.1%。降低頻率,可以獲得更高的分辨率。
要完成燈的漸亮和漸滅控制,只要定時(shí)改變TIM3_CCR1的值就行了。
如何改變呢?這里用到STM32提供的系統(tǒng)定時(shí)器(SysTick)
數(shù)據(jù)手冊(cè)中關(guān)于這個(gè)定時(shí)器的描述如下:
-------------------------------------------------------------
系統(tǒng)時(shí)基定時(shí)器
這個(gè)定時(shí)器是專用于實(shí)時(shí)操作系統(tǒng),也可當(dāng)成一個(gè)標(biāo)準(zhǔn)的遞減計(jì)數(shù)器。它具有下述特性:
● 24位的遞減計(jì)數(shù)器
● 自動(dòng)重加載功能
● 當(dāng)計(jì)數(shù)器為0時(shí)能產(chǎn)生一個(gè)可屏蔽系統(tǒng)中斷
● 可編程時(shí)鐘源
而它的使用方法可以在庫提供的例子中找到。
有一個(gè)初始化函數(shù):
void SysTick_Configuration(void)
{
if (SysTick_Config((SystemFrequency) / 10)) //經(jīng)實(shí)際測(cè)試發(fā)現(xiàn),除以10是100ms,除以100是10ms,依此類推
{
/* Capture error */
while (1);
}
NVIC_SetPriority(SysTick_IRQn, 0x0);
}
這里將其初始化為每100ms產(chǎn)生一次中斷。
將這個(gè)函數(shù)放在main.c中,在main函數(shù)中調(diào)用它,即完成初始化工作。在system32_it.c中有中斷處理函數(shù)。
void SysTick_Handler(void)
{}
原例子中這里沒有寫代碼,可以根據(jù)需要自行增加相關(guān)代碼來處理每100ms時(shí)間到的事件。
代碼如下:
extern uint16_t dutyRatio;
extern uint8_t ChangDuty;
void SysTick_Handler(void)
{ static uint8_t Counter;
if(Counter》16)
dutyRatio-=62;
else
{ dutyRatio+=62;
if(dutyRatio》999)
dutyRatio=999;
}
if(++Counter》=32)
Counter=0;
ChangDuty=1;
}
這里定義了兩個(gè)變量,一個(gè)是dutyRatio,用來控制占空比的變化。它在main.c中定義,并初始化為6。初始化TIM3_CH1通道時(shí)使用該變量。
每次中斷則視情況增加或者減少,每次變化的量是62。在SysTick_Handler函數(shù)中,定義了一個(gè)static型的變量Counter,它的值在 0~31之間變化。當(dāng)其值在0~15之間時(shí),dutyRatio每次加1,這樣一共是加16次,即其最終的值是:6+16*62=998,正好比ARR的值小1。當(dāng)Counter的值在16~31之間變化時(shí),dutyRatio每次減62。這樣,dutyRatio的值始終在6~998之間變化,對(duì)應(yīng)的是占空比在:
6/999*100%=0.6% ~ 998/999*100%=99.89% 之間變化。
ChangDuty是一個(gè)標(biāo)志,用途是通知main函數(shù),占空比已發(fā)生變化,要求更新CCR1。Mina函數(shù)的處理如下:
while (1)
{ if(ChangDuty==1)
{
TIM3-》CCR1=dutyRatio;
ChangDuty=0;
}
}
在用軟件仿真時(shí),執(zhí)行到TIM3-》CCR1=dutyRatio;時(shí),外圍部件中的相應(yīng)值并沒有立即變化。目前還沒有弄清楚是調(diào)試器的問題還是確實(shí)不立即發(fā)生變化。
使用硬件來測(cè)試,由于我手邊的板子TIM3_CH1上沒有接LED,所以就看不出燈亮的效果了,不過,不要緊,還有示波器。將程序下載入FLASH后運(yùn)行,觀察GPIOA.6,可以看到非常漂亮的波形。用萬用表電壓檔測(cè)該引腳的電壓,可以看到電壓平穩(wěn)地上升和下降。所以,我有些懷疑上面提到的那個(gè)CCR1沒有立即變化僅僅只是調(diào)試器的問題。//藍(lán)色的字這個(gè)不對(duì),下面有說明。
??? 用PWM生成正弦波
有了PWM,自然就可以用PWM的方法生成正弦波了。下面生成500Hz正弦波的方法參考自張明峰的《PIC單片機(jī)入門與實(shí)踐》
每個(gè)正弦波分成四個(gè)像限,每個(gè)像限16點(diǎn),共64點(diǎn),每點(diǎn)出現(xiàn)2個(gè)PWM周期,故PWM的周期為:2ms/128=156.25us,頻率為64KHz。
TIM3 Frequency = TIM3 counter clock/(ARR + 1)
倒過來:
ARR=TIM3 Counter Clock/TIM3 Frequenc - 1 =562.5-1 =561
如果取ARR的值是561的話,那么實(shí)際的頻率是64.056KHz,即最終生成為的正弦波頻率是:500.4Hz
有了ARR,占空比就取決于CCR1的值了,使用EXCEL可以方便地計(jì)算出第一象限的16個(gè)點(diǎn)的數(shù)據(jù):
280,307,335,361,387,412,436,458,478,496,513,527,539,548,555,559
有了第一象限,其他象限都可以鏡像生成了。具體方法請(qǐng)看源程序。
要用上面的例子修改,還需要做一些工作。
前面是在SysTick中做出標(biāo)志,然后在主程序中修改CCR1的值,現(xiàn)在不行了,肯定會(huì)有時(shí)間的誤差,不能這做么,要在PWM輸出后修正,這樣就要在PWM波形輸出時(shí)產(chǎn)生中斷。因此,需要在main函數(shù)中增加以下這個(gè)函數(shù)。
這個(gè)函數(shù)哪里來的呢,很簡(jiǎn)單,從timebase工程中中抄來的然后將TIM2改成TIM3就行了^_^。然后在main函數(shù)中調(diào)用它。
注意,還需要打開stm32f10x_conf.h文件,將下面:
藍(lán)色框里面的包含文件給“解放”出來。當(dāng)然,同時(shí)要把庫中的misc.c源程序文件加入工程中來。否則,編譯是通不過的。
為了讓通道1可以產(chǎn)生中斷,還需要做一件事,就是下面藍(lán)色的部分。
/* TIM IT enable */
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
//也是從TIMEBASE工程中抄來,再將TIM2改成TIM3的。
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
現(xiàn)在該到stm32f10x_it.c中去了,增加一個(gè)中斷處理函數(shù):
uint16_t sinTab[]={280,307,335,361,387,412,436,458,478,496,513,527,539,548,555,559};
uint8_t Count1,Count2; //1.像限計(jì)數(shù)器,其值在0~3之間變化 2.其值在0~31之間變化
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
if(Count2%2==0) //準(zhǔn)備更新,新的值會(huì)在下一次更新
{ switch(Count1)
{ case 0: //象限1
{
TIM3-》CCR1= sinTab[Count2/2];
break;
}
case 1: //象限2
{ TIM3-》CCR1=sinTab[15-Count2/2];
break;
}
case 2: //象限3
{ TIM3-》CCR1=560-sinTab[Count2/2];
break;
}
case 3: //象限4
{ TIM3-》CCR1=560-sinTab[15-Count2/2];
break;
}
default:break;
}
}
}
if(++Count2==32)
{ Count2=0;
if(++Count1==4)
Count1=0;
}
}
也就是在這里,搞清楚了,所謂的“我有些懷疑上面提到的那個(gè)CCR1沒有立即變化僅僅只是調(diào)試器的問題”不對(duì),這是CCR1更新方法的問題,
注意上圖中紅色框中的描述。
這里就是不用立即更新的方法。因?yàn)槊總€(gè)點(diǎn)的PWM波形會(huì)出現(xiàn)2次,因此,用
if(Count2%2==0) 來判斷是第一次產(chǎn)生PWM波形,更新CCR1,但是這個(gè)CCR1不會(huì)立即更新,而會(huì)在下一次產(chǎn)生PWM事件時(shí)更新。
STM32的SDIO控制SD卡
一、STM32的SDIO適配器的結(jié)構(gòu)
STM32的SDIO適配器包括與AHB總線接口和SD卡接口兩個(gè)大塊兒。如下面圖中兩個(gè)灰色陰影區(qū)域。
左側(cè)的陰影部分又細(xì)分兩個(gè)子單無,適配器寄存器組和FIFO。適配器寄存器組包含了所有SDIO所有的寄存器,這些寄存器用于配置一些參數(shù),以實(shí)現(xiàn)一些SD協(xié)議中的時(shí)序,最終于實(shí)現(xiàn)SD卡的命令傳輸。FIFO則是為了實(shí)現(xiàn)數(shù)據(jù)的傳輸,這兩者部分分別代表者對(duì)SD卡的兩種傳輸操作,即命令的傳輸和數(shù)據(jù)的傳輸。
右側(cè)的陰影部分,即SDIO適配器的SD卡接口大塊兒,又細(xì)分為三個(gè)子單元??刂茊卧–ontrol Unit),命令通道和數(shù)據(jù)通道(Command Path and Data Path)??刂茊卧饕獙?shí)現(xiàn)電源和時(shí)鐘的控制。它根據(jù)適配器寄存器組中寄存器的配置,開啟和關(guān)閉SDIO適配器模塊的電源,改變工作的時(shí)鐘源(直接使用 SDIO_CLK還是其分頻后的時(shí)鐘等)。命令通道連接CMD線,控制命令的傳輸。數(shù)據(jù)通道連接數(shù)據(jù)線(DAT0~DAT7),控制數(shù)據(jù)的傳輸。
二、分單元詳述
下面按照上面一章提到的五個(gè)子單元的劃分,對(duì)各子單元進(jìn)行詳細(xì)的描述。
1、適配器寄存器組
The adapter register block contains all system registers. This block also generates the signals that clear the static flags in the multimedia card. The clear signals are generated when 1 is written into the corresponding bit location in the SDIO Clear register.
這個(gè)STM32數(shù)據(jù)手冊(cè)上關(guān)于這一部分的全部描述,大體上就是說:適配器寄存器組包含了所有的系統(tǒng)寄存器。它還產(chǎn)生用于清除MMC卡的靜態(tài)標(biāo)志位的信號(hào)。當(dāng)向SDIO Clear register(清除寄存器)的對(duì)應(yīng)位寫1,即產(chǎn)生這個(gè)信號(hào)。
2、控制單元
這一單元又在內(nèi)部分為電源管理和時(shí)鐘管理兩個(gè)子單元,他們都受控制適配器寄存器組。時(shí)鐘管理子單元產(chǎn)生和控制時(shí)鐘信號(hào)SDIO_CK,也就是SD卡最絡(luò)接收到的SCK。時(shí)鐘管理子單元工作于兩種模式時(shí)鐘分頻模式和時(shí)鐘直通模式(Bypass,標(biāo)準(zhǔn)的翻譯不知是什么,似乎可以是“旁路”,但“直通”更容易理解些)。當(dāng)工作在直通模式進(jìn),SDIO_CK==SDIO_CLK.工作于分頻模塊時(shí),SDIO_CK==SDIO_CLK/div.
在如下情形下,時(shí)鐘是不輸出時(shí)鐘信號(hào)的:
復(fù)位后
上電和掉電期間
省電模式下總線處于空閑模式時(shí)
電源管理子單元在上電和掉電時(shí)關(guān)繼時(shí)適配器的輸出信號(hào)。
3、命令通道
命令通道向卡發(fā)送命令和接收回應(yīng)。
如圖所示,圖上左側(cè)陰影部分是屬于適配器寄存器組子單元里的兩個(gè)寄存器,分別為SDIO_ARG和SDIO_CMD,后者用于添加想要發(fā)送的命令,前者用于添加所要發(fā)送的命令的參數(shù),將兩個(gè)添好之后使能命令發(fā)送,命令就會(huì)自動(dòng)發(fā)送出去。適配器上述兩個(gè)寄存器的內(nèi)容進(jìn)行組合,并最終形成48位長(zhǎng)的命令,這48 位首先進(jìn)入移位寄存器,即圖中的Shift register,在這里由并轉(zhuǎn)串一位一位的發(fā)送,由圖可見,這些位要經(jīng)過CRC后,才發(fā)送出去。實(shí)際上,前面講的總位數(shù)并非48,在這里經(jīng)過CRC,生成那些位的CRC較驗(yàn)值,并追加到其尾部,最絡(luò)才是48位。命令分為有回應(yīng)的和沒有回應(yīng)的兩種。如果發(fā)送的是沒有回應(yīng)的命令,發(fā)送之后會(huì)對(duì)標(biāo)志位中的相送位置位,通知系統(tǒng),命令發(fā)送正常,然后進(jìn)入空閉狀態(tài)。如果發(fā)送的命令是有回應(yīng)的命令,則要等待回應(yīng)。接收到回應(yīng)之后,會(huì)對(duì)回應(yīng)進(jìn)行CRC校驗(yàn),并設(shè)定相關(guān)位。下面是命令通道的狀態(tài)機(jī):
進(jìn)入等待狀態(tài)后,命令時(shí)鐘(command timer)開始計(jì)時(shí),如果到達(dá)超時(shí)時(shí)間CPSM狀態(tài)機(jī)還沒移動(dòng)到接收狀態(tài),則置位超時(shí)標(biāo)志并進(jìn)入空閑狀態(tài)。
注意:命令超時(shí)時(shí)間是固定值,為64個(gè)SDIO_CK。
如果在命令寄存器中設(shè)置了中斷位(interrupt bit),就不會(huì)啟用上面講到的超時(shí)時(shí)鐘,CPSM狀態(tài)機(jī)會(huì)一直等待來自卡的中斷請(qǐng)求。如果在命令寄存器中置位了懸停位(pending bit),CPSM狀態(tài)機(jī)會(huì)進(jìn)入懸停狀態(tài)(所謂的掛起狀態(tài)),并等待來自數(shù)據(jù)通道子單元的CmdPend信號(hào)。檢測(cè)到CmdPend位以為,CPSM狀態(tài)機(jī)會(huì)移動(dòng)到發(fā)送狀態(tài)(Send state),這將使數(shù)據(jù)計(jì)數(shù)器停止命令的傳輸。
注意:CPSM會(huì)在空閑模式停留至少8個(gè)SDIO_CK時(shí)間,以滿足Ncc和Nrc的時(shí)間要求。Ncc時(shí)兩次主機(jī)命令傳輸?shù)淖钚⊙舆t,而Nrc時(shí)主機(jī)命令與卡的回應(yīng)之間的最小延遲。如下圖:
命令的格式:
命令即是開始傳輸?shù)囊粋€(gè)標(biāo)記。命令由主機(jī)發(fā)送給單個(gè)卡(尋址性命令)或是所有的卡(廣播性命令,MMC V3.31及更早的MMC卡標(biāo)準(zhǔn)中支持)。命令通過CMD信號(hào)線串行傳輸,所有的命令都有一個(gè)固定的長(zhǎng)度,即48位。命令的格式如下圖:
命令通道工作于半雙工模式,所以可以發(fā)送,也可以接收命令或回應(yīng)。如果CPSM狀態(tài)機(jī)不在發(fā)送狀態(tài)(Send State),SDIO_CMD為高阻狀態(tài)(Hi-Z state),如下圖:
SDIO_CMD在SDIO_CK的上升沿進(jìn)行同步。
回應(yīng):
回應(yīng)是由被尋址的卡發(fā)出的一個(gè)標(biāo)記(或是在MMC V3.31及以前標(biāo)準(zhǔn)中,所有連接在適配器上的卡同步發(fā)送),此標(biāo)記由卡發(fā)給主機(jī),是對(duì)剛剛接收到的命令的回答?;貞?yīng)是在CMD信號(hào)線上串行傳輸?shù)摹?/p>
SDIO支持兩種回應(yīng)類型,都是進(jìn)行CRC校驗(yàn)的:
48位的短回應(yīng)(short response)
136位的長(zhǎng)回應(yīng)(long response)
注意:如果回應(yīng)不包含CRC校驗(yàn)信息(如CMD1的回應(yīng)),設(shè)備驅(qū)動(dòng)就必須忽略CRC錯(cuò)誤的狀態(tài)。
下面兩張表是兩種回應(yīng)的格式:
前面講到,SDIO適配器包含兩個(gè)大塊兒,詳見本帖開頭,這里只拿出圖來:
其中,與AHB接口相連的有兩個(gè)塊兒,就是上圖中左側(cè)陰影部分,Adapter registers 和FIFO,即適配器寄存器組和數(shù)據(jù)FIFO。前者包含了適配器所有的寄存器,用于配置相應(yīng)時(shí)序,產(chǎn)生相應(yīng)的信號(hào)。
這里面,用于控制命令通道產(chǎn)生命令時(shí)序的就有兩個(gè)寄存器,名為SDIO_ARG和SDIO_CMD,SDIO_ARG的三十二位全部用來存儲(chǔ)命令參數(shù),也就沒什么好講的了。SDIO_CMD則不同,它有六個(gè)位,用來識(shí)別不同的命令,總共可以區(qū)別64個(gè),但實(shí)際上SD卡的命令集沒有那么多。 SDIO_CMD還有一些位,用來表示些命令時(shí)否有回應(yīng),是長(zhǎng)回應(yīng)還是短回應(yīng),命令的類型是什么等等。適配器最終根據(jù)這些,加上CRC組合成一個(gè)48位的命令。
另外,我們還提到過命令發(fā)送之后,如果這是一個(gè)沒有回應(yīng)的命令,那么就很簡(jiǎn)單,命令通道直接置位CMDSENT標(biāo)志,或進(jìn)入空閑狀態(tài)。如果是有回應(yīng)的,則要等待回應(yīng),并設(shè)定相關(guān)的標(biāo)志位。命令通道的相關(guān)標(biāo)志位如下:
CRC 產(chǎn)生器計(jì)算的是CRC碼前面的所有位的校驗(yàn)和。這包括開始位,傳輸位,命令索引(command index)和命令參數(shù)(和卡狀態(tài))。對(duì)長(zhǎng)回應(yīng)格式來說,CRC校驗(yàn)和計(jì)算的是CID或CSD的前120位。這里不包括開始位,傳輸位和六個(gè)保留位。 CRC是一個(gè)7位的值,其計(jì)算方法如下:
?
補(bǔ)充一些硬件知識(shí)
SEGGER 給出的Jlink引腳圖
開發(fā)板上的連接圖
標(biāo)準(zhǔn)的JTAG連接圖,供對(duì)照參考。
調(diào)試方式既可以用JTAG,也可以用SW。
以下是轉(zhuǎn)載:
SWD 仿真模式概念簡(jiǎn)述
先所說 SWD 和傳統(tǒng)的調(diào)試方式有什么不一樣:
首先給大家介紹下經(jīng)驗(yàn)之談:
?。ㄒ唬?SWD 模式比 JTAG 在高速模式下面更加可靠。 在大數(shù)據(jù)量的情況下面 JTAG 下載程序會(huì)失敗, 但是 SWD 發(fā)生的幾率會(huì)小很多。 基本使用 JTAG 仿真模式的情況下是可以直接使用 SWD 模式的, 只要你的仿真器支持。 所以推薦大家使用這個(gè)模式。
(二): 在大家 GPIO 剛好缺一個(gè)的時(shí)候, 可以使用 SWD 仿真, 這種模式支持更少的引腳。
(三): 在大家板子的體積有限的時(shí)候推薦使用 SWD 模式, 他需要的引腳少, 當(dāng)然需要的 PCB 空間就小啦。 比如: 你可以選擇一個(gè)很小的 2.54 間距的 5 芯端子做仿真接口。
?。?) 仿真器對(duì) SWD 模式支持情況
再說說市面上的常用仿真器對(duì) SWD 仿真的支持情況。
(1) JLINKV6 支持 SWD 仿真模式。 速度較慢。
?。?) JLINKV7 比較好的支持 SWD 仿真模式, 速度有了明顯的提高。 速度是 JLINKV6 的 6 倍。
(3) JLINKV8 非常好的支持 SWD 仿真模式, 速度可以到 10M.
(4) ULINK1 不支持 SWD 模式
?。?) 盜版 ULINK2 非常好的支持 SWD 模式。 速度可以達(dá)到 10M.
(6) 正版 ULINK2 非常好的支持 SWD 模式。 速度可以達(dá)到 10M.
再所說硬件上的不同:
?。?) JLINKV6 需要的硬件接口為: GND, RST, SWDIO, SWDCLK
?。?) JLINKV7 需要的硬件接口為: GND, RST, SWDIO, SWDCLK
?。?) JLINKV8 需要的硬件接口為: VCC, GND, RST, SWDIO, SWDCLK
注:下面有我自己實(shí)驗(yàn)的結(jié)果
?。?) ULINK1 不支持 SWD 模式
?。?) 盜版 ULINK2 需要的硬件接口為: GND, RST, SWDIO, SWDCLK
(6) 正版 ULINK2 需要的硬件接口為: GND, RST, SWDIO, SWDCLK
由此可以看到只有 JLINKV8 需要 5 個(gè)引腳。 那么給大家介紹下為什么有了 VCC 這個(gè)引腳時(shí)候有好處, 我的個(gè)人理解: 我認(rèn)為有這個(gè)引腳是最合適的, 仿真器對(duì)目標(biāo)板子的仿真需要用到 RST 引腳, 其實(shí)使用仿真器內(nèi)部的 VCC 做這個(gè)功能其實(shí)并不是非常美妙。 因此 JLINKV8 選擇了只和目標(biāo)板共 GND, 但是不共 VCC. 因此我覺得這種模式最合理, 當(dāng)然通常情況下仿真器和目標(biāo)板共 GND 和 VCC 是沒有錯(cuò)的。
?。?) 在 MDK 中SWD 模式設(shè)置
接下來告訴大家怎么使用 SWD 設(shè)置:
打開工程 Option 設(shè)置:
在設(shè)置中按照上圖設(shè)置成 SWD 模式, 速度你可以按照你的實(shí)際需求來設(shè)置, 如果你的板子供電系統(tǒng)不是特別穩(wěn)定, 紋波比較大或者仿真線比較長(zhǎng)可以設(shè)置成 500K 或者 1M , 如果環(huán)境很好當(dāng)然可以選擇 10M , 當(dāng)然速度會(huì)飛起來。 記得不要忽略了左下方的那個(gè)USB 還是 TCP 模式, 當(dāng)然我們是 USB 模式, 因?yàn)橛械臅r(shí)候默認(rèn)是 TCP 模式, 這個(gè)時(shí)候我們忽略這個(gè)設(shè)置后會(huì)仿真常常連接不上的。
JTAG引腳可以被復(fù)用為IO口,但是這樣一來,JLINK就不能夠連上芯片了。解決的方法有兩種:
?。?) 另寫一段程序,不要將JTAG復(fù)用為I/O口,然后將這段程序用串口工具寫入芯片中;
?。?) 將BOOT0/BOOT1設(shè)置成為內(nèi)部RAM啟動(dòng),那么上電后就不會(huì)執(zhí)行FLASH中的程序,這樣JLINK就能順利“接管”JTAG引腳。
按SW方式來調(diào)試
1腳不接時(shí)出現(xiàn)的畫面
DMA初步
DMA有什么用?
直接存儲(chǔ)器存取用來提供在外設(shè)和存儲(chǔ)器之間或者存儲(chǔ)器和存儲(chǔ)器之間的高速數(shù)據(jù)傳輸。無須CPU的干預(yù),通過DMA數(shù)據(jù)可以快速地移動(dòng)。這就節(jié)省了CPU的資源來做其他操作。
有多少個(gè)DMA資源
有兩個(gè)DMA控制器,DMA1有7個(gè)通道,DMA2有5個(gè)通道。
數(shù)據(jù)從什么地方送到什么地方?
外設(shè)到SRAM(I2C/UART等獲取數(shù)據(jù)并送入SRAM);
SRAM的兩個(gè)區(qū)域之間;
外設(shè)到外設(shè)(ADC讀取數(shù)據(jù)后送到TIM1控制其產(chǎn)生不同的PWM占空比);
SRAM到外設(shè)(SRAM中預(yù)先保存的數(shù)據(jù)送入DAC產(chǎn)生各種波形);
……還有一些目前還搞不清楚的。
DMA可以傳遞多少數(shù)據(jù)?
傳統(tǒng)的DMA的概念是用于大批量數(shù)據(jù)的傳輸,但是我理解,在STM32中,它的概念被擴(kuò)展了,也許更多的時(shí)候快速是其應(yīng)用的重點(diǎn)。數(shù)據(jù)可以從1~65535個(gè)。
通道是如何分配的?
見下面的這個(gè)表:
如何來用DMA?
確定數(shù)據(jù)來源,確定數(shù)據(jù)目的地,選擇使用哪個(gè)通道,設(shè)定傳輸多少個(gè)數(shù)據(jù),設(shè)定數(shù)據(jù)傳遞模式等等就可以了。且讀一下STM32提供給我們的例子。
//////////////////////////////////////////
……
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM1_CCR3_Address;
//設(shè)定外圍設(shè)備的地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SRC_Buffer;
//設(shè)定內(nèi)存地址,SRC_Buffer是前面定義的一個(gè)數(shù)組
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向控制
DMA_InitStructure.DMA_BufferSize = 3; //緩沖區(qū)大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外圍地址增量控制
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內(nèi)存地址增量控制
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
//DMA_PeripheralDataSize_HalfWord的值為0x100,后一個(gè)為0x400,在在stm32f10x_dma.h中定義,用于決定存儲(chǔ)器數(shù)據(jù)寬度
*/
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
以下是stm32f10x_dma.c中有關(guān)DMA的初始化設(shè)置代碼
tmpreg |= DMA_InitStruct-》DMA_DIR | DMA_InitStruct-》DMA_Mode |
DMA_InitStruct-》DMA_PeripheralInc | DMA_InitStruct-》DMA_MemoryInc |
DMA_InitStruct-》DMA_PeripheralDataSize | DMA_InitStruct-》DMA_MemoryDataSize |
DMA_InitStruct-》DMA_Priority | DMA_InitStruct-》DMA_M2M;
/* Write to DMAy Channelx CCR */
DMAy_Channelx-》CCR = tmpreg;
/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/
/* Write to DMAy Channelx CNDTR */
DMAy_Channelx-》CNDTR = DMA_InitStruct-》DMA_BufferSize;
/*--------------------------- DMAy Channelx CPAR Configuration ----------------*/
/* Write to DMAy Channelx CPAR */
DMAy_Channelx-》CPAR = DMA_InitStruct-》DMA_PeripheralBaseAddr;
/*--------------------------- DMAy Channelx CMAR Configuration ----------------*/
/* Write to DMAy Channelx CMAR */
DMAy_Channelx-》CMAR = DMA_InitStruct-》DMA_MemoryBaseAddr;
//內(nèi)存地址送入CMAR寄存器
說明:這個(gè)圖從PDF截下來,圖中那個(gè)DMA_CPARx寫錯(cuò)了,應(yīng)該是DMA_CMARx
------------------------------------------------------------------------------------------
再來看一個(gè)DMA的例子
/* DMA1 Channel5 configuration ----------------------------------------------*/
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM1_CCR1_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC1_DR_Address;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
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_Channel5, &DMA_InitStructure);
/* Enable DMA1 Channel5 */
DMA_Cmd(DMA1_Channel5, ENABLE);
還有一些目前暫時(shí)還沒有去搞清楚的,比如中斷處理等,等到需要時(shí)再看吧。
——下次將討論學(xué)習(xí)用認(rèn)識(shí)ADC兼進(jìn)一步看懂STM的庫,敬請(qǐng)繼續(xù)關(guān)注。
?
評(píng)論