stm32F407之USART6的DMA工作方式


  

  力求簡潔,stm32的DMA就不介紹了,不了解的可以搜索一下。這里重點介紹一下DMA的外設地址如何確定,這個是網上很少涉及但是很重要的一塊,如果不清楚如何確定外設寄存器地址就無法進行DMA功能,這里以stm32F407的USART6為例介紹,參考手冊為“RM0090 Reference manual”。

  在進行DMA參數配置時有這樣一項 DMA_InitStructure.DMA_PeripheralBaseAddr = ?;這句是要確定Memory與Peripheral數據傳輸時的外設數據地址,因為這里我們用到的是USART6從Memory的數組中取出數據並發送給上位機,所以這里用到的外設地址其實是USART6的數據寄存器地址 USART6_DR,關鍵是確定他的地址。好了我們現在打開參考手冊,找到“Memory Map”一項,

  看到USART_DR的OFFSET地址為0x04,則USART6的真實地址為 0x4001 1400+0x04 = 0x4001 1404;這樣便確定了USART6_DR的地址。其他的就好說了,代碼如下

  /************************************************************

  Copyright (C), 2012-2022, yin.

  FileName: main.c

  Author: ycw Version : 1.0 Date: 2012.04.27

  Description: USART6 DMA SendData

  Version: V3.0

  Function List:USART6 DMA SendData

  History: V1.0

  YCW 12/04/27 1.0 build this moudle

  ***********************************************************/

  #include

  /*定義USART6的數據寄存器地址,DMA功能要用到外設的數據地址

  *USART6的數據地址為外設基地址+偏移地址,基地址在RM0090 Reference

  *manual(參考手冊)的地址映射表里(P50),為0x40011400,USART_DR

  *偏移地址在P657,為0x04,故實際地址為0x40011400+0x04 = 0x40011404 */

  #define USART6_DR_Addr 0x40011404

  /*定義一個數組,DMA工作時從內存取數組的數據傳給USART6 */

  uint8_t Buffer[] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};

  uint8_t Buffer2[] = {0x99,0x6f};

  void GPIO_Config(void);

  void USART_Config(void);

  void USART6_Puts(char * str);

  void DMA_Config(void);

  void NVIC_Config(void);

  void Delay(uint32_t nCount);

  main()

  {

  /*在主函數main之前通過調用啟動代碼運行了SystemInit函數,而這個函數位於system_stm32f4xx.c”。

  程序運行起始於啟動文件的第175行(LDR R0, =SystemInit)。sys時鍾為HSE頻率/PLL_M*PLL_N/PLL_P,

  定義HSE為25M,則sys時鍾頻率為168M */

  GPIO_Config();

  USART_Config();

  DMA_Config();

  NVIC_Config();

  GPIO_SetBits(GPIOG, GPIO_Pin_6); //關閉LED

  while (1)

  {

  USART_DMACmd(USART6, USART_DMAReq_Tx, ENABLE); //使能USART6的發送數據DMA請求,至此USART6與DMA開始工作

  /*因為DMA工作是獨立於CPU之外的,所以在DMA工作的同時CPU可以做其他事

  *我們等到DMA傳輸完畢后產生一個狀態指示,即點亮一個LED */

  /*查詢模式

  while (DMA_GetFlagStatus(DMA2_Stream6, DMA_FLAG_TCIF6) == RESET)

  {

  GPIO_ResetBits(GPIOG,GPIO_Pin_6); //點亮LED

  }

  */

  //DMA_Cmd(DMA2_Stream6, DISABLE); //DMA傳輸完畢后會自動關閉通道,這句可以不寫

  }

  }

  /*************************************************

  Function: void GPIO_Config(void)

  Description: GPIO配置函數

  Input: 無

  Output:無

  Return:無

  *************************************************/

  void GPIO_Config(void)

  {

  /*定義了一個GPIO_InitStructure的結構體,方便一下使用 */

  GPIO_InitTypeDef GPIO_InitStructure;

  /* 初始化GPIOG時鍾*/

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG , ENABLE);//使能GPIOG時鍾(時鍾結構參見“stm32圖解.pdf”)

  /*僅設置結構體中的部分成員:這種情況下,用戶應當首先調用函數PPP_SturcInit(..)

  *來初始化變量PPP_InitStructure,然后再修改其中需要修改的成員。這樣可以保證其他

  *成員的值(多為缺省值)被正確填入。

  */

  GPIO_StructInit(&GPIO_InitStructure);

  /* 初始化GPIOG的Pin_6為推挽輸出*/

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //指定第六引腳

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //模式為輸出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //頻率為快速

  GPIO_Init(GPIOG, &GPIO_InitStructure); //調用IO初始化函數

  }

  /*************************************************

  Function: void USART_Config(void)

  Description: USART配置函數

  Input: 無

  Output:無

  Return:無

  *************************************************/

  void USART_Config(void)

  {

  GPIO_InitTypeDef GPIO_InitStructure;

  USART_InitTypeDef USART_InitStructure;

  USART_ClockInitTypeDef USART_ClockInitStruct;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE); //開啟USART6時鍾

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //開啟GPIOC時鍾

  GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6);//這相當於M3的開啟復用時鍾?只配置復用的引腳,

  GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6);//

  /*配置GPIOC*/

  GPIO_StructInit(&GPIO_InitStructure); //缺省值填入

  /*配置GPIOC_Pin6為TX輸出*/

  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;

  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //設置為復用,必須為AF,OUT不行

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOC,&GPIO_InitStructure);

  /*配置GPIOC_Pin7為RX輸入*/

  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;

  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //這也必須為復用,與M3不同!

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOC,&GPIO_InitStructure);

  /*IO引腳復用功能設置,與之前版本不同*/

  /*配置USART6*/

  USART_StructInit(&USART_InitStructure);

  USART_InitStructure.USART_BaudRate =115200;

  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(USART6, &USART_InitStructure);

  USART_ClockStructInit(&USART_ClockInitStruct); //之前沒有填入缺省值,是不行的

  USART_ClockInit(USART6, &USART_ClockInitStruct);

  USART_ITConfig(USART6, USART_IT_RXNE, ENABLE); //使能 USART6中斷

  USART_Cmd(USART6, ENABLE); //使能 USART6

  //USART_DMACmd(USART6, USART_DMAReq_Tx, ENABLE); //使能USART6的發送數據DMA請求,至此USART6與DMA開始工作,可以寫在主函數里隨時工作

  }

  void NVIC_Config()

  {

  /*USART6中斷配置*/

  NVIC_InitTypeDef NVIC_InitStructure;

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //嵌套優先級分組為 1

  NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn; //嵌套通道為USART6_IRQn

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //搶占優先級為 0

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //響應優先級為 0

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中斷使能

  NVIC_Init(&NVIC_InitStructure);

  /*DMA中斷配置*/

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //嵌套優先級分組為 1

  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream6_IRQn; //嵌套通道為DMA2_Stream6_IRQn

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //搶占優先級為 1

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //響應優先級為 0

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中斷使能

  NVIC_Init(&NVIC_InitStructure);

  }

  /*************************************************

  Function: void USART6_Puts(char * str)

  Description: USART6發送數據

  Input: 待發送數據指針

  Output:無

  Return:無

  *************************************************/

  void USART6_Puts(char * str)

  {

  while (*str)

  {

  USART_SendData(USART6, *str++);

  /* Loop until the end of transmission */

  while (USART_GetFlagStatus(USART6, USART_FLAG_TXE) == RESET); //詳見英文參考的521頁,當TXE被置起時,一幀數據傳輸完成

  }

  }

  /*************************************************

  Function: void DMA_Config(void)

  Description: DMA配置函數

  Input: 延時的時間

  Output:無

  Return:無

  *************************************************/

  void DMA_Config(void)

  {

  DMA_InitTypeDef DMA_InitStructure;

  /*首先開DMA2時鍾,由407參考手冊-RM0090-Reference manual

  165頁可知,UASRT6與DMA2映射,而且DMA2掛載在AHB1時鍾總線上*/

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

  /*由RM0090-Reference manual第165頁映射表可知,USART6映射在

  Channel_5的Stream6和Stream7上,在這里可以選擇Stream6 */

  DMA_DeInit(DMA2_Stream6);

  DMA_StructInit( &DMA_InitStructure);

  DMA_InitStructure.DMA_Channel = DMA_Channel_5; //選擇Channel_5

  DMA_InitStructure.DMA_PeripheralBaseAddr = USART6_DR_Addr; //數據傳輸的外設首地址,詳解見上

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)Buffer; //自己定義待發送數組的首地址,要強制轉換為32位

  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //數據傳輸方向選擇為內存->外設

  DMA_InitStructure.DMA_BufferSize = 8; //傳輸數據大小為8,單位由以下確定,大小要配合定義的數組類型和外設數據類型

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設地址寄存器自動增加禁止,因為這里只用到了DR數據寄存器

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內存地址自增允許,因為要讀取一個數組

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外設的數據大小,因為USART6_DR數據寄存器為8為,故選Byte

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //這里也選Byte

  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMA傳輸模式為Normal,如果為Circular,將會循環傳輸

  DMA_InitStructure.DMA_Priority = DMA_Priority_High; //優先級為High

  /*雙緩沖模式,在DMA_Init之前調用在Circular模式有效,會強制Circular,

  *不支持Memory toMemory,(uint32_t)Buffer2為DMA_Memory_1,DMA先將Buffer

  *中的數據發送完畢后在發送Buffer2的數據,當然順序可以改變

  DMA_DoubleBufferModeConfig(DMA2_Stream6, (uint32_t)Buffer2, DMA_Memory_0);

  DMA_DoubleBufferModeCmd(DMA2_Stream6, ENABLE);

  */

  DMA_Init(DMA2_Stream6, &DMA_InitStructure);

  DMA_Cmd(DMA2_Stream6, ENABLE); //使能DMA2_Stream6通道

  /*DMA中斷開*/

  DMA_ITConfig(DMA2_Stream6, DMA_IT_TC, ENABLE);

  }

  /*************************************************

  Function: void Delay(uint32_t nCount)

  Description: 延時函數

  Input: 延時的時間

  Output:無

  Return:無

  *************************************************/

  void Delay(uint32_t nCount)

  {

  while (nCount--);

  }

  中斷服務函數:

  /**名稱:DMA中斷服務程序

  *作用:DMA數據完全完成后產生中斷,並點亮LED

  */

  void DMA2_Stream6_IRQHandler(void)

  {

  if (DMA_GetITStatus(DMA2_Stream6, DMA_IT_TCIF6) != RESET) //判斷為接收中斷

  {

  DMA_ClearITPendingBit(DMA2_Stream6, DMA_IT_TCIF6);

  GPIO_ResetBits(GPIOG, GPIO_Pin_6); //點亮LED,起到中斷指示作用

  }

  }

  調試結果如下:

  

 

stm32F407之USART6的DMA工作方式 - 小棗年糕 - 小棗年糕
分享有些DMA的資料

(DMA專題講解)
http://www.makeru.com.cn/live/1392_1048.html?s=45051
http://www.makeru.com.cn/live/1392_1020.html?s=45051
stm32 如何用DMA搬運數據
http://www.makeru.com.cn/live/detail/1484.html?s=45051


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM