bufor cykliczny UART DMA

Wszystko o co chcesz zapytać na temat mikrokontrolerów ARM firmy STMicroelectronics: problemy z pisaniem programu, problemy sprzętowe, niejasności w DS czy AN itp.
Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

bufor cykliczny UART DMA

Postautor: dambo » środa 30 lis 2016, 19:34

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 :)
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1168
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: bufor cykliczny UART DMA

Postautor: Antystatyczny » środa 30 lis 2016, 19:43

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.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: bufor cykliczny UART DMA

Postautor: dambo » środa 30 lis 2016, 20:11

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ł)
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1168
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: bufor cykliczny UART DMA

Postautor: Antystatyczny » środa 30 lis 2016, 20:14

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.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1168
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: bufor cykliczny UART DMA

Postautor: Antystatyczny » środa 30 lis 2016, 20:22

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.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: bufor cykliczny UART DMA

Postautor: dambo » środa 30 lis 2016, 20:32

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ę?
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1168
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: bufor cykliczny UART DMA

Postautor: Antystatyczny » środa 30 lis 2016, 20:37

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...
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: bufor cykliczny UART DMA

Postautor: dambo » środa 30 lis 2016, 20:41

faktycznie teraz możemy głównie gdybać - jak skończę pisać tą obsługę to zrobię porównanie i wstawię wyniki :)
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: bufor cykliczny UART DMA

Postautor: dambo » środa 30 lis 2016, 20:55

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).
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1168
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: bufor cykliczny UART DMA

Postautor: Antystatyczny » środa 30 lis 2016, 20:59

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ę ;)
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: bufor cykliczny UART DMA

Postautor: dambo » środa 30 lis 2016, 21:02

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
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1168
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: bufor cykliczny UART DMA

Postautor: Antystatyczny » środa 30 lis 2016, 23:33

A ile cykli zużyjesz na obsługę wysyłki przerwaniem?
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: bufor cykliczny UART DMA

Postautor: dambo » czwartek 01 gru 2016, 00:08

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
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1168
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: bufor cykliczny UART DMA

Postautor: Antystatyczny » czwartek 01 gru 2016, 00:09

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ę...
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: bufor cykliczny UART DMA

Postautor: dambo » czwartek 01 gru 2016, 00:14

właśnie testuję to wszystko :)
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: bufor cykliczny UART DMA

Postautor: dambo » czwartek 01 gru 2016, 10:37

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 :)
Nowy blog o tematyce embedded -> https://www.embedownik.pl/


Wróć do „ARM STMicroelectronics”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 3 gości