Strona 1 z 1

bufor cykliczny UART DMA

: środa 30 lis 2016, 19:34
autor: dambo
Witam,

napisałem sobie mały libsik do komunikacji za pomocą komend AT, gdzie odbiór jest zrobiony za pomocą bufora cyklicznego z DMA, rdzeń nie zajmuje się kompletnie obsługą odbioru do czasu aż pojawi się znak powrotu karetki - UART generuje wtedy przerwanie, ustawiam flagę i potem w pętli głównej mogę sobie w wolnej chwili pobrać ten łańcuch. Działa to bardzo ładnie.
I tu pytanie - w jaki sposób zrobić coś podobnego, ale do wysyłania? Nie mam pomysłu jak tu w połączeniu z DMA zrobić bufor cykliczny - w RX jest to proste - DMA w trybie circ i lata po tablicy i zapisuje jak coś odbierze. Jakiś pomysł jak to zrobić w przypadku wysyłania? Jak zatrzymać wysyłanie? pierwszym pomysłem jest utworzenie w ramach bufora kilku mniejszych (o długości maxymalnej komendy) i "skakanie" po nich, ale czy można jakoś lepiej? Wszelkie pomysły mile widziane :)

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 19:43
autor: Antystatyczny
Zasadniczo tak samo. Ustawiasz bufor z zapętlaniem adresu. Pchasz do bufora dane, znasz adres początkowy oraz ilość danych do wysłania. na tej podstawie możesz jednym działaniem obliczyć adres końcowy. Startujesz wysyłkę przez dma i gotowe. Po wysłaniu wszystkich danych możesz zaktualizować adres początkowy w oparciu o znany Ci adres końcowy, obliczony wcześniej. Nie wiem tylko, czy to ma jakikolwiek sens. Do wysyłki zrób prosty bufor i wysyłaj za jego pomocą. Zazwyczaj i tak trzeba zaczekać na koniec transmisji, by rozpocząć kolejną, więc dane i tak zawsze będziesz pakował od początku bufora.

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 20:11
autor: dambo
no właśnie w przypadku wysyłania nie jest tak samo:
jako CNT dla kanału podaje długość bufora i on sam się kręci.

Dla wysyłania - powiedzmy, że mam do wysłania 30 znaków, bufor ma 255 i zaczynam zapis od 240 komórki - żeby rozpocząć wysyłanie muszę wklepać najpierw dane do bufora ręcznie i zrobię to bez problemu i będą zajmować przedział od 240 do 15 komórki i jak mam to wklepać do DMA? podając 30 wyjadę poza obszar tablicy, bo tu DMA samo nie zrobi bufora cyklicznego.

Wydaje mi się, że faktycznie można się ograniczyć do bufora i wklejać tylko od początku (lub zastosować kilka buforów i kolejkować - to zaraz będę klepał)

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 20:14
autor: Antystatyczny
jeśli zależy Ci na bardzo wysokiej wydajności, można pokusić się o dwa, trzy bufory. Z 1 wysyłasz, a 2 i 3 można zapełniać. Przełączać je można karuzelowo, a można sobie wymyślić jakiś mechanizm sprawdzający obłożenie buforów i oczekiwać na opustoszenie tego, w którym jest akurat najmniej danych do wysłania. To tak na szybko mi się jawi w głowie, ale niekoniecznie są to dobre pomysły.

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 20:22
autor: Antystatyczny
rezasurmar pisze:czasem wystarczy zwykły bufor kołowy w RAM.


Zdecydowanie potwierdzam. Dla małych ilości danych nie ma sensu tracić czasu na każdorazowe odpalanie dma, żeby wysłać kilka znaków.

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 20:32
autor: dambo
tylko, że bez DMA, trzema samemu wklepać dane do bufora i samemu w przerwaniu od uartu od wysłania znaku wyliczać kolejny index itp, w przypadku DMA też sami wklepujemy dane do bufora, ustawiamy raz DMA i nie interesują nas przerwania od wysłanego znaku w uarcie, jedynie od zakończenia transferu przez DMA, aby zapalić sobie flagę, że już można wysyłać.

Faktycznie przy małych komendach przewagę może mieć to podejście, żeby samemu to wyliczać (chociaż wejście w przerwanie to za każdym razem 12 cykli zegara w M0 - wtedy je oszczędzamy i wchodzi to jakby w konfigurację)

