记录在使用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