[STM32F1] DMA方式读取外部Flash失败,麻烦帮我看看是什么原因。谢谢

[复制链接]
3072|12
 楼主| rookie108 发表于 2024-6-8 22:28 | 显示全部楼层 |阅读模式
本帖最后由 rookie108 于 2024-6-8 22:31 编辑

一、DMA1的配置初始化函数:

  1. //DMA1的各通道配置
  2. //这里的传输形式是固定的,这点要根据不同的情况来修改
  3. //从存储器->外设模式/8位数据宽度/存储器增量模式
  4. //DMA_CHx:DMA通道CHx  //cpar:外设地址  //cmar:存储器地址   //cndtr:数据传输量
  5. void MYDMA_Config2(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u8 cmar,u16 cndtr)//从外设读到内存
  6. {
  7.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  //使能DMA传输

  8.     DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
  9.     DMA1_MEM_LEN=cndtr;
  10.     DMA_InitStructure.DMA_PeripheralBaseAddr =  cpar;  //DMA外设基地址
  11.     DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
  12.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读到内存
  13.     DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
  14.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
  15.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
  16.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
  17.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
  18.     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
  19.     DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //DMA通道 x拥有中优先级
  20.     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
  21.     DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
  22.     DMA_Cmd(DMA_CHx, ENABLE);
  23. }


二、SPI DMA方式读取外部flash
  1. void SPI_Flash_DMA_Read(void)
  2. {
  3.     u8 cdArray[360];
  4.     u32 Addr=0x9600;
  5.     u16 dataSize=360;

  6.     //先利用非DMA方式读取外部flash内容验证SPI通信是否正常
  7.     SPI_Flash_Read(cdArray, Addr, dataSize);
  8.     printf("1--cdArray[]=\r\n");
  9.     for (int j=0;j<dataSize;j++){
  10.         printf("%.2x ",cdArray[j]);
  11.     }
  12.     printf("\r\n1--OK!\r\n");
  13.     //将cdArray数组清0,
  14.     for (int m=0;m<dataSize;m++){
  15.         cdArray[m]=0;
  16.     }

  17.     //以下为DMA方式从外部flash里读取数据
  18.     MYDMA_Config2(DMA1_Channel2, (u32)&SPI1->DATAR, (u32)cdArray, 360); //DMA初始化

  19.     Flash_CS_Low;   //CS拉低
  20.     SPI1_ReadWriteByte(W25X_ReadData);
  21.     SPI1_ReadWriteByte((u8)((Addr) >> 16));//发送24位地址
  22.     SPI1_ReadWriteByte((u8)((Addr) >> 8));
  23.     SPI1_ReadWriteByte((u8)Addr);

  24.     SPI_InitTypeDef SPI_InitStructure;
  25.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_RxOnly;//SPI1配置为只读模式
  26.     SPI_Init(SPI1, &SPI_InitStructure);

  27.     SPI1_ReadWriteByte(0xff);
  28.     SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Rx,ENABLE);

  29.     while(DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET);//等待通道2传输完成

  30.     Flash_CS_Hight;//spi cs拉高

  31.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//恢复双工模式
  32.     SPI_Init(SPI1, &SPI_InitStructure);

  33.     printf("2--cdArray=\r\n");
  34.         for (int q=0;q<dataSize;q++){
  35.             printf("%.2x ",cdArray[q]);
  36.         }
  37.         printf("\r\2--OK!\r\n");
  38. }


三、SPI初始化函数:
  1. void SPI1_Init(void)
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
  4.         SPI_InitTypeDef SPI_InitStructure;

  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
  6.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  7.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  8.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  9.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  10.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  11.     GPIO_SetBits(GPIOA, GPIO_Pin_0);

  12.     GPIO_InitStructure.GPIO_Pin =GPIO_Pin_5|GPIO_Pin_7;
  13.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  14.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
  15.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  16.     GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7);

  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  19.     GPIO_Init(GPIOA, &GPIO_InitStructure);


  20.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双工模式
  21.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  22.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  23.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  24.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  25.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS?????????????
  26.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
  27. //        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
  28.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  29.         SPI_InitStructure.SPI_CRCPolynomial = 7;
  30.         SPI_Init(SPI1,&SPI_InitStructure);
  31.         SPI_Cmd(SPI1, ENABLE);
  32. }