Co masz na myśli poprzez jak długo uruchamia się kanał DMA? Jest jakiś delay pomiędzy zapaleniem bitu uruchomienia, a faktycznym uruchomienie, czy chodzi o czas na konfigurację?

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 20:37
autor: Antystatyczny
Bardziej chodzi o fakt każdorazowego wklepywania konfiguracji do rejestrów opisujących parametry konkretnej transmisji, no i potem sam start transmisji (to w końcu też polecenie). Może po prostu policz sobie, ile stracisz na każdorazowe ustawianie dma, ile stracisz na obsługę przerwaniem (lub w trybie poll - jak wolisz). Tutaj jeszcze naszła mnie pewna myśl. jeśli masz potworną różnicę w prędkościach rdzenia i uartu (np. rdzeń 48MHz, a uart na 2400), to może być sens wysyłania przez dma nawet tych kilku znaków. Dlaczego? Ano dlatego, bo wysłanie kilku znaków przez uart pracujący w tempie ślimaka skutecznie spowolni działanie reszty programu. Przemyśl to szerzej...

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 20:41
autor: dambo
faktycznie teraz możemy głównie gdybać - jak skończę pisać tą obsługę to zrobię porównanie i wstawię wyniki :)

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 20:55
autor: dambo
rezasurmar pisze:Bierz pod uwagę, że DMA, to nie jest dowolny bufor który możesz sobie ładować jaką chcesz długością znaków. On musi mieć stałą długość jaką ustawisz, inaczej będziesz musiał i tak 'pilnować' np. końca linii CRLF itp. co traci sens w przypadku DMA.


No ale przy ustawieniu wysyłania przez DMA zawsze mogę podać taki rozmiar jaki potrzebuję.

W przypadku odbierania mam cykliczny bufor i informację o znaku końca linii generuje mi osobne przerwanie (jest to w dokumentacji nazwane jako MODBUS mode - wykrywa odebranie określonego znaku).

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 20:59
autor: Antystatyczny
dambo pisze: nazwane jako MODBUS mode - wykrywa odebranie określonego znaku)


A to jest dość cenna wskazówka. Swego czasu rozmyślałem o odbiorze danych przez dma i zastanawiałem się nad efektywnym sposobem wykrywania końca ramki. Dziękuję ;)

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 21:02
autor: dambo
no właśnie cały ten pomysł z pobawieniem się DMA itp wyniknął z tego, że przeczytałem o tej możliwości wykrycia znaku :)

Z innych bajerów - można jednym bitem zamienić piny RX i TX jakby ktoś się na płytce kopnął :p

Edit:
Mała poprawka co do tego co pisałem wyżej - w M0 wejście w przerwanie to 16 cykli. Tu jest wszystko ładnie opisane: https://community.arm.com/docs/DOC-2607

Re: bufor cykliczny UART DMA

: środa 30 lis 2016, 23:33
autor: Antystatyczny
A ile cykli zużyjesz na obsługę wysyłki przerwaniem?

Re: bufor cykliczny UART DMA

: czwartek 01 gru 2016, 00:08
autor: dambo
no obsługa wysyłki przerwaniem to po każdym wysłanym znaku wejście w przerwanie, sprawdzenie od czego to przerwanie wystąpiło, obliczenie kolejnego indexu w buforze cyklicznym i wstawienie nowej wartości do rejestru wysyłającego i tak co znak. Nie liczę funkcji, która zapisze dane do wysłania do bufora cyklicznego, bo ona będzie (prawie) taka sama w obu przypadkach

Re: bufor cykliczny UART DMA

: czwartek 01 gru 2016, 00:09
autor: Antystatyczny
No dobrze, w takim razie można jeszcze sprawdzić, ile cykli zużyjesz na każdorazową konfiguracje parametrów transmisji przez DMA. Będzie jakiś pogląd na tę kwestię...

Re: bufor cykliczny UART DMA

: czwartek 01 gru 2016, 00:14
autor: dambo
właśnie testuję to wszystko :)

Re: bufor cykliczny UART DMA

: czwartek 01 gru 2016, 10:37
autor: dambo
Przedstawiam wstępny zarys libskika do komunikacji za pomocą komend kończących się określonym znakiem tylko za pomocą DMA.

