STM32F4Discovery UART DMA

Тема в разделе "Флудилка", создана пользователем Alex19, 9 ноя 2014.

  1. Alex19

    Alex19 Гуру

    День добрый.
    Пока писали ТЗ к проекту, тестировал основные подходы, приемы решения проблем которые подымались в проекте. Последняя проблема была передача данных, она ко всему еще и трансформировалась по ходу написания ТЗ:).

    Нюансы подымать не буду, мной уже подымалась данная тема и пользователь Unixon предложил на мой взгляд хорошее решение. Но так как в одном из проектов столкнулся с STM32F4Discovery и узнал про DMA решил делать проект на STM32F4Discovery. Мне показалось, такое решение более логичным.

    Задумка такая есть пакеты данных по 12 байт на отправку и получение.
    2 отдельных UART, один на прием, другой на отправку через свои потоки DMA.

    Подробное описание DMA на русском в контексте STM32F4Discovery.
    https://www.youtube.com/playlist?list=PL8OgDYWys_b6XtOjCejd37aVv0ic24jqV
    Уроки 14-16.

    Среда Keil μVision. Основная библиотека STM32F4xx_DSP_StdPeriph_Lib_V1.4.0.

    Файл usart.c
    Код (Text):

    #include "usart.h"

    // Размер буфера для получения данных по DMA, через UART2
    static const uint8_t bufferSizeDMAUSARTSend = 12;

    // Размер буфера для отправки данных по DMA, через UART3
    static const uint8_t bufferSizeDMAUSARTReceive  = 12;

    // Буфер для отправки данных по DMA, через UART2
    char bufferDMAUSARTSend[bufferSizeDMAUSARTSend];

    // Последний полученный Char
    char rxCharReceive;

    // Буфер для получения данных по DMA, через UART3
    char bufferDMAUSARTReceive[bufferSizeDMAUSARTReceive];

    // Индекс буфера для получения данных
    int16_t bufferDMAUSARTReceiveIndex = 0;

    // Статус полученных данных
    int8_t receiveDataStatus = 0;

    // Инициализация UART2 на отправку данных
    void USART2Init(void)
    {
        GPIO_InitTypeDef GPIO_Init_USART;
        USART_InitTypeDef USART_InitUser;
       
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
       
        GPIO_Init_USART.GPIO_Pin = GPIO_Pin_2;
        GPIO_Init_USART.GPIO_Mode = GPIO_Mode_AF;
        GPIO_Init_USART.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_Init_USART.GPIO_OType = GPIO_OType_PP;
        GPIO_Init_USART.GPIO_PuPd = GPIO_PuPd_UP;
       
        GPIO_Init(GPIOA, &GPIO_Init_USART);
       
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
       
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
       
        USART_InitUser.USART_BaudRate = 115200;
        USART_InitUser.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitUser.USART_Mode = USART_Mode_Tx;
        USART_InitUser.USART_Parity = USART_Parity_No;
        USART_InitUser.USART_StopBits = USART_StopBits_1;
        USART_InitUser.USART_WordLength = USART_WordLength_8b;
       
        USART_Init(USART2, &USART_InitUser);
       
        USART_Cmd(USART2, ENABLE);
    }

    // Инициализация UART3 на получение данных
    void USART3Init(void)
    {
        GPIO_InitTypeDef GPIO_Init_USART;
        USART_InitTypeDef USART_InitUser;
       
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
       
        GPIO_Init_USART.GPIO_Pin = GPIO_Pin_11;
        GPIO_Init_USART.GPIO_Mode = GPIO_Mode_AF;
        GPIO_Init_USART.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_Init_USART.GPIO_OType = GPIO_OType_PP;
        GPIO_Init_USART.GPIO_PuPd = GPIO_PuPd_UP;
       
        GPIO_Init(GPIOB, &GPIO_Init_USART);
       
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);
       
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
       
        USART_InitUser.USART_BaudRate=115200;
        USART_InitUser.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
        USART_InitUser.USART_Mode=USART_Mode_Rx;
        USART_InitUser.USART_Parity=USART_Parity_No;
        USART_InitUser.USART_StopBits=USART_StopBits_1;
        USART_InitUser.USART_WordLength=USART_WordLength_8b;
       
        USART_Init(USART3, &USART_InitUser);
       
        //NVIC_EnableIRQ(USART3_IRQn);
       
        //USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
       
        USART_Cmd(USART3, ENABLE);
    }

    // Инициализация DMA (DMA1_Channel_4_Stream6) для USRT2 на отправку данных
    void DMAUSART2Init(void)
    {
        DMA_InitTypeDef DMA_Init_USART;
       
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
       
        DMA_Init_USART.DMA_Channel = DMA_Channel_4;
        DMA_Init_USART.DMA_PeripheralBaseAddr = (uint32_t)&(USART2->DR);
        DMA_Init_USART.DMA_Memory0BaseAddr = (uint32_t)bufferDMAUSARTSend;
        DMA_Init_USART.DMA_DIR = DMA_DIR_MemoryToPeripheral;
        DMA_Init_USART.DMA_BufferSize = bufferSizeDMAUSARTSend;
        DMA_Init_USART.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
        DMA_Init_USART.DMA_MemoryInc = DMA_MemoryInc_Enable;
        DMA_Init_USART.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
        DMA_Init_USART.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
        DMA_Init_USART.DMA_Mode = DMA_Mode_Normal;
        DMA_Init_USART.DMA_Priority = DMA_Priority_Medium;
        DMA_Init_USART.DMA_FIFOMode = DMA_FIFOMode_Disable;
        DMA_Init_USART.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
        DMA_Init_USART.DMA_MemoryBurst = DMA_MemoryBurst_Single;
        DMA_Init_USART.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
       
        DMA_Init(DMA1_Stream6, &DMA_Init_USART);
       
        USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
       
        NVIC_EnableIRQ(DMA1_Stream6_IRQn);
       
        DMA_ITConfig(DMA1_Stream6, DMA_IT_TC, ENABLE);
    }

    // Инициализация DMA (DMA1_Stream1) для UART3 на получение данных
    void DMAUSART3Init(void)
    {
        DMA_InitTypeDef DMA_Init_USART;
       
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
       
        DMA_Init_USART.DMA_Channel = DMA_Channel_4;
        DMA_Init_USART.DMA_PeripheralBaseAddr = (uint32_t)&(USART3->DR);
        DMA_Init_USART.DMA_Memory0BaseAddr = (uint32_t)bufferDMAUSARTReceive;
        DMA_Init_USART.DMA_DIR = DMA_DIR_PeripheralToMemory;
        DMA_Init_USART.DMA_BufferSize = bufferSizeDMAUSARTReceive;
       
        DMA_Init_USART.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
      DMA_Init_USART.DMA_MemoryInc = DMA_MemoryInc_Enable;
      DMA_Init_USART.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
      DMA_Init_USART.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
      DMA_Init_USART.DMA_Mode = DMA_Mode_Circular;
      DMA_Init_USART.DMA_Priority = DMA_Priority_High;
      DMA_Init_USART.DMA_FIFOMode = DMA_FIFOMode_Disable;
      DMA_Init_USART.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
      DMA_Init_USART.DMA_MemoryBurst = DMA_MemoryBurst_Single;
      DMA_Init_USART.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
       
        DMA_Init(DMA1_Stream1, &DMA_Init_USART);
       
        USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);
       
        NVIC_EnableIRQ(DMA1_Stream1_IRQn);
       
        DMA_ITConfig(DMA1_Stream1, DMA_IT_TC, ENABLE);
       
        DMA_Cmd(DMA1_Stream1, ENABLE);
    }

    // Функция отправки данных через DMA
    void SendDataToPC(char *SendData)
    {
        int8_t sizeSendData = strlen(SendData);
        if (sizeSendData == bufferSizeDMAUSARTSend)
        {
            memcpy(bufferDMAUSARTSend, SendData, bufferSizeDMAUSARTSend);
            DMA_Cmd(DMA1_Stream6, ENABLE);
        }
    }

    // Функция получения массива отправленных данных
    char * GetSendDataToPC()
    {
        return bufferDMAUSARTSend;
    }

    // Функция получения массива полученных данных
    char * GetReceiveDataToPC()
    {
        receiveDataStatus = 2;
        return bufferDMAUSARTReceive;
    }

    // Функция получения статуса полученных данных
    int8_t GetReceiveDataStatus()
    {
        return receiveDataStatus;
    }

    // Функция очистка массива полученных данных
    void ClearReceiveData()
    {
        memset(&bufferDMAUSARTReceive, '\0', sizeof(bufferDMAUSARTReceive));
       
        receiveDataStatus = 0;
    }

    // Прерывание DMA1_Stream6
    void DMA1_Stream6_IRQHandler(void)
    {
        if (DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6) == SET)
        {
            DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6);
        }
    }

    // Прерывание DMA1_Stream1
    void DMA1_Stream1_IRQHandler(void)
    {
      if (DMA_GetITStatus(DMA1_Stream1, DMA_IT_TCIF1) == SET)
      {
        DMA_ClearITPendingBit(DMA1_Stream1, DMA_IT_TCIF1);
           
            receiveDataStatus = 1;
      }
    }

    // Прерывание USART3
    void USART3_IRQHandler(void)
    {
        if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
        {
            USART_ClearITPendingBit(USART3, USART_IT_RXNE);
           
            rxCharReceive = (char)USART_ReceiveData(USART3);
            if ((rxCharReceive == '\r') || (rxCharReceive == '\n'))
        {
                if (bufferDMAUSARTReceiveIndex != 0)
          {
                    receiveDataStatus = 1;
                    bufferDMAUSARTReceiveIndex = 0;
                }
            }
            else
            {
                bufferDMAUSARTReceive[bufferDMAUSARTReceiveIndex] = rxCharReceive;
                bufferDMAUSARTReceiveIndex++;
            }
        }
    }
     
    Файл usart.h

    Код (Text):
    #ifndef USART_H
    #define USART_H
       
    #include "stm32f4xx.h"
    #include <string.h>  // Библиотека для работы со строкой

    // Инициализация USART2 на отправку данных
    void USART2Init(void);

    // Инициализация DMA (DMA1_Stream6) для USART2 на отправку данных
    void DMAUSART2Init(void);

    // Инициализация USART3 на получение данных
    void USART3Init(void);

    // Инициализация DMA (DMA1_Stream1) для USART3 на получение данных
    void DMAUSART3Init(void);

    // Функция отправки данных через DMA
    void SendDataToPC(char *SendData);

    // Функция получения массива отправленных данных
    char * GetSendDataToPC(void);

    // Функция получения массива полученных данных
    char * GetReceiveDataToPC(void);

    // Функция получения статуса полученных данных
    int8_t GetReceiveDataStatus(void);

    // Функция очистка массива полученных данных
    void ClearReceiveData(void);

    #endif
     
     
  2. Alex19

    Alex19 Гуру

    Файл main.с

    Код (Text):

    #include "main.h"

    int main(void)
    {
     
        // Обновляем, скрость работы процессора
        SystemCoreClockUpdate();
     
        //SysTick_Config(SystemCoreClock/1000000);
     
        //LedOnBoard_Init();
     
        //ButtonOnBoard_Init();
     
        USART2Init();
     
        DMAUSART2Init();
     
        USART3Init();
     
        DMAUSART3Init();
     
        SendDataToPC("Hello World!");
     
        while(1)
        {
            if (GetReceiveDataStatus() == 1)
            {
                char* bufferReceive = GetReceiveDataToPC();
                ClearReceiveData();
            }
         
            /*if (ButtonOnBoardRead() == 1)
            {
                LedOnBoard_On(BlueLedOnBoard);
            }*/
        }
    }
    Идею с отправкой по UART + DMA позаимствована от сюда.
    С получением, пришлось поковыряться. Так же загружаю проект, в папке Arburg_Keli.rar.

    Из интересного там еще ADC+DMA.

    Если кому-то нужна отправка и получение UART без DMA.
    Получение, можно просто изменить код файла usart.c найти void USART3Init(void) и в этом методе раскомментировать строки
    Код (Text):
     
    //NVIC_EnableIRQ(USART3_IRQn);
     
    //USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
     
    И в файле main.с найти int main(void) и в этом методе закомментировать
    Код (Text):
    DMAUSART3Init();
    Отправка смотрите уроки, там есть и прием, но сделан упрощено, чтобы просто пояснить принцип работы.

    Возможно кому-то будет интересно клиентское приложение на C#, для чтения и отправки в Serial.
    Его можно использовать и для Ардуины и для STM32F4Discovery и т.д.
    Загружаю проект, в папке Default Serial Talk.rar (Visual Studio 2010 C#), как-то был найден мной на просторах инета.

    Файлы большие, не удалось загрузить здесь, поэтому на Yandex-е.
    https://yadi.sk/d/9tD4KUfscb2od
    https://yadi.sk/d/r3FuMxRRcb2tx

    P.S. Следующем шагом, была идея сделать UDP+DMA, но проект полностью заморожен.
    ТЗ полностью удалось закончить, начиная от гидравлических схем, схем шкафа электроники, схемой работы и т.д., на 2 различных ТПА.

    UPD. Готовые библиотеки для работы с USART, Но без DMA.
    http://stm32f4-discovery.com/2014/04/library-04-connect-stm32f429-discovery-to-computer-with-usart/

    И Русского производства;)
    http://microtechnics.ru/biblioteka-dlya-raboty-s-usart-v-stm32/
     
    Последнее редактирование: 9 ноя 2014
    Megakoteyka нравится это.
  3. Megakoteyka

    Megakoteyka Оракул Модератор

    Пакет 12 байт и буфер 12 байт - есть вероятность испортить пакет, если 2 пакета придут подряд.
    Обычно буфер делается вдвое больше и настраивается прерывание по середине буфера и по концу. Пока DMA пишет в первую половину буфера, вы обрабатываете вторую, потом наоборот.
    Я как раз ковыряю stm32f407 по работе, позже тоже попробую поиграться с DMA.
     
  4. Alex19

    Alex19 Гуру

    Возможно Вы правы.
    Просто пока еще не до конца, собрал все кучу и не проверял. Сейчас проект, мертв, просто упертый хотел разобраться до конца:). Тоже надо было для работы, плата и возможности просто превосходные.

    Можно расширить буфер и добавить прерывание на получение половины пакета.
    Код (Text):
    DMA_ITConfig(DMA1_Stream1, DMA_IT_TC | DMA_IT_HT , ENABLE);
    Вместо
    Код (Text):
    DMA_ITConfig(DMA1_Stream1, DMA_IT_TC, ENABLE);
    Тогда надо добавить, в прерывание DMA1_Stream1_IRQHandler
    Код (Text):

    if (DMA_GetITStatus(DMA1_Stream1, DMA_IT_HTIF1))
    {
        DMA_ClearITPendingBit(DMA1_Stream1, DMA_IT_HTIF1);
    }
     
    Вроде ни чего не напутал.
    Удачи в изучении, сам потихоньку ковыряю, хоть и нет проекта под нее. Покупал для мишени, но ума не хватило довести до конца.
     
    Megakoteyka нравится это.