打印
[STM32F4]

STM32f407 DMA

[复制链接]
888|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
DMA简介:

DMA : Data Memory Access ,直接存储器访问

DMA1: P->M, M->P, P为外设数据,M为SRAM

DMA2: P->M, M->P, M->M,P为外设数据,M为SRAM,M为flash

DMA功能框图


通道和流

流:是数据传输的一条链路, 每个 DMA 控制器有 8 条独立的数据流,每次传输的数据量最大为 65535,如果数据的单位为字的话,那一次可以传输 256KB。
通道:每个数据流有 8 个通道选择,每个通道对应不同的 DMA 请求。



DMA2



使用特权

评论回复
沙发
xiyaoko2365|  楼主 | 2024-3-31 23:55 | 只看该作者
通道选择,DMA_SxCR:CHSEL



仲裁器

多个 DMA 请求一起来, 怎么办?
1、软件阶段, DMAS XCR:PL
2、硬件阶段,数据流编号小的优先级大同一个数据流只能使用一个通道, 同一个 DMA 控制器可以使用多个数据流。

FIFO

FIFO:源和目标之间的一个数据中转站。
1-每个数据流有 4 字 FIFO,阈值级别有 1/4、1/2、
3/4 或满,DMA_SxFCR:FTH。
2-在开启 FIFO 的时候,直接模式要禁止,MA_SxFCR:DMDIS 在存储器到存储器传输的时候会自动启动 FIFO 模式,软件禁止不了。

FIFO 阈值与突发配置
1-FIFO 大小:4 个字,16 个字节,半字即 2 个字节,字即 4 个字节
2-节拍:即 MSIZE 的单位





使用特权

评论回复
板凳
xiyaoko2365|  楼主 | 2024-3-31 23:56 | 只看该作者
DMA初始化结构体简介

DMA_Channel:DMA 请求通道选择,可选通道 0 至通道 7,每个外设对应固定的通道,DMA_SxCR:CHSEL[2:0]。
DMA Peripheral Base Addr:
外设地址
DMA_Sx PAR。
DMA_Memory O Base Addr:存储器 0 地址, DMA_SxM0AR。
DMA_DIR:传输方向选择, 可选外设到存储器、存储器到外设以及存储器到存储器,DMA_SxCR:DIR[1:0]

DMA_BufferSize:设定一次传输的数据个数,DMA_SxNDTR。
DMA_PeripheralInc:外设地址是否递增,DMA_SxCR: PINC。
DMA_MemoryInc:存储器地址是否递增,DMA_SxCR: MINC。
DMA_PeripheralDataSize:外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位), DMA_SxCR : PSIZE[1:0]。

DMA_Mode: DMA 传输模式选择,可选一次传输或者循环传输, DMA_SxCR : CIRC 位的值。在存储器到存储器模式的时候,只能是一次传输,即 DMA_Mode_Normal 模式。
DMA_Priority:优先级,非常高、高、中和低,DMA_SxCR : PL[1:0]。

DMA_FIFOMode:FIFO 模式使能,DMA_SxFCR:DMDIS。在存储器到存储器传输的时候,FIFO 自动开启,软件禁止不了。
DMA_FIFOThreshold:FIFO 阈值选择,1/4、1/2、3/4 和满,DMA_SxFCR : FTH[1:0]。
DMA_MemoryBurst:存储器突发模式选择,单次模式、4 节拍、
8 节拍、16 节拍 , DMA_SxCR:MBURST[1:0]。
DMA_PeripheralBurst:外设突发模式选择,单次模式、4 节拍、
8 节拍、16 节拍 , DMA_SxCR :PBURST[1:0] 。


使用特权

评论回复
地板
xiyaoko2365|  楼主 | 2024-3-31 23:56 | 只看该作者
编程时需要用到的固件库函数
1-初始化 DMA 的寄存器到复位状态
DMA_DeInit (DMA_Stream_TypeDef* DMAy_Streamx);
2-DMA 初始化函数
void DMA_Init (DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);
3-DMA 使能函数
DMA_Cmd (DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState);

