STM32F10x 學習筆記8(USART實現串口通訊 DMA 方式)
在普通的8位或16位單片機中很少有包含DMA控制器的,所以可能許多嵌入式程序員對DMA方式并不熟悉。簡單的說,直接存儲器存取(DMA)用來提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。由于無須CPU干預,數據可以通過DMA快速地移動,這就節(jié)省了CPU的資源來做其他操作。
STM32F10x上具有兩個DMA控制器,共有12個通道(DMA1有7個通道,DMA2有5個通道),每個通道專門用來管理來自于一個或多個外設對存儲器訪問的請求。還有一個仲裁器來協(xié)調各個DMA請求的優(yōu)先權。
按照STM32參考手冊上的說法:“DMA控制器和Cortex™-M3核心共享系統(tǒng)數據總線,執(zhí)行直接存儲器數據傳輸。當CPU和DMA同時訪問相同的目標(RAM或外設)時,DMA請求會暫停CPU訪問系統(tǒng)總線達若干個周期,總線仲裁器執(zhí)行循環(huán)調度,以保證CPU至少可以得到一半的系統(tǒng)總線(存儲器或外設)帶寬。”所以我們不必擔心DMA控制器霸占總線資源。CPU總是可以得到一般的總線時間的。
下面我們以USART2的數據發(fā)送為例來介紹DMA。首先由STM32參考手冊的圖22可知。USART2的發(fā)送功能可以使用DMA1的第7個通道。
利用的DMA傳輸的設置工作大體可以分為6步:
1.在DMA1_CPAR7寄存器中設置外設寄存器的地址。發(fā)生外設數據傳輸請求時,這個地址將是數據傳輸的源或目標。
2.在DMA1_CMAR7寄存器中設置數據存儲器的地址。發(fā)生外設數據傳輸請求時,傳輸的數據將從這個地址讀出或寫入這個地址。
3.在DMA1_CNDTR7寄存器中設置要傳輸的數據量。在每個數據傳輸后,這個數值遞減。
4.在DMA1_CCR7寄存器的PL[1:0]位中設置通道的優(yōu)先級。
5.在DMA1_CCR7寄存器中設置數據傳輸的方向、循環(huán)模式、外設和存儲器的增量模式、外設和存儲器的數據寬度、傳輸一半產生中斷或傳輸完成產生中斷。
6.設置DMA1_CCR7寄存器的ENABLE位,啟動該通道。
第1步對應的代碼為:
- DMA1_Channel7->CPAR=(uint32_t)&(USART2->DR);
第2步對應的代碼如下,其中p_str是一個指針,指向要傳輸的數據的首地址:
- DMA1_Channel7->CMAR=(uint32_t)p_str;
- DMA1_Channel7->CNDTR=cnt;
- DMA1_Channel7->CCR|=DMA_Priority_Low;
- DMA1_Channel7->CCR|=DMA_DIR_PeripheralDST|
- DMA_Mode_Normal|
- DMA_PeripheralInc_Disable|
- DMA_MemoryInc_Enable|
- DMA_PeripheralDataSize_Byte|
- DMA_MemoryDataSize_Byte|
- DMA_M2M_Disable;
- DMA1_Channel7->CCR|=DMA_CCR1_EN;
實際上在這6步之前應該還有2步操作。首先設置DMA之前,要打開DMA的時鐘:
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
- USARTx->CR3|=USART_DMAReq_Tx;
或者用下面的函數:
- USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
一旦啟動了DMA通道,它既可響應連到該通道上的外設的DMA請求。完成一次DMA傳輸后如何開啟下一次傳輸呢,這個問題困擾了我好幾天,最后在STM32參考手冊上發(fā)現如下的一句話:
當通道配置為非循環(huán)模式時,傳輸結束后(即傳輸計數變?yōu)?)將不再產生DMA操作。要開始新的DMA傳輸,需要在關閉DMA通道的情況下,在DMA_CNDTRx寄存器中重新寫入傳輸數目。
下面先給一個簡單的示例程序:
- voidUSART2_Init(void)
- {
- GPIO_InitTypeDefGPIO_InitStructure;
- USART_InitTypeDefUSART_InitStructure;
- NVIC_InitTypeDefNVIC_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_AFIO,ENABLE);
- /*ConfigureUSARTTxasalternatefunctionpush-pull*/
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- /*ConfigureUSARTRxasinputfloating*/
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- GPIO_PinRemapConfig(GPIO_Remap_USART2,ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
- USART_InitStructure.USART_BaudRate=9600;
- USART_InitStructure.USART_WordLength=USART_WordLength_8b;
- USART_InitStructure.USART_StopBits=USART_StopBits_1;
- USART_InitStructure.USART_Parity=USART_Parity_No;
- USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
- USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
- USART_Init(USART2,&USART_InitStructure);
- USART_Cmd(USART2,ENABLE);
- }
- voidUART2_TX_DMA_Init(uint8_t*p_str,uint16_tcnt)
- {
- //DMA_InitTypeDefDMA_InitStructure;
- //DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&(USART2->DR);
- //DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)str;
- //DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
- //DMA_InitStructure.DMA_BufferSize=14;
- //DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
- //DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
- //DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
- //DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
- //DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
- //DMA_InitStructure.DMA_Priority=DMA_Priority_Low;
- //DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
- //DMA_Init(DMA1_Channel7,&DMA_InitStructure);
- DMA1_Channel7->CPAR=(uint32_t)&(USART2->DR);
- DMA1_Channel7->CMAR=(uint32_t)p_str;
- DMA1_Channel7->CNDTR=cnt;
- DMA1_Channel7->CCR=DMA_DIR_PeripheralDST|DMA_Priority_Low|
- DMA_Mode_Normal|DMA_PeripheralInc_Disable|
- DMA_MemoryInc_Enable|DMA_PeripheralDataSize_Byte|
- DMA_MemoryDataSize_Byte|DMA_M2M_Disable;
- }
- uint8_tstr[]="HelloWorld!!!";
- voidTaskStart(void*pdata)
- {
- SysTick_Config(SystemCoreClock/10);
- USART2_Init();
- UART2_TX_DMA_Init(str,14);
- for(;;)
- {
- LED_Spark();
- //DMA_Cmd(DMA1_Channel7,DISABLE);
- DMA1_Channel7->CCR&=(uint16_t)(~DMA_CCR1_EN);
- //DMA_Init(DMA1_Channel7,&DMA_InitStructure);
- DMA1_Channel7->CNDTR=14;
- //DMA_Cmd(DMA1_Channel7,ENABLE);
- DMA1_Channel7->CCR|=DMA_CCR1_EN;
- //USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
- OSTimeDly(10);
- }
- }
- intmain(void)
- {
- SystemInit();
- LED_Init();
- OSInit();
- OSTaskCreate(TaskStart,(void*)0,&(TaskStartStk[TASK_STK_SIZE-1]),1);
- OSStart();
- for(;;)
- {
- }
- }
下面再說說如何在DMA傳輸完成之后產生中斷。當傳輸一半的數據后,半傳輸標志(HTIF)被置1,當設置了允許半傳輸中斷位(HTIE)時,將產生一個中斷請求。在數據傳輸結束后,傳輸完成標志(TCIF)被置1,當設置了允許傳輸完成中斷位(TCIE)時,將產生一個中斷請求。
DMA的CCR寄存器中有1位TCIE(Transfercompleteinterruptenable)
該位由軟件設置和清除。
0:禁止TC中斷
1:允許TC中斷
所以為了使用DMA中斷,我們需要下面的代碼:
- DMA1_Channel7->CCR|=DMA_IT_TC;//Transfercompleteinterruptenable
- NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel7_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=5;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
- NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
- NVIC_Init(&NVIC_InitStructure);
下面還是給個簡單的示例程序,示例程序中還用到了uCOS的信號量,中斷處理函數通過信號量通知Task完成了DMA傳輸:
- #include"stm32f10x.h"
- #include"uart.h"
- #include"led.h"
- #include"COMMRTOS.H"
- #include"ucos_ii.h"
- #defineTASK_STK_SIZE128
- OS_STKTaskStartStk[TASK_STK_SIZE];
- voidUSART2_Init(void)
- {
- GPIO_InitTypeDefGPIO_InitStructure;
- USART_InitTypeDefUSART_InitStructure;
- NVIC_InitTypeDefNVIC_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_AFIO,ENABLE);
- /*ConfigureUSARTTxasalternatefunctionpush-pull*/
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- /*ConfigureUSARTRxasinputfloating*/
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- GPIO_PinRemapConfig(GPIO_Remap_USART2,ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
- USART_InitStructure.USART_BaudRate=9600;
- USART_InitStructure.USART_WordLength=USART_WordLength_8b;
- USART_InitStructure.USART_StopBits=USART_StopBits_1;
- USART_InitStructure.USART_Parity=USART_Parity_No;
- USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
- USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
- USART_Init(USART2,&USART_InitStructure);
- USART_Cmd(USART2,ENABLE);
- NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
- NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- }
- voidUART2_TX_DMA_Init(uint8_t*p_str,uint16_tcnt)
- {
- //DMA_InitTypeDefDMA_InitStructure;
- //DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&(USART2->DR);
- //DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)str;
- //DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
- //DMA_InitStructure.DMA_BufferSize=14;
- //DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
- //DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
- //DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
- //DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
- //DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
- //DMA_InitStructure.DMA_Priority=DMA_Priority_Low;
- //DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
- //DMA_Init(DMA1_Channel7,&DMA_InitStructure);
- DMA1_Channel7->CPAR=(uint32_t)&(USART2->DR);
- DMA1_Channel7->CMAR=(uint32_t)p_str;
- DMA1_Channel7->CNDTR=cnt;
- DMA1_Channel7->CCR=DMA_DIR_PeripheralDST|DMA_Priority_Low|
- DMA_Mode_Normal|DMA_PeripheralInc_Disable|
- DMA_MemoryInc_Enable|DMA_PeripheralDataSize_Byte|
- DMA_MemoryDataSize_Byte|DMA_M2M_Disable;
- }
- OS_EVENT*UART2_DMA_TX_Sem;
- uint8_tstr[]="HelloWorld!!!";
- voidTaskStart(void*pdata)
- {
- unsignedcharerr;
- SysTick_Config(SystemCoreClock/10);
- USART2_Init();
- USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
- UART2_TX_DMA_Init(str);
- UART2_DMA_TX_Sem=OSSemCreate(1);
- for(;;)
- {
- LED_Spark();
- OSSemPend(UART2_DMA_TX_Sem,0,&err);
- //DMA_Cmd(DMA1_Channel7,DISABLE);
- DMA1_Channel7->CCR&=(uint16_t)(~DMA_CCR1_EN);
- //DMA_Init(DMA1_Channel7,&DMA_InitStructure);
- DMA1_Channel7->CNDTR=14;
- //DMA_Cmd(DMA1_Channel7,ENABLE);
- DMA1_Channel7->CCR|=DMA_CCR1_EN;
- //USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
- OSTimeDly(10);
- }
- }
- intmain(void)
- {
- SystemInit();
- LED_Init();
- OSInit();
- OSTaskCreate(TaskStart,(void*)0,&(TaskStartStk[TASK_STK_SIZE-1]),1);
- OSStart();
- for(;;)
- {
- }
- }
- voidDMA1_Channel7_IRQHandler(void)
- {
- OS_CPU_SRcpu_sr;
- OS_ENTER_CRITICAL();/*TelluC/OS-IIthatwearestartinganISR*/
- OSIntNesting++;
- OS_EXIT_CRITICAL();
- OSSemPost(UART2_DMA_TX_Sem);
- //UART_PutChar(USART2,+);
- DMA1->IFCR=DMA1_FLAG_TC7;
- OSIntExit();
- }
評論