新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > STM32F10x 學習筆記8(USART實現串口通訊 DMA 方式)

        STM32F10x 學習筆記8(USART實現串口通訊 DMA 方式)

        作者: 時間:2016-11-20 來源:網絡 收藏
        STM32F10xUSART支持DMA方式,并且在DMA完成后可以產生中斷。這對于需要接收或發送大量數據的應用情景是很有幫助的。

        本文引用地址:http://www.104case.com/article/201611/318844.htm

        在普通的8位或16位單片機中很少有包含DMA控制器的,所以可能許多嵌入式程序員對DMA方式并不熟悉。簡單的說,直接存儲器存取(DMA)用來提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。由于無須CPU干預,數據可以通過DMA快速地移動,這就節省了CPU的資源來做其他操作。

        STM32F10x上具有兩個DMA控制器,共有12個通道(DMA1有7個通道,DMA2有5個通道),每個通道專門用來管理來自于一個或多個外設對存儲器訪問的請求。還有一個仲裁器來協調各個DMA請求的優先權。

        按照STM32參考手冊上的說法:“DMA控制器和Cortex™-M3核心共享系統數據總線,執行直接存儲器數據傳輸。當CPU和DMA同時訪問相同的目標(RAM或外設)時,DMA請求會暫停CPU訪問系統總線達若干個周期,總線仲裁器執行循環調度,以保證CPU至少可以得到一半的系統總線(存儲器或外設)帶寬。”所以我們不必擔心DMA控制器霸占總線資源。CPU總是可以得到一般的總線時間的。

        下面我們以USART2的數據發送為例來介紹DMA。首先由STM32參考手冊的圖22可知。USART2的發送功能可以使用DMA1的第7個通道。

        利用的DMA傳輸的設置工作大體可以分為6步:

        1.在DMA1_CPAR7寄存器中設置外設寄存器的地址。發生外設數據傳輸請求時,這個地址將是數據傳輸的源或目標。

        2.在DMA1_CMAR7寄存器中設置數據存儲器的地址。發生外設數據傳輸請求時,傳輸的數據將從這個地址讀出或寫入這個地址。

        3.在DMA1_CNDTR7寄存器中設置要傳輸的數據量。在每個數據傳輸后,這個數值遞減。

        4.在DMA1_CCR7寄存器的PL[1:0]位中設置通道的優先級。

        5.在DMA1_CCR7寄存器中設置數據傳輸的方向、循環模式、外設和存儲器的增量模式、外設和存儲器的數據寬度、傳輸一半產生中斷或傳輸完成產生中斷。

        6.設置DMA1_CCR7寄存器的ENABLE位,啟動該通道。

        第1步對應的代碼為:

        1. DMA1_Channel7->CPAR=(uint32_t)&(USART2->DR);

        第2步對應的代碼如下,其中p_str是一個指針,指向要傳輸的數據的首地址:

        1. DMA1_Channel7->CMAR=(uint32_t)p_str;
        第3步對應的代碼如下,cnt為要傳輸的數據量,串口數據是以字節為傳輸單位的,所以這里cnt就是要傳輸數據的字節數。

        1. DMA1_Channel7->CNDTR=cnt;
        第4步對應的代碼如下,DMA通道的優先級分為4級,分別是:DMA_Priority_VeryHigh、DMA_Priority_High、DMA_Priority_Medium、DMA_Priority_Low。這里設為最低。

        1. DMA1_Channel7->CCR|=DMA_Priority_Low;
        第5步對應的代碼如下:

        1. DMA1_Channel7->CCR|=DMA_DIR_PeripheralDST|
        2. DMA_Mode_Normal|
        3. DMA_PeripheralInc_Disable|
        4. DMA_MemoryInc_Enable|
        5. DMA_PeripheralDataSize_Byte|
        6. DMA_MemoryDataSize_Byte|
        7. DMA_M2M_Disable;
        第6步對應的代碼如下:

        1. DMA1_Channel7->CCR|=DMA_CCR1_EN;

        實際上在這6步之前應該還有2步操作。首先設置DMA之前,要打開DMA的時鐘:

        1. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
        其次,也要設置USART使其支持DMA方式:
        1. USARTx->CR3|=USART_DMAReq_Tx;

        或者用下面的函數:


        1. USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);

        一旦啟動了DMA通道,它既可響應連到該通道上的外設的DMA請求。完成一次DMA傳輸后如何開啟下一次傳輸呢,這個問題困擾了我好幾天,最后在STM32參考手冊上發現如下的一句話:

        當通道配置為非循環模式時,傳輸結束后(即傳輸計數變為0)將不再產生DMA操作。要開始新的DMA傳輸,需要在關閉DMA通道的情況下,在DMA_CNDTRx寄存器中重新寫入傳輸數目。

        下面先給一個簡單的示例程序:

        1. voidUSART2_Init(void)
        2. {
        3. GPIO_InitTypeDefGPIO_InitStructure;
        4. USART_InitTypeDefUSART_InitStructure;
        5. NVIC_InitTypeDefNVIC_InitStructure;
        6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_AFIO,ENABLE);
        7. /*ConfigureUSARTTxasalternatefunctionpush-pull*/
        8. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
        9. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
        10. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
        11. GPIO_Init(GPIOD,&GPIO_InitStructure);
        12. /*ConfigureUSARTRxasinputfloating*/
        13. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
        14. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
        15. GPIO_Init(GPIOD,&GPIO_InitStructure);
        16. GPIO_PinRemapConfig(GPIO_Remap_USART2,ENABLE);
        17. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
        18. USART_InitStructure.USART_BaudRate=9600;
        19. USART_InitStructure.USART_WordLength=USART_WordLength_8b;
        20. USART_InitStructure.USART_StopBits=USART_StopBits_1;
        21. USART_InitStructure.USART_Parity=USART_Parity_No;
        22. USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
        23. USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
        24. USART_Init(USART2,&USART_InitStructure);
        25. USART_Cmd(USART2,ENABLE);
        26. }
        27. voidUART2_TX_DMA_Init(uint8_t*p_str,uint16_tcnt)
        28. {
        29. //DMA_InitTypeDefDMA_InitStructure;
        30. //DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&(USART2->DR);
        31. //DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)str;
        32. //DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
        33. //DMA_InitStructure.DMA_BufferSize=14;
        34. //DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
        35. //DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
        36. //DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
        37. //DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
        38. //DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
        39. //DMA_InitStructure.DMA_Priority=DMA_Priority_Low;
        40. //DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
        41. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
        42. //DMA_Init(DMA1_Channel7,&DMA_InitStructure);
        43. DMA1_Channel7->CPAR=(uint32_t)&(USART2->DR);
        44. DMA1_Channel7->CMAR=(uint32_t)p_str;
        45. DMA1_Channel7->CNDTR=cnt;
        46. DMA1_Channel7->CCR=DMA_DIR_PeripheralDST|DMA_Priority_Low|
        47. DMA_Mode_Normal|DMA_PeripheralInc_Disable|
        48. DMA_MemoryInc_Enable|DMA_PeripheralDataSize_Byte|
        49. DMA_MemoryDataSize_Byte|DMA_M2M_Disable;
        50. }
        51. uint8_tstr[]="HelloWorld!!!";
        52. voidTaskStart(void*pdata)
        53. {
        54. SysTick_Config(SystemCoreClock/10);
        55. USART2_Init();
        56. UART2_TX_DMA_Init(str,14);
        57. for(;;)
        58. {
        59. LED_Spark();
        60. //DMA_Cmd(DMA1_Channel7,DISABLE);
        61. DMA1_Channel7->CCR&=(uint16_t)(~DMA_CCR1_EN);
        62. //DMA_Init(DMA1_Channel7,&DMA_InitStructure);
        63. DMA1_Channel7->CNDTR=14;
        64. //DMA_Cmd(DMA1_Channel7,ENABLE);
        65. DMA1_Channel7->CCR|=DMA_CCR1_EN;
        66. //USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
        67. OSTimeDly(10);
        68. }
        69. }
        70. intmain(void)
        71. {
        72. SystemInit();
        73. LED_Init();
        74. OSInit();
        75. OSTaskCreate(TaskStart,(void*)0,&(TaskStartStk[TASK_STK_SIZE-1]),1);
        76. OSStart();
        77. for(;;)
        78. {
        79. }
        80. }

        下面再說說如何在DMA傳輸完成之后產生中斷。當傳輸一半的數據后,半傳輸標志(HTIF)被置1,當設置了允許半傳輸中斷位(HTIE)時,將產生一個中斷請求。在數據傳輸結束后,傳輸完成標志(TCIF)被置1,當設置了允許傳輸完成中斷位(TCIE)時,將產生一個中斷請求。

        DMA的CCR寄存器中有1位TCIE(Transfercompleteinterruptenable)

        該位由軟件設置和清除。

        0:禁止TC中斷

        1:允許TC中斷

        所以為了使用DMA中斷,我們需要下面的代碼:

        1. DMA1_Channel7->CCR|=DMA_IT_TC;//Transfercompleteinterruptenable
        1. NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel7_IRQn;
        2. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=5;
        3. NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
        4. NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        5. NVIC_Init(&NVIC_InitStructure);

        下面還是給個簡單的示例程序,示例程序中還用到了uCOS的信號量,中斷處理函數通過信號量通知Task完成了DMA傳輸:

        1. #include"stm32f10x.h"
        2. #include"uart.h"
        3. #include"led.h"
        4. #include"COMMRTOS.H"
        5. #include"ucos_ii.h"
        6. #defineTASK_STK_SIZE128
        7. OS_STKTaskStartStk[TASK_STK_SIZE];
        8. voidUSART2_Init(void)
        9. {
        10. GPIO_InitTypeDefGPIO_InitStructure;
        11. USART_InitTypeDefUSART_InitStructure;
        12. NVIC_InitTypeDefNVIC_InitStructure;
        13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_AFIO,ENABLE);
        14. /*ConfigureUSARTTxasalternatefunctionpush-pull*/
        15. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
        16. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
        17. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
        18. GPIO_Init(GPIOD,&GPIO_InitStructure);
        19. /*ConfigureUSARTRxasinputfloating*/
        20. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
        21. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
        22. GPIO_Init(GPIOD,&GPIO_InitStructure);
        23. GPIO_PinRemapConfig(GPIO_Remap_USART2,ENABLE);
        24. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
        25. USART_InitStructure.USART_BaudRate=9600;
        26. USART_InitStructure.USART_WordLength=USART_WordLength_8b;
        27. USART_InitStructure.USART_StopBits=USART_StopBits_1;
        28. USART_InitStructure.USART_Parity=USART_Parity_No;
        29. USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
        30. USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
        31. USART_Init(USART2,&USART_InitStructure);
        32. USART_Cmd(USART2,ENABLE);
        33. NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
        34. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
        35. NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
        36. NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        37. NVIC_Init(&NVIC_InitStructure);
        38. }
        39. voidUART2_TX_DMA_Init(uint8_t*p_str,uint16_tcnt)
        40. {
        41. //DMA_InitTypeDefDMA_InitStructure;
        42. //DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&(USART2->DR);
        43. //DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)str;
        44. //DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
        45. //DMA_InitStructure.DMA_BufferSize=14;
        46. //DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
        47. //DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
        48. //DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
        49. //DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
        50. //DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
        51. //DMA_InitStructure.DMA_Priority=DMA_Priority_Low;
        52. //DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
        53. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
        54. //DMA_Init(DMA1_Channel7,&DMA_InitStructure);
        55. DMA1_Channel7->CPAR=(uint32_t)&(USART2->DR);
        56. DMA1_Channel7->CMAR=(uint32_t)p_str;
        57. DMA1_Channel7->CNDTR=cnt;
        58. DMA1_Channel7->CCR=DMA_DIR_PeripheralDST|DMA_Priority_Low|
        59. DMA_Mode_Normal|DMA_PeripheralInc_Disable|
        60. DMA_MemoryInc_Enable|DMA_PeripheralDataSize_Byte|
        61. DMA_MemoryDataSize_Byte|DMA_M2M_Disable;
        62. }
        63. OS_EVENT*UART2_DMA_TX_Sem;
        64. uint8_tstr[]="HelloWorld!!!";
        65. voidTaskStart(void*pdata)
        66. {
        67. unsignedcharerr;
        68. SysTick_Config(SystemCoreClock/10);
        69. USART2_Init();
        70. USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
        71. UART2_TX_DMA_Init(str);
        72. UART2_DMA_TX_Sem=OSSemCreate(1);
        73. for(;;)
        74. {
        75. LED_Spark();
        76. OSSemPend(UART2_DMA_TX_Sem,0,&err);
        77. //DMA_Cmd(DMA1_Channel7,DISABLE);
        78. DMA1_Channel7->CCR&=(uint16_t)(~DMA_CCR1_EN);
        79. //DMA_Init(DMA1_Channel7,&DMA_InitStructure);
        80. DMA1_Channel7->CNDTR=14;
        81. //DMA_Cmd(DMA1_Channel7,ENABLE);
        82. DMA1_Channel7->CCR|=DMA_CCR1_EN;
        83. //USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
        84. OSTimeDly(10);
        85. }
        86. }
        87. intmain(void)
        88. {
        89. SystemInit();
        90. LED_Init();
        91. OSInit();
        92. OSTaskCreate(TaskStart,(void*)0,&(TaskStartStk[TASK_STK_SIZE-1]),1);
        93. OSStart();
        94. for(;;)
        95. {
        96. }
        97. }
        98. voidDMA1_Channel7_IRQHandler(void)
        99. {
        100. OS_CPU_SRcpu_sr;
        101. OS_ENTER_CRITICAL();/*TelluC/OS-IIthatwearestartinganISR*/
        102. OSIntNesting++;
        103. OS_EXIT_CRITICAL();
        104. OSSemPost(UART2_DMA_TX_Sem);
        105. //UART_PutChar(USART2,+);
        106. DMA1->IFCR=DMA1_FLAG_TC7;
        107. OSIntExit();
        108. }



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 青阳县| 安乡县| 建平县| 澄江县| 秭归县| 龙里县| 托里县| 交口县| 兴业县| 柘荣县| 龙州县| 宜川县| 隆尧县| 武夷山市| 福泉市| 东乡族自治县| 茌平县| 龙陵县| 宁晋县| 太湖县| 怀远县| 鹤峰县| 进贤县| 克山县| 惠来县| 磐安县| 建平县| 承德县| 元氏县| 布拖县| 婺源县| 页游| 新宁县| 浮梁县| 正镶白旗| 石渠县| 古交市| 宾阳县| 秦皇岛市| 仁怀市| 阿图什市|