STM32F407 DMA+SPI+M95512数据交互

记录在使用DMA+SPI读写M95512 EEPROM填坑过程。

首先采用CubeMX生成初始化代码:

DMA部分

void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
  /* DMA1_Stream5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);

}

SPI部分

/* SPI3 init function */
void MX_SPI3_Init(void)
{

  /* USER CODE BEGIN SPI3_Init 0 */

  /* USER CODE END SPI3_Init 0 */

  /* USER CODE BEGIN SPI3_Init 1 */

  /* USER CODE END SPI3_Init 1 */
  hspi3.Instance = SPI3;
  hspi3.Init.Mode = SPI_MODE_MASTER;
  hspi3.Init.Direction = SPI_DIRECTION_2LINES;
  hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi3.Init.NSS = SPI_NSS_HARD_OUTPUT;
  hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi3.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI3_Init 2 */

  /* USER CODE END SPI3_Init 2 */

}

void HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance == SPI3)
  {
  /* USER CODE BEGIN SPI3_MspInit 0 */

  /* USER CODE END SPI3_MspInit 0 */
    /* SPI3 clock enable */
    __HAL_RCC_SPI3_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**SPI3 GPIO Configuration
    PA15     ------> SPI3_NSS
    PC10     ------> SPI3_SCK
    PC11     ------> SPI3_MISO
    PC12     ------> SPI3_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
        
        /* SPI3 DMA Init */
    /* SPI3_RX Init */
    hdma_spi3_rx.Instance = DMA1_Stream0;
    hdma_spi3_rx.Init.Channel = DMA_CHANNEL_0;
    hdma_spi3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_spi3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi3_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi3_rx.Init.Mode = DMA_NORMAL;
    hdma_spi3_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi3_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi3_rx);

    /* SPI3_TX Init */
    hdma_spi3_tx.Instance = DMA1_Stream5;    //DMA1的第5个数据流
    hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0; //DMA通道为0
    hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; //传输数据方向为从内存到外设
    hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE; //禁用外设地址自增模式
    hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE; //启用内存地址自增模式
    hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //外设数据对齐方式为字节对齐
    hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //内存数据对齐方式为字节对齐
    hdma_spi3_tx.Init.Mode = DMA_NORMAL; //DMA模式为正常模式
    hdma_spi3_tx.Init.Priority = DMA_PRIORITY_LOW; //DMA传输优先级为低优先级
    hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; //禁用FIFO模式
    if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi3_tx);

  /* USER CODE BEGIN SPI3_MspInit 1 */

  /* USER CODE END SPI3_MspInit 1 */
  }
}

采用HAL库函数进行SPI3 DMA数据传输

HAL_SPI_Transmit_DMA(&hspi3, tx_buffer, BUFFER_SIZE);

追踪寄存器,发现DR的值一直为0xFF

每天进步一点点,正在排查问题中,希望给迷茫的人一点方向指引。

烂尾了,这个程序本身没问题

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

刚开始这个IO配置的速度是低速,所以没有SPI的输出波形,改成高速就解决了问题,居然因为这个问题摸索了两天。。。

希望给后来人留点经验,顺便记录下以便下次再用到时可以快速上手。

hspi3.Init.CLKPhase = SPI_PHASE_2EDGE; //需要根据实际从机确认1个沿还是2个沿
hspi3.Init.NSS = SPI_NSS_HARD_OUTPUT;//最好用软件实现,这个硬件实现需要保证从机只有一个

附上驱动程序:

#include "eeprom.h"
#include "main.h"
#include "SPI/bsp_spi.h"
#include "stdio.h"
#include "string.h"
#include "crc.h"