使用特权

评论回复
5
xiyaoko2365|  楼主 | 2024-3-31 23:56 | 只看该作者
实验1:1-M to M: FLASH to SRAM,把内部 FLASH 的数据传输到内部的 SRAM

                        const uint32_t temp1=1;                //存储在Flash中                                                                                               
      uint32_t temp;//定义全局变量储存在SRAM中         
1-在 FLASH 中定义好要传输的数据, 在 SRAM 中定义好用来接收 FLASH 数据的变量。
2-确定使用 DMA 2, 哪个数据流, 哪个通道?然后定义成宏,方便修改。
3-初始化 DMA, 主要是配置 DMA 初始化结构体。

4-编写数据比较函数。
5-编写 main 函数

使用特权

评论回复
6
xiyaoko2365|  楼主 | 2024-3-31 23:56 | 只看该作者
问题:MToM 只能使用 DMA2 控制器,那数据流和通道应该如何选择?

STM32F407的DMA2控制器支持存储器到存储器(mem-to-mem)模式,可以在内存之间传输数据。在配置DMA2通道时,需要注意以下几点:

数据流选择
DMA2控制器提供了不同的数据流(stream),每个数据流都有一组DMA通道可以使用。在选择数据流时,需要考虑到数据传输的大小和优先级。如果需要高优先级的数据传输,可以选择更低的数据流编号,例如0或1。如果传输的数据量比较大,建议选择更高编号的数据流(2-7),可以提高数据传输的效率,减少CPU的处理负担。

通道选择
在DMA2控制器中,每个数据流都有多个DMA通道可以使用。通常情况下,建议选择DMA2通道0或1,这两个通道支持循环模式,并且具有高优先级。如果需要同时进行多个数据传输,可以选择使用不同的通道来避免冲突。

传输模式
在存储器到存储器模式下,可以选择普通模式或者循环模式。如果需要重复传输相同的数据,建议选择循环模式,可以减少DMA配置的次数。如果每次传输的数据不一样,则应该选择普通模式。

使用特权

评论回复
7
xiyaoko2365|  楼主 | 2024-3-31 23:57 | 只看该作者
综上所述,选择DMA2的数据流和通道,应该根据具体的应用需求来确定。建议选择高效的数据流和优先级较高的DMA通道,并选择合适的传输模式来实现存储器到存储器的数据传输。

#include "stm32f4xx.h"
#include "./led/bsp_led.h"

/* 第二步:相关宏定义,使用存储器到存储器传输必须使用DMA2 */
#define DMA_STREAM               DMA2_Stream0
#define DMA_CHANNEL              DMA_Channel_0
#define DMA_STREAM_CLOCK         RCC_AHB1Periph_DMA2
#define DMA_FLAG_TCIF            DMA_FLAG_TCIF0

#define BUFFER_SIZE              32
#define TIMEOUT_MAX              10000 /* Maximum timeout value */

/* 第一步:定义aSRC_Const_Buffer数组作为DMA传输数据源
  const关键字将aSRC_Const_Buffer数组变量定义为常量类型 ,存储在Flash*/
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
                                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                                    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
                                    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
                                    0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
                                    0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                                    0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                                    0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
