stm32f4 usart+DMA

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

Re: stm32f4 usart+DMA

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

Спойлер

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

Это не так. Дал команду на передачу и забыл.
Тогда какая у вас настройка в майне?.Я знаю что при передаче стрим(или канал) нужно отключать DMA? Так у других написано.
Реклама
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

VladislavS, это оправдывает при заранее известном количестве байт, строка 16 символов, можно константой задать.
А если количество символов неизвестно, в первом случае 8 символов, во втором 11, в третьем 16. Тогда как? Где код подсчета количества символов в строке?

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

Re: stm32f4 usart+DMA

Сообщение VladislavS »

[uquote="Dimon456",url="/forum/viewtopic.php?p=4171551#p4171551"]VladislavS, это оправдывает при заранее известном количестве байт, строка 16 символов, можно константой задать.[/uquote]Это оправдывается при любом количестве символов.


[uquote="Dimon456",url="/forum/viewtopic.php?p=4171551#p4171551"]А если количество символов неизвестно, в первом случае 8 символов, во втором 11, в третьем 16. Тогда как?[/uquote]Второй аргумент функции видели?

[uquote="Dimon456",url="/forum/viewtopic.php?p=4171551#p4171551"]Где код подсчета количества символов в строке?[/uquote] Откройте для себя strlen.

[uquote="Dimon456",url="/forum/viewtopic.php?p=4171551#p4171551"]Второй вопрос: А разве ждать отключение канала не надо? Сразу будем писать в CNDTR?[/uquote]Что у вас с этим тёпленьким за маниакальное желание что-то ждать?
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

VladislavS писал(а):Откройте для себя strlen.
Вот это и имел ввиду: больше телодвижений на подготовку буфера и включение канала.
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f4 usart+DMA

Сообщение VladislavS »

Вы это серьёзно?

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

void Send_str_USART1_DMA(const char *adr)
{
  constexpr uint32_t dma_cfg = _VAL2FLD(DMA_CCR_MSIZE, 0) | _VAL2FLD(DMA_CCR_PSIZE, 0) | DMA_CCR_DIR | DMA_CCR_MINC;
  DMA1_Channel1->CCR = dma_cfg;
  DMA1_Channel1->CMAR = (uint32_t) adr;
  char *p = (char *)adr;
  while(*p) p++;
  DMA1_Channel1->CNDTR = p-adr;
  DMA1_Channel1->CCR = dma_cfg | DMA_CCR_EN;
}
Реклама
Аватара пользователя
AlanDrakes
Прорезались зубы
Сообщения: 236
Зарегистрирован: Пн июл 04, 2016 16:51:22
Откуда: Россия, Омск

Re: stm32f4 usart+DMA

Сообщение AlanDrakes »

[uquote="jcxz",url="/forum/viewtopic.php?p=4171336#p4171336"][uquote="AlanDrakes",url="/forum/viewtopic.php?p=4171241#p4171241"]Используется прерывание USART:IDLE, соответственно, ожидаем не принятия ОДНОГО байта, а принятие некоторого количества непрерывных байт[/uquote]А как именно работает это прерывание IDLE - знаете? А то в мануале оно очень туманно описано. И не факт, что удастся использовать его совместно с DMA.
И причём (насколько знаю): в разных STM32 есть разные средства обнаружения таймаута на UART.RXD. В младших, слыхал, есть RTOF, который похож, но не совсем.[/uquote]
По RM - 30.3.1
An Idle character is interpreted as an entire frame of “1”s followed by the start bit of the next frame which contains data (The number of “1” ‘s will include the number of stop bits).
Символ IDLE - целый фрейм из таймслотов в состоянии "1", за которым следует стартовый бит следующего фрейма, содержащий данные (Количество единиц включает так же стоп-биты).

Мне кажется, здесь не совсем корректно описан принцип работы события. Поскольку, собственно, детектирование свободной линии (Idle line) происходит после принятия IDLE фрейма. Внятных диаграмм не привели, но код работает с тем, что есть. Просто передатчик выжидает пару символьных интервалов между отправками, так что у меня с детектированием этого события проблем не было.

[uquote="ivan dimir",url="/forum/viewtopic.php?p=4171262#p4171262"]
Спойлер

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

Используется прерывание USART:IDLE,
Где это написано? Покажите.DMA и прирываниеIDLE USART?[/uquote]
В спойлере с кодом.

[uquote="ivan dimir",url="/forum/viewtopic.php?p=4171416#p4171416"]Я включал ADC+DMA.Я выключаю прерывание по ADC.Иначе работать не будет.[/uquote]
Не работает USART:RXNE, потому как этот флаг захватывается DMA контроллером. Остальные можно ловить по прерыванию.