EepromOperations EEPROM_WritePage(uint8_t* TxData, uint16_t WriteAddr, uint16_t NumByteToWrite) 
{
  HAL_StatusTypeDef spiTransmitStatus;

  sEE_WriteEnable();

  /* We gonna send commands in one packet of 3 bytes */
  uint8_t header[3];

  header[0] = EEPROM_WRITE;   // Send "Write to Memory" instruction
  header[1] = WriteAddr >> 8; // Send 16-bit address
  header[2] = WriteAddr;

  // Select the EEPROM: Chip Select low
  EE_NSS_LOW;

  EEPROM_SendInstruction(header, 3);

	spiTransmitStatus = HAL_SPI_Transmit_DMA(&hspi3, TxData, NumByteToWrite);
	
	while (HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY) {
  }
	
  // Deselect the EEPROM: Chip Select high
  EE_NSS_HIGH;
	
	HAL_Delay(5); 
  // Wait the end of EEPROM writing
  EEPROM_WaitStandbyState();

  // Disable the write access to the EEPROM
  sEE_WriteDisable();

  if (spiTransmitStatus == HAL_ERROR) {
    return EEPROM_STATUS_ERROR;
  } else {
    return EEPROM_STATUS_COMPLETE;
  }
}

/**
  * @brief  Writes block of data to the EEPROM. In this function, the number of
  *         WRITE cycles are reduced, using Page WRITE sequence.
  *
  * @param  pBuffer: pointer to the buffer  containing the data to be written
  *         to the EEPROM.
  * @param  WriteAddr: EEPROM's internal address to write to.
  * @param  NumByteToWrite: number of bytes to write to the EEPROM.
  * @retval EepromOperations value: EEPROM_STATUS_COMPLETE or EEPROM_STATUS_ERROR
  */
EepromOperations EEPROM_Write(uint8_t* TxData, uint16_t WriteAddr, uint16_t NumByteToWrite) 
{
  uint16_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
  uint16_t sEE_DataNum = 0;

  EepromOperations pageWriteStatus = EEPROM_STATUS_PENDING;

  Addr = WriteAddr % EEPROM_PAGESIZE;
  count = EEPROM_PAGESIZE - Addr;
  NumOfPage =  NumByteToWrite / EEPROM_PAGESIZE;
  NumOfSingle = NumByteToWrite % EEPROM_PAGESIZE;

  if (Addr == 0) { /* WriteAddr is EEPROM_PAGESIZE aligned  */
    if (NumOfPage == 0) { /* NumByteToWrite < EEPROM_PAGESIZE */
			sEE_DataNum = NumByteToWrite;
      pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);

      if (pageWriteStatus != EEPROM_STATUS_COMPLETE) {
        return pageWriteStatus;
      }
    } else { /* NumByteToWrite > EEPROM_PAGESIZE */
      while (NumOfPage--) {
        sEE_DataNum = EEPROM_PAGESIZE;
        pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);

        if (pageWriteStatus != EEPROM_STATUS_COMPLETE) {
          return pageWriteStatus;
        }

        WriteAddr +=  EEPROM_PAGESIZE;
        TxData += EEPROM_PAGESIZE;
      }

      sEE_DataNum = NumOfSingle;
      pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);

      if (pageWriteStatus != EEPROM_STATUS_COMPLETE) {
        return pageWriteStatus;
      }
    }
  } else { /* WriteAddr is not EEPROM_PAGESIZE aligned  */
    if (NumOfPage == 0) { /* NumByteToWrite < EEPROM_PAGESIZE */
      if (NumOfSingle > count) { /* (NumByteToWrite + WriteAddr) > EEPROM_PAGESIZE */
        temp = NumOfSingle - count;
        sEE_DataNum = count;
        pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);

        if (pageWriteStatus != EEPROM_STATUS_COMPLETE) {
          return pageWriteStatus;
        }

        WriteAddr +=  count;
        TxData += count;

        sEE_DataNum = temp;
        pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);
      } else {
        sEE_DataNum = NumByteToWrite;
        pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);
      }

      if (pageWriteStatus != EEPROM_STATUS_COMPLETE) {
				return pageWriteStatus;
      }
    } else { /* NumByteToWrite > EEPROM_PAGESIZE */
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / EEPROM_PAGESIZE;
      NumOfSingle = NumByteToWrite % EEPROM_PAGESIZE;

      sEE_DataNum = count;

      pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);

      if (pageWriteStatus != EEPROM_STATUS_COMPLETE) {
        return pageWriteStatus;
      }

      WriteAddr +=  count;
      TxData += count;

      while (NumOfPage--) {
        sEE_DataNum = EEPROM_PAGESIZE;

        pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);

        if (pageWriteStatus != EEPROM_STATUS_COMPLETE) {
          return pageWriteStatus;
        }

        WriteAddr +=  EEPROM_PAGESIZE;
        TxData += EEPROM_PAGESIZE;
      }

      if (NumOfSingle != 0) {
        sEE_DataNum = NumOfSingle;

        pageWriteStatus = EEPROM_WritePage(TxData, WriteAddr, sEE_DataNum);

        if (pageWriteStatus != EEPROM_STATUS_COMPLETE) {
          return pageWriteStatus;
        }
      }
    }
  }

  return EEPROM_STATUS_COMPLETE;
}

