Unica-RocketSpace / CanSat-2017

Код для аппарата
3 stars 0 forks source link

Пример для прогона кадра из камеры при помощи DMA и таймера #3

Open 14droplets opened 7 years ago

14droplets commented 7 years ago
#include <stm32f10x_conf.h>

uint8_t _buffer[100] = {0x00};

int main(int argc, char* argv[])
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    GPIO_InitTypeDef pa;
    GPIO_StructInit(&pa);
    pa.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    pa.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |
            GPIO_Pin_6 | GPIO_Pin_7;
    pa.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &pa);

    // переключаем таймер2 на альтернативные пины
    GPIO_InitTypeDef pb;
    // включаем на вывод пин CCR3
    GPIO_StructInit(&pb);
    GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);
    pb.GPIO_Mode = GPIO_Mode_AF_OD; // FIXME: для отладки на лампочке. Заменить на AF_PP
    pb.GPIO_Pin = GPIO_Pin_10;
    pb.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &pb);

    // настраиваем таймер для генерации выводной частоты
    TIM_TimeBaseInitTypeDef tim;
    TIM_TimeBaseStructInit(&tim);
    tim.TIM_Prescaler = 0x0000;
    tim.TIM_CounterMode = TIM_CounterMode_Up;
    tim.TIM_Period = 0x02; // два такта таймера до переполнения. это даст нам частоту в 72/2 = 36 мгц?
    tim.TIM_ClockDivision = TIM_CKD_DIV1;
    // tim.TIM_RepetitionCounter не используется на нашем таймере
    TIM_TimeBaseInit(TIM2, &tim);

    // настраиваем PWM, чтобы формировать выходной клок
    // Используем третий канал, так как он позволяет расталкивать DMA по его первому каналу
    TIM_OCInitTypeDef tim_oc;
    TIM_OCStructInit(&tim_oc);
    tim_oc.TIM_OCMode = TIM_OCMode_PWM1; // FIXME: Почему pwm1?
    tim_oc.TIM_OutputState = TIM_OutputState_Enable; // включаем вывод на пин
    //tim_pwm.TIM_OCNIdleState // только для таймеров 1, 8
    tim_oc.TIM_Pulse = 0x01; // значение в CCR регистре
    tim_oc.TIM_OCPolarity = TIM_OCPolarity_Low; // ?
    //tim_pwm.TIM_OCNPolarity; // только для таймеров 1, 8
    //tim_pwm.TIM_OCIdleState; // только для таймеров 1, 8
    //tim_pwm.TIM_OCNIdleState; // только для таймеров 1, 8
    TIM_OC3Init(TIM2, &tim_oc);

    // настраиваем DMA для чтения данных по клоку, генерируемому таймером
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_InitTypeDef dma;
    dma.DMA_BufferSize = sizeof(_buffer);
    dma.DMA_DIR = DMA_DIR_PeripheralSRC;
    dma.DMA_M2M = DMA_M2M_Disable;
    dma.DMA_MemoryBaseAddr = (uint32_t)_buffer;
    dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma.DMA_Mode = DMA_Mode_Normal;
    dma.DMA_PeripheralBaseAddr = (uint32_t)&GPIOA->IDR;
    dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma.DMA_Priority = DMA_Priority_High;

    // Настраиваем первый канал DMA и включаем его
    DMA_Init(DMA1_Channel1, &dma);
    DMA_Cmd(DMA1_Channel1, ENABLE);

    // разрешаем таймеру обращаться к этому каналу DMA по событию совпадения счетчика с 3им CCR регистром
    // для прогона одного байта через DMA
    TIM_DMAConfig(TIM2, TIM_DMABase_CCR3, TIM_DMABurstLength_1Byte);
    TIM_DMACmd(TIM2, TIM_DMA_CC3, ENABLE);

    /*
    // FIXME: для отладки - включаем прерывание по завершению прогона блока с DMA
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    NVIC_InitTypeDef dma_nvic;
    dma_nvic.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    dma_nvic.NVIC_IRQChannelPreemptionPriority = 0;
    dma_nvic.NVIC_IRQChannelSubPriority = 0;
    dma_nvic.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&dma_nvic);
    */

    // запускаем таймер!
    TIM_Cmd(TIM2, ENABLE);

    while (1)
    {
    }
}

void DMA1_Channel1_IRQHandler()
{
    volatile int x = 0;
}
14droplets commented 7 years ago

Улучшенная версия


#include <stm32f10x_conf.h>

#define BUFFER_SIZE (1280*5)
#define DMA_KICK_PERIOD (10)

uint8_t _buffer[BUFFER_SIZE] = {0x00};