Działanie odbioru już opisywałem - DMA jest skonfigurowane jednorazowo do pracy z buforem cyklicznym i pobieram z niego dane, kiedy wystąpi przerwanie np od powrotu karetki. Dane z cyklicznego bufora pobieram potem do bufora do przetworzenia.
Zalety względem programowej obsługi:
- nie wchodzimy w przerwanie od UARTu po każdym znaku, jedynie na końcu linii. W takim przerwaniu, trzeba było sprawdzić źródło wystąpienia, porównać indexy głowy i ogona węża, wpisać dane do bufora, obsłużyć porównanie znaku końca linii i zinkrementować głowę węża
Wady:
- kod do konfiguracji DMA, chociaż jest on wykonywany tylko jednorazowo

W przypadku wysyłania - tu jest więcej zabawy i inna koncepcja. DMA nie zrobi nam samo wysyłania cyklicznego - musimy podać ile chcemy zapisać. Pierwszy pomysł był taki - jeśli napis przekroczy index ostatniego elementu to rozbijamy to na 2 stringi - jeden od końcówki do końca i drugi od początku bufora do końca napisu, ale wydało mi się to jakoś nieoptymalne. Przyjąłem koncepcję kilku mniejszych buforów - musimy ich długość wyznaczyć na podstawie najdłuższej oczekiwanej wiadomości do przesłania. Działa to tak:
- ładujemy dane do pierwszego wolnego bufora i odpalamy DMA, jeśli wcześniej nie było uruchomione, jeśli juz jest uruchomione to przerwanie od zakonczenia wysyłania samo odpali kolejne wysyłanie. Jeśli wszystkie bufory są do wysłania - czekamy na zwolnienie. Można powiedzieć, że jest to "bufor cykliczny wskaźników na bufory"
Zalety:
- uproszczona funkcja wrzucająca dane do bufora - nie musimy już przeliczać tam indexów. Wrzucamy nasz string do danego bufora, jedynie licząc ilość elementów
- 0 wejsć w przerwanie UARTu od wysyłania przy każdym znaku, jedynie przy końcu transmisji w przerwanie od DMA
Wady:
- kod na obsługę tego wszystkiego za każdym razem przy odpaleniu nowego wysyłania
- koncepcja z kilkoma buforami może być trochę marnowaniem zasobów - powiedzmy, że nasze komendy mają zazwyczaj 10 znaków, ale mamy jeden przypadek z 50 znakami - wtedy minimalna długość pojedynczego bufora musi mieć te 50 bajtów
- w buforze cyklicznym, miejsce w pamięci zwalnia się natychmiastowo po przesłaniu bajtu - tutaj dopiero to przesłaniu całego pojedynczego bufora - to może być główną przeszkodą w użyciu

Kod napisany na STM32F030F4P6 na małym chińskim moduliku.
Kody z plików UARTu:

Kod: Zaznacz cały

/*
 * uart.h
 *
 *  Created on: 29 sie 2015
 *      Author: Przemek
 */

#ifndef __UART_H
#define __UART_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include "../src/definicje.h"
#include "stm32f0xx.h"

#define UART_ZNAK_KONCA_LINII '\r'

#define UART_RX_BUF_SIZE 256

#define UART_TX_BUFFS_NUMBER 3
#define UART_TX_BUF_SIZE 50

// rejestry uartu jako makra:
#define UART_CR1(x) (x->CR1)
#define UART_CR2(x) (x->CR2)
#define UART_CR3(x) (x->CR3)
#define UART_BRR(x) (x->BRR)
#define UART_GTPR(x) (x->GTPR)
#define UART_RTOR(x) (x->RTOR)
#define UART_RQR(x) (x->RQR)
#define UART_ISR(x) (x->ISR)
#define UART_ICR(x) (x->ICR)
#define UART_RDR(x) (x->RDR)
#define UART_TDR(x) (x->TDR)

// numer naszego uartu:
#define UARTx USART1

#define UART_INTERRUPT_NUMBER USART1_IRQn

#define UART_RX_PORT GPIOA
#define UART_RX_PIN    10
#define UART_RX_ALTERNATE_FUNCTION_NUMBER 1

#define UART_TX_PORT GPIOA
#define UART_TX_PIN    9
#define UART_TX_ALTERNATE_FUNCTION_NUMBER 1

#define UART_RX_ALTERNATE _MODER(UART_RX_PORT) |= ( 2 << ( 2 * UART_RX_PIN ) )
#if UART_RX_PIN < 8
   #define UART_RX_ALTERNATE_NR _AFRL(UART_RX_PORT) |= ( UART_RX_ALTERNATE_FUNCTION_NUMBER << ( 4 * UART_RX_PIN ) )
