/**
  ******************************************************************************
  * @file    main.c
  *
  * @brief   Тест режима разборка-сборка (периферия) DMA.
  *
  *          Пересылка 32-битных значений из массива в массив по цепочке
  *          по 0 каналу.  Создаются 3 альтернативные конфигурации
  *          данных для осуществления пересылок по цепочке:
  *          массив 0 -> массив 1 -> массив 2 -> массив 3. Запускается процесс
  *          пересылки, затем проверяется соответсвие данных массива 0 данным
  *          массива 3.
  *
  *          Компилятор: GCC ARM 4.9.3
  *          Среда: Qt Creator 3.4.2
  *
  * @author  НИИЭТ
  *             - Богдан Колбов (bkolbov), kolbov@niiet.ru
  * @date    16.11.2015
  *
  ******************************************************************************
  * @attention
  *
  * ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО
  * ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ
  * ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ
  * НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ
  * ПРЕДНАЗНАЧЕНО ДЛЯ ОЗНАКОМИТЕЛЬНЫХ ЦЕЛЕЙ И НАПРАВЛЕНО ТОЛЬКО НА
  * ПРЕДОСТАВЛЕНИЕ ДОПОЛНИТЕЛЬНОЙ ИНФОРМАЦИИ О ПРОДУКТЕ, С ЦЕЛЬЮ СОХРАНИТЬ ВРЕМЯ
  * ПОТРЕБИТЕЛЮ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ
  * ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА ПРЯМОЙ ИЛИ КОСВЕННЫЙ УЩЕРБ, ИЛИ
  * ПО ИНЫМ ТРЕБОВАНИЯМ, ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
  * ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
  *
  * <h2><center>&copy; 2015 ОАО "НИИЭТ"</center></h2>
  ******************************************************************************
  */

#include "niietcm4.h"
#include "retarget.h"

/* настройка теста */
#define TRANSFERS_TOTAL    32

#define __NOP10()   __NOP();\
                    __NOP();\
                    __NOP();\
                    __NOP();\
                    __NOP();\
                    __NOP();\
                    __NOP();\
                    __NOP();\
                    __NOP();\
                    __NOP()

#define __NOP100()  __NOP10();\
                    __NOP10();\
                    __NOP10();\
                    __NOP10();\
                    __NOP10();\
                    __NOP10();\
                    __NOP10();\
                    __NOP10();\
                    __NOP10();\
                    __NOP10()


DMA_ConfigData_TypeDef DMA_CONFIGDATA __attribute__ ((aligned (1024)));

uint32_t array0[TRANSFERS_TOTAL];
uint32_t array1[TRANSFERS_TOTAL];
uint32_t array2[TRANSFERS_TOTAL];
uint32_t array3[TRANSFERS_TOTAL];

volatile uint32_t irq_status = 0;

DMA_Channel_TypeDef Task[3]; /* 3 набора альтернативных управляющих стркутур */