/**
  * @brief  Reads a block of data from the EEPROM.
  *
  * @param  pBuffer: pointer to the buffer that receives the data read from the EEPROM.
  * @param  ReadAddr: EEPROM's internal address to read from.
  * @param  NumByteToRead: number of bytes to read from the EEPROM.
  * @retval None
  */
EepromOperations EEPROM_Read(uint8_t* RxData, uint16_t ReadAddr, uint16_t NumByteToRead) 
{
  /* We gonna send all commands in one packet of 3 bytes */
  uint8_t header[3];

  header[0] = EEPROM_READ;    // Send "Read from Memory" instruction
  header[1] = ReadAddr >> 8;  // Send 16-bit address
  header[2] = ReadAddr;

  // Select the EEPROM: Chip Select low
  EE_NSS_LOW;

  /* Send WriteAddr address byte to read from */
  EEPROM_SendInstruction(header, 3);
	
  HAL_SPI_Receive_DMA(&hspi3, (uint8_t*)RxData, NumByteToRead);

	while (HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY) {
  }

  // Deselect the EEPROM: Chip Select high
  EE_NSS_HIGH;

  return EEPROM_STATUS_COMPLETE;
}

/**
  * @brief  Sends a byte through the SPI interface and return the byte received
  *         from the SPI bus.
  *
  * @param  byte: byte to send.
  * @retval The value of the received byte.
  */
uint8_t EEPROM_SendByte(uint8_t byte) 
{
  uint8_t answerByte;

  /* Send byte through the SPI peripheral */
	HAL_SPI_Transmit_DMA(&hspi3, &byte, 1);
	
	/* Loop while DR register in not empty */
  while (HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY) {
  }

	/* Return the byte read from the SPI bus */
	HAL_SPI_Transmit_DMA(&hspi3, &answerByte, 1);
	
  /* Wait to receive a byte */
  while (HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY) {
  }

  return (uint8_t)answerByte;
}
/**
  * @brief  Enables the write access to the EEPROM.
  *
  * @param  None
  * @retval None
  */
void sEE_WriteEnable(void) 
{
  // Select the EEPROM: Chip Select low
  EE_NSS_LOW;

  uint8_t command[1] = { EEPROM_WREN };
  /* Send "Write Enable" instruction */
  EEPROM_SendInstruction(command, 1);

  // Deselect the EEPROM: Chip Select high
  EE_NSS_HIGH;
}

/**
  * @brief  Disables the write access to the EEPROM.
  *
  * @param  None
  * @retval None
  */
void sEE_WriteDisable(void) 
{
  // Select the EEPROM: Chip Select low
  EE_NSS_LOW;

  uint8_t command[1] = { EEPROM_WRDI };

  /* Send "Write Disable" instruction */
  EEPROM_SendInstruction(command, 1);

  // Deselect the EEPROM: Chip Select high
  EE_NSS_HIGH;
}

/**
  * @brief  Write new value in EEPROM Status Register.
  *
  * @param  regval : new value of register
  * @retval None
  */