#else
   #define UART_RX_ALTERNATE_NR _AFRH(UART_RX_PORT) |= ( UART_RX_ALTERNATE_FUNCTION_NUMBER << ( 4 * ( UART_RX_PIN - 8 ) ) )
#endif

#define UART_TX_ALTERNATE _MODER(UART_TX_PORT) |= ( 2 << ( 2 * UART_TX_PIN ) )
#if UART_TX_PIN < 8
   #define UART_TX_ALTERNATE_NR _AFRL(UART_TX_PORT) |= ( UART_TX_ALTERNATE_FUNCTION_NUMBER << ( 4 * UART_TX_PIN ) )
#else
   #define UART_TX_ALTERNATE_NR _AFRH(UART_TX_PORT) |= ( UART_TX_ALTERNATE_FUNCTION_NUMBER << ( 4 * ( UART_TX_PIN - 8 ) ) )
#endif

// inicjalizacja uartu
void init_uart(void);

// nasza funkcja przerwania
void USART1_IRQHandler(void);

void uart_wyslij_lancuch(char *s);
void uart_wyslij_liczbe(int16_t liczba);

// funkcja sprawdza, czy jakies dane znajduja sie w buforze odbiorczym
uint8_t uart_odbior_sprawdz();

// fukcja pobiera pojedynczy znak z bufora odbiorczego
uint8_t uart_pobierz_znak(void);

// funkcja sprawdza, czy odebrano linie tekstu
uint8_t uart_odebrano_linie( void );

// funkcja pobiera linie tekstu
void uart_pobierz_linie( uint8_t *buff );

void uruchom_wysylanie();

void DMA1_Channel2_3_IRQ();


#ifdef __cplusplus
}
#endif

#endif /* __UART_H */


Kod: Zaznacz cały

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include "stdlib.h"
#include "uart.h"
#include "definicje.h"
#include "stm32f0xx.h"
#include "delay_systick.h"

// definiujemy nasz bufor UART_RxBuf
volatile char UART_RxBuf[UART_RX_BUF_SIZE];

volatile uint32_t rx_buff_pos = UART_RX_BUF_SIZE;

// definiujemy nasze bufory UART_RxBuf jako dwuwymiarowa tablice
volatile char UART_TxBuf[UART_TX_BUFFS_NUMBER][UART_TX_BUF_SIZE];

// tablica ze stanami naszych buforow uartu
// w tej zmiennej zawarte sa 3 rzeczy
// najmlodsze 8 bitow to ilosc znakow ktore DMA ma przeslac
// bit 8 zapalony oznacza, ze bufor wlanie jest przesylany
// bit 9 oznacza, ze bufor jest do przeslania
volatile uint16_t UART_TXBuf_status[UART_TX_BUFFS_NUMBER];

// numer kolejnego bufora w kolejce do wyslania
volatile uint8_t UART_TX_next_DMA_buff = 0;

// numer kolejnego bufora do wpisywania danych
volatile uint8_t UART_TX_next_RAM_buff = 0;

// zmienna oznaczajaca prace DMA
volatile uint8_t DMA_praca = 0;

// zmienna informujaca nas, ze czeka nowa linia do przetworzenia
volatile uint8_t otrzymane_linie = 0;

void USART1_IRQHandler(void)
{

   // odbior znaku konca linii
   if( ( UART_ISR(UARTx) & USART_ISR_CMF ) )
   {
      otrzymane_linie++;
      // czyszczenie flagi przerwania
      UART_ICR( UARTx ) = USART_ICR_CMCF;
   }

}

uint8_t uart_odebrano_linie( void )
{
   if( otrzymane_linie )
   {
      return 1;
   }
   return 0;
}

void uart_pobierz_linie( uint8_t *buf )
{
    char c;

   if(otrzymane_linie)
   {
      while( (c = uart_pobierz_znak() ) != UART_ZNAK_KONCA_LINII )
      {
         *buf++ = c;
      }
      *buf = 0;
      otrzymane_linie--;
   }
}

