stm32f4 usart+DMA

Кто любит RISC в жизни, заходим, не стесняемся.
jcxz
Мудрый кот
Сообщения: 1725
Зарегистрирован: Вт авг 15, 2017 10:51:13

Re: stm32f4 usart+DMA

Сообщение jcxz »

[uquote="AlanDrakes",url="/forum/viewtopic.php?p=4174996#p4174996"]

Код: Выделить всё

uint8_t len =* string ;
Это вообще что? Получение указателя на строку в переменную размером 1 байт?[/uquote]Вам бы самому что-ль си подучить. :dont_know:
Реклама
Аватара пользователя
AlanDrakes
Прорезались зубы
Сообщения: 236
Зарегистрирован: Пн июл 04, 2016 16:51:22
Откуда: Россия, Омск

Re: stm32f4 usart+DMA

Сообщение AlanDrakes »

[uquote="ivan dimir",url="/forum/viewtopic.php?p=4175342#p4175342"]Протеус.Не отладчик.У меня не IAR.Cubeide[/uquote]
Выбросите. И поставьте хотя бы Keil. Бесплатная версия может отлаживать приложение размером до 32кБ.
Зато в нём можно подсмотреть содержимое регистров, пройтись по памяти, заглянуть в значения переменных, пройтись по программе в пошаговом режиме, чтобы понять, что делает код, который ВЫ написали.

Вообще, я начинал с ассемблера под AVR. Ну так получилось.
И моим первым проектом была отладочная плата для контроллера. Кусок текстолита, ZIF сокет, куча светодиодов на портах, и гнёзда для подключения, затем... затем прошло много времени и я взялся за более продвинутый чип ATMega1284p, который по началу тоже был замучан на ассемблере. Уже после я взялся за Си. Так что мне страшно смотреть на Ваш код.

Отладочная плата тогда выглядела как-то так:
Спойлер
20140106_160528.jpg
Дичь, но работало. Сама печатная плата - готовая.
(103.75 КБ) 69 скачиваний
Добавлено after 1 minute 47 seconds:
[uquote="jcxz",url="/forum/viewtopic.php?p=4175421#p4175421"][uquote="AlanDrakes",url="/forum/viewtopic.php?p=4174996#p4174996"]

Код: Выделить всё

uint8_t len =* string ;
Это вообще что? Получение указателя на строку в переменную размером 1 байт?[/uquote]Вам бы самому что-ль си подучить. :dont_know:[/uquote]
Ох, простите, я забыл знак [Sarcasm] из IT Crowd.
Реклама
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f4 usart+DMA

Сообщение ivan dimir »

А сколько стоит что бы снять ограничение кода?.

Добавлено after 1 minute 12 seconds:
Если есть оганичение кода .Нет смысла всем этим заниматся.
Аватара пользователя
AlanDrakes
Прорезались зубы
Сообщения: 236
Зарегистрирован: Пн июл 04, 2016 16:51:22
Откуда: Россия, Омск

Re: stm32f4 usart+DMA

Сообщение AlanDrakes »

[uquote="ivan dimir",url="/forum/viewtopic.php?p=4175568#p4175568"]А сколько стоит что бы снять ограничение кода?.

Добавлено after 1 minute 12 seconds:
Если есть оганичение кода .Нет смысла всем этим заниматся.[/uquote]
Вас в Гугле забанили?
https://ru.farnell.com/keil/mdk-arm-es- ... dp/3107107
Цифровая доставка
Скачать в течение 3 рабочих дней
Этот товар не подлежит отмене и возврату
3 192,00 €
Либо бесплатно на всем известных сайтах.
Йо-хо-хо! [SFX: He's a pirate]
Публиковать ссылки на последние никто не будет, они и так известны.
Реклама
Эиком - электронные компоненты и радиодетали
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f4 usart+DMA

Сообщение ivan dimir »

Бесплатно это какк.С ограничением кода?
Реклама
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f4 usart+DMA

Сообщение VladislavS »

Бесплатно это даром :)

Такими темпами 32К вы достигнете чуть позже чем никогда.
Реклама
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f4 usart+DMA

Сообщение ivan dimir »

Добавить вещественное число вот и будет 32 кбайта.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f4 usart+DMA

Сообщение VladislavS »

С дуру и хрен сломать можно.
Аватара пользователя
AlanDrakes
Прорезались зубы
Сообщения: 236
Зарегистрирован: Пн июл 04, 2016 16:51:22
Откуда: Россия, Омск

Re: stm32f4 usart+DMA