/* 定义DMA传输目标存储器 储存在SRAM中*/
uint32_t aDST_Buffer[BUFFER_SIZE];
                 
                              
static void Delay(__IO uint32_t nCount);
static void DMA_Config(void);
uint8_t Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength);
/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{
        //第五步:编写main函数
  /* 定义存放比较结果变量 */
  uint8_t TransferStatus;
  
        /* LED 端口初始化 */
        LED_GPIO_Config();
   
  /* 设置RGB彩色灯为紫色 */
  LED_PURPLE;  
  
  /* 简单延时函数 */
  Delay(0xFFFFFF);  
  
  /* DMA传输配置 */
  DMA_Config();
  
  /* 等待DMA传输完成 */
  while(DMA_GetFlagStatus(DMA_STREAM,DMA_FLAG_TCIF)==DISABLE)
  {
   
  }   
  
  /* 比较源数据与传输后数据 */
  TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
  
  /* 判断源数据与传输后数据比较结果*/
  if(TransferStatus==0)  
  {
    /* 源数据与传输后数据不相等时RGB彩色灯显示红色 */
    LED_RED;
  }
  else
  {
    /* 源数据与传输后数据相等时RGB彩色灯显示蓝色 */
    LED_BLUE;
  }

        while (1)
        {               
        }
}

/* 简单的延时函数 */
static void Delay(__IO uint32_t nCount)
{
        for(; nCount != 0; nCount--);
}

/**
  * 第三步:DMA传输配置
  */
static void DMA_Config(void)
{
  DMA_InitTypeDef  DMA_InitStructure;
  __IO uint32_t    Timeout = TIMEOUT_MAX;
   
  /* 使能DMA时钟 */
  RCC_AHB1PeriphClockCmd(DMA_STREAM_CLOCK, ENABLE);
  
  /* 复位初始化DMA数据流 */
  DMA_DeInit(DMA_STREAM);

  /* 确保DMA数据流复位完成 */
  while (DMA_GetCmdStatus(DMA_STREAM) != DISABLE)
  {
  }
  
  /* DMA数据流通道选择 */
  DMA_InitStructure.DMA_Channel = DMA_CHANNEL;  
  /* 源数据地址 */
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
  /* 目标地址 */
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer;
  /* 存储器到存储器模式 */
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
  /* 数据数目 */
  DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_SIZE;
  /* 使能自动递增功能 */
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
  /* 使能自动递增功能 */
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  /* 源数据是字大小(32位) */
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  /* 目标数据也是字大小(32位) */
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  /* 一次传输模式,存储器到存储器模式不能使用循环传输 */
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  /* DMA数据流优先级为高 */
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  /* 使用FIFO模式,使用M->M模式的时候,FIFO硬件开启,软件控制不了  */
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;     
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  /* 单次模式 */
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  /* 单次模式 */
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  /* 完成DMA数据流参数配置 */
  DMA_Init(DMA_STREAM, &DMA_InitStructure);
  
  /* 清除DMA数据流传输完成标志位 */
  DMA_ClearFlag(DMA_STREAM,DMA_FLAG_TCIF);
  
  /* 使能DMA数据流,开始DMA数据传输 */
  DMA_Cmd(DMA_STREAM, ENABLE);

  /* 检测DMA数据流是否有效并带有超时检测功能 */
  Timeout = TIMEOUT_MAX;
  while ((DMA_GetCmdStatus(DMA_STREAM) != ENABLE) && (Timeout-- > 0))
  {
  }
   
  /* 判断是否超时 */
  if (Timeout == 0)
  {
    /* 超时就让程序运行下面循环:RGB彩色灯闪烁 */
    while (1)
    {      
      LED_RED;
      Delay(0xFFFFFF);
      LED_RGBOFF;
      Delay(0xFFFFFF);
    }
  }
}

/**
        *第四步:编写数据比较函数
  * 判断指定长度的两个数据源是否完全相等,
  * 如果完全相等返回1,只要其中一对数据不相等返回0
  */
uint8_t Buffercmp(const uint32_t* pBuffer,
                  uint32_t* pBuffer1, uint16_t BufferLength)
{
  /* 数据长度递减 */
  while(BufferLength--)
  {
    /* 判断两个数据源是否对应相等 */
    if(*pBuffer != *pBuffer1)
    {
      /* 对应数据源不相等马上退出函数,并返回0 */
      return 0;
    }
    /* 递增两个数据源的地址指针 */
    pBuffer++;
    pBuffer1++;
  }
  /* 完成判断并且对应数据相对 */
  return 1;  
}

