1 前言
直接存儲(chǔ)器訪問(Direct Memory Access),簡稱
DMA。DMA是CPU一個(gè)用于數(shù)據(jù)從一個(gè)地址空間到另一地址空間“搬運(yùn)”(拷貝)的組件,數(shù)據(jù)拷貝過程不需CPU干預(yù),數(shù)據(jù)拷貝結(jié)束則通知CPU處理。因此,大量數(shù)據(jù)拷貝時(shí),使用DMA可以釋放CPU資源。DMA數(shù)據(jù)拷貝過程,典型的有:
- 內(nèi)存—>內(nèi)存,內(nèi)存間拷貝
- 外設(shè)—>內(nèi)存,如uart、spi、i2c等總線接收數(shù)據(jù)過程
- 內(nèi)存—>外設(shè),如uart、spi、i2c等總線發(fā)送數(shù)據(jù)過程
2 串口有必要使用DMA嗎
串口(uart)是一種低速的串行異步通信,適用于低速通信場景,通常使用的波特率小于或等于115200bps。對于小于或者等于115200bps波特率的,而且數(shù)據(jù)量不大的通信場景,一般沒必要使用DMA,或者說使用DMA并未能充分發(fā)揮出DMA的作用。對于數(shù)量大,或者波特率提高時(shí),必須使用DMA以釋放CPU資源,因?yàn)楦卟ㄌ芈士赡軒磉@樣的問題:
- 對于發(fā)送,使用循環(huán)發(fā)送,可能阻塞線程,需要消耗大量CPU資源“搬運(yùn)”數(shù)據(jù),浪費(fèi)CPU
- 對于發(fā)送,使用中斷發(fā)送,不會(huì)阻塞線程,但需浪費(fèi)大量中斷資源,CPU頻繁響應(yīng)中斷;以115200bps波特率,1s傳輸11520字節(jié),大約69us需響應(yīng)一次中斷,如波特率再提高,將消耗更多CPU資源
- 對于接收,如仍采用傳統(tǒng)的中斷模式接收,同樣會(huì)因?yàn)轭l繁中斷導(dǎo)致消耗大量CPU資源
因此,高波特率場景下,串口非常有必要使用DMA。
3 實(shí)現(xiàn)方式
4 STM32串口使用DMA
關(guān)于STM32串口使用DMA,不乏一些開發(fā)板例程及網(wǎng)絡(luò)上一些博主的使用教程。使用步驟、流程、配置基本大同小異,正確性也沒什么毛病,但都是一些基本的Demo例子,作為學(xué)習(xí)過程沒問題;實(shí)際項(xiàng)目使用缺乏嚴(yán)謹(jǐn)性,數(shù)據(jù)量大時(shí)可能導(dǎo)致數(shù)據(jù)異常。測試平臺(tái):
- STM32F030C8T6
- UART1/UART2
- DMA1 Channel2—Channel5
- ST標(biāo)準(zhǔn)庫
- 主頻48MHz(外部12MHz晶振)
5 串口DMA接收
5.1 基本流程
5.2 相關(guān)配置
關(guān)鍵步驟【1】初始化串口【2】使能串口DMA接收模式,使能串口空閑中斷【3】配置DMA參數(shù),使能DMA通道buf半滿(傳輸一半數(shù)據(jù))中斷、buf溢滿(傳輸數(shù)據(jù)完成)中斷為什么需要使用DMA 通道buf半滿中斷?很多串口DMA模式接收的教程、例子,基本是使用了“空間中斷” “DMA傳輸完成中斷”來接收數(shù)據(jù)。實(shí)質(zhì)上這是存在風(fēng)險(xiǎn)的,當(dāng)DMA傳輸數(shù)據(jù)完成,CPU介入開始拷貝DMA通道buf數(shù)據(jù),如果此時(shí)串口繼續(xù)有數(shù)據(jù)進(jìn)來,DMA繼續(xù)搬運(yùn)數(shù)據(jù)到buf,就有可能將數(shù)據(jù)覆蓋,因?yàn)镈MA數(shù)據(jù)搬運(yùn)是不受CPU控制的,即使你關(guān)閉了CPU中斷。嚴(yán)謹(jǐn)?shù)淖龇ㄐ枰鲭pbuf,CPU和DMA各自一塊內(nèi)存交替訪問,即是"乒乓緩存” ,處理流程步驟應(yīng)該是這樣:【1】第一步,DMA先將數(shù)據(jù)搬運(yùn)到buf1,搬運(yùn)完成通知CPU來拷貝buf1數(shù)據(jù) 【2】第二步,DMA將數(shù)據(jù)搬運(yùn)到buf2,與CPU拷貝buf1數(shù)據(jù)不會(huì)沖突 【3】第三步,buf2數(shù)據(jù)搬運(yùn)完成,通知CPU來拷貝buf2數(shù)據(jù) 【4】執(zhí)行完第三步,DMA返回執(zhí)行第一步,一直循環(huán) STM32F0系列DMA不支持雙緩存(以具體型號(hào)為準(zhǔn))機(jī)制,但提供了一個(gè)buf
"半滿中斷"
,即是數(shù)據(jù)搬運(yùn)到buf大小的一半時(shí),可以產(chǎn)生一個(gè)中斷信號(hào)。基于這個(gè)機(jī)制,我們可以實(shí)現(xiàn)雙緩存功能,只需將buf空間開辟大一點(diǎn)即可。【1】第一步,DMA將數(shù)據(jù)搬運(yùn)完成buf的前一半時(shí),產(chǎn)生“半滿中斷”,CPU來拷貝buf前半部分?jǐn)?shù)據(jù) 【2】第二步,DMA繼續(xù)將數(shù)據(jù)搬運(yùn)到buf的后半部分,與CPU拷貝buf前半部數(shù)據(jù)不會(huì)沖突 【3】第三步,buf后半部分?jǐn)?shù)據(jù)搬運(yùn)完成,觸發(fā)“溢滿中斷”,CPU來拷貝buf后半部分?jǐn)?shù)據(jù) 【4】執(zhí)行完第三步,DMA返回執(zhí)行第一步,一直循環(huán) UART2 DMA模式接收配置代碼如下,與其他外設(shè)使用DMA的配置基本一致,留意關(guān)鍵配置:
- 串口接收,DMA通道工作模式設(shè)為連續(xù)模式
- 使能DMA通道接收buf半滿中斷、溢滿(傳輸完成)中斷
- 啟動(dòng)DMA通道前清空相關(guān)狀態(tài)標(biāo)識(shí),防止首次傳輸錯(cuò)亂數(shù)據(jù)
- void?bsp_uart2_dmarx_config(uint8_t?*mem_addr,?uint32_t?mem_size)
- {
- ???DMA_InitTypeDef?DMA_InitStructure;
- ?
- ?DMA_DeInit(DMA1_Channel5);?
- ?DMA_Cmd(DMA1_Channel5,?DISABLE);
- ?DMA_InitStructure.DMA_PeripheralBaseAddr??=?(uint32_t)