Как сейчас в работающем устройстве сделано:
Спойлер

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

void InitUART(void) {
<...>
	USART1->CR2 = 0;
	USART1->CR3 = 0;
	USART1->BRR = (SystemCoreClock / 38400 / presc / APB_Presc);	// Делим системную частоту на скорость порта, на делители APB и AHB, чтобы получить искомое значение регистра.
	USART1->CR1 = (USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE);
	USART1->CR3 |= USART_CR3_DMAR | USART_CR3_DMAT;
	USART1->SR &= ~(USART_SR_TC | USART_SR_RXNE);
	DMA2_Stream5->PAR = (uint32_t)&(USART1->DR);
	DMA2_Stream7->PAR = (uint32_t)&(USART1->DR);
	DMA2_Stream5->M0AR = (uint32_t)&BoxCon_RX_BUF;
	DMA2_Stream7->M0AR = (uint32_t)&BoxCon_TX_BUF;
	DMA2_Stream5->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_CIRC;
	DMA2_Stream7->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE;
	NVIC_EnableIRQ(DMA2_Stream7_IRQn);
	NVIC_SetPriority(DMA2_Stream7_IRQn, 0x04);
	NVIC_EnableIRQ(USART1_IRQn);
	NVIC_SetPriority(USART1_IRQn, 0x02);
};

int main(void) {
	InitVariables();
	NVIC_SetPriorityGrouping(3);			// Инициализация NVIC - 4 bits for pre-emption priority, 0 - subgroups
	SysInit();					// Настройка отладки, переключение на высокую тактовую частоту.
	DWT_Init();					// Настройка микросекундного таймера
	InitIO();					// Настройка портов IO
	InitUART();					// Настройка UART
	InitDMA();					// Настройка DMA
<...>
	xTaskCreate(vTaskHWCon,		( const char * ) "HWCon",	256,	NULL, 2, &Task_HWCon);
<...>	
	vTaskStartScheduler();
};

void USART1_IRQHandler(void) {
	if (USART1->SR & USART_SR_IDLE) {
		(void)USART1->DR;		// Очистка флага IDLE. Мне лично кажется такой подход странным.
		BOX_CON_RX_DONE = 1;
		if ((sizeof(BoxCon_RX_BUF) - DMA2_Stream5->NDTR) != DMA_BUF_START_LAST) {
			DMA_BUF_START[DMA_CURR_WR_BUF] = DMA_BUF_START_LAST;
			DMA_BUF_START_LAST = (sizeof(BoxCon_RX_BUF) - DMA2_Stream5->NDTR);
			DMA_BUF_END[DMA_CURR_WR_BUF] = DMA_BUF_START_LAST;
			DMA_CURR_WR_BUF++;
			if (DMA_CURR_WR_BUF >= (MAX_DMA_BUFFERS_COUNT)) { DMA_CURR_WR_BUF = 0; };
		};
	};
};

void vTaskHWCon (void *pvParameters) {
<...>
	USART1->CR1 |= USART_CR1_UE;		// Enable box console.
<...>
	while(1) {
<...>
		while (DMA_CURR_RD_BUF != DMA_CURR_WR_BUF) {
			CPU_LED_BLINK(CPU_LEDS_HWCON);
			b_start = DMA_BUF_START[DMA_CURR_RD_BUF];	// Копируем позиции начала
			b_end = DMA_BUF_END[DMA_CURR_RD_BUF];		// И конца буфера
			// Переводим на следующий (но не проверяем его данные)
			DMA_CURR_RD_BUF++;
			if (DMA_CURR_RD_BUF >= (MAX_DMA_BUFFERS_COUNT)) DMA_CURR_RD_BUF = 0;
<...>
		};
		BOX_CON_RX_DONE = 0; // Сбрасываем флаг.
		vTaskDelay(1); // Сообщаем Scheduler'у, что сейчас нам делать нечего.
	};
};

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

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

VladislavS писал(а):Вы это серьёзно?
конечно

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

while(*p) p++;
Вы делаете упор на тактовую частоту мк, возможно это и будет быстрее, чем uart выплеснет обычным способом на скорости, скажем 19200.
Если строку готовить с помощью sprintf, то она уже возвращает количество преобразованных символов.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f4 usart+DMA

Сообщение VladislavS »

Я делаю упор на думать головой. При частоте процессор 100 МГц один символ на скорости 19200 будет передаваться 52'000 тактов. Подсчёт длины строки 10-20 символов не больше 100 тактов. На такой скорости даже два символы выгоднее DMA передавать.