void setup_timers()
{

    // НАСТРОЙКА ТАЙМЕРА 2 (тот, который пинает DMA)
    // ===============================================================
    // ===============================================================
    // ===============================================================
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    // настраиваем таймер для генерации выводной частоты
    TIM_TimeBaseInitTypeDef tim;
    TIM_TimeBaseStructInit(&tim);
    tim.TIM_Prescaler = 0x0000;
    tim.TIM_CounterMode = TIM_CounterMode_Up;
    tim.TIM_Period = DMA_KICK_PERIOD;
    tim.TIM_ClockDivision = TIM_CKD_DIV1;
    // tim.TIM_RepetitionCounter не используется на нашем таймере
    TIM_TimeBaseInit(TIM2, &tim);

    // настраиваем PWM, чтобы формировать выходной клок
    // Используем третий канал, так как он позволяет расталкивать DMA по его первому каналу
    TIM_OCInitTypeDef tim_oc;
    TIM_OCStructInit(&tim_oc);
    tim_oc.TIM_OCMode = TIM_OCMode_PWM2; // FIXME: Почему pwm1?
    tim_oc.TIM_OutputState = TIM_OutputState_Enable; // включаем вывод на пин
    //tim_pwm.TIM_OCNIdleState // только для таймеров 1, 8
    tim_oc.TIM_Pulse = DMA_KICK_PERIOD / 2;
    tim_oc.TIM_OCPolarity = TIM_OCPolarity_High; // FIXME: Тут нужно подумать
    //tim_pwm.TIM_OCNPolarity; // только для таймеров 1, 8
    //tim_pwm.TIM_OCIdleState; // только для таймеров 1, 8
    //tim_pwm.TIM_OCNIdleState; // только для таймеров 1, 8
    TIM_OC3Init(TIM2, &tim_oc);

    // разрешаем таймеру обращаться к этому каналу DMA по событию совпадения счетчика с 3им CCR регистром
    // для прогона одного байта через DMA
    TIM_DMAConfig(TIM2, TIM_DMABase_CCR3, TIM_DMABurstLength_1Byte);
    TIM_DMACmd(TIM2, TIM_DMA_CC3, ENABLE);

    // РАзрешаем этому таймеру управлять ведомыми таймерами
    TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
    // Выдаем им сигнал UPDATE
    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
        // Включаем для этого таймепра входной триггер с таймера 3
    TIM_SelectInputTrigger(TIM2, TIM_TS_ITR2);
    // Разрешаем другим таймерам управлять нашим клоком режимом гейтирования
    TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Gated);

    // НАСТРОЙКА ТАЙМЕРА 3 (тот, который выключает таймер 2)
    // ===============================================================
    // ===============================================================
    // ===============================================================
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    //настраиваем таймер для отсчета количества прерываний таймера 2
    TIM_TimeBaseInitTypeDef tim_cnt;
    TIM_TimeBaseStructInit(&tim_cnt);
    tim_cnt.TIM_Prescaler = 0x0000;
    tim_cnt.TIM_CounterMode = TIM_CounterMode_Up;
    tim_cnt.TIM_Period = 0xFFFF-1; // ведем таймер до максимума
    tim_cnt.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM3, &tim_cnt);

    // настраиваем PWM, чтобы формировать выходной клок
    TIM_OCStructInit(&tim_oc);
    tim_oc.TIM_OCMode = TIM_OCMode_PWM1;
    tim_oc.TIM_OutputState = TIM_OutputState_Disable;//TIM_OutputState_Enable;
    //tim_pwm.TIM_OCNIdleState // только для таймеров 1, 8
    tim_oc.TIM_Pulse = BUFFER_SIZE; // значение в CCR регистре
    tim_oc.TIM_OCPolarity = TIM_OCPolarity_High; // FIXME: Тут нужно подумать
    //tim_pwm.TIM_OCNPolarity; // только для таймеров 1, 8
    //tim_pwm.TIM_OCIdleState; // только для таймеров 1, 8
    //tim_pwm.TIM_OCNIdleState; // только для таймеров 1, 8
    TIM_OC1Init(TIM3, &tim_oc);

    // Разрешаем ему управлять ведомыми таймерами
    TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);
    // Передаем им наш сигнал с CCR1
    TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_OC1Ref);
    // Будем слушать его клоки и тикать
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);
    // Сами будем использовать сигнал с таймера 2
    TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1);

    // Настройка DMA
    // настраиваем DMA для чтения данных по клоку, генерируемому таймером
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_InitTypeDef dma;
    dma.DMA_PeripheralBaseAddr = (uint32_t)&GPIOA->IDR;
    dma.DMA_MemoryBaseAddr = (uint32_t)_buffer;
    dma.DMA_DIR = DMA_DIR_PeripheralSRC;
    dma.DMA_BufferSize = sizeof(_buffer);
    dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//DMA_PeripheralDataSize_HalfWord;
    dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    dma.DMA_Mode = DMA_Mode_Normal;
    dma.DMA_Priority = DMA_Priority_High;
    dma.DMA_M2M = DMA_M2M_Disable;
    // Настраиваем первый канал DMA и включаем его
    DMA_Init(DMA1_Channel1, &dma);
    DMA_Cmd(DMA1_Channel1, ENABLE);

    // FIXME: для отладки - включаем прерывание по завершению прогона блока с DMA
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    NVIC_InitTypeDef dma_nvic;
    dma_nvic.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    dma_nvic.NVIC_IRQChannelPreemptionPriority = 0;
    dma_nvic.NVIC_IRQChannelSubPriority = 0;
    dma_nvic.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&dma_nvic);

}

int main(int argc, char* argv[])
{
    setup_timers();

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    // TIM 2
    GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);
    GPIO_InitTypeDef p;
    p.GPIO_Mode = GPIO_Mode_AF_OD;
    p.GPIO_Pin = GPIO_Pin_10;
    p.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &p);

    // PORTA
    p.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
    p.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    p.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &p);

    TIM_Cmd(TIM3, ENABLE);
    TIM_Cmd(TIM2, ENABLE);

    volatile int x = 0;
    while(1)
    {}

    while(1)
    {
        TIM_Cmd(TIM2, DISABLE);
        TIM_Cmd(TIM3, DISABLE);
        TIM_GenerateEvent(TIM2, TIM_EventSource_Update);
        TIM_GenerateEvent(TIM3, TIM_EventSource_Update);

        while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET)
        {}

        DMA_DeInit(DMA1_Channel1);

        TIM_Cmd(TIM3, ENABLE);
        TIM_Cmd(TIM2, ENABLE);
    }
}

void DMA1_Channel1_IRQHandler()
{
    volatile int x = 0;
}