四、SPI普通方式读取外部flash数据
  1. void SPI_Flash_Read(u8 *pBuffer, u32 ReadAddr, u16 size)
  2. {
  3.     u16 i;

  4.     Flash_CS_Low;
  5.     SPI1_ReadWriteByte(W25X_ReadData);
  6.     SPI1_ReadWriteByte((u8)((ReadAddr) >> 16));//发送24位地址
  7.     SPI1_ReadWriteByte((u8)((ReadAddr) >> 8));
  8.     SPI1_ReadWriteByte((u8)ReadAddr);
  9.     for(i = 0; i < size; i++){
  10.         pBuffer = SPI1_ReadWriteByte(0Xff);//循环读数  0xff可以为任何数,只是为了产生时钟信号

  11.     }
  12.     Flash_CS_Hight;
  13. }

  14. u8 SPI1_ReadWriteByte(u8 TxData)
  15. {               
  16.         u8 retry=0;
  17.         while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检测TXE是否置位,是,则表示 DR为空,可以写入数据
  18.                 {
  19.                 retry++;
  20.                 if(retry>200)return 0;
  21.                 }                          
  22.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据
  23.         retry=0;

  24.         while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //检测RXE是否置位,是,则表示 DR接收到数据,即发送完成
  25.                 {
  26.                 retry++;
  27.                 if(retry>200)return 0;
  28.                 }                                                              
  29.         return SPI_I2S_ReceiveData(SPI1); //  接收数据
  30. }

 楼主| rookie108 发表于 2024-6-8 23:26 | 显示全部楼层
卡在DMA读取FLASH函数中的while(DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET);无法跳出。不知道是哪里的问题。是配置不对还是什么原因?
classroom 发表于 2024-6-11 16:28 | 显示全部楼层
DMA配置问题
laocuo1142 发表于 2024-6-11 16:29 | 显示全部楼层
软件代码问题
flycamelaaa 发表于 2024-6-11 16:29 | 显示全部楼层
硬件连接问题
probedog 发表于 2024-6-12 16:00 | 显示全部楼层
Flash设备问题
rzjvv 发表于 2024-8-31 17:44 | 显示全部楼层
当使用 DMA 方式读取外部 Flash 时,如果出现问题,可能涉及多个方面,包括 DMA 配置、外部 Flash 配置、和硬件连接等。
发顺丰更大nc 发表于 2024-9-29 13:26 | 显示全部楼层
DMA配置初始化函数
c

void MYDMA_Config2(DMA_Channel_TypeDef* DMA_CHx, u32 cpar, u8 cmar, u16 cndtr) {
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  // 使能DMA时钟
    DMA_DeInit(DMA_CHx);  // 重设DMA通道寄存器

    DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  // 外设基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  // 存储器基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  // 数据传输方向:从外设到内存
    DMA_InitStructure.DMA_BufferSize = cndtr;  // 数据传输量
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  // 外设地址不增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  // 存储器地址增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  // 8位数据宽度
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  // 8位数据宽度
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  // 正常模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;  // 高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  // 非内存到内存传输

    DMA_Init(DMA_CHx, &DMA_InitStructure);  // 初始化DMA通道
    DMA_Cmd(DMA_CHx, ENABLE);  // 启用DMA通道
}


g36xcv 发表于 2024-9-29 22:49 来自手机 | 显示全部楼层
DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
在水一方00 发表于 2024-9-30 23:55 来自手机 | 显示全部楼层
使用DMA从外部Flash读取数据c
void SPI_Flash_DMA_Read(void) {
    u8 cdArray[360];
    u32 Addr = 0x9600;
    u16 dataSize = 360;

    // 验证SPI通信是否正常
    SPI_Flash_Read(cdArray, Addr, dataSize);
    printf("1--cdArray[]=\r\n");
    for (int j = 0; j < dataSize; j++) {
        printf("%.2x ", cdArray[j]);
    }
    printf("\r\n1--OK!\r\n");

    // 清空cdArray
    for (int m = 0; m < dataSize; m++) {
        cdArray[m] = 0;
    }

    // DMA方式读取数据
    MYDMA_Config2(DMA1_Channel2, (u32)&SPI1->DATAR, (u32)cdArray, 360);

    Flash_CS_Low;  // CS拉低
    SPI1_ReadWriteByte(W25X_ReadData);
    SPI1_ReadWriteByte((u8)((Addr) >> 16));
    SPI1_ReadWriteByte((u8)((Addr) >> 8));
    SPI1_ReadWriteByte((u8)Addr);

    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_RxOnly;  // 配置为只读模式
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI1_ReadWriteByte(0xff);  // 发送空字节以产生时钟信号
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);  // 启用DMA接收

    while (DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET);  // 等待传输完成

    Flash_CS_Hight;  // CS拉高

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  // 恢复双工模式
    SPI_Init(SPI1, &SPI_InitStructure);

    printf("2--cdArray=\r\n");
    for (int q = 0; q < dataSize; q++) {
        printf("%.2x ", cdArray[q]);
    }
    printf("\r\n2--OK!\r\n");
}



wangtaohui 发表于 2024-10-2 20:39 来自手机 | 显示全部楼层
通过DMA方式,数据传输效率得以提高,且CPU资源占用减少。在调试过程中,确保SPI通信正常并注意CS信号的控制,以避免数据错误。
jack_huang80 发表于 2025-6-23 17:05 | 显示全部楼层
请问你的问题解决了吗?我也是有同样的现象
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

29

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部