[uquote="Dimon456",url="/forum/viewtopic.php?p=4171596#p4171596"]Если строку готовить с помощью sprintf, то она уже возвращает количество преобразованных символов.[/uquote]И после этого на strlen экономить. Зашибись.
tonyk
Это не хвост, это антенна
Сообщения: 1309
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Re: stm32f4 usart+DMA

Сообщение tonyk »

Вы читаете, что вам пишут, а?

IDLE задуман как детектор ошибок приёма для Модбас. Устанавливается строго после 20 идущих подряд единичных бит в линии. Отсюда и выстраивайте логику работы.

Количество принятых байтов находится вычитанием из размера приёмного буфера (ты его загружаешь при настройке приёма) содержимого регистра CNDTR во время обработки прерывания. Не забудь сохранить его для обработчика.

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

// +1 из-за уменьшения sizeOfBuff для отлова кадров длиной
            // больше 256 байт.
            rxCnt = rx_n - ( dma_stream_rx -> NDTR );
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: stm32f4 usart+DMA

Сообщение Аlex »

tonyk писал(а):Вы читаете, что вам пишут, а?
Это Вы кому ?
tonyk писал(а):Устанавливается строго после 20 идущих подряд единичных бит в линии
Что-то я в РМ нигде не нахожу ничего про этот IDLE и условия его возникновения. Хоть бы диаграммку какую нарисовали.
Судя по всему, если мы будем ловить от него прерывания, то оно будет постоянно вылетать, через каждые 20 бит, пока на RX тишина. Это же жесть, дёргать постоянно проц без повода :roll:
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f4 usart+DMA

Сообщение VladislavS »

[uquote="Аlex",url="/forum/viewtopic.php?p=4171613#p4171613"]Что-то я в РМ нигде не нахожу ничего про этот IDLE и условия его возникновения. Хоть бы диаграммку какую нарисовали.[/uquote]Он есть не во всех контроллерах. Смотрите конкретно тот который применяете.

[uquote="Аlex",url="/forum/viewtopic.php?p=4171613#p4171613"]Судя по всему, если мы будем ловить от него прерывания, то оно будет постоянно вылетать, через каждые 20 бит, пока на RX тишина. Это же жесть, дёргать постоянно проц без повода :roll:[/uquote]Можно и без прерываний по IDLE, просто иногда подсматривать за битом состояния.
tonyk
Это не хвост, это антенна
Сообщения: 1309
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Re: stm32f4 usart+DMA

Сообщение tonyk »

Аlex писал(а):Судя по всему, если мы будем ловить от него прерывания, то оно будет постоянно вылетать, через каждые 20 бит, пока на RX тишина. Это же жесть, дёргать постоянно проц без повода :roll:
Ты RM почитай, а потом выводы делай.
Пауза анализируется после приёма очередного байта. Если после приёма байта прошло время в 20 битов, то установится IDLE, а если пришёл следующий байт, то IDLE будет лежать. Поэтому перед началом приёма через DMA, бит IDLE должен быть сброшен, а запрос прерывания от него IDLEIE- разрешён.

Никто проц не дёргает. Приём по Модбас- одно прерывание, отправка- два. И всё.
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: stm32f4 usart+DMA

Сообщение Аlex »

tonyk писал(а):Ты RM почитай, а потом выводы делай.
Я выводы сделал из твоих слов :
tonyk писал(а):Устанавливается строго после 20 идущих подряд единичных бит в линии.

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

Re: stm32f4 usart+DMA

Сообщение AlanDrakes »

[uquote="Аlex",url="/forum/viewtopic.php?p=4171613#p4171613"]Судя по всему, если мы будем ловить от него прерывания, то оно будет постоянно вылетать, через каждые 20 бит, пока на RX тишина. Это же жесть, дёргать постоянно проц без повода :roll:[/uquote]
Нет, аппратный флаг выставляется один раз, когда ловится IDLE состояние. И у меня по какой-то причине этот флаг поднимается через 10-15 интервалов после собственно конца сообщения, а не при приёме следующего байта.
Мне такой вариант нравится, хоть он и описан не так в RM.
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

VladislavS писал(а):Я делаю упор на думать головой.
VladislavS писал(а): При частоте процессор 100 МГц
Как я и сказал: Вы делаете упор на тактовую частоту мк.
VladislavS писал(а):И после этого на strlen экономить.
Будем рассматривать альтернативные варианты для каждого случая?
СпойлерДостаточно ответа - НЕТ
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f4 usart+DMA

Сообщение VladislavS »