Сообщение AlanDrakes »

Мой код занимает...

Код: Выделить всё

Building RAM: ./Build/KeyBrain.elf
   text    data     bss     dec     hex filename
  41740      20   85052  126812   1ef5c ./Build/KeyBrain.elf
Включая работу с сетью, DHCP, UART, сканер отпечатка пальцев, NTP, Telnet (хотя реализация топорная, но работает), FATFS + SDIO, FreeRTOS, и некоторое количество массивов с данными.
Так что расслабьтесь, небольшой проект 32кБ не займёт.

Другой проект:

Код: Выделить всё

:~/MCU_Proj/STM32F030-EEPROM$ make
Compiling: startup_stm32f030x8.s
Compiling: system_stm32f0xx.c
Compiling: main.c
Compiling: diskio.c
Compiling: ff.c
Compiling: uSD_SPI.c
Building: ./debug/ee_rw.elf
   text    data     bss     dec     hex filename
  21568    1260    2776   25604    6404 ./debug/ee_rw.elf
LCD + FatFS + SPI uSD + Кнопки + чтение-запись внешней EEPROM, парсер/экспортер Intel HEX. Меню на обратных вызовах (кое-где крайне костыльная реализация, но работает).
Чуть меньше 22кБ.

И это компилируется не Keil'овским компилятором, а обычным arm-none-eabi-gcc, хотя и с оптимизацией -Os.


[offtop]Я бы порекомендовал скачать eclipse, но его нужно настраивать под контроллер, искать .svd файлы, настраивать отладчик... муторно. У меня вообще make + gcc + gdb и в некоторых случаях отладка в том самом тормозном eclipse.[/offtop]
ivan dimir
Мучитель микросхем
Сообщения: 440
Зарегистрирован: Вс дек 29, 2019 08:05:21

Re: stm32f4 usart+DMA

Сообщение ivan dimir »

Я не спец.Ассемблер я не изучал.Он конечно лучше.Места меньше занимает.Но ассемблер это не моё.Тогда я буду всю жизнь программировать.Я не профи.
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

Вернемся к нашим буферам.

За основу был взят код передачи AlanDrakes с не большой переделкой, что бы действительно можно было использовать произвольный размер буфера
Спойлерсам код

Код: Выделить всё

volatile uint8_t DMA_STATE=0; // Флаг работы DMA
volatile uint32_t tty0_TX_POS=0; // Указатель позиции записи в буфер
volatile uint32_t tty0_WR_POS=0; // Указатель позиции чтения из буфера (DMA)
volatile uint8_t tty0_TX_BUF[32]={0}; // Размер буфера равным степени двойки, минимум 2

#define BUF_MASK    (sizeof(tty0_TX_BUF)-1)

volatile uint32_t len_WR_POS;
volatile uint32_t poz_WR_POS;

#define DMA_tty0_TX_ACTIVE 1

void DMA1_Channel4_IRQHandler(){
    if(DMA1->ISR & DMA_ISR_TCIF4){ // Tx
        DMA1->IFCR = DMA_IFCR_CTCIF4; // clear TC flag
        DMA1_Channel4->CCR &= ~DMA_CCR1_EN;   // Отключаем поток DM
		
        DMA_STATE &= ~DMA_tty0_TX_ACTIVE;

        while(len_WR_POS--){ 
			poz_WR_POS--; 
        	tty0_TX_BUF[poz_WR_POS & BUF_MASK]=0;
        }
        tty0_ActivateDMA();
    }
}

void tty0_ActivateDMA(void) {
   uint32_t CurrWrPos = tty0_WR_POS;
   uint32_t DataToSend;
   uint32_t mask = tty0_TX_POS & BUF_MASK;
   uint32_t curr = CurrWrPos & BUF_MASK;
   DataToSend = 0;
   if(!(DMA_STATE & DMA_tty0_TX_ACTIVE)) {
	   DMA1_Channel4->CCR &= ~DMA_CCR1_EN;   // Отключаем поток DMA
      if (tty0_TX_POS != CurrWrPos) {
         // Если не совпадает - значит, данные есть. Или малый шанс на переполнение буфера.
    	  DMA1_Channel4->CMAR = (uint32_t)&(tty0_TX_BUF[mask]);
         if ((mask) < (curr)) {
            // Нет перехода через конец буфера
            DataToSend = (CurrWrPos - tty0_TX_POS);
            tty0_TX_POS = CurrWrPos;
         } else {
            // Нужно сделать кольцо.
            DataToSend = (sizeof(tty0_TX_BUF) - (mask));
            tty0_TX_POS += DataToSend;
         };

         len_WR_POS = DataToSend;
         poz_WR_POS = (tty0_TX_POS & BUF_MASK);

         DMA1_Channel4->CNDTR = DataToSend;
         USART1->SR = ~(USART_SR_TC);
         // И только ПОСЛЕ этого включаем его. Да, странность. Но иначе он уходит в ошибку.
         DMA1_Channel4->CCR |= DMA_CCR1_EN;
         DMA_STATE |= DMA_tty0_TX_ACTIVE;
      };
   };   // Если активен - сработает при вызове события завершения обмена.
};

