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