Перейти к содержанию
    

stm32f100 USART задержка передачи и потеря символов

Кристалл stm32f100. Плата Discovery.

 

Вижу, что передача по USART начинается с задержкой. Т е после записи в DR, на ножке процессора посылка появляется примерно через время равное передачи байта (без стопов и паритета) на выбранной скорости. Для 115200 физически передача начинается через 8 микросекунд, для 19200 - через 400, для 1200 - через 10 миллисекунд. Т е такое впечатление, что после записи в DR автомат вхолостую отрабатывает сдвиг регистра ничего не выдавая на ножку и после одного холостого цикла уже забирает данные из DR.

 

При одновременной работе нескольких портов теряется первый или второй байт в передаваемой последовательности или даже первый со вторым переставляются местами (во всех портах или в каком то одном , картина может меняться при изменении кода). Причем, даже в длинной последовательности все остальные байты передаются нормально. Это возникает при как при одновременной работе нескольких портов так и если оставить один порт.

 

 

Инициирование USART

 

//Инициируем UARTы

void USART1_INI(unsigned char spd, unsigned char par, unsigned char stp)
//spd - скорости обмена
// 10  115200
// 9	57600
// 8	38400
// 7	19200
// 6	9600
// 5	4800
// 4	2400
// 3	1200
// 2	600
//par -  паритет
// 0  без паритета
// 1  нечетный паритет
// 2  четный паритет
//stp -  число стоп битов
// 0, 1  - 1
// 2 - 2
//
//
//
{
GPIO_InitTypeDef GPIO_InitStructure;
 short unsigned int i;



//*********************************************************	
//Конфигурируем ножки под ввод вывод


//включаем тактирование портов
RCC->APB2ENR|=RCC_APB2ENR_IOPAEN;//GPIOA


//TX1 PA9 
//Ножка назначается на вывод пуш пуль
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	GPIO_Init( GPIOA , &GPIO_InitStructure);	
//RX1	PA10
//Ножка назначается на ввод 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	GPIO_Init( GPIOA , &GPIO_InitStructure);	

USART1->CR1=0;
	USART1->CR1|=USART_CR1_UE;//включили UART
	USART1->CR1&=~USART_CR1_M;//8 бит
	USART1->CR1&=~USART_CR1_WAKE;// пробуждение по IDLE LINE

//if(par==0){
	   USART1->CR1&=~USART_CR1_PCE;//без паритета
		   USART1->CR1&=~USART_CR1_PS;// нечетный паритет
				 USART1->CR1&=~USART_CR1_M;//8 бит
//		 }
if(par==1){
	   USART1->CR1|=USART_CR1_PCE;//паритет
		   USART1->CR1|=USART_CR1_PS;// нечетный паритет
		 USART1->CR1|=USART_CR1_M;//9 бит
	 }
if(par==2){
		USART1->CR1|=USART_CR1_PCE;//паритет
			USART1->CR1&=~USART_CR1_PS;// четный паритет
		  USART1->CR1|=USART_CR1_M;//9 бит
	 }



	USART1->CR1|=USART_CR1_PEIE;//общее разрешение прерывания
//	USART1->CR1|=USART_CR1_TXEIE;//разрешение прерывания по опустошению буфера передачи 
	USART1->CR1&=~USART_CR1_TXEIE;//запрещение прерывания по опустошению буфера передачи 
	USART1->CR1&=~USART_CR1_TCIE;//запрещено прерывание от "передача закончена"
  //USART1->CR1|=USART_CR1_TCIE;// прерывание от "передача закончена"
	USART1->CR1|=USART_CR1_RXNEIE;// разрешение прерывания по приему
	USART1->CR1&=~USART_CR1_IDLEIE;//запрещено прерывание от IDLE
	USART1->CR1|=USART_CR1_TE;//передатчик включен
	USART1->CR1|=USART_CR1_RE;//приемник включен
	USART1->CR1&=~USART_CR1_RWU;//приемник в активном режиме
	USART1->CR1&=~USART_CR1_SBK;//запрос передачи длинного 0 выключен

	USART1->CR2=0;
//if(stp<=1){//1  стоп
			   USART1->CR2&=~USART_CR2_STOP;//1 стоп
//		  }
if(stp==2){//2  стоп
			   USART1->CR2&=~USART_CR2_STOP;//1 стоп
	   USART1->CR2|=USART_CR2_STOP_1;//2 стоп
	   }

	USART1->CR3=0;

	USART1->SR&=0;//Флаги всех прерываний сбросили


USART1->BRR=0X0480;//19200
if(spd==10){					 
		USART1->BRR=0X00C0;//115200
	   }

// готовим буферы обмена

//#define rx_buf_long  256
//#define tx_buf_long  256


i=0;
while(i<rx_buf_long){
						 rx_buf1[i]=0;
						 i++;
						}
	i=0;
while(i<tx_buf_long){
						 tx_buf1[i]=0;
						 i++;
						}

USART1->SR=0;//СБРАСЫВАЕМ ФЛАГИ
// CTS
// LBD
// TC
// RXNE
// IDLE
// NE
// 		 										

rx_ptr1=0;
tx_ptr1=0;
//индекс последнего передаваемого элемента буфера
tx_last1=0;

	rx_state1=0;
tx_state1=0;

}

 