// Принимаем СТРОКУ символов с нуль-терминатором.
void console_put(const char *text) {
   while(*text) {
      // Пока не нуль-терминатор
	  while(tty0_TX_BUF[tty0_WR_POS & BUF_MASK]) { };	// цикл ожидания с проверкой на \0
      tty0_TX_BUF[tty0_WR_POS & BUF_MASK] = *text;      // Копируем данные в буфер
      text++;                  // Сдвигаем указатель текста.
      tty0_WR_POS++;               // Сдвигаем указатель на 1 байт дальше.
      if((tty0_WR_POS & BUF_MASK) == 0) tty0_ActivateDMA();
   };
   // Запускаем.
   tty0_ActivateDMA();
};
Кому надо, можете оптимизировать
Ну а теперь тесты
Спойлерсам тестовый код

Код: Выделить всё

	uint32_t start = DWT->CYCCNT;
	SEND("\r\n");
	printu(ms_tick);
	SEND("\r\n");
	SEND("\r\nSystem Frequency: ");
	printu(SystemCoreClock);
	SEND(" MHz \r\n");

	SEND("\r\n STM32F100RB USART DMA-tx !!! 31");
	SEND("\r\n STM32F100RB USART DMA-tx !!! 32");
	SEND("\r\n STM32F100RB USART DMA-tx !!! 33");

	SEND("\r\n");
	printu(ms_tick);
	SEND("\r\n");

	uint32_t end = DWT->CYCCNT;

	delay_ms(2000);

    SEND("\r\nstart-end = ");
    printu(end - start);
    SEND("\r\n");

    delay_ms(2000);

    uint32_t start_h = DWT->CYCCNT;
	SEND("Hello, world!\r\n");
	uint32_t end_h = DWT->CYCCNT;

	delay_ms(2000);

    SEND("\r\nstart_h-end_h = ");
    printu(end_h - start_h);
    SEND("\r\n");

    delay_ms(4000);

    uint32_t start_m = DWT->CYCCNT;
    SEND("... строка длиною 627 символов ...");
	uint32_t end_m = DWT->CYCCNT;

	delay_ms(2000);

    SEND("\r\nstart_m-end_m = ");
    printu(end_m - start_m);
    SEND("\r\n");
картинка
Изображение
В коде AlanDrakes при размере буфера в 256, было выведено последние 256 символов 627 символьной строки.
Всего отправлено до моего теста 857 символов.

В моем тесте отправляется 2596 символов, вот код моего теста

Код: Выделить всё

#define BUFFER_SIZE 512
volatile uint16_t Buffer[BUFFER_SIZE]={0};

void test (void){
   SEND("\r\n");
   printu(ms_tick);
   SEND("\r\n");

   for (uint16_t i = 1000; i<(1000+BUFFER_SIZE); i++){
      Buffer[i-1000] = i;
   }

    SEND("\r\n");
   printu(ms_tick);
   SEND("\r\n");

   for (uint16_t i = 0; i<BUFFER_SIZE; i++){
      printu(Buffer[i]);
      SEND(" ");
   }
   SEND("\r\n");

    SEND("\r\n");
   printu(ms_tick);
   SEND("\r\n");
}
и подсчитано затраченное время в милли секундах.
Пока что другой реализации я не видел.

Усе, с передачей закончили, переходим к приему.
Аватара пользователя
AlanDrakes
Прорезались зубы
Сообщения: 236
Зарегистрирован: Пн июл 04, 2016 16:51:22
Откуда: Россия, Омск

Re: stm32f4 usart+DMA

Сообщение AlanDrakes »