void SystemInit()
{
    /* DMA инициализация */
    /* Базовый указатель */
    DMA_BasePtrConfig((uint32_t)(&DMA_CONFIGDATA));
    /* Инициализация канала */
    DMA_ChannelInit_TypeDef DMA_ChannelInitStruct;
    DMA_ChannelStructInit(&DMA_ChannelInitStruct);
    DMA_ChannelInitStruct.DMA_SrcDataSize = DMA_DataSize_32;
    DMA_ChannelInitStruct.DMA_SrcDataInc = DMA_DataInc_32;
    DMA_ChannelInitStruct.DMA_DstDataSize = DMA_DataSize_32;
    DMA_ChannelInitStruct.DMA_DstDataInc = DMA_DataInc_32;
    DMA_ChannelInitStruct.DMA_ArbitrationRate = DMA_ArbitrationRate_4;
    DMA_ChannelInitStruct.DMA_TransfersTotal = 12;
    DMA_ChannelInitStruct.DMA_Mode = DMA_Mode_PrmPeriphScatGath;

    DMA_ChannelInitStruct.DMA_SrcDataEndPtr = &(Task[2].RESERVED);
    DMA_ChannelInitStruct.DMA_DstDataEndPtr = &(DMA_CONFIGDATA.ALT_DATA.CH[0].RESERVED);
    DMA_ChannelInit(&DMA_CONFIGDATA.PRM_DATA.CH[0], &DMA_ChannelInitStruct);

    /* Инициализация контроллера */
    DMA_Init_TypeDef DMA_InitStruct;
    DMA_StructInit(&DMA_InitStruct);
    DMA_InitStruct.DMA_Channel = DMA_Channel_0;
    DMA_InitStruct.DMA_ChannelEnable = ENABLE;
    DMA_Init(&DMA_InitStruct);
    DMA_MasterEnableCmd(ENABLE);

    /* инициализация альтернативных структур */
    DMA_ChannelInitStruct.DMA_ArbitrationRate = DMA_ArbitrationRate_32;
    DMA_ChannelInitStruct.DMA_TransfersTotal = TRANSFERS_TOTAL;
    DMA_ChannelInitStruct.DMA_Mode = DMA_Mode_AltPeriphScatGath;
    DMA_ChannelInitStruct.DMA_SrcDataEndPtr = &array0[TRANSFERS_TOTAL-1];
    DMA_ChannelInitStruct.DMA_DstDataEndPtr = &array1[TRANSFERS_TOTAL-1];
    DMA_ChannelInit(&Task[0], &DMA_ChannelInitStruct);
    DMA_ChannelInitStruct.DMA_SrcDataEndPtr = &array1[TRANSFERS_TOTAL-1];
    DMA_ChannelInitStruct.DMA_DstDataEndPtr = &array2[TRANSFERS_TOTAL-1];
    DMA_ChannelInit(&Task[1], &DMA_ChannelInitStruct);
    DMA_ChannelInitStruct.DMA_Mode = DMA_Mode_Basic;
    DMA_ChannelInitStruct.DMA_SrcDataEndPtr = &array2[TRANSFERS_TOTAL-1];
    DMA_ChannelInitStruct.DMA_DstDataEndPtr = &array3[TRANSFERS_TOTAL-1];
    DMA_ChannelInit(&Task[2], &DMA_ChannelInitStruct);


    /* инициализация printf */
    retarget_init();

    /* NVIC init */
    NVIC_EnableIRQ(DMA_Stream0_IRQn);

    __enable_irq();
}

int main()
{
    /* инициализация массивов */
    for (uint32_t elem = 0; elem < TRANSFERS_TOTAL; elem++)
    {
        array0[elem] = 0x10000000;
        array1[elem] = 0x11000000;
        array2[elem] = 0x12000000;
        array3[elem] = 0x13000000;
    }

    /* Режим рассчитан на постоянно висящий запрос от периферии, поэтому
     * будем эмулировать его вручную, посылая программные запросы.
     * И делать это будем столько, чтобы наверняка за это время были завершены
     * передачи и вызвано прерывание.
     */
    for (uint32_t i = 0; i < 500; i++)
    {
        DMA_SWRequestCmd(DMA_Channel_0);
    }
    while (irq_status != DMA_Channel_0);

    /* проверяем результаты */
    uint32_t check_error = 0;
    for (uint32_t elem = 0; elem < TRANSFERS_TOTAL; elem++)
    {
        if (check_error) break;
        if (array0[elem] != array3[elem])
        {
            check_error = 1;
        }
    }

    if (check_error)
    {
        printf("Check results error.\n");
        printf("Test failed!\n");
    }
    else
    {
        printf("All transfers done and checked. Test success!\n");
    }

    while(1);
}

void DMA_Stream0_IRQHandler()
{
    irq_status |= DMA_Channel_0;
}