Само прерывание от одного порта

 

void USART1_IRQHandler(void)
{
//прием
if((USART1->SR&USART_SR_RXNE)!=0) {//что то пришло
	USART1->SR&=~USART_SR_RXNE;//сбросим флаг
	rx_buf1[rx_ptr1]=USART1->DR;
	if(rx_ptr1<rx_buf_long){
		rx_ptr1++;
	}

}//что то пришло

//передача

if((USART1->SR&USART_SR_TC)!=0) {//данные переписались из буфера в сдвиговый регистр	и ушли из него
if(tx_state1==1){//идет передача
	USART1->SR&=~USART_SR_TC;//сбросим флаг
	if(tx_ptr1>=tx_last1){//ушел последний байт
		tx_state1=0;
		GPIOC->ODR &= ~GPIO_ODR_ODR4;// смотрим этой ножкой момент, когда закончилась передача
		USART1->CR1&=~USART_CR1_TCIE ;
	}//ушел последний байт
	else{//передаем дальше
		tx_ptr1++;
		USART1->DR=tx_buf1[tx_ptr1];
	}//передаем дальше
}//идет передача
else{//фантомное прерывание- мы ничего не должны передавать
	USART1->CR1&=~USART_CR1_TCIE ;//сбросим разрешение этого прерывания
}//фантомное прерывание- мы ничего не должны передавать
}//данные переписались из буфера в сдвиговый регистр
USART1->SR=0;

}

 

 

и затравка которая начинает передачу:

 

 InitGPIO();
Init_OSC_my();//инициализация и переключение синхронизации

USART1_INI(3,2,2);//1200







tx_buf1[0]=0x30;
tx_buf1[1]=0x31;
tx_buf1[2]=0x32;
tx_buf1[3]=0x33;
tx_buf1[4]=0x34;
tx_buf1[5]=0x35;
tx_buf1[6]=0x36;
tx_buf1[7]=0x37;
tx_buf1[8]=0x38;
tx_buf1[9]=0x39;
tx_buf1[10]=0x41;
tx_buf1[11]=0x42;
tx_buf1[12]=0x43;
tx_buf1[13]=0x44;
tx_buf1[14]=0x45;
tx_buf1[15]=0x46;
tx_buf1[16]=0x47;
tx_buf1[17]=0x48;
tx_buf1[18]=0x49;
tx_buf1[19]=0x4a;
tx_buf1[20]=0x4b;

tx_state1=1;
tx_last1=20;
tx_ptr1=0;


__NOP();



USART1->CR1|=USART_CR1_TCIE ;
GPIOC->ODR |= GPIO_ODR_ODR4;// между выставлением этой ножки и началом передачи на ножке - выдержка времени
USART1->DR=tx_buf1[tx_ptr1];

Изменено пользователем IgorKossak
[codebox] для длинного кода, [code] - для короткого!!! форматирование

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На кристалле STM32F107 работает следующий код:

uint8_t UART4_TX_BUFFER[uART_TX_BUFFER_SIZE];		// Буфер передачи UART'
volatile uint16_t UART4_TX_POS, UART4_WR_POS;		// Указатели буфера.

void DMA2_Channel5_IRQHandler(void) {
if (DMA2->ISR & DMA_ISR_TCIF5) {
	DMA2->IFCR = DMA_IFCR_CTCIF5;			// Сбрасываем бит прерывания.
	DMA_STATE &= ~DMA_STATE_UART_ACTIVE;
	UART4_ActivateDMA();
};
};
void init_DMA_uart(void) {
RCC->AHBENR |= RCC_AHBENR_DMA2EN;
UART4->CR3 |= USART_CR3_DMAT;
UART4->SR &= ~USART_SR_TC;
DMA2_Channel5->CCR = 0;
DMA2_Channel5->CPAR = (uint32_t)&(UART4->DR);
NVIC_EnableIRQ(DMA2_Channel5_IRQn);
NVIC_SetPriority(DMA2_Channel5_IRQn, 0x04);
};