[uquote="Dimon456",url="/forum/viewtopic.php?p=4186120#p4186120"]В коде AlanDrakes при размере буфера в 256, было выведено последние 256 символов 627 символьной строки.[/uquote]
Собственно, об этом переполнении буфера я и предупреждал.
Фактически, в буфер данные были положены, но их затёрло свежими. И это нормально для кольцевого буфера.
Ещё можно извернуться, ускорив вывод в буфер другими методами, но игра не стоит свеч.
В любом случае время передачи не станет меньше того, что требуется периферии, чтобы отправить данные в линию.

А у меня в проектах используется минимум 2к буфер под передачу, поскольку время от времени запись в него происходит крайне интенсивно.
В частности, лог первичной инициализации может занимать почти весь буфер.
В железке с кристаллом по больше - буфер передачи уже 4к.

Правда, рядом с буфером экрана в ~38кБ он смотрится мелковато.
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

AlanDrakes писал(а):Фактически, в буфер данные были положены, но их затёрло свежими. И это нормально для кольцевого буфера.
Нет, это не нормально, только представьте, что было бы с жестким диском компа или еще с чем.

Добавлено after 1 hour 13 minutes 11 seconds:
Переходим к приему.
За основу был взят код приема AlanDrakes с не большой переделкой
Спойлерсам код

Код: Выделить всё

#define MAX_DMA_BUFFERS_COUNT 64
char BoxCon_RX_BUF[MAX_DMA_BUFFERS_COUNT]={0};
volatile uint8_t DMA_BUF_START[MAX_DMA_BUFFERS_COUNT]={0};
volatile uint8_t DMA_BUF_END[MAX_DMA_BUFFERS_COUNT]={0};
volatile uint8_t DMA_CURR_WR_BUF = 0;
char buff[MAX_DMA_BUFFERS_COUNT] = {0};

Код: Выделить всё

void USART1_IRQHandler(){
    if (USART1->SR & USART_SR_IDLE) {
       (void)USART1->DR;      // Очистка флага IDLE. Мне лично кажется такой подход странным.
	   static uint32_t DMA_BUF_START_LAST = 0; // 	Устранен Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
       if ((sizeof(BoxCon_RX_BUF) - DMA1_Channel5->CNDTR) != DMA_BUF_START_LAST) {
          DMA_BUF_START[DMA_CURR_WR_BUF] = DMA_BUF_START_LAST;
          DMA_BUF_START_LAST = (sizeof(BoxCon_RX_BUF) - DMA1_Channel5->CNDTR);
          DMA_BUF_END[DMA_CURR_WR_BUF] = DMA_BUF_START_LAST;
          DMA_CURR_WR_BUF++;
          if (DMA_CURR_WR_BUF >= (MAX_DMA_BUFFERS_COUNT-1)) { DMA_CURR_WR_BUF = 0; };
       };
    }
}