// funkcja uruchamia DMA do wyslania stringu
void uruchom_wysylanie()
{
   // jesli kolejny bufor jest gotowy do wylania - rozpocznij wysylanie
   if(UART_TXBuf_status[UART_TX_next_DMA_buff] & 0x0200 )
   {
      // zapalenie flagi dzialania DMA
      DMA_praca = 1;

      // wylaczenie kanalu - niezbedne, zeby zmienic parametry
      DMA1_Channel2->CCR &= ~DMA_CCR_EN;

      // wpisanie adresu startowego
      DMA1_Channel2->CMAR = (uint32_t)( UART_TxBuf[UART_TX_next_DMA_buff] );

      // wpisanie liczby znakow do wyslania
      DMA1_Channel2->CNDTR = 0x00ff & UART_TXBuf_status[UART_TX_next_DMA_buff];

      // wlaczenie DMA
      DMA1_Channel2->CCR |= DMA_CCR_EN;

      // zapalenie flagi przetwarzania - reszte danych mozemy utracic
      UART_TXBuf_status[UART_TX_next_DMA_buff] = 0x0100;
   }
   // w przeciwnymprzypadku - nie ma nic do wysylania - wychodzimy bez niczego
}

void uart_wyslij_lancuch(char *s)      // wysyła łańcuch z pamięci RAM na UART
{
   // oczekiwanie na dostepnosc bufora uartu TODO jakis timeout itp
   while( UART_TXBuf_status[UART_TX_next_RAM_buff] & 0x0100 )
   {

   }
   // przesylanie naszego stringa TODO zapytac o to czy register dziala globalnie/lokalnie
   register char c;
   uint8_t licznik = 0;
   uint8_t *wsk = UART_TxBuf[UART_TX_next_RAM_buff];

   // przekopiowanie stringa do bufora
   while ((c = *s++))
   {
      *wsk = c;
      wsk++;
      licznik++;
   }
   // wpisanie statusu dla bufora
   UART_TXBuf_status[UART_TX_next_RAM_buff] = 0x0200 | licznik;

   // inkrementacja indexu bufora
   UART_TX_next_RAM_buff++;
   if( UART_TX_next_RAM_buff > UART_TX_BUFFS_NUMBER )
   {
      UART_TX_next_RAM_buff = 0;
   }

   // sprawdzamy teraz, czy DMA jest uruchomione, jesli tak to ono samo zajmie sie wyslaniem naszego stringa
   // jesli nie - musimy je uruchomic
   if( !DMA_praca )
   {
      uruchom_wysylanie();
   }
}

// definiujemy funkcję pobierającą jeden bajt z bufora cyklicznego
uint8_t uart_pobierz_znak(void)
{
   // inkrementacja wskaznika na bufor i jego ograniczenie
   rx_buff_pos++;
   if( rx_buff_pos == UART_RX_BUF_SIZE )
   {
      rx_buff_pos = 0;
   }
   return UART_RxBuf[rx_buff_pos];
}

void uart_wyslij_liczbe(int16_t liczba)
{
   char buf[17];
   itoa(liczba, buf, 10);
   uart_wyslij_lancuch(buf);
}