void init_uart(void) {
SystemCoreClockUpdate();			// Обновляем рабочую частоту.
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;		// Включаем тактирование порта с UART
GPIOC->ODR |= 0x00000C00;			// Поднимаем пины.
GPIOC->CRH |= 0x0000FF00;			// GPIOC[10:11] -> 50MHz, OpenDrain Alternate.
RCC->APB1ENR |= RCC_APB1ENR_UART4EN;		// Включаем UART4
UART4->CR2 = 0;					// Настройки. Сбрасываем.
UART4->CR3 = 0;					// Продолжаем.
if (RCC->CFGR & RCC_CFGR_PPRE1_DIV2) {
	UART4->BRR = (SystemCoreClock / 115200 / 2);	// Ибо делим на 2
} else {
	UART4->BRR = (SystemCoreClock / 115200);	// Или не делим.
};
UART4->CR1 = (USART_CR1_TE | USART_CR1_RE | USART_CR1_UE);	// Разрешаем работу UART.
init_DMA_uart();
};

void UART4_ActivateDMA(void) {
uint16_t CURR_WR_POS = UART4_WR_POS;
if (!(DMA_STATE & DMA_STATE_UART_ACTIVE)) {
	DMA2_Channel5->CCR &= ~DMA_CCR_EN;
	if (UART4_TX_POS == CURR_WR_POS) {
		// Собственно, позиции начала и конца совпали.
	} else {
		// Есть разница между позициями буфера. Значит, нужно передавать данные.
		// Начальная точка передачи - текущее значение указателя TX.
		DMA2_Channel5->CMAR = (uint32_t)&(UART4_TX_BUFFER[(UART4_TX_POS)]);	// Указатель на память
		if (UART4_TX_POS < CURR_WR_POS) {
			// Линия
			DMA2_Channel5->CNDTR = (CURR_WR_POS - UART4_TX_POS);		// Как раз нужная разница.
			UART4_TX_POS = CURR_WR_POS;
		} else {
			// Кольцо
			DMA2_Channel5->CNDTR = (UART_TX_BUFFER_SIZE - UART4_TX_POS);
			UART4_TX_POS = 0;
		};
		DMA_STATE |= DMA_STATE_UART_ACTIVE;
		DMA2_Channel5->CCR = (DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE | DMA_CCR_EN);
	};
};
};

void console_put(char *text) {
// Можно попытаться написать под реализацию на двух каналах DMA... Тогда будет ещё быстрее.
while(*text) {
	// Пока не нуль-терминатор
	UART4_TX_BUFFER[uART4_WR_POS] = *text;		// Копируем данные в буфер
	text++;						// Сдвигаем указатель текста.
	UART4_WR_POS++;					// Сдвигаем указатель на 1 байт дальше.
	UART4_WR_POS = UART4_WR_POS & UART_TX_MASK;	// Указатель должен быть в пределах допустимых значений.
};
// Здесь всё хорошо, записываем от старой позиции до новой.
UART4_ActivateDMA();
};

Используется DMA и буфер передачи (32/64/128 и более байт).

Данные не теряются при передаче.

Изменено пользователем IgorKossak
[codebox] для длинного кода, [code] - для короткого!!!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да не за что.

Хотя, я просмотрел ваш код и тоже не нашёл никаких грубых ошибок. По идее, должны уходить нормально.

И учтите, я использую заголовочники от HAL, но сам стараюсь писать больше на регистрах.

А строки - нуль-терминированые.

Пример

uint8_t TXT_VAL = "Hello";

'H','e','l','l','o',/0

Ноль после последнего байта обязателен. Ну это так, на всякий случай.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ну это важно если мы каким то подпиленным sprintf работаем. так я указываю индекс последнего символа в массиве который должен уйти на передачу. да я бы и не заметил подвоха если бы не стал внимательно смотреть что уходит и тестить в условиях одновременной работы трех портов.

 

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

Изменено пользователем IgorKossak
бездумное цитирование

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А нуль-терминированая строка может оказаться удобнее. Для функции передачи нужен только один параметр в стэке - позиция начала строки. Всё, что до нулевого символа будет скопировано в буфер отправки и передано.

Кстати, в моём коде - только отправка. Приём не требовался, потому под него ничего не писалось.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

это если передаем символы. последний раз у меня это было году так в 99. а дальше модбас RTU. там в пределах строки все что угодно может быть.

