#include "mik32_hal_pcc.h" #include "mik32_hal_gpio.h" #include "mik32_hal_spifi_w25.h" /* * Программа демонстрирует переход из FLASH в RAM и обратно * FLASH (QPI+XIP) -> RAM (write_flash) RAM -> FLASH (QPI+XIP) * Сначала переводим микросхему W25Q64 в режим работы с периферией, * затем переводим ее в режим работы с командами и проверяем * успешность перехода чтением JDEC ID (0x60), пробуем * стереть последний сектор флеши и затем в первую страницу последнего * сектора записываем тестовые данные, затем снова возвращаем режим QPI+XIP * и возвращаемся к первоначальной работе из флеш. * Все переходы сопровождаются миганием светодиодов. * Результат записи во флеш можно увидеть в отладчике по адресу 0х801FF000 * после возвращения в режим работы с памятью */ void SystemClock_Config(); void GPIO_Init(); void flash_write_page_qpi(void); int SPIFI_WaitIntrqTimeout(uint32_t timeout); uint32_t get_flash_usage_end(void); bool is_sector_free(uint32_t sector_address); uint8_t read_sreg_1(void); bool w25q_is_busy(uint32_t timeout); void write_enable(void); void w25q_sector_erase(uint32_t address); void page_program(unsigned int ByteAddress, uint8_t data[], int byte_count); bool write_to_last_sector(uint8_t *data); #define TIMEOUT 100000 #define FLASH_TOTAL_SIZE (2 * 1024 * 1024) #define FLASH_BASE_ADDRESS 0x80000000 #define SECTOR_SIZE 4096 #define PAGE_SIZE 256 volatile uint8_t jedec_id_qpi[3] = {0}; extern uint32_t __DATA_IMAGE_END__; extern uint32_t __TEXT_END__; int main() { SystemClock_Config(); GPIO_Init(); flash_write_page_qpi(); while (1) { // если мы попали сюда, значит мы успешно поработали с FLASH // из RAM и снова вернулись к работе программы из FLASH // и в отладчике теперь можно просмотреть результат записи HAL_GPIO_TogglePin(GPIO_0, GPIO_PIN_9 | GPIO_PIN_10); HAL_DelayMs(700); } } void SystemClock_Config(void) { PCC_InitTypeDef PCC_OscInit = {0}; PCC_OscInit.OscillatorEnable = PCC_OSCILLATORTYPE_ALL; PCC_OscInit.FreqMon.OscillatorSystem = PCC_OSCILLATORTYPE_OSC32M; PCC_OscInit.FreqMon.ForceOscSys = PCC_FORCE_OSC_SYS_UNFIXED; PCC_OscInit.FreqMon.Force32KClk = PCC_FREQ_MONITOR_SOURCE_OSC32K; PCC_OscInit.AHBDivider = 0; PCC_OscInit.APBMDivider = 0; PCC_OscInit.APBPDivider = 0; PCC_OscInit.HSI32MCalibrationValue = 128; PCC_OscInit.LSI32KCalibrationValue = 8; PCC_OscInit.RTCClockSelection = PCC_RTC_CLOCK_SOURCE_AUTO; PCC_OscInit.RTCClockCPUSelection = PCC_CPU_RTC_CLOCK_SOURCE_OSC32K; HAL_PCC_Config(&PCC_OscInit); } void GPIO_Init() { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_PCC_GPIO_0_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10; GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); } void __attribute__((used, section(".ram_text"))) flash_write_page_qpi(void) { // мы сейчас с флеш точно работаем XIP + QPI, но перешли в ОЗУ и теперь нужно уйти из XIP, // т.е работали в режиме памяти, для этого надо перейти в режим периферии // сначала делаем сброс текущей команды работы в режиме памяти SPIFI_CONFIG->STAT = SPIFI_CONFIG_STAT_RESET_M; //выводим QPI из "без команд" в режим "с командами" const uint32_t cmd_chip_qpi_w_cmd = SPIFI_DIRECTION_INPUT | SPIFI_CONFIG_CMD_INTLEN(1) | SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) | SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_3ADDR) | /* нет кода операции, 3 адреса */ SPIFI_CONFIG_CMD_OPCODE(0xEB); SPIFI_CONFIG->ADDR = 0; SPIFI_CONFIG->IDATA = 0; SPIFI_CONFIG->CMD = cmd_chip_qpi_w_cmd; if(SPIFI_WaitIntrqTimeout(TIMEOUT) != 0){ SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; // команда выполнена, выставлен IRQ, сбросим IRQ } else { asm("nop"); // таймаут, что-то пошло не так } // контрольная проверка принимает ли флеш команды или нет, для этого пробуем прочитать JDEC ID const uint32_t cmd_jedec_qpi = SPIFI_DIRECTION_INPUT | SPIFI_CONFIG_CMD_INTLEN(0) | SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) | SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE) | SPIFI_CONFIG_CMD_OPCODE(0x9F); SPIFI_CONFIG->ADDR = 0; SPIFI_CONFIG->IDATA = 0; SPIFI_CONFIG->CMD = cmd_jedec_qpi | SPIFI_CONFIG_CMD_DATALEN(3); if(SPIFI_WaitIntrqTimeout(TIMEOUT) != 0){ SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; // сбросим IRQ } else { asm("nop"); // таймаут, что-то пошло не так } for (int i = 0; i < 3; i++) jedec_id_qpi[i] = SPIFI_CONFIG->DATA8; // если успешно прочитали JDEC ID, то поморгаем вразнобой if(jedec_id_qpi[1] == 0x60){ for(int i=0; i<16; i++) { GPIO_0->OUTPUT ^= 1<<9; for(int i=0; i<500000; i++) { asm("nop"); } GPIO_0->OUTPUT ^= 1<<10; for(int i=0; i<500000; i++) { asm("nop"); } } } for(int i=0; i<500000; i++){ asm("nop"); } //***** пробуем стереть и записать первую страницу последнего сектора ****** uint8_t my_data[PAGE_SIZE]; // формируем данные для записи for(int i=0; iADDR = 0; SPIFI_CONFIG->IDATA = 0x20; SPIFI_CONFIG->CMD = cmd_chip_qpi_wo_cmd; if(SPIFI_WaitIntrqTimeout(TIMEOUT) != 0){ SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; // команда выполнена, выставлен IRQ, сбросим IRQ } else { asm("nop"); // таймаут, что-то пошло не так } // а теперь вернем SPIFI в режим работы с памятью // настройки кэш не трогаем, они не изменились const uint32_t mcmd_chip_qpi_wo_cmd = SPIFI_DIRECTION_INPUT | SPIFI_CONFIG_CMD_INTLEN(1) | SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) | SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_3ADDR) | /* нет кода операции, 3 адреса */ SPIFI_CONFIG_CMD_OPCODE(0xEB); SPIFI_CONFIG->ADDR = 0; SPIFI_CONFIG->IDATA = 0x20; SPIFI_CONFIG->MCMD = mcmd_chip_qpi_wo_cmd; if(SPIFI_WaitIntrqTimeout(TIMEOUT) != 0){ SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; // команда выполнена, выставлен IRQ, сбросим IRQ } else { asm("nop"); // таймаут, что-то пошло не так } // поморгали перед выходом из ОЗУ, если настройки правильные, // то после этого мы вернемся к работе программы из флеш GPIO_0->OUTPUT ^= 1<<9 | 1<<10; for(int i=0; i<16; i++) { GPIO_0->OUTPUT ^= 1<<9 | 1<<10; for(int i=0; i<500000; i++){ asm("nop"); } } } //***************************************************************************** // Получить конец используемой флеш-памяти __attribute__((used, section(".ram_text"))) uint32_t get_flash_usage_end(void) { // Используем максимальный адрес между концом кода и концом данных uint32_t code_end = (uint32_t)&__TEXT_END__; uint32_t data_end = (uint32_t)&__DATA_IMAGE_END__; return (code_end > data_end) ? code_end : data_end; } //***************************************************************************** // Проверить, свободен ли сектор __attribute__((used, section(".ram_text"))) bool is_sector_free(uint32_t sector_address) { // Сектор свободен, если он находится за пределами используемой памяти uint32_t usage_end = get_flash_usage_end(); return (sector_address >= usage_end); } //***************************************************************************** __attribute__((used, section(".ram_text"))) uint8_t read_sreg_1(void) { static uint8_t read_sreg = 0; const uint32_t cmd_read_sreg1 = SPIFI_DIRECTION_INPUT | SPIFI_CONFIG_CMD_INTLEN(0) | SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) | SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE) | SPIFI_CONFIG_CMD_OPCODE(0x05); SPIFI_CONFIG->CMD = cmd_read_sreg1 | SPIFI_CONFIG_CMD_DATALEN(1); if(SPIFI_WaitIntrqTimeout(TIMEOUT) != 0){ SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; // сбросим IRQ } else { asm("nop"); // таймаут, что-то пошло не так return 0; } read_sreg = SPIFI_CONFIG->DATA8; return read_sreg; } //***************************************************************************** __attribute__((used, section(".ram_text"))) bool w25q_is_busy(uint32_t timeout){ while (timeout-- != 0) { if ((read_sreg_1() & 1) == 0) { asm("nop"); return 0; } } return 1; } //***************************************************************************** __attribute__((used, section(".ram_text"))) void write_enable(void) { const uint32_t cmd_write_enable = SPIFI_DIRECTION_INPUT | SPIFI_CONFIG_CMD_INTLEN(0) | SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) | SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE) | SPIFI_CONFIG_CMD_OPCODE(0x06); SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; SPIFI_CONFIG->CMD = cmd_write_enable; if(SPIFI_WaitIntrqTimeout(TIMEOUT) == 0) { return; } } //***************************************************************************** __attribute__((used, section(".ram_text"))) void w25q_sector_erase(uint32_t address){ write_enable(); const uint32_t cmd_sector_erase_4k = SPIFI_DIRECTION_INPUT | SPIFI_CONFIG_CMD_INTLEN(0) | SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) | SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE_3ADDR) | SPIFI_CONFIG_CMD_OPCODE(0x20); SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; SPIFI_CONFIG->ADDR = address; SPIFI_CONFIG->CMD = cmd_sector_erase_4k; if(SPIFI_WaitIntrqTimeout(TIMEOUT) == 0) { return; } } //***************************************************************************** __attribute__((used, section(".ram_text"))) void page_program(unsigned int ByteAddress, uint8_t data[], int byte_count) { if(byte_count > 256) { return; } SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; SPIFI_CONFIG->ADDR = ByteAddress; SPIFI_CONFIG->IDATA = 0x00; const uint32_t cmd_page_program = SPIFI_DIRECTION_OUTPUT | SPIFI_CONFIG_CMD_INTLEN(0) | SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) | SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE_3ADDR) | SPIFI_CONFIG_CMD_OPCODE(0x02); SPIFI_CONFIG->CMD = cmd_page_program | SPIFI_CONFIG_CMD_DATALEN(byte_count); for(int i = 0; i < byte_count; i++) { SPIFI_CONFIG->DATA8 = data[i]; } SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; } //***************************************************************************** __attribute__((used, section(".ram_text"))) bool write_to_last_sector(uint8_t *data){ // Определяем адрес последнего сектора uint32_t last_sector_address = FLASH_BASE_ADDRESS + FLASH_TOTAL_SIZE - SECTOR_SIZE; // Проверяем, свободен ли сектор if (!is_sector_free(last_sector_address)) { // Сектор занят - нельзя использовать return 0; } // Стираем сектор (это необходимо перед записью) w25q_sector_erase(last_sector_address); if(w25q_is_busy(TIMEOUT) == 1){ return 0; } write_enable(); page_program(last_sector_address, data, PAGE_SIZE); if(w25q_is_busy(TIMEOUT) == 1){ return 0; } return 1; } //***************************************************************************** int __attribute__((used, section(".ram_text"))) SPIFI_WaitIntrqTimeout(uint32_t timeout) { while (timeout-- > 0) { if((SPIFI_CONFIG->STAT & SPIFI_CONFIG_STAT_INTRQ_M) != 0) { return 1; } } return 0; } //*****************************************************************************