/*********************************************END OF FILE**********************/

使用特权

评论回复
8
xiyaoko2365|  楼主 | 2024-3-31 23:57 | 只看该作者
实验2:2-M to P: SRAM to 串口,同时 LED 灯闪烁,演示 DMA 传数据不需要占用 CPU。

1-初始化串口 (从现有的例程移植过来,不需要使用中断,记得把中断部分代码删除)
2-配置 DMA 初始化结构体。
3-编写主函数 (开启串口发送 DMA 请求)。

  
#include "./usart/bsp_usart_dma.h"

uint8_t SendBuff[SENDBUFF_SIZE];



/**
  * @brief  USART GPIO 配置,工作模式配置。115200 8-N-1
  * @param  无
  * @retval 无
  */
void Debug_USART_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
               
  RCC_AHB1PeriphClockCmd( DEBUG_USART_RX_GPIO_CLK|DEBUG_USART_TX_GPIO_CLK, ENABLE);

  /* Enable UART clock */
  RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE);
  
  /* Connect PXx to USARTx_Tx*/
  GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT,DEBUG_USART_RX_SOURCE, DEBUG_USART_RX_AF);

  /* Connect PXx to USARTx_Rx*/
  GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT,DEBUG_USART_TX_SOURCE,DEBUG_USART_TX_AF);

  /* Configure USART Tx as alternate function  */
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN  ;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  /* Configure USART Rx as alternate function  */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;
  GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
                       
  /* USART mode config */
  USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
  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(DEBUG_USART, &USART_InitStructure);
  USART_Cmd(DEBUG_USART, ENABLE);
}

///重定向c库函数printf到USART1
int fputc(int ch, FILE *f)
{
                /* 发送一个字节数据到USART1 */
                USART_SendData(DEBUG_USART, (uint8_t) ch);
               
                /* 等待发送完毕 */
                while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);               
       
                return (ch);
}

///重定向c库函数scanf到USART1
int fgetc(FILE *f)
{
                /* 等待串口1输入数据 */
                while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_RXNE) == RESET);

                return (int)USART_ReceiveData(DEBUG_USART);
}


/**
  * @brief  USART1 TX DMA 配置,内存到外设(USART1->DR)
  * @param  无
  * @retval 无
  */
void USART_DMA_Config(void)
{
  DMA_InitTypeDef DMA_InitStructure;

  /*开启DMA时钟*/
  RCC_AHB1PeriphClockCmd(DEBUG_USART_DMA_CLK, ENABLE);
  
  /* 复位初始化DMA数据流 */
  DMA_DeInit(DEBUG_USART_DMA_STREAM);

  /* 确保DMA数据流复位完成 */
  while (DMA_GetCmdStatus(DEBUG_USART_DMA_STREAM) != DISABLE)  {
  }

  /*usart1 tx对应dma2,通道4,数据流7*/       
  DMA_InitStructure.DMA_Channel = DEBUG_USART_DMA_CHANNEL;  
  /*设置DMA源:串口数据寄存器地址*/
  DMA_InitStructure.DMA_PeripheralBaseAddr = DEBUG_USART_DR_BASE;         
  /*内存地址(要传输的变量的指针)*/
  DMA_InitStructure.DMA_Memory0BaseAddr = (u32)SendBuff;
  /*方向:从内存到外设*/               
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;       
  /*传输大小DMA_BufferSize=SENDBUFF_SIZE*/       
  DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
  /*外设地址不增*/            
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  /*内存地址自增*/
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;       
  /*外设数据单位*/       
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  /*内存数据单位 8bit*/
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;       
  /*DMA模式:不断循环*/
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;         
  /*优先级:中*/       
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;      
  /*禁用FIFO*/
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;        
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;   
  /*存储器突发传输 16个节拍*/
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;   
  /*外设突发传输 1个节拍*/
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;   
  /*配置DMA2的数据流7*/                  
  DMA_Init(DEBUG_USART_DMA_STREAM, &DMA_InitStructure);
  
  /*使能DMA*/
  DMA_Cmd(DEBUG_USART_DMA_STREAM, ENABLE);
  
  /* 等待DMA数据流有效*/
  while(DMA_GetCmdStatus(DEBUG_USART_DMA_STREAM) != ENABLE)
  {
  }   
}


