From b8bd4034d12b5f10b9b910428028ce5f81556cf2 Mon Sep 17 00:00:00 2001 From: alexey Date: Tue, 13 Aug 2024 13:28:09 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B0=20SPI=20EEPROM:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Функция для записи в EEPROM MEMSPI_EEPROM_Write - Библиотека переименована из flash в memory spi - Функции для вызова SPI Transmit/Receive вынесены в дефайн --- memory_spi/memory_spi.c | 775 ++++++++++++++++++ .../spi_flash.h => memory_spi/memory_spi.h | 282 ++++--- spi_flash/spi_flash.c | 657 --------------- 3 files changed, 937 insertions(+), 777 deletions(-) create mode 100644 memory_spi/memory_spi.c rename spi_flash/spi_flash.h => memory_spi/memory_spi.h (51%) delete mode 100644 spi_flash/spi_flash.c diff --git a/memory_spi/memory_spi.c b/memory_spi/memory_spi.c new file mode 100644 index 0000000..30711a7 --- /dev/null +++ b/memory_spi/memory_spi.c @@ -0,0 +1,775 @@ +/********************************MEMSPI FLASH********************************** +Данный файл содержит базовые функции для общения с памятью FLASH по SPI. +//-------------------Функции-------------------// +@func users + - MEMSPI_Read_Memory Считывание внешней FLASH/EEPROM + - MEMSPI_EEPROM_Write Запись данных в внешнюю EEPROM + - MEMSPI_FLASH_Write Запись данных в внешнюю FLASH (функция сама очищает нужные сектора, и если надо сохраняет выбранные данные) + - MEMSPI_FLASH_Program Программирование внешней FLASH (выбранный участок FLASH должен быть очищен) + - MEMSPI_FLASH_Erase Очистка внешней FLASH + +@func initialization + - MEMSPI_Base_Init Инициализация SPI и GPIO для FLASH + +@func service + - MEMSPI_FLASH_Erase_Sector Очистка сектора FLASH. *есть более общая функция MEMSPI_FLASH_Erase, которая может ощичать несколько секторов + - MEMSPI_FLASH_Program_Page Программирование страницы. *есть более общая функция MEMSPI_FLASH_Program, которая программирует участки больше страницы + - MEMSPI_WriteEnablingUntilTimeout Разрешение записи, пока не будет ответа или не истек таймаут + - MEMSPI_WaitOnFlagUntilTimeout Ожидание флага пока не истек таймаута + +@func cmd functions + - MEMSPI_CMD_Read_Status_Register Отправка комманд Read Status Register / Read Status Register 2 (0x05h / 0x35h) + - MEMSPI_CMD_Write_Status_Register Отправка комманды Write Status Register (0x01h) + - MEMSPI_CMD_Write_Enable Отправка комманды Write Enable (0x06h) + - MEMSPI_CMD_Write_Disable Отправка комманды Write Disable (0x04h) + - MEMSPI_CMD_Read_Data Отправка комманды Read Data (0x03h) + - MEMSPI_CMD_FLASH_Page_Program Отправка комманды Write (eeprom) (0x02h) + - MEMSPI_CMD_FLASH_Page_Program Отправка комманды Page Program (flash) (0x02h) + - MEMSPI_CMD_FLASH_Erase_Sector Отправка комманды Erase Sector (flash) (0x20h) + - MEMSPI_CMD_Fast_Read Отправка комманды Fast Read (0x0Bh) + - MEMSPI_CMD_Read_JEDEC_ID Отправка комманды Read JEDEC ID (0x4Bh) + - MEMSPI_CMD_Read_Device_ID Отправка комманды Read Manufacture / Device Id (0x90) + +@func SPI functions + - SPI_RES = MEMSPI_SPI_Transmit Функция отправки по SPI (содержит только HAL_SPI_Transmit) + - SPI_RES = MEMSPI_SPI_Receive Функция приема по SPI (содержит только HAL_SPI_Receive) +***************************************************************************/ +#include "memory_spi.h" + +//------------------------------------------------------------- +//--------------------------FOR USER--------------------------- +/** + * @brief Initialize SPI and GPIO for MEMSPI FLASH. + * @param hmemspi - указатель на структуру с настройками SPI и GPIO портов. + */ +void MEMSPI_Base_Init(MEMSPI_HandleTypeDef *hmemspi) +{ + + // SPI PERIPH INIT + if(hmemspi->hspi.Instance == NULL) + hmemspi->hspi.Instance = SPI1; + + hmemspi->hspi.Init.Mode = SPI_MODE_MASTER; + hmemspi->hspi.Init.Direction = SPI_DIRECTION_2LINES; + hmemspi->hspi.Init.DataSize = SPI_DATASIZE_8BIT; + hmemspi->hspi.Init.CLKPolarity = SPI_POLARITY_LOW; + hmemspi->hspi.Init.CLKPhase = SPI_PHASE_1EDGE; + hmemspi->hspi.Init.NSS = SPI_NSS_SOFT; + hmemspi->hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; + hmemspi->hspi.Init.TIMode = SPI_TIMODE_DISABLE; + hmemspi->hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + hmemspi->hspi.Init.CRCPolynomial = 10; + + // CLOCK + if(hmemspi->hspi.Instance == SPI1) + __HAL_RCC_SPI2_CLK_ENABLE(); + else if (hmemspi->hspi.Instance == SPI2) + __HAL_RCC_SPI2_CLK_ENABLE(); + else if (hmemspi->hspi.Instance == SPI3) + __HAL_RCC_SPI3_CLK_ENABLE(); + + // SPI INIT + HAL_SPI_Init(&hmemspi->hspi); + + // GPIO INIT + GPIO_Clock_Enable(hmemspi->GPIOs.CS_GPIOx); + GPIO_Clock_Enable(hmemspi->GPIOs.CLK_GPIOx); + GPIO_Clock_Enable(hmemspi->GPIOs.MISO_GPIOx); + GPIO_Clock_Enable(hmemspi->GPIOs.MOSI_GPIOx); + // CHIP SELECT PIN INIT + GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = hmemspi->GPIOs.CS_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + HAL_GPIO_Init(hmemspi->GPIOs.CS_GPIOx, &GPIO_InitStruct); + // CLK PIN INIT + GPIO_InitStruct.Pin = hmemspi->GPIOs.CLK_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; + HAL_GPIO_Init(hmemspi->GPIOs.CLK_GPIOx, &GPIO_InitStruct); + // MISO PIN INIT + GPIO_InitStruct.Pin = hmemspi->GPIOs.MISO_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; + HAL_GPIO_Init(hmemspi->GPIOs.MISO_GPIOx, &GPIO_InitStruct); + // MOSI PIN INIT + GPIO_InitStruct.Pin = hmemspi->GPIOs.MOSI_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; + HAL_GPIO_Init(hmemspi->GPIOs.MOSI_GPIOx, &GPIO_InitStruct); + +} + +/** + * @brief Read external FLASH/EEPROM. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс откуда начинать считывание. + * @param pBuff - куда записывать данные из FLASH. + * @param Size - сколько байтов считывать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return HAL status. + * @note Включает в себя проверку на доступность памяти (флаг BUSY) + */ +HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout) +{ + HAL_StatusTypeDef MEMSPI_Status; + uint32_t tickstart = HAL_GetTick(); + + // wait for unbusy + if(MEMSPI_WaitOnFlagUntilTimeout(hmemspi, MEMSPI_SR_BUSY, 0, &Timeout, &tickstart) != HAL_OK) // if its unbusy for timeout + return HAL_TIMEOUT; // return timeout error + + MEMSPI_Status = MEMSPI_CMD_Read_Data(hmemspi, FLASH_Address, pBuff, Size, Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + return HAL_OK; +} + +/** + * @brief Read external FLASH/EEPROM. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в EEPROM. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return HAL status. + * @note Включает в себя проверку на доступность памяти (флаг BUSY) + */ +HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout) +{ + uint32_t tickstart = HAL_GetTick(); + HAL_StatusTypeDef MEMSPI_Status; + + // enable writting and waiting for unbusy + if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, &Timeout, &tickstart) != HAL_OK) // if writting isnt enable + return HAL_TIMEOUT; // return timeout + + MEMSPI_Status = MEMSPI_CMD_EEPROM_Write(hmemspi, FLASH_Address, pData, Size, Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // waiting for ending of writting + if(MEMSPI_WaitOnFlagUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, &Timeout, &tickstart) != HAL_OK) // if writting isnt done (MEMSPI busy and WEL bit isnt in reset state) + return HAL_TIMEOUT; + + // update handle variables + hmemspi->hNextAddr = (FLASH_Address+Size); + hmemspi->hNextPage = (FLASH_Address+Size)/MEMSPI_PAGE_SIZE; + hmemspi->hNextSector = (FLASH_Address+Size)/MEMSPI_SECTOR_SIZE; + return HAL_OK; +} +/** + * @brief Write external FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param WriteInit - указатель на структуру, определяющую участок памяти для записи. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return HAL status. + * @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц. + * Данные в сектора участка, но за пределами участка не сохраняются. + */ +HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_WriteInitTypeDef *WriteInit, uint32_t Timeout) +{ + uint32_t tickstart = HAL_GetTick(); + uint32_t timeoutcnt = Timeout; + uint8_t *writebuff = WriteInit->pDataPtr; + HAL_StatusTypeDef MEMSPI_Status; + + // WAIT FOR UNBUSY + if(MEMSPI_WaitOnFlagUntilTimeout(hmemspi, MEMSPI_SR_BUSY, 0, &Timeout, &tickstart) != HAL_OK) // if its unbusy for timeout + return HAL_TIMEOUT; // return timeout error + + // ERASE FLASH + MEMSPI_Status = MEMSPI_FLASH_Erase(hmemspi, WriteInit->Sector_Address, WriteInit->Sector_Size, Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // WRITE FLASH WITH SAVING PREVIOUS DATA + if(WriteInit->fSavePrevoisData) + { + uint8_t sector_buff[WriteInit->Sector_Size]; + // store data from flash + MEMSPI_Status = MEMSPI_Read_Memory(hmemspi, WriteInit->Sector_Address, sector_buff, WriteInit->Sector_Size, Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // CHANGE DATA IN USER "SECTOR" + uint32_t addr_shift = WriteInit->Data_Address - WriteInit->Sector_Address; + for(int i = 0; i < WriteInit->Data_Size; i++) + { + sector_buff[addr_shift+i] = WriteInit->pDataPtr[addr_shift+i]; + } + + writebuff = sector_buff; // set pointer to buffer that need to be restored + + // PROGRAM FLASH WITH NEW DATA + // program data to flash + MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, WriteInit->Sector_Address, writebuff, WriteInit->Sector_Size, Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + } + // WRITE FLASH WITHOUT SAVING PREVIOUS DATA + else + { + // PROGRAM FLASH WITH NEW DATA + timeoutcnt = HAL_GetTick() - tickstart; // update timeout + Timeout -= timeoutcnt; + tickstart += timeoutcnt; + // program data to flash + MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, WriteInit->Sector_Address, writebuff, WriteInit->Sector_Size, Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + } + return HAL_OK; +} + +/** + * @brief Program external FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в FLASH. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return HAL status. + * @note Программирование участка памяти, без ограничений на кол-во байт + */ +HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout) +{ + uint32_t tickstart = HAL_GetTick(); + HAL_StatusTypeDef MEMSPI_Status; + + // CALC AREA TO PROGRAM + uint16_t bytecnt = 0; + uint16_t currentpage_size = 0; + uint16_t lastpage_size = Size; + uint16_t firstpage = (FLASH_Address/MEMSPI_PAGE_SIZE); + uint16_t lastpage = ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE); + if(firstpage != lastpage) // if area is on several pages + { + currentpage_size = (firstpage+1)*MEMSPI_PAGE_SIZE - FLASH_Address; // set size of data on first page + lastpage_size = (FLASH_Address+Size) - lastpage*MEMSPI_PAGE_SIZE; // set size of data on last page + } + + // PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST + hmemspi->hNextAddr = FLASH_Address; // address would increase automatically in this variable + for(int i = 0; i < lastpage - firstpage; i++) + { + MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], currentpage_size, &Timeout, &tickstart); // programm page + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // note for multiple page program: first we program rest of the first page, + // then we shift byte count to data, that shoud be on the next page + bytecnt += currentpage_size; + // and set current size as page size. because next pages will be fully programmed + currentpage_size = MEMSPI_PAGE_SIZE; + } + + // PROGRAM LAST PAGE + MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], lastpage_size, &Timeout, &tickstart); // programm page + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + return HAL_OK; // if all ok return HAL_OK +} + + +/** + * @brief Erase external FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс где надо данные стереть. + * @param Size - сколько байтов стереть. + * @param Timeout - время, за которое должно быть осуществлена очистка. + * @return HAL status. + * @note Т.к. очитска происходит по секторам, Size нужен, чтобы определить сколько секторов очистить + * И если начальны адресс будет на Sector 0, а последний байт на Sector 1, то произойдет очистка Sector 0 и Sector 1 + */ +HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout) +{ + uint32_t tickstart = HAL_GetTick(); + HAL_StatusTypeDef MEMSPI_Status; + + // CALC AREA TO ERASE + uint16_t bytecnt = 0; + uint16_t firstsector = (FLASH_Address/MEMSPI_SECTOR_SIZE); + uint16_t lastsector = ((FLASH_Address+Size-1)/MEMSPI_SECTOR_SIZE); + + for(int i = 0; i <= (lastsector - firstsector); i++) + { + MEMSPI_Status = MEMSPI_FLASH_Erase_Sector(hmemspi, FLASH_Address, &Timeout, &tickstart); // programm page + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + FLASH_Address += MEMSPI_SECTOR_SIZE; + } + return HAL_OK; +} + +/** + * @brief Erase external FLASH Sector. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс где надо данные стереть. + * @param Timeout - время, за которое должно быть осуществлена очистка. + * @param tickstart - время, относительно которого надо отсчитывать таймаут. + * @return HAL status. + * @note При Timeout = 0, функция не будет ожидать окончания очистки (выставления в 0 флагов BUSY и WEL) + * @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом. + */ +HAL_StatusTypeDef MEMSPI_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t *Timeout, uint32_t *tickstart) +{ + HAL_StatusTypeDef MEMSPI_Status; + // enable writting and waiting for unbusy + if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) // if writting isnt enable + return HAL_TIMEOUT; // return timeout + + // erase sector (instruction) + MEMSPI_CMD_FLASH_Erase_Sector(hmemspi, FLASH_Address, *Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // waiting for ending of erasing + if(Timeout) // if timeout isn zero - wait it. + if(MEMSPI_WaitOnFlagUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if erase isnt done (MEMSPI busy and WEL bit isnt in reset state) + return HAL_TIMEOUT; // return timeout because erasing instruction accepted, but arent done + // note: if timeout == 0, erasing wouldnt be checking for ending (check busy flag) + + return HAL_OK; // if all ok return HAL_OK +} + +/** + * @brief Program page in external FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в FLASH. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @param tickstart - время, относительно которого надо отсчитывать таймаут. + * @return HAL status. + * @note Позволяет перепрограммировать только байты в прелелах одной страницы. + Для более гибкого программирования есть функция MEMSPI_FLASH_Program, которая программирует участки любой длины (в теории). + */ +HAL_StatusTypeDef MEMSPI_FLASH_Program_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart) +{ + HAL_StatusTypeDef MEMSPI_Status; + // enable writting and waiting for unbusy + if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) // if writting isnt enable + return HAL_TIMEOUT; // return timeout + + // check if flash range is placed at one page + if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte + return HAL_ERROR; // return error + + // programm page (instruction) + MEMSPI_Status = MEMSPI_CMD_FLASH_Page_Program(hmemspi, FLASH_Address, pData, Size, *Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // waiting for ending of writting + if(MEMSPI_WaitOnFlagUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if writting isnt done (MEMSPI busy and WEL bit isnt in reset state) + return HAL_TIMEOUT; + + // update handle variables + hmemspi->hNextAddr = (FLASH_Address+Size); + hmemspi->hNextPage = (FLASH_Address+Size)/MEMSPI_PAGE_SIZE; + hmemspi->hNextSector = (FLASH_Address+Size)/MEMSPI_SECTOR_SIZE; + + return HAL_OK; +} +/** + * @brief Setting WEL bit until it setted or until timeout. + * @param hmemspi - указатель на хендл внешней памяти. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @param tickstart - время, относительно которого надо отсчитывать таймаут. + * @return HAL status. + * @note Подает команду на разрешение записи до тех пор, пока она запись не разрешиться или до тех пор, пока таймаут не истечет. + */ +HAL_StatusTypeDef MEMSPI_WriteEnablingUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint32_t *Timeout, uint32_t *tickstart) +{ + HAL_StatusTypeDef MEMSPI_Status; + // enable writting + MEMSPI_Status = MEMSPI_CMD_Write_Enable(hmemspi, *Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // check is writting enabled + MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, *Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + while((hmemspi->SR&MEMSPI_SR_WEL) != MEMSPI_SR_WEL) + { + // if flash isnt busy - set WEL flag + if((hmemspi->SR&MEMSPI_SR_BUSY) == 0) + MEMSPI_CMD_Write_Enable(hmemspi, *Timeout); + + // check is writting enabled +// MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, *Timeout); + MEMSPI_Status = MEMSPI_SPI_Receive(hmemspi, (uint8_t *)&hmemspi->SR, 1, *Timeout); // receive response + if(MEMSPI_Status != HAL_OK) + { + MEMSPI_Deselect(hmemspi); + return MEMSPI_Status; + } + + if((HAL_GetTick() - *tickstart) >= *Timeout) // if time is out + { + MEMSPI_Deselect(hmemspi); + return HAL_TIMEOUT; // set timeout + } + } + MEMSPI_Deselect(hmemspi); + MEMSPI_Update_Timeout_Variables(Timeout, tickstart); + return HAL_OK; // if all ok return HAL_OK +} + +/** + * @brief Wait for flag until timeout. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FlagMask - маска для флагов, какие флаги считывать. + * @param FlagStatus - какое состояние должно быть у выбранных флагов. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @param tickstart - время, относительно которого надо отсчитывать таймаут. + * @return HAL status. + * @note Считывает флаги до тех пор, пока они не будут в состоянии FlagStatus или до тех пор, пока таймаут не истечет. + */ +HAL_StatusTypeDef MEMSPI_WaitOnFlagUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint16_t FlagMask, uint16_t FlagStatus, uint32_t *Timeout, uint32_t *tickstart) +{ + HAL_StatusTypeDef MEMSPI_Status; + // check flags + MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, FlagMask, 0, *Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + while((hmemspi->SR&FlagMask) != FlagStatus) + { + // check flags +// MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, FlagMask, *Timeout); + MEMSPI_Status = MEMSPI_SPI_Receive(hmemspi, (uint8_t *)&hmemspi->SR, 1, *Timeout); // receive response + if(MEMSPI_Status != HAL_OK) + { + MEMSPI_Deselect(hmemspi); + return MEMSPI_Status; + } + + if((HAL_GetTick() - *tickstart) >= *Timeout) // if time is out + { + MEMSPI_Deselect(hmemspi); + return HAL_TIMEOUT; // set timeout + } + } + MEMSPI_Deselect(hmemspi); + MEMSPI_Update_Timeout_Variables(Timeout, tickstart); + return HAL_OK; // if all ok return HAL_OK +} + +/** + * @brief Update Timeout variables. + * @param Timeout - указатель на переменную содержащию значение таймаута + * @param tickstart - указатель на переменную содержащию начальное кол-во тиков. + */ +void MEMSPI_Update_Timeout_Variables(uint32_t *Timeout, uint32_t *tickstart) +{ + uint32_t timeoutcnt = HAL_GetTick() - *tickstart; // update timeout + *Timeout -= timeoutcnt; + *tickstart += timeoutcnt; +} +//------------------------------------------------------------- +//----------------------COMMAND FUNCTIONS---------------------- +/** + * @brief Send command to read Status Register. + * @param hmemspi - указатель на хендл внешней памяти. + * @param RequestedBits - какие биты запросить. + * @param EndCMD - завершать комманду или нет. (очистка Chip Selected пина) + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return Заполняет Status Register в hmemspi. + * @note Всего есть две комманды: на запрос верхнего или нижниго байта. + * Функция в соответствии с RequestedBits определяет какой байт запросить, или два байта сразу. + */ +HAL_StatusTypeDef MEMSPI_CMD_Read_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t RequestedBits, uint8_t EndCMD, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + uint8_t command[2]; + uint8_t *pSRPtr = 0; + uint8_t size = 1; + +#ifdef MEMSPI_READ_STATUS_REG_2 + if(RequestedBits >> 8) // if its high byte of status register + { + command[0] = MEMSPI_READ_STATUS_REG_2; + pSRPtr = (uint8_t *)(&hmemspi->SR) + 1; // set pointer to HI byte of SR register + size = 1; + if(RequestedBits & 0xFF) // if low byte also requester + { + size = 2; // set size to 2 bytes + command[1] = MEMSPI_READ_STATUS_REG; + } + } + else // of its low byte of status register + { + command[0] = MEMSPI_READ_STATUS_REG; + pSRPtr = (uint8_t *)(&hmemspi->SR); // set pointer to LO byte of SR register + size = 1; + } + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); // send insctruction to read SR + SPI_RES = MEMSPI_SPI_Receive(hmemspi, pSRPtr, 1, Timeout); // receive response + if(size > 1) // if 2 bytes are requested + { + MEMSPI_Deselect(hmemspi); + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command+1, 1, Timeout); // send insctruction to read SR + SPI_RES = MEMSPI_SPI_Receive(hmemspi, pSRPtr-1, 1, Timeout); // receive response + MEMSPI_Deselect(hmemspi); + } +#else + command[0] = MEMSPI_READ_STATUS_REG; + pSRPtr = (uint8_t *)(&hmemspi->SR); // set pointer to HI byte of SR register + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); // send insctruction to read SR + SPI_RES = MEMSPI_SPI_Receive(hmemspi, pSRPtr, 1, Timeout); // receive response +#endif + if(EndCMD) + MEMSPI_Deselect(hmemspi); + return SPI_RES; +} + +/** + * @brief Send command to write bits in Status Register. + * @param hmemspi - указатель на хендл внешней памяти. + * @param WrittenBits - какие биты запросить. + * @note Данная команда посылает биты, как сдвинутые на 2 вправо. Т.е. 0-й бит в комманде - 2-й бит BP0. + Но биты указываются в также как они расположены и регистре. Функция сама выполняет сдвиг. + */ +HAL_StatusTypeDef MEMSPI_CMD_Write_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t WrittenBits, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + uint8_t command[2]; + + command[0] = MEMSPI_WRITE_STATUS_REG; + command[1] = WrittenBits >> 2; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +} + +/** + * @brief Send command to set Write Enable Latch (WEL) in Status Register. + * @param hmemspi - указатель на хендл внешней памяти. + * @note Разрешает запись в FLASH, путем высталения WEL в Status Register + */ +HAL_StatusTypeDef MEMSPI_CMD_Write_Enable(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + uint8_t command[1]; + command[0] = MEMSPI_WRITE_ENABLE; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +} + + +/** + * @brief Send command to read data from FLASH/EEPROM. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс откуда начинать считывание. + * @param pBuff - куда записывать данные из FLASH. + * @param Size - сколько байтов считывать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return pBuff. + */ +HAL_StatusTypeDef MEMSPI_CMD_Read_Data(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + uint8_t command[4]; + uint8_t response[2] = {0}; + + command[0] = MEMSPI_READ_DATA; + command[1] = FLASH_Address >> 16 & 0xFF; + command[2] = FLASH_Address >> 8 & 0xFF; + command[3] = FLASH_Address & 0xFF; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); + SPI_RES = MEMSPI_SPI_Receive(hmemspi, pBuff, Size, Timeout); + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +} + +/** + * @brief Send command to write eeprom. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в FLASH. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должна быть осуществлена запись. + * @note Рзамер данных для записи в EEPROM без ограничений. + */ +HAL_StatusTypeDef MEMSPI_CMD_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + // 1 command byte + 3 address bytes + 256 data bytes + uint8_t command[1+3+MEMSPI_PAGE_SIZE]; + FLASH_Address = FLASH_Address & 0xFFFFFF; + + command[0] = MEMSPI_WRITE_EEPROM; + command[1] = FLASH_Address >> 16 & 0xFF; + command[2] = FLASH_Address >> 8 & 0xFF; + command[3] = FLASH_Address & 0xFF; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); // send insctruction to write + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, pData, Size, Timeout); // send data to write + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +} +/** + * @brief Send command to page program in FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в FLASH. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должна быть осуществлена запись. + * @note Программирование FLASH только в пределах одной страницы. + * Т.е. если запись с 0x0, то не больше 256 байт. Если с 0ч40, то не больше 192 байт. + */ +HAL_StatusTypeDef MEMSPI_CMD_FLASH_Page_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + // 1 command byte + 3 address bytes + 256 data bytes + uint8_t command[1+3+MEMSPI_PAGE_SIZE]; + FLASH_Address = FLASH_Address & 0xFFFFFF; + + command[0] = MEMSPI_PAGE_PROGRAM; + command[1] = FLASH_Address >> 16 & 0xFF; + command[2] = FLASH_Address >> 8 & 0xFF; + command[3] = FLASH_Address & 0xFF; + + // check if flash range is placed at one page + if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte + return HAL_ERROR; // return error + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); // send insctruction to write + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, pData, Size, Timeout); // send data to write + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +} + +/** + * @brief Send command to erase sector. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс где надо данные стереть. + * @param Timeout - время, за которое должна быть осуществлена очистка. + * @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом. + */ +HAL_StatusTypeDef MEMSPI_CMD_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + uint8_t command[4]; + uint8_t response[8]; + FLASH_Address = FLASH_Address & 0xFFFFFF; + + command[0] = MEMSPI_ERASE_SECTOR; + command[1] = FLASH_Address >> 16; + command[2] = FLASH_Address >> 8; + command[3] = FLASH_Address; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +} + + +/** + * @brief Send command to read JEDEC ID. + * @param hmemspi - указатель на хендл внешней памяти. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return JEDEC ID микросхемы. + */ +uint32_t MEMSPI_CMD_Read_JEDEC_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + uint8_t command[1] = {0}; + uint8_t receive[4] = {0}; + uint32_t return_val; + + command[0] = MEMSPI_READ_JEDEC_ID; + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); + SPI_RES = MEMSPI_SPI_Receive(hmemspi, &receive[1], 3, Timeout); + MEMSPI_Deselect(hmemspi); + + return_val = (*(uint64_t *)receive); + return __REV(return_val) & 0xFFFFFF; +} + +/** + * @brief Send command to read JEDEC ID. + * @param hmemspi - указатель на хендл внешней памяти. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return Device ID микросхемы. + */ +uint64_t MEMSPI_CMD_Read_Device_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + uint8_t command[1] = {0}; + uint8_t receive[8] = {0}; + uint64_t return_val_LO; + uint64_t return_val_HI; + command[0] = MEMSPI_READ_UNIQUE_ID; + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); + SPI_RES = MEMSPI_SPI_Receive(hmemspi, receive, 8, Timeout); + MEMSPI_Deselect(hmemspi); + + return_val_LO = (*(uint64_t *)receive) >> 32; + return_val_HI = (*(uint64_t *)receive) & 0xFFFFFFFF; + return ((uint64_t)__REV(return_val_HI) << 32) | __REV(return_val_LO); +} +/** + * @brief Send command to fast read data from FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс откуда начинать считывание. + * @param pBuff - куда записывать данные из FLASH. + * @param Size - сколько байтов считывать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @note Данная функция предполагает отправку одного dummy байта после адресса, но у меня поч не работает пока :( + */ +HAL_StatusTypeDef MEMSPI_CMD_Fast_Read(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + uint8_t command[5] = {0}; + uint8_t response[2] = {0}; + + command[0] = MEMSPI_READ_DATA; + command[1] = FLASH_Address >> 16 & 0xFF; + command[2] = FLASH_Address >> 8 & 0xFF; + command[3] = FLASH_Address & 0xFF; + command[4] = 0xFF; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 5, Timeout); + SPI_RES = MEMSPI_SPI_Receive(hmemspi, pBuff, Size, Timeout); + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +} +//------------------------------------------------------------- + diff --git a/spi_flash/spi_flash.h b/memory_spi/memory_spi.h similarity index 51% rename from spi_flash/spi_flash.h rename to memory_spi/memory_spi.h index cb8b077..59f70a2 100644 --- a/spi_flash/spi_flash.h +++ b/memory_spi/memory_spi.h @@ -1,59 +1,85 @@ -/********************************W25 FLASH********************************** -Данный файл содержит инклюды и дефайны для общения с памятью FLASH по SPI. +/********************************SPI MEMORY********************************* +Данный файл содержит инклюды и дефайны для общения с FLASH/EEPROM по SPI. ***************************************************************************/ -#ifndef __SPI_FLASH_H_ -#define __SPI_FLASH_H_ +#ifndef __SPI_MEMORY_H_ +#define __SPI_MEMORY_H_ #include "stm32f4xx_hal.h" #include "gpio_general.h" /////////////////////////---USER SETTINGS---///////////////////////// +//#define EXT_FLASH +#define EXT_EEPROM -#define W25_Internal_Flash_Buffer_For_reProgram_BankNumber FLASH_BANK_1 -#define W25_Internal_Flash_Buffer_For_reProgram_SectNumber FLASH_SECTOR_11 -#define W25_Internal_Flash_Buffer_For_reProgram_Address 0x08E000000 +#if defined(EXT_FLASH) & defined(EXT_EEPROM) + #error Choose only one memory +#endif + +//#define SEPARATED_STATUS_REGISTER ///////////////////////////////////////////////////////////////////// ////////////////////////////---DEFINES---//////////////////////////// -/** - * @brief Defines for W25 chip. +/** + * @brief SPI Transmit. + * @param _hmemspi_ - указатель на хендл внешней памяти. + * @param _data_ - указатель на данные для отправки. + * @param _size_ - размер данных для отправки. + * @param _timeout_ - время, за которое должна быть осуществлена отправка. + * @note Здесь вызывается только функция HAL, и ничего больше. */ -#define W25_Select(_hw25_) (_hw25_->GPIOs.CS_GPIOx->BSRR = _hw25_->GPIOs.CS_PIN << 16) -#define W25_Deselect(_hw25_) (_hw25_->GPIOs.CS_GPIOx->BSRR = _hw25_->GPIOs.CS_PIN) -#define W25_SECTOR_SIZE (0x1000) -#define W25_PAGE_SIZE (0x100) +#define MEMSPI_SPI_Transmit(_hmemspi_, _data_, _size_, _timeout_) HAL_SPI_Transmit(&_hmemspi_->hspi, _data_, _size_, _timeout_) +/** + * @brief SPI Receive. + * @param _hmemspi_ - указатель на хендл внешней памяти. + * @param _data_ - указатель на буффер для прниема данных. + * @param _size_ - размер данных для приема. + * @param _timeout_ - время, за которое должен быть осуществлен прием. + * @note Здесь вызывается только функция HAL, и ничего больше. + */ +#define MEMSPI_SPI_Receive(_hmemspi_, _data_, _size_, _timeout_) HAL_SPI_Receive(&_hmemspi_->hspi, _data_, _size_, _timeout_) + +/** + * @brief Defines for MEMORY chip. + */ +#define MEMSPI_Select(_hmemspi_) (_hmemspi_->GPIOs.CS_GPIOx->BSRR = _hmemspi_->GPIOs.CS_PIN << 16) +#define MEMSPI_Deselect(_hmemspi_) (_hmemspi_->GPIOs.CS_GPIOx->BSRR = _hmemspi_->GPIOs.CS_PIN) +#define MEMSPI_SECTOR_SIZE (0x1000) +#define MEMSPI_PAGE_SIZE (0x100) /** * @brief Defines for CMD. */ -#define W25_READ_JEDEC_ID 0x9F -#define W25_READ_UNIQUE_ID 0x4B -#define W25_WRITE_ENABLE 0x06 -#define W25_WRITE_DISABLE 0x04 -#define W25_WRITE_STATUS_REG 0x01 +#define MEMSPI_READ_JEDEC_ID 0x9F +#define MEMSPI_READ_UNIQUE_ID 0x4B +#define MEMSPI_WRITE_ENABLE 0x06 +#define MEMSPI_WRITE_DISABLE 0x04 +#define MEMSPI_WRITE_STATUS_REG 0x01 -#define W25_ERASE_SECTOR 0x20 -#define W25_PAGE_PROGRAM 0x02 -#define W25_READ_STATUS_REG_1 0x05 -#define W25_READ_STATUS_REG_2 0x35 -#define W25_READ_DATA 0x03 +#define MEMSPI_ERASE_SECTOR 0x20 +#define MEMSPI_PAGE_PROGRAM 0x02 +#define MEMSPI_WRITE_EEPROM MEMSPI_PAGE_PROGRAM +#define MEMSPI_READ_STATUS_REG 0x05 +#if defined(SEPARATED_STATUS_REGISTER) + #define MEMSPI_READ_STATUS_REG_2 0x35 +#endif +#define MEMSPI_READ_DATA 0x03 /** * @brief Defines for Status Register. */ -#define W25_Get_Flag(_hw25_,_flag_) (((W25_StatusRegisterTypeDef)(_hw25_->SR))._flag_) +#define MEMSPI_Get_Flag(_hmemspi_,_flag_) (((MEMSPI_StatusRegisterTypeDef)(_hmemspi_->SR))._flag_) -#define W25_SR_SUS (1<<15) -#define W25_SR_QE (1<<9) -#define W25_SR_SRP1 (1<<8) -#define W25_SR_SRP0 (1<<7) -#define W25_SR_SEC (1<<8) -#define W25_SR_TB (1<<5) -#define W25_SR_BP2 (1<<4) -#define W25_SR_BP1 (1<<3) -#define W25_SR_BP0 (1<<2) -#define W25_SR_WEL (1<<1) -#define W25_SR_BUSY (1<<0) +#define MEMSPI_SR_SUS (1<<15) +#define MEMSPI_SR_QE (1<<9) +#define MEMSPI_SR_SRP1 (1<<8) +#define MEMSPI_SR_SRP0 (1<<7) +#define MEMSPI_SR_SEC (1<<8) +#define MEMSPI_SR_TB (1<<5) +#define MEMSPI_SR_BP2 (1<<4) +#define MEMSPI_SR_BP1 (1<<3) +#define MEMSPI_SR_BP0 (1<<2) +#define MEMSPI_SR_WEL (1<<1) +#define MEMSPI_SR_BUSY (1<<0) /** * @brief Calc dividing including remainder (divide and ceil) @@ -79,7 +105,7 @@ typedef struct uint32_t Sector_Address; uint32_t Sector_Size; unsigned fSavePrevoisData:1; -}W25_WriteInitTypeDef; +}MEMSPI_WriteInitTypeDef; typedef struct { @@ -93,32 +119,32 @@ typedef struct uint32_t MISO_PIN; GPIO_TypeDef *MOSI_GPIOx; uint32_t MOSI_PIN; -}W25_GPIOTypeDef; +}MEMSPI_GPIOTypeDef; typedef struct { uint16_t SR; - SPI_HandleTypeDef hspi; - W25_GPIOTypeDef GPIOs; - uint32_t hNextAddr; - uint16_t hNextPage; - uint16_t hNextSector; -}W25_HandleTypeDef; -extern W25_HandleTypeDef hw25; + SPI_HandleTypeDef hspi; + MEMSPI_GPIOTypeDef GPIOs; + uint32_t hNextAddr; + uint16_t hNextPage; + uint16_t hNextSector; +}MEMSPI_HandleTypeDef; +extern MEMSPI_HandleTypeDef hmemspi; ///////////////////////---STRUCTURES & ENUMS---////////////////////// ///////////////////////////////////////////////////////////////////// ///////////////////////---FUNCTIONS FOR USER---////////////////////// /** - * @brief Initialize SPI and GPIO for W25 FLASH. - * @param hw25 - указатель на структуру с настройками SPI и GPIO портов. + * @brief Initialize SPI and GPIO for MEMSPI FLASH. + * @param hmemspi - указатель на структуру с настройками SPI и GPIO портов. */ -void W25_Base_Init(W25_HandleTypeDef *hw25); +void MEMSPI_Base_Init(MEMSPI_HandleTypeDef *hmemspi); /** * @brief Read external FLASH. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param FLASH_Address - адресс откуда начинать считывание. * @param pBuff - куда записывать данные из FLASH. * @param Size - сколько байтов считывать. @@ -126,22 +152,34 @@ void W25_Base_Init(W25_HandleTypeDef *hw25); * @return HAL status. * @note Включает в себя проверку на доступность памяти (флаг BUSY) */ -HAL_StatusTypeDef W25_FLASH_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout); +HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout); + +/** + * @brief Read external FLASH/EEPROM. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в EEPROM. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return HAL status. + * @note Включает в себя проверку на доступность памяти (флаг BUSY) + */ +HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout); /** * @brief Write external FLASH. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param WriteInit - указатель на структуру, определяющую участок памяти для записи. * @param Timeout - время, за которое должно быть осуществлено чтение. * @return HAL status. * @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц. * Данные в сектора участка, но за пределами участка не сохраняются. */ -HAL_StatusTypeDef W25_FLASH_Write(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout); +HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_WriteInitTypeDef *WriteInit, uint32_t Timeout); /** * @brief Program external FLASH. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param FLASH_Address - адресс куда начинать записывать. * @param pData - откуда брать данные для записи в FLASH. * @param Size - сколько байтов записать. @@ -149,11 +187,11 @@ HAL_StatusTypeDef W25_FLASH_Write(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef * @return HAL status. * @note Программирование участка памяти, без ограничений на кол-во байт */ -HAL_StatusTypeDef W25_FLASH_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout); +HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout); /** * @brief Erase external FLASH. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param FLASH_Address - адресс где надо данные стереть. * @param Size - сколько байтов стереть. * @param Timeout - время, за которое должно быть осуществлена очистка. @@ -161,11 +199,14 @@ HAL_StatusTypeDef W25_FLASH_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Addr * @note Т.к. очитска происходит по секторам, Size нужен, чтобы определить сколько секторов очистить * И если начальны адресс будет на Sector 0, а последний байт на Sector 1, то произойдет очистка Sector 0 и Sector 1 */ -HAL_StatusTypeDef W25_FLASH_Erase(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout); +HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout); +///////////////////////---FUNCTIONS FOR USER---////////////////////// +///////////////////////////////////////////////////////////////////// +////////////////////////---SERVICE FUNCTIONS---////////////////////// /** * @brief Erase external FLASH Sector. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param FLASH_Address - адресс где надо данные стереть. * @param Timeout - время, за которое должно быть осуществлена очистка. * @param tickstart - время, относительно которого надо отсчитывать таймаут. @@ -173,11 +214,11 @@ HAL_StatusTypeDef W25_FLASH_Erase(W25_HandleTypeDef *hw25, uint32_t FLASH_Addres * @note При Timeout = 0, функция не будет ожидать окончания очистки (выставления в 0 флагов BUSY и WEL) * @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом. */ -HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint32_t Timeout, uint32_t tickstart); +HAL_StatusTypeDef MEMSPI_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t *Timeout, uint32_t *tickstart); /** * @brief Program page in external FLASH. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param FLASH_Address - адресс куда начинать записывать. * @param pData - откуда брать данные для записи в FLASH. * @param Size - сколько байтов записать. @@ -185,23 +226,23 @@ HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH * @param tickstart - время, относительно которого надо отсчитывать таймаут. * @return HAL status. * @note Позволяет перепрограммировать только байты в прелелах одной страницы. - Для более гибкого программирования есть функция W25_FLASH_Program, которая программирует участки любой длины (в теории). + Для более гибкого программирования есть функция MEMSPI_FLASH_Program, которая программирует участки любой длины (в теории). */ -HAL_StatusTypeDef W25_FLASH_Program_Page(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint32_t tickstart); +HAL_StatusTypeDef MEMSPI_FLASH_Program_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart); /** * @brief Setting WEL bit until it setted or until timeout. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param Timeout - время, за которое должно быть осуществлено чтение. * @param tickstart - время, относительно которого надо отсчитывать таймаут. * @return HAL status. * @note Подает команду на разрешение записи до тех пор, пока она запись не разрешиться или до тех пор, пока таймаут не истечет. */ -HAL_StatusTypeDef W25_WriteEnablingUntilTimeout(W25_HandleTypeDef *hw25, uint32_t Timeout, uint32_t tickstart); +HAL_StatusTypeDef MEMSPI_WriteEnablingUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint32_t *Timeout, uint32_t *tickstart); /** * @brief Wait for flag until timeout. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param FlagMask - маска для флагов, какие флаги считывать. * @param FlagStatus - какое состояние должно быть у выбранных флагов. * @param Timeout - время, за которое должно быть осуществлено чтение. @@ -209,113 +250,114 @@ HAL_StatusTypeDef W25_WriteEnablingUntilTimeout(W25_HandleTypeDef *hw25, uint32_ * @return HAL status. * @note Считывает флаги до тех пор, пока они не будут в состоянии FlagStatus или до тех пор, пока таймаут не истечет. */ -HAL_StatusTypeDef W25_WaitOnFlagUntilTimeout(W25_HandleTypeDef *hw25, uint16_t FlagMask, uint16_t FlagStatus, uint32_t Timeout, uint32_t tickstart); -///////////////////////---FUNCTIONS FOR USER---////////////////////// +HAL_StatusTypeDef MEMSPI_WaitOnFlagUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint16_t FlagMask, uint16_t FlagStatus, uint32_t *Timeout, uint32_t *tickstart); +/** + * @brief Update Timeout variables. + * @param Timeout - указатель на переменную содержащию значение таймаута + * @param tickstart - указатель на переменную содержащию начальное кол-во тиков. + */ +void MEMSPI_Update_Timeout_Variables(uint32_t *Timeout, uint32_t *tickstart); +////////////////////////---SERVICE FUNCTIONS---////////////////////// ///////////////////////////////////////////////////////////////////// //////////////////////---FUNCTION FOR COMMAND---///////////////////// /** * @brief Send command to read Status Register. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param RequestedBits - какие биты запросить. - * @return Заполняет Status Register в hw25. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @return Заполняет Status Register в hmemspi. * @note Всего есть две комманды: на запрос верхнего или нижниго байта. * Функция в соответствии с RequestedBits определяет какой байт запросить, или два байта сразу. */ -void W25_CMD_Read_Status_Register(W25_HandleTypeDef *hw25, uint16_t RequestedBits); +HAL_StatusTypeDef MEMSPI_CMD_Read_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t RequestedBits, uint8_t EndCMD, uint32_t Timeout); /** * @brief Send command to write bits in Status Register. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. * @param WrittenBits - какие биты запросить. + * @param Timeout - время, за которое должна быть осуществлена запись. * @note Данная команда посылает биты, как сдвинутые на 2 вправо. Т.е. 0-й бит в комманде - 2-й бит BP0. Но биты указываются в также как они расположены и регистре. Функция сама выполняет сдвиг. */ -void W25_CMD_Write_Status_Register(W25_HandleTypeDef *hw25, uint16_t WrittenBits); +HAL_StatusTypeDef MEMSPI_CMD_Write_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t WrittenBits, uint32_t Timeout); /** * @brief Send command to set Write Enable Latch (WEL) in Status Register. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. + * @param Timeout - время, за которое должна быть осуществлена запись. * @note Разрешает запись в FLASH, путем высталения WEL в Status Register */ -void W25_CMD_Write_Enable(W25_HandleTypeDef *hw25); +HAL_StatusTypeDef MEMSPI_CMD_Write_Enable(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout); /** - * @brief Send command to read data from FLASH. - * @param hw25 - указатель на хендл flash. + * @brief Send command to read data from FLASH/EEPROM. + * @param hmemspi - указатель на хендл внешней памяти. * @param FLASH_Address - адресс откуда начинать считывание. * @param pBuff - куда записывать данные из FLASH. * @param Size - сколько байтов считывать. + * @param Timeout - время, за которое должно быть осуществлено чтение. * @return pBuff. */ -void W25_CMD_Read_Data(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size); - +HAL_StatusTypeDef MEMSPI_CMD_Read_Data(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout); /** - * @brief Send command to fast read data from FLASH. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс откуда начинать считывание. - * @param pBuff - куда записывать данные из FLASH. - * @param Size - сколько байтов считывать. - * @note Данная функция предполагает отправку одного dummy байта после адресса, но у меня поч не работает пока :( - */ -void W25_CMD_Fast_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size); - -/** - * @brief Send command to fast page program. - * @param hw25 - указатель на хендл flash. + * @brief Send command to write eeprom. + * @param hmemspi - указатель на хендл внешней памяти. * @param FLASH_Address - адресс куда начинать записывать. * @param pData - откуда брать данные для записи в FLASH. * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должна быть осуществлена запись. + * @note Рзамер данных для записи в EEPROM без ограничений. + */ +HAL_StatusTypeDef MEMSPI_CMD_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout); +/** + * @brief Send command to page program in FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в FLASH. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должна быть осуществлена запись. * @note Программирование FLASH только в пределах одной страницы. * Т.е. если запись с 0x0, то не больше 256 байт. Если с 0ч40, то не больше 192 байт. */ -void W25_CMD_Page_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size); +HAL_StatusTypeDef MEMSPI_CMD_FLASH_Page_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout); /** - * @brief Send command to erase sector. - * @param hw25 - указатель на хендл flash. + * @brief Send command to erase sector of FLASH. + * @param hmemspi - указатель на хендл внешней памяти. * @param FLASH_Address - адресс где надо данные стереть. + * @param Timeout - время, за которое должна быть осуществлена очистка. * @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом. */ -void W25_CMD_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address); +HAL_StatusTypeDef MEMSPI_CMD_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout); /** * @brief Send command to read JEDEC ID. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. + * @param Timeout - время, за которое должно быть осуществлено чтение. * @return JEDEC ID микросхемы. */ -uint32_t W25_CMD_Read_JEDEC_ID(W25_HandleTypeDef *hw25); +uint32_t MEMSPI_CMD_Read_JEDEC_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout); /** * @brief Send command to read JEDEC ID. - * @param hw25 - указатель на хендл flash. + * @param hmemspi - указатель на хендл внешней памяти. + * @param Timeout - время, за которое должно быть осуществлено чтение. * @return Device ID микросхемы. */ -uint64_t W25_CMD_Read_Device_ID(W25_HandleTypeDef *hw25); +uint64_t MEMSPI_CMD_Read_Device_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout); + +/** + * @brief Send command to fast read data from FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс откуда начинать считывание. + * @param pBuff - куда записывать данные из FLASH. + * @param Size - сколько байтов считывать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @note Данная функция предполагает отправку одного dummy байта после адресса, но у меня поч не работает пока :( + */ +HAL_StatusTypeDef MEMSPI_CMD_Fast_Read(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout); //////////////////////---FUNCTION FOR COMMAND---///////////////////// -///////////////////////////////////////////////////////////////////// -//////////////////////---FUNCTION FOR PERIPTH---///////////////////// -/** - * @brief SPI Transmit. - * @param hw25 - указатель на хендл flash. - * @param data - указатель на данные для отправки. - * @param size - размер данных для отправки. - * @return Device ID микросхемы. - * @note Здесь вызывается только функция HAL, и ничего больше. - */ -void W25_SPI_Transmit (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size); -/** - * @brief SPI Receive. - * @param hw25 - указатель на хендл flash. - * @param data - указатель на буффер для прниема данных. - * @param size - размер данных для приема. - * @return Device ID микросхемы. - * @note Здесь вызывается только функция HAL, и ничего больше. - */ -void W25_SPI_Receive (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size); - -//////////////////////---FUNCTION FOR PERIPTH---///////////////////// - -#endif // __SPI_FLASH_H_ \ No newline at end of file +#endif // __SPI_MEMORY_H_ \ No newline at end of file diff --git a/spi_flash/spi_flash.c b/spi_flash/spi_flash.c deleted file mode 100644 index b65711f..0000000 --- a/spi_flash/spi_flash.c +++ /dev/null @@ -1,657 +0,0 @@ -/********************************W25 FLASH********************************** -Данный файл содержит базовые функции для общения с памятью FLASH по SPI. -//-------------------Функции-------------------// -@func users - - W25_FLASH_Read Считывание внешней FLASH - - W25_FLASH_Write Запись данных в внешнюю FLASH (функция сама очищает нужные сектора, и если надо сохраняет выбранные данные) - - W25_FLASH_Program Программирование внешней FLASH (выбранный участок FLASH должен быть очищен) - - W25_FLASH_Erase Очистка внешней FLASH - -@func initialization - - W25_Base_Init Инициализация SPI и GPIO для FLASH - -@func process interaction with flash - - W25_FLASH_Erase_Sector Очистка сектора FLASH. *есть более общая функция W25_FLASH_Erase, которая может ощичать несколько секторов - - W25_FLASH_Program_Page Программирование страницы. *есть более общая функция W25_FLASH_Program, которая программирует участки больше страницы - - W25_WriteEnablingUntilTimeout Разрешение записи, пока не будет ответа или не истек таймаут - - W25_WaitOnFlagUntilTimeout Ожидание флага пока не истек таймаута - -@func cmd functions - - W25_CMD_Read_Status_Register Отправка комманд Read Status Register 1 / Read Status Register 1 (0x05h / 0x35h) - - W25_CMD_Write_Status_Register Отправка комманды Write Status Register (0x01h) - - W25_CMD_Write_Enable Отправка комманды Write Enable (0x06h) - - W25_CMD_Write_Disable Отправка комманды Write Disable (0x04h) - - W25_CMD_Read_Data Отправка комманды Read Data (0x03h) - - W25_CMD_Fast_Read Отправка комманды Fast Read (0x0Bh) - - W25_CMD_Page_Program Отправка комманды Page Program (0x02h) - - W25_CMD_Erase_Sector Отправка комманды Erase Sector (0x20h) - - W25_CMD_Read_JEDEC_ID Отправка комманды Read JEDEC ID (0x4Bh) - - W25_CMD_Read_Device_ID Отправка комманды Read Manufacture / Device Id (0x90) - -@func SPI functions - - W25_SPI_Transmit Функция отправки по SPI (содержит только HAL_SPI_Transmit) - - W25_SPI_Receive Функция приема по SPI (содержит только HAL_SPI_Receive) -***************************************************************************/ -#include "spi_flash.h" -uint8_t sector_buff[W25_SECTOR_SIZE]; - -/* USER CODE BEGIN PV */ -char str1[30]; - -//------------------------------------------------------------- -//--------------------------FOR USER--------------------------- -/** - * @brief Initialize SPI and GPIO for W25 FLASH. - * @param hw25 - указатель на структуру с настройками SPI и GPIO портов. - */ -void W25_Base_Init(W25_HandleTypeDef *hw25) -{ - - // SPI PERIPH INIT - if(hw25->hspi.Instance == NULL) - hw25->hspi.Instance = SPI1; - - hw25->hspi.Init.Mode = SPI_MODE_MASTER; - hw25->hspi.Init.Direction = SPI_DIRECTION_2LINES; - hw25->hspi.Init.DataSize = SPI_DATASIZE_8BIT; - hw25->hspi.Init.CLKPolarity = SPI_POLARITY_LOW; - hw25->hspi.Init.CLKPhase = SPI_PHASE_1EDGE; - hw25->hspi.Init.NSS = SPI_NSS_SOFT; - hw25->hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; - hw25->hspi.Init.TIMode = SPI_TIMODE_DISABLE; - hw25->hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; - hw25->hspi.Init.CRCPolynomial = 10; - - // CLOCK - if(hw25->hspi.Instance == SPI1) - __HAL_RCC_SPI2_CLK_ENABLE(); - else if (hw25->hspi.Instance == SPI2) - __HAL_RCC_SPI2_CLK_ENABLE(); - else if (hw25->hspi.Instance == SPI3) - __HAL_RCC_SPI3_CLK_ENABLE(); - - // SPI INIT - HAL_SPI_Init(&hw25->hspi); - - // GPIO INIT - GPIO_Clock_Enable(hw25->GPIOs.CS_GPIOx); - GPIO_Clock_Enable(hw25->GPIOs.CLK_GPIOx); - GPIO_Clock_Enable(hw25->GPIOs.MISO_GPIOx); - GPIO_Clock_Enable(hw25->GPIOs.MOSI_GPIOx); - // CHIP SELECT PIN INIT - GPIO_InitTypeDef GPIO_InitStruct = {0}; - GPIO_InitStruct.Pin = hw25->GPIOs.CS_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - HAL_GPIO_Init(hw25->GPIOs.CS_GPIOx, &GPIO_InitStruct); - // CLK PIN INIT - GPIO_InitStruct.Pin = hw25->GPIOs.CLK_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; - HAL_GPIO_Init(hw25->GPIOs.CLK_GPIOx, &GPIO_InitStruct); - // MISO PIN INIT - GPIO_InitStruct.Pin = hw25->GPIOs.MISO_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; - HAL_GPIO_Init(hw25->GPIOs.MISO_GPIOx, &GPIO_InitStruct); - // MOSI PIN INIT - GPIO_InitStruct.Pin = hw25->GPIOs.MOSI_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; - HAL_GPIO_Init(hw25->GPIOs.MOSI_GPIOx, &GPIO_InitStruct); - -} - -/** - * @brief Read external FLASH. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс откуда начинать считывание. - * @param pBuff - куда записывать данные из FLASH. - * @param Size - сколько байтов считывать. - * @param Timeout - время, за которое должно быть осуществлено чтение. - * @return HAL status. - * @note Включает в себя проверку на доступность памяти (флаг BUSY) - */ -HAL_StatusTypeDef W25_FLASH_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout) -{ - uint32_t tickstart = HAL_GetTick(); - - // wait for unbusy - if(W25_WaitOnFlagUntilTimeout(hw25, W25_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if its unbusy for timeout - return HAL_TIMEOUT; // return timeout error - - W25_CMD_Read_Data(hw25, FLASH_Address, pBuff, Size); - return HAL_OK; -} -/** - * @brief Write external FLASH. - * @param hw25 - указатель на хендл flash. - * @param WriteInit - указатель на структуру, определяющую участок памяти для записи. - * @param Timeout - время, за которое должно быть осуществлено чтение. - * @return HAL status. - * @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц. - * Данные в сектора участка, но за пределами участка не сохраняются. - */ -HAL_StatusTypeDef W25_FLASH_Write(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout) -{ - uint32_t tickstart = HAL_GetTick(); - uint32_t timeoutcnt = Timeout; - uint8_t *writebuff = WriteInit->pDataPtr; - HAL_StatusTypeDef W25_Status; - - // WAIT FOR UNBUSY - if(W25_WaitOnFlagUntilTimeout(hw25, W25_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if its unbusy for timeout - return HAL_TIMEOUT; // return timeout error - - // ERASE FLASH - timeoutcnt = HAL_GetTick() - tickstart; // update timeout - Timeout -= timeoutcnt; - tickstart += timeoutcnt; - W25_Status = W25_FLASH_Erase(hw25, WriteInit->Sector_Address, WriteInit->Sector_Size, Timeout); - if(W25_Status != HAL_OK) - return W25_Status; - - // WRITE FLASH WITH SAVING PREVIOUS DATA - if(WriteInit->fSavePrevoisData) - { - uint8_t sector_buff[WriteInit->Sector_Size]; - timeoutcnt = HAL_GetTick() - tickstart; // update timeout - Timeout -= timeoutcnt; - tickstart += timeoutcnt; - // store data from flash - W25_Status = W25_FLASH_Read(hw25, WriteInit->Sector_Address, sector_buff, WriteInit->Sector_Size, Timeout); - if(W25_Status != HAL_OK) - return W25_Status; - - // CHANGE DATA IN USER "SECTOR" - uint32_t addr_shift = WriteInit->Data_Address - WriteInit->Sector_Address; - for(int i = 0; i < WriteInit->Data_Size; i++) - { - sector_buff[addr_shift+i] = WriteInit->pDataPtr[addr_shift+i]; - } - - writebuff = sector_buff; // set pointer to buffer that need to be restored - - // PROGRAM FLASH WITH NEW DATA - timeoutcnt = HAL_GetTick() - tickstart; // update timeout - Timeout -= timeoutcnt; - tickstart += timeoutcnt; - // program data to flash - W25_Status = W25_FLASH_Program(hw25, WriteInit->Sector_Address, writebuff, WriteInit->Sector_Size, Timeout); - if(W25_Status != HAL_OK) - return W25_Status; - } - // WRITE FLASH WITHOUT SAVING PREVIOUS DATA - else - { - // PROGRAM FLASH WITH NEW DATA - timeoutcnt = HAL_GetTick() - tickstart; // update timeout - Timeout -= timeoutcnt; - tickstart += timeoutcnt; - // program data to flash - W25_Status = W25_FLASH_Program(hw25, WriteInit->Sector_Address, writebuff, WriteInit->Sector_Size, Timeout); - if(W25_Status != HAL_OK) - return W25_Status; - } - return HAL_OK; -} - -/** - * @brief Program external FLASH. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс куда начинать записывать. - * @param pData - откуда брать данные для записи в FLASH. - * @param Size - сколько байтов записать. - * @param Timeout - время, за которое должно быть осуществлено чтение. - * @return HAL status. - * @note Программирование участка памяти, без ограничений на кол-во байт - */ -HAL_StatusTypeDef W25_FLASH_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout) -{ - uint32_t tickstart = HAL_GetTick(); - HAL_StatusTypeDef W25_Status; - - // CALC AREA TO PROGRAM - uint16_t bytecnt = 0; - uint16_t currentpage_size = 0; - uint16_t lastpage_size = Size; - uint16_t firstpage = (FLASH_Address/W25_PAGE_SIZE); - uint16_t lastpage = ((FLASH_Address+Size-1)/W25_PAGE_SIZE); - if(firstpage != lastpage) // if area is on several pages - { - currentpage_size = (firstpage+1)*W25_PAGE_SIZE - FLASH_Address; // set size of data on first page - lastpage_size = (FLASH_Address+Size) - lastpage*W25_PAGE_SIZE; // set size of data on last page - } - - // PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST - hw25->hNextAddr = FLASH_Address; // address would increase automatically in this variable - for(int i = 0; i < lastpage - firstpage; i++) - { - W25_Status = W25_FLASH_Program_Page(hw25, hw25->hNextAddr, &pData[bytecnt], currentpage_size, Timeout, tickstart); // programm page - if(W25_Status != HAL_OK) - return W25_Status; - - // note for multiple page program: first we program rest of the first page, - // then we shift byte count to data, that shoud be on the next page - bytecnt += currentpage_size; - // and set current size as page size. because next pages will be fully programmed - currentpage_size = W25_PAGE_SIZE; - } - - // PROGRAM LAST PAGE - W25_Status = W25_FLASH_Program_Page(hw25, hw25->hNextAddr, &pData[bytecnt], lastpage_size, Timeout, tickstart); // programm page - if(W25_Status != HAL_OK) - return W25_Status; - - return HAL_OK; // if all ok return HAL_OK -} - - -/** - * @brief Erase external FLASH. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс где надо данные стереть. - * @param Size - сколько байтов стереть. - * @param Timeout - время, за которое должно быть осуществлена очистка. - * @return HAL status. - * @note Т.к. очитска происходит по секторам, Size нужен, чтобы определить сколько секторов очистить - * И если начальны адресс будет на Sector 0, а последний байт на Sector 1, то произойдет очистка Sector 0 и Sector 1 - */ -HAL_StatusTypeDef W25_FLASH_Erase(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout) -{ - uint32_t tickstart = HAL_GetTick(); - HAL_StatusTypeDef W25_Status; - - // CALC AREA TO ERASE - uint16_t bytecnt = 0; - uint16_t firstsector = (FLASH_Address/W25_SECTOR_SIZE); - uint16_t lastsector = ((FLASH_Address+Size-1)/W25_SECTOR_SIZE); - - for(int i = 0; i <= (lastsector - firstsector); i++) - { - W25_Status = W25_FLASH_Erase_Sector(hw25, FLASH_Address, Timeout, tickstart); // programm page - if(W25_Status != HAL_OK) - return W25_Status; - FLASH_Address += W25_SECTOR_SIZE; - } - return HAL_OK; -} - -/** - * @brief Erase external FLASH Sector. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс где надо данные стереть. - * @param Timeout - время, за которое должно быть осуществлена очистка. - * @param tickstart - время, относительно которого надо отсчитывать таймаут. - * @return HAL status. - * @note При Timeout = 0, функция не будет ожидать окончания очистки (выставления в 0 флагов BUSY и WEL) - * @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом. - */ -HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint32_t Timeout, uint32_t tickstart) -{ - // enable writting and waiting for unbusy - if(W25_WriteEnablingUntilTimeout(hw25, Timeout, tickstart) != HAL_OK) // if writting isnt enable - return HAL_TIMEOUT; // return timeout - - // erase sector (instruction) - W25_CMD_Erase_Sector(hw25, FLASH_Address); - - // waiting for ending of erasing - if(Timeout) // if timeout isn zero - wait it. - if(W25_WaitOnFlagUntilTimeout(hw25, W25_SR_WEL|W25_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if erase isnt done (W25 busy and WEL bit isnt in reset state) - return HAL_TIMEOUT; // return timeout because erasing instruction accepted, but arent done - // note: if timeout == 0, erasing wouldnt be checking for ending (check busy flag) - - return HAL_OK; // if all ok return HAL_OK -} - -/** - * @brief Program page in external FLASH. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс куда начинать записывать. - * @param pData - откуда брать данные для записи в FLASH. - * @param Size - сколько байтов записать. - * @param Timeout - время, за которое должно быть осуществлено чтение. - * @param tickstart - время, относительно которого надо отсчитывать таймаут. - * @return HAL status. - * @note Позволяет перепрограммировать только байты в прелелах одной страницы. - Для более гибкого программирования есть функция W25_FLASH_Program, которая программирует участки любой длины (в теории). - */ -HAL_StatusTypeDef W25_FLASH_Program_Page(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint32_t tickstart) -{ - // enable writting and waiting for unbusy - if(W25_WriteEnablingUntilTimeout(hw25, Timeout, tickstart) != HAL_OK) // if writting isnt enable - return HAL_TIMEOUT; // return timeout - - // check if flash range is placed at one page - if((FLASH_Address/W25_PAGE_SIZE) != ((FLASH_Address+Size-1)/W25_PAGE_SIZE)) // if page of first byte isnt equal page of last byte - return HAL_ERROR; // return error - - // programm page (instruction) - W25_CMD_Page_Program(hw25, FLASH_Address, pData, Size); - - // waiting for ending of writting - if(W25_WaitOnFlagUntilTimeout(hw25, W25_SR_WEL|W25_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if writting isnt done (W25 busy and WEL bit isnt in reset state) - return HAL_TIMEOUT; - - // update handle variables - hw25->hNextAddr = (FLASH_Address+Size); - hw25->hNextPage = (FLASH_Address+Size)/W25_PAGE_SIZE; - hw25->hNextSector = (FLASH_Address+Size)/W25_SECTOR_SIZE; - - return HAL_OK; -} -/** - * @brief Setting WEL bit until it setted or until timeout. - * @param hw25 - указатель на хендл flash. - * @param Timeout - время, за которое должно быть осуществлено чтение. - * @param tickstart - время, относительно которого надо отсчитывать таймаут. - * @return HAL status. - * @note Подает команду на разрешение записи до тех пор, пока она запись не разрешиться или до тех пор, пока таймаут не истечет. - */ -HAL_StatusTypeDef W25_WriteEnablingUntilTimeout(W25_HandleTypeDef *hw25, uint32_t Timeout, uint32_t tickstart) -{ - // enable writting - W25_CMD_Write_Enable(hw25); - W25_CMD_Read_Status_Register(hw25, W25_SR_WEL|W25_SR_BUSY); - while((hw25->SR&W25_SR_WEL) != W25_SR_WEL) - { - // if flash isnt busy - set WEL flag - if((hw25->SR&W25_SR_BUSY) == 0) - W25_CMD_Write_Enable(hw25); - - W25_CMD_Read_Status_Register(hw25, W25_SR_WEL); - if((HAL_GetTick() - tickstart) >= Timeout) // if time is out - return HAL_TIMEOUT; // set timeout - } - return HAL_OK; // if all ok return HAL_OK -} - -/** - * @brief Wait for flag until timeout. - * @param hw25 - указатель на хендл flash. - * @param FlagMask - маска для флагов, какие флаги считывать. - * @param FlagStatus - какое состояние должно быть у выбранных флагов. - * @param Timeout - время, за которое должно быть осуществлено чтение. - * @param tickstart - время, относительно которого надо отсчитывать таймаут. - * @return HAL status. - * @note Считывает флаги до тех пор, пока они не будут в состоянии FlagStatus или до тех пор, пока таймаут не истечет. - */ -HAL_StatusTypeDef W25_WaitOnFlagUntilTimeout(W25_HandleTypeDef *hw25, uint16_t FlagMask, uint16_t FlagStatus, uint32_t Timeout, uint32_t tickstart) -{ - // enable writting - W25_CMD_Read_Status_Register(hw25, FlagMask); - while((hw25->SR&FlagMask) != FlagStatus) - { - W25_CMD_Read_Status_Register(hw25, FlagMask); - if((HAL_GetTick() - tickstart) >= Timeout) // if time is out - return HAL_TIMEOUT; // set timeout - } - return HAL_OK; // if all ok return HAL_OK -} - - - -//------------------------------------------------------------- -//----------------------COMMAND FUNCTIONS---------------------- -/** - * @brief Send command to read Status Register. - * @param hw25 - указатель на хендл flash. - * @param RequestedBits - какие биты запросить. - * @return Заполняет Status Register в hw25. - * @note Всего есть две комманды: на запрос верхнего или нижниго байта. - * Функция в соответствии с RequestedBits определяет какой байт запросить, или два байта сразу. - */ -void W25_CMD_Read_Status_Register(W25_HandleTypeDef *hw25, uint16_t RequestedBits) -{ - uint8_t command[2]; - uint8_t *pSRPtr = 0; - uint8_t size = 1; - - if(RequestedBits >> 8) // if its high byte of status register - { - command[0] = W25_READ_STATUS_REG_2; - pSRPtr = (uint8_t *)(&hw25->SR) + 1; // set pointer to HI byte of SR register - size = 1; - if(RequestedBits & 0xFF) // if low byte also requester - { - size = 2; // set size to 2 bytes - command[1] = W25_READ_STATUS_REG_1; - } - } - else // of its low byte of status register - { - command[0] = W25_READ_STATUS_REG_1; - pSRPtr = (uint8_t *)(&hw25->SR); // set pointer to LO byte of SR register - size = 1; - } - - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 1); // send insctruction to read SR - W25_SPI_Receive(hw25, pSRPtr, 1); // receive response - W25_Deselect(hw25); - if(size > 1) // if 2 bytes are requested - { - W25_Select(hw25); - W25_SPI_Transmit(hw25, command+1, 1); // send insctruction to read SR - W25_SPI_Receive(hw25, pSRPtr-1, 1); // receive response - W25_Deselect(hw25); - } -} - -/** - * @brief Send command to write bits in Status Register. - * @param hw25 - указатель на хендл flash. - * @param WrittenBits - какие биты запросить. - * @note Данная команда посылает биты, как сдвинутые на 2 вправо. Т.е. 0-й бит в комманде - 2-й бит BP0. - Но биты указываются в также как они расположены и регистре. Функция сама выполняет сдвиг. - */ -void W25_CMD_Write_Status_Register(W25_HandleTypeDef *hw25, uint16_t WrittenBits) -{ - uint8_t command[2]; - - command[0] = W25_WRITE_STATUS_REG; - command[1] = WrittenBits >> 2; - - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 1); - W25_Deselect(hw25); -} - -/** - * @brief Send command to set Write Enable Latch (WEL) in Status Register. - * @param hw25 - указатель на хендл flash. - * @note Разрешает запись в FLASH, путем высталения WEL в Status Register - */ -void W25_CMD_Write_Enable(W25_HandleTypeDef *hw25) -{ - uint8_t command[1]; - command[0] = W25_WRITE_ENABLE; - - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 1); - W25_Deselect(hw25); -} - - -/** - * @brief Send command to read data from FLASH. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс откуда начинать считывание. - * @param pBuff - куда записывать данные из FLASH. - * @param Size - сколько байтов считывать. - * @return pBuff. - */ - -void W25_CMD_Read_Data(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size) -{ - uint8_t command[4]; - uint8_t response[2] = {0}; - - command[0] = W25_READ_DATA; - command[1] = FLASH_Address >> 16 & 0xFF; - command[2] = FLASH_Address >> 8 & 0xFF; - command[3] = FLASH_Address & 0xFF; - - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 4); - W25_SPI_Receive(hw25, pBuff, Size); - W25_Deselect(hw25); -} -/** - * @brief Send command to fast page program. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс куда начинать записывать. - * @param pData - откуда брать данные для записи в FLASH. - * @param Size - сколько байтов записать. - * @note Программирование FLASH только в пределах одной страницы. - * Т.е. если запись с 0x0, то не больше 256 байт. Если с 0ч40, то не больше 192 байт. - */ -void W25_CMD_Fast_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size) -{ - uint8_t command[5] = {0}; - uint8_t response[2] = {0}; - - command[0] = W25_READ_DATA; - command[1] = FLASH_Address >> 16 & 0xFF; - command[2] = FLASH_Address >> 8 & 0xFF; - command[3] = FLASH_Address & 0xFF; - command[4] = 0xFF; - - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 5); - W25_SPI_Receive(hw25, pBuff, Size); - W25_Deselect(hw25); -} -/** - * @brief Send command to fast page program. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс куда начинать записывать. - * @param pData - откуда брать данные для записи в FLASH. - * @param Size - сколько байтов записать. - * @note Программирование FLASH только в пределах одной страницы. - * Т.е. если запись с 0x0, то не больше 256 байт. Если с 0ч40, то не больше 192 байт. - */ -void W25_CMD_Page_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size) -{ - // 1 command byte + 3 address bytes + 256 data bytes - uint8_t command[1+3+W25_PAGE_SIZE]; - FLASH_Address = FLASH_Address & 0xFFFFFF; - - command[0] = W25_PAGE_PROGRAM; - command[1] = FLASH_Address >> 16 & 0xFF; - command[2] = FLASH_Address >> 8 & 0xFF; - command[3] = FLASH_Address & 0xFF; - - // check if flash range is placed at one page - if((FLASH_Address/W25_PAGE_SIZE) != ((FLASH_Address+Size-1)/W25_PAGE_SIZE)) // if page of first byte isnt equal page of last byte - return; // return error - - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 4); // send insctruction to write - W25_SPI_Transmit(hw25, pData, Size); // send data to write - W25_Deselect(hw25); -} - -/** - * @brief Send command to erase sector. - * @param hw25 - указатель на хендл flash. - * @param FLASH_Address - адресс где надо данные стереть. - * @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом. - */ -void W25_CMD_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address) -{ - uint8_t command[4]; - uint8_t response[8]; - FLASH_Address = FLASH_Address & 0xFFFFFF; - - command[0] = W25_ERASE_SECTOR; - command[1] = FLASH_Address >> 16; - command[2] = FLASH_Address >> 8; - command[3] = FLASH_Address; - - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 4); - W25_Deselect(hw25); -} - - -/** - * @brief Send command to read JEDEC ID. - * @param hw25 - указатель на хендл flash. - * @return JEDEC ID микросхемы. - */ -uint32_t W25_CMD_Read_JEDEC_ID(W25_HandleTypeDef *hw25) -{ - uint8_t command[1] = {0}; - uint8_t receive[4] = {0}; - uint32_t return_val; - - command[0] = W25_READ_JEDEC_ID; - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 1); - W25_SPI_Receive(hw25, &receive[1], 3); - W25_Deselect(hw25); - - return_val = (*(uint64_t *)receive); - return __REV(return_val) & 0xFFFFFF; -} - -/** - * @brief Send command to read JEDEC ID. - * @param hw25 - указатель на хендл flash. - * @return Device ID микросхемы. - */ -uint64_t W25_CMD_Read_Device_ID(W25_HandleTypeDef *hw25) -{ - uint8_t command[1] = {0}; - uint8_t receive[8] = {0}; - uint64_t return_val_LO; - uint64_t return_val_HI; - command[0] = W25_READ_UNIQUE_ID; - W25_Select(hw25); - W25_SPI_Transmit(hw25, command, 1); - W25_SPI_Receive(hw25, receive, 8); - W25_Deselect(hw25); - - return_val_LO = (*(uint64_t *)receive) >> 32; - return_val_HI = (*(uint64_t *)receive) & 0xFFFFFFFF; - return ((uint64_t)__REV(return_val_HI) << 32) | __REV(return_val_LO); -} -//------------------------------------------------------------- - -//------------------------------------------------------------- -//---------------------PERIPTH FUNCTIONS----------------------- -/** - * @brief SPI Transmit. - * @param hw25 - указатель на хендл flash. - * @param data - указатель на данные для отправки. - * @param size - размер данных для отправки. - * @return Device ID микросхемы. - * @note Здесь вызывается только функция HAL, и ничего больше. - */ -void W25_SPI_Transmit (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size) -{ - HAL_SPI_Transmit (&hw25->hspi, data, size, HAL_MAX_DELAY); - -} -/** - * @brief SPI Receive. - * @param hw25 - указатель на хендл flash. - * @param data - указатель на буффер для прниема данных. - * @param size - размер данных для приема. - * @return Device ID микросхемы. - * @note Здесь вызывается только функция HAL, и ничего больше. - */ -void W25_SPI_Receive (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size) -{ - HAL_SPI_Receive (&hw25->hspi, data, size, HAL_MAX_DELAY); -} -//-------------------------------------------------------------