[uquote="Dimon456",url="/forum/viewtopic.php?p=4171759#p4171759"]Как я и сказал: Вы делаете упор на тактовую частоту мк.[/uquote]Глупости. Я делаю упор на разгрузку процессора. И чем меньше его тактовая, тем это будет более заметно.

[uquote="Dimon456",url="/forum/viewtopic.php?p=4171759#p4171759"]Будем рассматривать альтернативные варианты для каждого случая?[/uquote]Будете натягивать сову на глобус? Одной универсальной и единственно правильной программы в природе не существует. Придётся ещё не раз включать голову и не только чтобы в неё есть, смиритесь.
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

VladislavS, весь глобус охватить конечно не возможно, хотя бы пару примерчиков разберем.
Вот первый примерчик:
СпойлерИмеем какой-то буфер, АЦП измерили, хочу вывести

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

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

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

void printu(uint32_t val)
{
    char bufa[11], bufb[10];
    int l = 0, bpos = 0;
    if(!val){
        bufa[0] = '0';
        l = 1;
    }else{
        while(val){
            bufb[l++] = val % 10 + '0';
            val /= 10;
        }
        int i;
        bpos += l;
        for(i = 0; i < l; ++i){
            bufa[--bpos] = bufb[i];
        }
    }
    bufa[l + bpos] = 0;
    usart_send_str(bufa);
}	
и сам вывод

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

void usart_send_str(char* str)
{
    while(*str) {
    while ((USART1->ISR & USART_ISR_TXE) == 0) {}
    	USART1->TDR = *str++;
    }
}
printu меня не интересует, хотел бы еще и отрицательный результат, ну да ладно.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: stm32f4 usart+DMA

Сообщение VladislavS »

Что ты этими "примерчиками" хотел сказать? "Я не умею программировать" пишется сильно короче.
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: stm32f4 usart+DMA

Сообщение Dimon456 »

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

Re: stm32f4 usart+DMA

Сообщение AlanDrakes »

[uquote="Dimon456",url="/forum/viewtopic.php?p=4172203#p4172203"]Как ваш dma сюда прикрутить?[/uquote]
Используете буфер вывода. В него ПИШЕТЕ текстом, запускаете DMA, который будет читать данные из буфера вывода и пихать их в USARTx->DR.
Что сложного-то?
Пример кольцевого буфера привести? Их есть у меня.
Спойлер

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

void tty0_ActivateDMA(void) {
	uint16_t CurrWrPos = tty0_WR_POS;
	uint16_t DataToSend;
	DataToSend = 0;
	if(!(DMA_STATE & DMA_tty0_TX_ACTIVE)) {
		DMA1_Stream3->CR &= ~DMA_SxCR_EN;	// Отключаем поток DMA
		if (tty0_TX_POS != CurrWrPos) {
			// Если не совпадает - значит, данные есть. Или малый шанс на переполнение буфера.
			DMA1_Stream3->M0AR = (uint32_t)&(tty0_TX_BUF[tty0_TX_POS]);
			if (tty0_TX_POS < CurrWrPos) {
				// Нет перехода через конец буфера
				DataToSend = (CurrWrPos - tty0_TX_POS);
				tty0_TX_POS = CurrWrPos;
			} else {
				// Нужно сделать кольцо.
				DataToSend = (sizeof(tty0_TX_BUF) - tty0_TX_POS);
				tty0_TX_POS = 0;
			};
			DMA1_Stream3->NDTR = DataToSend;
			DMA1_Stream3->FCR = 0;
			USART3->SR = ~(USART_SR_TC);
			// И только ПОСЛЕ этого включаем его. Да, странность. Но иначе он уходит в ошибку.
			DMA1_Stream3->CR |= DMA_SxCR_EN;
		};
	};	// Если активен - сработает при вызове события завершения обмена.
};

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

int main(void) {
<...>
	InitIO();					// Настройка портов IO
	InitUART();					// Настройка UART
	InitDMA();					// Настройка DMA

	console_timestamp();
	console_put("System Frequency: ");
	console_put_int(SystemCoreClock / 1000000);
	console_put("MHz\r\n");
	console_timestamp();
	console_put("Initializing RTC.\r\n");
};
Функции типа console_put_int(uint32_t value), console_timestamp() и некоторые другие - просто подготавливают данные во временном буфере и передают их в основную функцию console_put(char *text). А уже та - копирует данные в кольцевой буфер и запускает отправку данных через DMA (если уже не запущена). Если запущена - не перезапускает, только обновляя ГЛОБАЛЬНЫЕ индексы буфера, до куда были записаны данные.
Ответить

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