Код: Выделить всё

    while(1)
    {		static uint8_t DMA_CURR_RD_BUF = 0;		// Устранен Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
			while (DMA_CURR_RD_BUF != DMA_CURR_WR_BUF) {
    	         uint8_t b_start = DMA_BUF_START[DMA_CURR_RD_BUF];   // Копируем позиции начала
    	         uint8_t b_end = DMA_BUF_END[DMA_CURR_RD_BUF];      // И конца буфера
    	         // Переводим на следующий (но не проверяем его данные)
    	         DMA_CURR_RD_BUF++;
    	         if (DMA_CURR_RD_BUF >= (MAX_DMA_BUFFERS_COUNT-1)) DMA_CURR_RD_BUF = 0;

    	         // сборка команды
    	         uint8_t len_buff = strlen(buff); // определяем длину

    	         if(b_start < b_end) {
    	           uint32_t len = b_end-b_start;
    	           if((len_buff+len)>(MAX_DMA_BUFFERS_COUNT-2)) len_buff=0;
    	           memcpy(&buff[len_buff],&BoxCon_RX_BUF[b_start],len);
    	           buff[len_buff+len]=0;
    	           }

    	           if(b_start > b_end) {
    	           uint32_t len = MAX_DMA_BUFFERS_COUNT-b_start;
    	           memcpy(&buff[len_buff],&BoxCon_RX_BUF[b_start],len);
    	           memcpy(&buff[len],BoxCon_RX_BUF,b_end);
    	           buff[len_buff+len+b_end]=0;
    	           }
    	           
    	           len_buff = strlen(buff)-1; // определяем длину
					
					// если BkSp - сдвигаем назад
    	           if((buff[len_buff] == 0x08)|| (buff[len_buff] == 0x7f)){
    	        	// стираем символ
    	           	// но не левее начала буфера
    	           	if(len_buff > 0) buff[len_buff-1]=0; else buff[len_buff]=0;
    	           	}

    	           // если Enter - ввод закончен
    	           if(((buff[len_buff] == '\n') || (buff[len_buff] == '\r'))) {
    	        	   flag=1; buff[len_buff]=0; // стираем символ Enter, заменяем концом строки \0
    	           }
    	      }
библиотека microrl оказалась слишком заумной
свою написал

Код: Выделить всё

static bool respcmp(char *s, const char  *resp)
{
	while(*resp)
		if(*resp++ != *s++) return false;
	return true;
}

static uint32_t my_atoi(char *str)
{
	int result = 0;

	if ('0'>*str || *str>'9')
	{
		str++;
	}
	while (*str != '\0')
	{
		if ('0'> *str || *str>'9')
			break;
		else
			result = result * 10 + (*str++ - '0');
	}
	return result;
}

Код: Выделить всё

      // поиск обработчика
      if (flag) {
        if (respcmp(buff, "LED ON")) {
          GPIOC - > ODR |= GPIO_Pin_9;
          SEND("\r\nLED ON\r\n");
        }
        if (respcmp(buff, "LED OFF")) {
          GPIOC - > ODR &= ~GPIO_Pin_9;
          SEND("\r\nLED OFF\r\n");
        }
        if (respcmp(buff, "B")) {
          GPIOC - > ODR |= GPIO_Pin_8;
          SEND("\r\nLED blue ON\r\n");
        }
        if (respcmp(buff, "S")) {
          GPIOC - > ODR &= ~GPIO_Pin_8;
          SEND("\r\nLED blue OFF\r\n");
        }
        if (respcmp(buff, "TOGGLE")) {
          GPIOC - > ODR ^= GPIO_Pin_9;
        }
        if (respcmp(buff, "PRINT")) {
          SEND("\r\nPRINT= ");
          printu(main_p);
          SEND("\r\n");
        }
        if (respcmp(buff, "COUNT=")) {
          main_p = my_atoi(buff + sizeof("COUNT=") - 1);
        }
        //if (!strcmp (buff, "LED ON")) { GPIOC->ODR |= GPIO_Pin_9; SEND("\r\nLED ON\r\n"); }
        //if (!strcmp (buff, "LED OFF")) { GPIOC->ODR &= ~GPIO_Pin_9; SEND("\r\nLED OFF\r\n"); }
        //if (!strcmp (buff, "TOGGLE")) { GPIOC->ODR ^= GPIO_Pin_9; }
        //sscanf(buff, "COUNT=%lu", &main_p);
        //if (!strcmp (buff, "B")) { GPIOC->ODR |= GPIO_Pin_8; SEND("\r\nLED blue ON\r\n"); }
        //if (!strcmp (buff, "S")) { GPIOC->ODR &= ~GPIO_Pin_8; SEND("\r\nLED blue OFF\r\n"); }
        buff[0] = 0;
        flag = 0;
        SEND("\nCMD > ");
      }
    } //while(1)
Что в итоге имеем:
-прерывание по IDLE
-поддерживается как по символьсный ввод, так и отправка строки по нажатию клавиши "Ввод"
-примитивное редактирование при помощи клавиши backspace
Длина команды ограничена 64 символами, учитывая нажатие клавиши backspace

Можете оптимизировать, или предлагать свои варианты.
Аватара пользователя
AlanDrakes
Прорезались зубы
Сообщения: 236
Зарегистрирован: Пн июл 04, 2016 16:51:22
Откуда: Россия, Омск

Re: stm32f4 usart+DMA

Сообщение AlanDrakes »

[uquote="Dimon456",url="/forum/viewtopic.php?p=4187072#p4187072"]
AlanDrakes писал(а):Фактически, в буфер данные были положены, но их затёрло свежими. И это нормально для кольцевого буфера.
Нет, это не нормально, только представьте, что было бы с жестким диском компа или еще с чем.[/uquote]
Ну так если не следить за свободным местом - то нормально =]
Где-то в другой версии кода, была проверка на наползание указателей друг на друга. Там в цикле записи процессор отправлялся в сон до следующего прерывания. Собственно, костыль.
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

AlanDrakes писал(а):Собственно, костыль.
Опять же подходим к тому условию - если есть куча свободной оперативки. Вот f030 мне не хватило оперативки, а хочется не большой выигрыш, пусть и маленький, но выигрыш.
И опять же, с этим костылем 100% гарантия что все выведется, а не затрется часть.
Ответить

Вернуться в «ARM»