//******************************************** Включаемые файлы *********************************************

#include "lmcal_cfg.h"
#include "lmcal_flash_port.h"

#include "at32f403a_407_conf.h"


//******************************************* Локальные константы *******************************************

// Размер возможных данных при обращении к Flash (1,2,4 байта)
#define FLASH_OPERATION_WORD_1  (1U)
#define FLASH_OPERATION_WORD_2  (2U)
#define FLASH_OPERATION_WORD_4  (4U)



//******************************************* Проверка параметров *******************************************


//******************************************** Определения типов ********************************************


//************************************ Глобальные (публичные) переменные ************************************


//************************************* Локальные (приватные) переменные ************************************


//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************

// Проверка на некорректную конфигурацию
static fun_res_t f_is_range_correct(u32 addr, u32 size)
{
    fun_res_t err = ERR_ASSERT_ERROR;

    // Проверка начала
    if ((addr > FLASH_START_ADDRESS ) && (FLASH_END_ADDRESS > addr))
    {
        // Проверка конца
        if (FLASH_END_ADDRESS >= (addr + size))
        {
            err = ERR_OK;
        }   
    }
    return err;
}


//***********************************************************************************************************
//******************************** Определения процедур обработки прерываний ********************************
//***********************************************************************************************************



//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************


fun_res_t lmcal_flash_port_init(void)
{
    fun_res_t err = ERR_OK;
       
    
    return err;
}


fun_res_t lmcal_flash_port_deinit(void)
{
    fun_res_t err = ERR_OK;
       
    
    return err;
}


fun_res_t lmcal_flash_port_write(u32 address, u8* src, u32 size, void* param)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(param);
    u32 remaining = size;
    u32 addr_pos  = address;
    u32 byte_cnt  = 0;

    // Проверка выхода за диапазоны памяти
    err = f_is_range_correct(address, size);
    L_CHECK_ERR(err);

    flash_unlock();

    // Сброс ошибок
    flash_flag_clear(FLASH_PRGMERR_FLAG);
    flash_flag_clear(FLASH_EPPERR_FLAG);

    while(0 != remaining)
    {
        // Получаем размер одной записи
        if(remaining >= FLASH_OPERATION_WORD_4)
        {
            // Формируем слово для записи
            u32 nData = *((u32*)(src + byte_cnt));

            if (FLASH_OPERATE_DONE != flash_word_program(addr_pos, nData))
            {
                // Ошибка или ресурс занят
                err = ERR_RUNTIME_ERROR;
                break;
            }

            // Смещаем адрес на 4 байта
            addr_pos += FLASH_OPERATION_WORD_4;
            byte_cnt += FLASH_OPERATION_WORD_4;
            remaining -=FLASH_OPERATION_WORD_4;
        }
        else if (remaining >= FLASH_OPERATION_WORD_2)
        {
            // Формируем слово для записи
            u16 nData = *((u16*)(src + byte_cnt));
            
            if (FLASH_OPERATE_DONE != flash_halfword_program(addr_pos, nData))
            {
                // Ошибка или ресурс занят
                err = ERR_RUNTIME_ERROR;
                break;
            }

            // Смещаем адрес на 2 байта
            addr_pos += FLASH_OPERATION_WORD_2;
            byte_cnt += FLASH_OPERATION_WORD_2;
            remaining -=FLASH_OPERATION_WORD_2;
        }
        else if (remaining >= FLASH_OPERATION_WORD_1)
        {
            // Формируем слово для записи
            u8 nData = *((u8*)(src + byte_cnt));

            if (FLASH_OPERATE_DONE != flash_byte_program(addr_pos, nData))
            {
                // Ошибка или ресурс занят
                err = ERR_RUNTIME_ERROR;
                break;
            }
            
            // Смещаем адрес на 1 байт
            addr_pos += FLASH_OPERATION_WORD_1;
            byte_cnt += FLASH_OPERATION_WORD_1;
            remaining -=FLASH_OPERATION_WORD_1;
        }
    } // while()

    flash_lock();

    L_EXIT;
    return err;
}


fun_res_t lmcal_flash_port_read(u32 address, u8* dst, u32 size, void* param)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(param);
    u32 remaining = size;
    u32 addr_pos  = address;
    u32 byte_cnt  = 0;

    // Проверка выхода за диапазоны памяти
    err = f_is_range_correct(address, size);
    L_CHECK_ERR(err);

    while(0 != remaining)
    {
        // Получаем размер одной записи
        if(remaining >= FLASH_OPERATION_WORD_4)
        {
            // Формируем слово для чтения
            u32 nData = *((u32*)(addr_pos));

            *((u32*)(dst + byte_cnt)) = nData;
            
            // Смещаем адрес на 4 байта
            addr_pos += FLASH_OPERATION_WORD_4;
            byte_cnt += FLASH_OPERATION_WORD_4;
            remaining -=FLASH_OPERATION_WORD_4;
        }
        else if (remaining >= FLASH_OPERATION_WORD_2)
        {
            // Формируем слово для чтения
            u16 nData = *((u16*)(addr_pos));

            *((u16*)(dst + byte_cnt)) = nData;

            // Смещаем адрес на 2 байта
            addr_pos += FLASH_OPERATION_WORD_2;
            byte_cnt += FLASH_OPERATION_WORD_2;
            remaining -=FLASH_OPERATION_WORD_2;
        }
        else if (remaining >= FLASH_OPERATION_WORD_1)
        {
            // Формируем слово для чтения
            u8 nData = *((u8*)(addr_pos));

            *((u8*)(dst + byte_cnt)) = nData;

            // Смещаем адрес на 1 байт
            addr_pos += FLASH_OPERATION_WORD_1;
            byte_cnt += FLASH_OPERATION_WORD_1;
            remaining -=FLASH_OPERATION_WORD_1;
        }
    } // while()
    
    L_EXIT;
    return err;
}


fun_res_t lmcal_flash_port_erase(u32 address, u32 size, void* param)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(param);
    u32 remaining = size;
    u32 addr_pos  = address;

    // Проверка выхода за диапазоны памяти
    err = f_is_range_correct(address, size);
    L_CHECK_ERR(err);

    flash_unlock();

    // Сброс ошибок
    flash_flag_clear(FLASH_PRGMERR_FLAG);
    flash_flag_clear(FLASH_EPPERR_FLAG);

    // Очистка сектора или нескольких секторов
    while(0 != remaining)
    {
        if (FLASH_OPERATE_DONE != flash_sector_erase(addr_pos))
        {
            // Ошибка или ресурс занят
            err = ERR_RUNTIME_ERROR;
            break;
        }
        // Смещаем адрес
        addr_pos += FLASH_SECTOR_SIZE;
        if (remaining > 0)
        {
            if (remaining > FLASH_SECTOR_SIZE)
            {
                remaining -= FLASH_SECTOR_SIZE;
            }
            else
            {
                remaining = 0;
            }
        }
    }

    flash_lock();

    L_EXIT;
    return err;
}


//*********************************************** КОНЕЦ ФАЙЛА ***********************************************