void sEE_WriteStatusRegister(uint8_t regval)
{
  uint8_t command[2];

  command[0] = EEPROM_WRSR;
  command[1] = regval;

  // Enable the write access to the EEPROM
  sEE_WriteEnable();

  // Select the EEPROM: Chip Select low
  EE_NSS_LOW;

  // Send "Write Status Register" instruction
  // and Regval in one packet
  EEPROM_SendInstruction(command, 2);

  // Deselect the EEPROM: Chip Select high
  EE_NSS_HIGH;

  sEE_WriteDisable();
}


/**
  * @brief  Polls the status of the Write In Progress (WIP) flag in the EEPROM's
  *         status register and loop until write operation has completed.
  *
  * @param  None
  * @retval None
  */
uint8_t EEPROM_WaitStandbyState(void)
{
  uint8_t sEEstatus[1] = { 0x00 };
  uint8_t command[1] = { EEPROM_RDSR };

  // Select the EEPROM: Chip Select low
  EE_NSS_LOW;

  // Send "Read Status Register" instruction
  EEPROM_SendInstruction(command, 1);

  // Loop as long as the memory is busy with a write cycle
  do {
		HAL_SPI_Receive_DMA(&hspi3, (uint8_t*)sEEstatus, 1);
		
		while (HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY) {
    }
  } while ((sEEstatus[0] & EEPROM_WIP_FLAG) == SET); // Write in progress

  // Deselect the EEPROM: Chip Select high
  EE_NSS_HIGH;

  return 0;
}

/**
 * @brief Low level function to send header data to EEPROM
 *
 * @param instruction array of bytes to send
 * @param size        data size in bytes
 */
void EEPROM_SendInstruction(uint8_t *instruction, uint16_t size)
{
  HAL_SPI_Transmit_DMA(&hspi3, instruction, size);
	
	while (HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY) {
  }
}
#ifndef _EEPROM_H
#define _EEPROM_H

/* C++ detection */
#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
#include "cmsis_os.h"
#include "common.h"

/* M95512 SPI EEPROM defines */
#define EEPROM_WREN  0x06  /*!< Write Enable */
#define EEPROM_WRDI  0x04  /*!< Write Disable */
#define EEPROM_RDSR  0x05  /*!< Read Status Register */
#define EEPROM_WRSR  0x01  /*!< Write Status Register */
#define EEPROM_READ  0x03  /*!< Read from Memory Array */
#define EEPROM_WRITE 0x02  /*!< Write to Memory Array */

#define EEPROM_WIP_FLAG     0x01  /*!< Write In Progress (WIP) flag */

#define EEPROM_PAGESIZE     128    /*!< Pagesize according to documentation */

#define EE_NSS_LOW    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
#define EE_NSS_HIGH   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);

/**
 * @brief EEPROM Operations statuses
 */
typedef enum {
    EEPROM_STATUS_PENDING,
    EEPROM_STATUS_COMPLETE,
    EEPROM_STATUS_ERROR
} EepromOperations;

EepromOperations EEPROM_Write(uint8_t* TxData, uint16_t WriteAddr, uint16_t NumByteToWrite);
EepromOperations EEPROM_WritePage(uint8_t* TxData, uint16_t WriteAddr, uint16_t NumByteToWrite);
EepromOperations EEPROM_Read(uint8_t* RxData, uint16_t ReadAddr, uint16_t NumByteToRead);
uint8_t EEPROM_WaitStandbyState(void);

/* Low layer functions */
uint8_t EEPROM_SendByte(uint8_t byte);
void sEE_WriteEnable(void);
void sEE_WriteDisable(void);
void sEE_WriteStatusRegister(uint8_t regval);
uint8_t sEE_ReadStatusRegister(void);

void EEPROM_SendInstruction(uint8_t *instruction, uint16_t size);
void EEPROM_ReadStatusByte(SPI_HandleTypeDef SPIe, uint8_t *statusByte );

#ifdef __cplusplus
}
#endif

#endif // _EEPROM_H

  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值