Изменено пользователем IgorKossak
бездумное цитирование

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

О, сколько раз уже можно просить читать документацию....

 

//прием
    if((USART1->SR&USART_SR_RXNE)!=0) {//что то пришло
        USART1->SR&=~USART_SR_RXNE;//сбросим флаг
        rx_buf1[rx_ptr1]=USART1->DR;

 

Бит RXNE It is cleared by a read to the USART_DR register

Сбрасывая его "вручную", рискуете сбросить лишнее.

Кроме того, сбрасывать надо так:

USART1->SR = ~ USART_SR_TC;

 

А не так, как у Вас:

 

USART1->SR&=~USART_SR_TC;

 

Разницу увидите сами (представьте себе, что флаг приёма символа появился после чтения SR, но перед обратной записью в этой строке).

На приёме тем более - гасятся прерывания от передатчика таким образом.

Изменено пользователем Genadi Zawidowski

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

firstvald, при ответе на крайнее сообщение нет нужды в цитировании. Это только загромождает форум.

Модератор

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

 

бит в регистре во все времена сбрасывался именно так:

регистр &=~ (слово размером с регистр с единичным выставленным битом);

 

хотя действительно предлагаемая функция USART_ClearFlag (из STM32F10X_USART.C)для сброса использует конструкцию

USARTx->SR = (uint16_t)~USART_FLAG; но это скорее исключение(довольно безграмотное), которое вероятно работает благодаря особенностям регистра SR.

 

 

 

При поочередной передаче флаг приема не может появится после попадания в тело прерывания, иначе как бы мы туда попали? и что вообще бы вызвало прерывание? это могло бы быть теоретически в случает одновременной передачи и приема, но обычный обмен с приборами такого не предполагает, идет запрос ответная работа. и при запросно -ответной работе появление прерывания от приема в момент передачи считается мусором и такие символы должны игнорироваться. аналогично появление прерывания от передачи в момент приема является мусором и его нужно игнорировать и выключить если оно по каким то причинам включилось.

 

на практике при передаче в приемник действительно может попадать мусор и в некоторых вариантах обмена передаваемая строка. во всех случаях эти символы должны игнорироваться.

 

посмотрел конструкцию

 

USART3->SR=~USART_SR_RXNE;//**сбросим флаг

 

тоже работает. но рекомендовать использовать это я бы никому не стал

Изменено пользователем IgorKossak
избыточное цитирование

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При поочередной передаче флаг приема не может появится после попадания в тело прерывания, иначе как бы мы туда попали

Попали по поводу прерывания от примника, а сбросили флаг передатчика. Как он появился там? Да потому, что передатчик передваал-передавал себе символ, да и передал.

 

Если не хотите, можете продолжать... и считать допустимой работу "иногда". Как бит в переменно сбрасывается это одно, а как сбросить запрос прерывания в регистре с особенным поведением, это другое. Ситуацию со сбросом лишнего я Вам нарисовал.

 

Изменено пользователем Genadi Zawidowski

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

да нет. по флагу от передатчика сбрасывается флаг передатчика и по флагу приемника сбрасывается флаг приемника. а если кто остался несброшеным - сбросится перед выходом из прерывания.

 

я нигде не встречал указания, чтобы для сброса битов регистра SR требовался особый синтаксис, нигде больше не применяемый.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Изменено пользователем Genadi Zawidowski

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

да сбросятся, но они мне тут не нужны . не в приеме дело - ту эффект вот какой:

 

я вижу вот что. пробую передать строку из 3 символов (0x30 0x31 0x32 ).

 

что происходит:

по записи (я все поотключал - таймеры и уарты- оставил только этот уарт) я сейчас с 3 работаю - там код одинаковый

USART3->DR=tx_buf3[tx_ptr3];

на ножку вовсе ничего не передается, но возникает прерывание с выставлением TC . у меня в прерывании честно указатель буфера инкрементируется и в регистр DR записывается следующий байт т е 1. и вот он появляется на ножке. а после выхода из прерывания на передачу отправляется 0 байт из буфера! т е в линии оказывается что нулевой и первый байты оказываются переставленными местами! а вот начиная со следующего прерывания последовательность передачи - правильная.

 

и я вижу что уходит 0x31 0x30 0x32

 

вот что происходит в начале - почему переставляется 0 и первый байт местами я понять не могу.

 

и спасибо огромное за внимание!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

acc = SR 0x01
interrupt!
SR=0x03 - появился дополнительный бит
acc = acc & 0xfe = 0x01 & 0xfe = 0x00

SR = 0x00 - сбрасывабтся все биты вообще

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...