void init_uart(void)
{
   rx_buff_pos = UART_RX_BUF_SIZE - 1;
   RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;

   // wlaczenie zegara dla UARTu
   RCC->APB2ENR |= RCC_APB2ENR_USART1EN;

   //wlaczenie zegara dla portu gpio:
   RCC->AHBENR |= RCC_AHBENR_GPIOAEN;

   // ustawienie piny jako alternate
   UART_TX_ALTERNATE;
   UART_RX_ALTERNATE;

   // ustawiamy numer opcji alternatywnej
   UART_TX_ALTERNATE_NR;
   UART_RX_ALTERNATE_NR;

   // dlugosc znaku (8bit) jest ustawione domyslnie

   // tak samo jak 1 bit stopu

   // ustawienie baudrate: zrobmy najpierw 9600:
   UART_BRR(UARTx) = 48000000UL / 9600UL;

   // uruchomienie DMA dla odbierania danych

   // wlaczenie zegara dla DMA1
   RCC->AHBENR |= RCC_AHBENR_DMA1EN;

   ////////////////////////////////////////////////////////
   //            DMA dla RX
   ////////////////////////////////////////////////////////
   // adres "z" - rejestr odbiorczy UARTU
   DMA1_Channel3->CPAR = (uint32_t) ( &UART_RDR( UARTx ) );

   // adres "do" - nasz bufor RX
   DMA1_Channel3->CMAR = (uint32_t)( UART_RxBuf );

   // licznik "przesylow" - rozmiar naszego bufora
   DMA1_Channel3->CNDTR = UART_RX_BUF_SIZE;

   // kierunek odczytu - z peryferium do pamieci jest domyslnym ustawieniem

   // tryb cyrkularny - bedziemy w kolko krecic sie po naszym buforze
   DMA1_Channel3->CCR |= DMA_CCR_CIRC;

   // rozmiary danych wejsciowych i wyjsciowych domyslnie sa 8-bitowe

   // inkrementacje wskaznikow tylko dla pamieci - dla peryferium staly adres
   DMA1_Channel3->CCR |= DMA_CCR_MINC;

   // wpisanie znaku konca linii ktory mamy wykrywac
   UART_CR2(UARTx)  &= 0x00ffffff;
   UART_CR2(UARTx) |= UART_ZNAK_KONCA_LINII << USART_CR2_ADD_Pos;

   // uruchomienie zezwolenia dla DMA od odbioru UARTU
   UART_CR3(UARTx) |= USART_CR3_DMAR;

   ////////////////////////////////////////////////////////
   //            DMA dla TX
   ////////////////////////////////////////////////////////
   // adres "x" - tego nie podajemy teraz, tylko w obsludze wysylania
   //DMA1_Channel2->CMAR = (uint32_t) ( UART_TxBuf1 );

   // adres "do" - rejestr wysylajacy dla UARTU
   DMA1_Channel2->CPAR = (uint32_t)( &UART_TDR( UARTx ) );

   // licznik "przesylow" - obecnie niepotrzebny - bedzie wpisywany w funkcji przesylajacej
   //DMA1_Channel3->CNDTR = UART_RX_BUF_SIZE;

   // kierunek odczytu - z pamieci do peryferium
   DMA1_Channel2->CCR |= DMA_CCR_DIR;

   // rozmiary danych wejsciowych i wyjsciowych domyslnie sa 8-bitowe

   // inkrementacje wskaznikow tylko dla pamieci - dla peryferium staly adres
   DMA1_Channel2->CCR |= DMA_CCR_MINC;

   // uruchomienie zezwolenia dla DMA od wyslania danej UARTU
   UART_CR3(UARTx) |= USART_CR3_DMAT;

   // uruchomienie przerwania od zakoncznia transferu po DMA
   DMA1_Channel2->CCR |= DMA_CCR_TCIE;


   // uruchomienie przerwania nadawczego
//   UART_CR1(UARTx)  |= USART_CR1_TXEIE;

//   // uruchomienie przerwania odbiorczego
//   UART_CR1(UARTx)  |= USART_CR1_RXNEIE;

   // uruchomienie przerwania od odebranego znaku konca linii:
   UART_CR1(UARTx)  |= USART_CR1_CMIE;

   // uruchomienie DMA dla RX
   DMA1_Channel3->CCR |= DMA_CCR_EN;

   // wlaczenie nadajnika:
   UART_CR1(UARTx)  |= (USART_CR1_TE);

   // wlaczenie odbiornika:
   UART_CR1(UARTx)  |= (USART_CR1_RE);

   // wlaczenie uartu:
   UART_CR1(UARTx) |= USART_CR1_UE;

   // wlaczenie przerwania w NVICu
   NVIC_EnableIRQ( USART1_IRQn );
   NVIC_EnableIRQ( DMA1_Channel2_3_IRQn );
}

void DMA1_Channel2_3_IRQHandler()
{
   // jesli jest to przerwanie od naszego uartu
   if(DMA1->ISR & DMA_ISR_TCIF2 )
   {
      // czyscimy flage wystapienia przerwania
      DMA1->IFCR = DMA_IFCR_CTCIF2;

      // obsluga naszego przerwania

      // wstepnie gasimy flage naszej pracy
      DMA_praca = 0;

      // gasimy flage przesylania ostatniego bufora
      UART_TXBuf_status[UART_TX_next_DMA_buff] = 0;

      // inkrementujemy nasz wskaznik na bufor
      UART_TX_next_DMA_buff++;
      if( UART_TX_next_DMA_buff > UART_TX_BUFFS_NUMBER )
      {
         UART_TX_next_DMA_buff = 0;
      }

      // spradzamy teraz, czy jest cos do uruchomienia - jest tak - zaczynamy kolejna wysylke
      // jesli nie - wychodzimy

      uruchom_wysylanie();
   }
}


#ifdef __cplusplus
}
#endif




Wszelkie uwagi mile widziane :)