/********************************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); } //-------------------------------------------------------------