/*********************************************END OF FILE**********************/

使用特权

评论回复
9
xiyaoko2365|  楼主 | 2024-3-31 23:57 | 只看该作者
STM32407 DMA和中断函数如何切换使用?

在STM32F407中使用DMA和中断函数可以通过以下步骤实现切换:

初始化DMA控制器和外设:配置DMA通道和外设,使其能够与DMA进行数据交换。

配置DMA传输参数:设置DMA传输的数据长度、传输方向、存储器地址和外设地址等参数。

启动DMA传输:使DMA开始传输数据。

配置中断:通过NVIC控制器配置中断。

编写中断服务程序:在中断服务程序中处理DMA传输完成后需要执行的操作,例如处理传输数据等。

在主程序中进行切换:根据需要,在主程序中选择使用DMA或中断函数。

使用特权

评论回复
10
xiyaoko2365|  楼主 | 2024-3-31 23:57 | 只看该作者
可以使用以下代码示例实现DMA和中断函数的切换:
#include "stm32f4xx.h"

// 定义数据缓冲区
#define BUFFER_SIZE 256
volatile uint8_t buffer[BUFFER_SIZE];

// DMA完成中断处理函数
void DMA2_Stream0_IRQHandler(void)
{
    // DMA传输完成后执行操作
    // ...
    // 清除中断标志位
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
}

int main(void)
{
    // 初始化DMA控制器和外设
    DMA_InitTypeDef DMA_InitStruct;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    DMA_InitStruct.DMA_Channel = DMA_Channel_0;
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI2->DR);
    DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)buffer;
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE;
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;
    DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA2_Stream0, &DMA_InitStruct);

    // 配置DMA传输参数
    DMA_Cmd(DMA2_Stream0, ENABLE);

    // 配置中断
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = DMA2_Stream0_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    // 启动DMA传输
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
    DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);

    // 在主程序中切换使用DMA或中断函数
    while (1)
    {
        // 使用DMA传输数据
        DMA_Cmd(DMA2_Stream0, ENABLE);

        // 或者使用中断函数传输数据
        // SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE);
    }
}

使用特权

评论回复
11
xiyaoko2365|  楼主 | 2024-3-31 23:58 | 只看该作者
STM32407 怎么触发串口中断服务函数?

STM32F407芯片的串口中断函数可以通过以下步骤触发:

开启串口中断功能,此功能可以通过设置USART_CR1寄存器的位0(USART_CR1_RE)和位5(USART_CR1_RXNEIE)来实现。设置USART_CR1_RE位可以开启USART的接收功能,设置USART_CR1_RXNEIE位可以开启接收数据寄存器非空中断。

使用特权

评论回复
12
xiyaoko2365|  楼主 | 2024-3-31 23:58 | 只看该作者
代码示例如下:
USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);

使用特权

评论回复
13
xiyaoko2365|  楼主 | 2024-3-31 23:58 | 只看该作者
在STM32F407的NVIC(Nested Vectored Interrupt Controller)中使能对应的串口中断。首先需要开启中断向量表,然后使能串口中断。中断向量表需要开启是因为STM32F407芯片中断向量表默认是在Flash中,而在使用中断之前需要将中断向量表复制到SRAM,否则中断无法正常工作。代码示例如下:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //设置NVIC中断优先级分组2
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USARTx_IRQn;  //选择串口中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //设置串口中断抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //设置串口中断子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  //使能串口中断通道
NVIC_Init(&NVIC_InitStructure);  //初始化NVIC

使用特权

评论回复
14
xiyaoko2365|  楼主 | 2024-3-31 23:58 | 只看该作者
编写串口中断函数,当接收数据寄存器非空时,串口中断函数会被自动触发。在串口中断函数中可以读取接收数据寄存器中的数据,并进行相应的处理。代码示例如下:

void USARTx_IRQHandler(void)
{
  if (USART_GetITStatus(USARTx, USART_IT_RXNE) != RESET)  //判断是否是接收中断
  {
    uint16_t ch = USART_ReceiveData(USARTx);  //读取接收数据寄存器中的数据
    //对接收到的数据进行处理
  }
}

使用特权

评论回复
15
xiyaoko2365|  楼主 | 2024-3-31 23:58 | 只看该作者
STM32407 怎么开启串口DMA传输?

在STM32F407芯片上开启串口DMA传输需要完成以下步骤:

使能DMA时钟,可以使用以下代码:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

使用特权

评论回复
16
xiyaoko2365|  楼主 | 2024-3-31 23:59 | 只看该作者
配置DMA通道和串口,可以使用以下代码:DMA_InitTypeDef DMA_InitStructure;
USART_InitTypeDef USART_InitStructure;
   
//配置USART_InitStructure结构体来初始化USART
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(USARTx, &USART_InitStructure); //初始化USART

//配置DMA_InitStructure结构体来初始化DMA
DMA_StructInit(&DMA_InitStructure); //将DMA配置为默认值
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //通道4对应USARTx_RX
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //从外设到内存
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //内存数据宽度为8bit
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据宽度为8bit
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环传输
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTx_DR_Base; //外设基地址
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RxBuffer; //内存0基地址
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; //缓冲区大小
DMA_Init(DMA1_Stream2, &DMA_InitStructure); //初始化DMA

使用特权

评论回复
17
xiyaoko2365|  楼主 | 2024-3-31 23:59 | 只看该作者
其中,DMA_Channel_4对应USARTx_RX,DMA_Channel_5对应USARTx_TX。

启动DMA传输,可以使用以下代码:
DMA_Cmd(DMA1_Stream2, ENABLE); //启动DMA

使用特权

评论回复
18
xiyaoko2365|  楼主 | 2024-3-31 23:59 | 只看该作者
在串口中断函数中检查DMA是否传输完成,可以使用以下代码:void USARTx_IRQHandler(void)
{
  if (USART_GetITStatus(USARTx, USART_IT_RXNE) != RESET) //接收中断
  {
    /* 处理接收数据,例如将数据存入接收缓冲区中 */
  }

  if (DMA_GetFlagStatus(DMA1_Stream2, DMA_FLAG_TCIF2) != RESET) //DMA传输完成中断
  {
    DMA_ClearFlag(DMA1_Stream2, DMA_FLAG_TCIF2); //清除传输完成标志
    /* 处理DMA传输完成,例如将接收到的数据存入缓冲区中 */
  }
}

使用特权

评论回复
19
xiyaoko2365|  楼主 | 2024-3-31 23:59 | 只看该作者
其中,DMA_FLAG_TCIF2表示DMA传输完成标志。如果需要启用DMA发送,可以选择使用DMA通道5并在串口的发送中断函数中检查DMA传输完成。

特别需要注意在使用DMA传输时,在串口初始化时,不能有以下代码:

USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);
如有需注释或者条件编译。

407DMA

串口1:usart1 rx使用DMA2,数据流5,通道4,数据接收.usart1 tx使用DMA2,数据流7,通道4,数据发送

串口2:usart2 rx使用DMA1,数据流5,通道4,数据接收.usart2 tx使用DMA1,数据流6,通道4,数据发送

串口3:usart3 rx使用DMA1,数据流1,通道4,数据接收.usart3 tx使用DMA1,数据流3,通道4,数据发送

串口2:usart6 rx使用DMA2,数据流1,通道5,数据接收.usart6 tx使用DMA2,数据流6,通道5,数据发送

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

32

主题

403

帖子

0

粉丝