Skocz do zawartości

[C] Problem z transmisja szeregowa Mega32


simoon87

Pomocna odpowiedź

Witam buduje pewnego rodzaju manipulator, szczerze to jest on dość dziwny 😉 no ale mniejsza o to... Mianowicie zachciało mi się przesyłać wartość z czujników, oraz pozycje serw przez RS232 do komputera. Mam problem gdyż transmisja danych nie wygląda tak jak bym chciał. Jak widać na poniższym screenie na początku każdej linijki w terminalu widnieje 7 znaków z poprzedniej linii a w przedziale czasu między kolejną serią danych jest znowu powielone kilka ostaniach znaków i teraz pytanie do Was jak sobie z tym poradzić?

Plik usart.h:

#ifndef UART_H_
#define UART_H_


#define UART_BAUD 9600					// tu definiujemy interesującą nas prędkość
#define __UBRR F_CPU/16/UART_BAUD-1  	// obliczamy UBRR dla U2X=0

// definicje na potrzeby RS485
#define UART_DE_PORT PORTD
#define UART_DE_DIR DDRD
#define UART_DE_BIT (1<<PD2)

#define UART_DE_ODBIERANIE  UART_DE_PORT |= UART_DE_BIT
#define UART_DE_NADAWANIE  UART_DE_PORT &= ~UART_DE_BIT


#define UART_RX_BUF_SIZE 32 // definiujemy bufor o rozmiarze 32 bajtów

#define UART_RX_BUF_MASK (UART_RX_BUF_SIZE - 1)	// definiujemy maskę dla naszego bufora

#define UART_TX_BUF_SIZE 16 // definiujemy bufor o rozmiarze 16 bajtów

#define UART_TX_BUF_MASK (UART_TX_BUF_SIZE - 1)	// definiujemy maskę dla naszego bufora




// deklaracje funkcji publicznych

void USART_Init(uint16_t baud);

char uart_getc(void);
void uart_putc(char data);
void uart_puts(char *s);
void uart_putint(int value, int radix);

Plik usart.c:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>


#include "usart.h"


// definiujemy w końcu nasz bufor UART_RxBuf
volatile char UART_RxBuf[UART_RX_BUF_SIZE];
// definiujemy indeksy określające ilość danych w buforze
volatile uint8_t UART_RxHead; // indeks oznaczający „głowę węża”
volatile uint8_t UART_RxTail; // indeks oznaczający „ogon węża”



// definiujemy w końcu nasz bufor UART_RxBuf
volatile char UART_TxBuf[UART_TX_BUF_SIZE];
// definiujemy indeksy określające ilość danych w buforze
volatile uint8_t UART_TxHead; // indeks oznaczający „głowę węża”
volatile uint8_t UART_TxTail; // indeks oznaczający „ogon węża”


void USART_Init(uint16_t baud) 
{
/* Ustawienie prędkości */
UBRRH = (uint8_t)(baud>>8);
UBRRL = (uint8_t)baud;
/* Załączenie nadajnika I odbiornika */
UCSRB = (1<<RXEN)|(1<<TXEN);
/* Ustawienie format ramki: 8bitów danych, 1 bit stopu */
UCSRC = (1<<URSEL)|(3<<UCSZ0);

// jeśli korzystamy z interefejsu RS485
#ifdef UART_DE_PORT
	// inicjalizujemy linię sterującą nadajnikiem
	UART_DE_DIR |= UART_DE_BIT;
	UART_DE_ODBIERANIE;
#endif

// jeśli korzystamy z interefejsu RS485
#ifdef UART_DE_PORT
	// jeśli korzystamy z interefejsu RS485 załączamy dodatkowe przerwanie TXCIE
	UCSRB |= (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE);
#else
	// jeśli nie  korzystamy z interefejsu RS485
	UCSRB |= (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
#endif
}

// procedura obsługi przerwania Tx Complete, gdy zostanie opóżniony UDR
// kompilacja gdy używamy RS485
#ifdef UART_DE_PORT

ISR(USART_TXC_vect)
{
UART_DE_PORT &= ~UART_DE_BIT;	// zablokuj nadajnik RS485
}
#endif


// definiujemy funkcję dodającą jeden bajtdoz bufora cyklicznego
void uart_putc(char data)
{
uint8_t tmp_head;

   tmp_head  = (UART_TxHead + 1) & UART_TX_BUF_MASK;


   while (tmp_head == UART_TxTail){}	// pętla oczekuje jeżeli brak miejsca w buforze cyklicznym na kolejne znaki

   UART_TxBuf[tmp_head] = data;
   UART_TxHead = tmp_head;

   // inicjalizujemy przerwanie występujące, gdy bufor jest pusty, dzięki
   // czemu w dalszej części wysyłaniem danych zajmie się już procedura
   // obsługi przerwania
   UCSRB |= (1<<UDRIE);
}


void uart_puts(char *s)		// wysyła łańcuch z pamięci RAM na UART
{
	register char c;
	while ((c = *s++)) uart_putc(c);	// dopóki nie napotkasz 0 wysyłaj znak
uart_putc('\n');					//znak konca lini			
}

void uart_putint(int value, int radix)	// wysyła na port szeregowy tekst
{
char string[17];			// bufor na wynik funkcji itoa
itoa(value, string, radix);		// konwersja value na ASCII
uart_puts(string);			// wyślij string na port szeregowy
}


// definiujemy procedurę obsługi przerwania nadawczego, pobierającą dane z bufora cyklicznego
ISR(USART_UDRE_vect) 
{
   // sprawdzamy czy indeksy są różne
   if (UART_TxHead != UART_TxTail)
{
   	// obliczamy i zapamiętujemy nowy indeks ogona węża (może się zrównać z głową)
   	UART_TxTail = (UART_TxTail + 1) & UART_TX_BUF_MASK;
   	// zwracamy bajt pobrany z bufora  jako rezultat funkcji
   	UDR = UART_TxBuf[UART_TxTail];
   } 
else 
{
	// zerujemy flagę przerwania występującego gdy bufor pusty
	UCSRB &= ~(1<<UDRIE);
   }
}


// definiujemy funkcję pobierającą jeden bajt z bufora cyklicznego
char uart_getc(void) 
{
   // sprawdzamy czy indeksy są równe
   if (UART_RxHead == UART_RxTail) return 0;

   // obliczamy i zapamiętujemy nowy indeks „ogona węża” (może się zrównać z głową)
   UART_RxTail = (UART_RxTail + 1) & UART_RX_BUF_MASK;
   // zwracamy bajt pobrany z bufora  jako rezultat funkcji
   return UART_RxBuf[UART_RxTail];
}

// definiujemy procedurę obsługi przerwania odbiorczego, zapisującą dane do bufora cyklicznego
ISR(USART_RXC_vect)
{
   uint8_t tmp_head;
   char data;

   data = UDR; //pobieramy natychmiast bajt danych z bufora sprzętowego

   // obliczamy nowy indeks „głowy węża”
   tmp_head = (UART_RxHead + 1) & UART_RX_BUF_MASK;

   // sprawdzamy, czy wąż nie zacznie zjadać własnego ogona
   if (tmp_head == UART_RxTail) 
{
   	// tutaj możemy w jakiś wygodny dla nas sposób obsłużyć  błąd spowodowany
   	// próbą nadpisania danych w buforze, mogłoby dojść do sytuacji gdzie
   	// nasz wąż zacząłby zjadać własny ogon
   } 
else 
{
	UART_RxHead = tmp_head; 		// zapamiętujemy nowy indeks
	UART_RxBuf[tmp_head] = data; 	// wpisujemy odebrany bajt do bufora
   }
}

Fragment main.c odpowiedzialny za wysyłanie danych:

	if(Timer0_Temp >= 250)
	{
		Timer0_Stop();
		Timer0_Temp = 0;


		//Wyslanie wartosci ADC do PC
		uart_puts("FT1 = ");
		uart_putint(Sensor_Value[0], 10);
		uart_puts("; FT2 = ");
		uart_putint(Sensor_Value[1], 10);
		uart_puts("; FT3 = ");
		uart_putint(Sensor_Value[2], 10);
		uart_puts("; FT4 = ");
		uart_putint(Sensor_Value[3], 10);
		uart_puts("; Vbat = ");
		uart_putint(Sensor_Value[4], 10);

		//Wyslanie wartosci pozycji Serw
		uart_puts("; pX = ");
		uart_putint(pX, 10);
		uart_puts("; pY = ");
		uart_putint(pY, 10);
		uart_putc('\r');
		uart_putc('\n');

		Timer0_Start();
		LIVE_TOGGLE;
	}

ISR(TIMER0_COMP_vect)
{
Timer0_Temp++;
}
Link do komentarza
Share on other sites

"Łatwiej", a na pewno skuteczniej byłoby, gdybyś transmisję zrealizował binarnie, nie przez ASCII i stworzył jakąś ramkę danych - chociażby start, dane, crc, stop. Unikniesz wtedy błędów, jeśli chcesz tym bawić się na poważniej.

Link do komentarza
Share on other sites

"Łatwiej", a na pewno skuteczniej byłoby, gdybyś transmisję zrealizował binarnie, nie przez ASCII i stworzył jakąś ramkę danych - chociażby start, dane, crc, stop. Unikniesz wtedy błędów, jeśli chcesz tym bawić się na poważniej.

Jeśli dobrze Ciebie zrozumiałem to w takim wypadku musiałbym stworzyć dodatkowo aplikacje na PC która będzie odbierać dane i odpowiadać. A mi chodzi o coś bardziej uniwersalnego mianowicie o podejrzenie danych byle jakim terminalem ;/ Ponadto coś skopałem chyba z buforem cyklicznym bo jak zmienię jego "pojemność" to albo dostaje mniej znaków z końca linii albo dostaje "ucięte dane"

Link do komentarza
Share on other sites

simoon87, tak byłoby najlepiej. Inna transmisja jest niesie za sobą spore ryzyko błędu, ale skoro piszesz, że masz bufory to jednak nadawanie powinno działaś też w miarę poprawnie w sposób jaki próbujesz je zrealizować.

Rozumiem, że masz bufor cykliczny i nadajesz na przerwaniach, tak?

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Też na początku myślałem, że coś z buforami cyklicznymi jest nie tak. Nawet zacząłem pisać w nocy "odkrywczą" odpowiedź ale jak przyszło do szczegółowego opisu błędu to wyszło, że to.. działa. No to wstawiłem Twój kod do procesora i.. też działa. Oczywiście okroiłem na szybko z odbioru i obsługi RS485 pozostawiając tylko samo jądro produkujące teksty, buforowanie cykliczne i wysyłanie w przerwaniach. Kod załączam dla pewności. W celu skrócenia pozwoliłem sobie usunąć komentarze.

#define	F_CPU	8000000UL

#include <avr/io.h> 
#include <avr/interrupt.h> 
#include <stdlib.h> 
#include <util/delay.h> 

#define UART_BAUD 9600                    // tu definiujemy interesującą nas prędkość 
#define __UBRR F_CPU/16/UART_BAUD-1      // obliczamy UBRR dla U2X=0 

#define UART_RX_BUF_SIZE 32 // definiujemy bufor o rozmiarze 32 bajtów 
#define UART_RX_BUF_MASK (UART_RX_BUF_SIZE - 1)    // definiujemy maskę dla naszego bufora 
#define UART_TX_BUF_SIZE 16 // definiujemy bufor o rozmiarze 16 bajtów 
#define UART_TX_BUF_MASK (UART_TX_BUF_SIZE - 1)    // definiujemy maskę dla naszego bufora 


volatile char UART_TxBuf[UART_TX_BUF_SIZE]; 

volatile uint8_t UART_TxHead; // indeks oznaczający „głowę węża” 
volatile uint8_t UART_TxTail; // indeks oznaczający „ogon węża” 

void USART_Init(uint16_t baud) 
{ 
   UBRRH = (uint8_t)(baud>>8); 
   UBRRL = (uint8_t)baud; 
   UCSRB = (1<<RXEN)|(1<<TXEN); 
   UCSRC = (1<<URSEL)|(3<<UCSZ0); 
   UCSRB |= (1<<RXEN)|(1<<TXEN)|(1<<RXCIE); 
} 

void uart_putc(char data) 
{ 
   uint8_t tmp_head; 

   tmp_head  = (UART_TxHead + 1) & UART_TX_BUF_MASK; 
   while (tmp_head == UART_TxTail){}    // pętla oczekuje jeżeli brak miejsca w buforze cyklicznym na kolejne znaki 

   UART_TxBuf[tmp_head] = data; 
   UART_TxHead = tmp_head; 
   UCSRB |= (1<<UDRIE);
} 

void uart_puts(char *s)        // wysyła łańcuch z pamięci RAM na UART 
{ 
    register char c; 
    while ((c = *s++)) uart_putc(c);    // dopóki nie napotkasz 0 wysyłaj znak 
//    uart_putc('\n');                    //znak konca lini            
} 

void uart_putint(int value, int radix)    // wysyła na port szeregowy tekst 
{ 
   char string[17];            // bufor na wynik funkcji itoa 
   itoa(value, string, radix);        // konwersja value na ASCII 
   uart_puts(string);            // wyślij string na port szeregowy 
} 

ISR(USART_UDRE_vect) 
{ 
   if (UART_TxHead != UART_TxTail) 
   { 
       UART_TxTail = (UART_TxTail + 1) & UART_TX_BUF_MASK; 
       UDR = UART_TxBuf[UART_TxTail]; 
   } 
   else 
   { 
       UCSRB &= ~(1<<UDRIE); 
   } 
} 

int main(void)
{
uint8_t n=0;

io_ports_init();
USART_Init(__UBRR);
sei();

while(1)
{
	_delay_ms(500);

	uart_puts("Vbat = "); 
	uart_putint(82, 10); 

	//Wyslanie wartosci pozycji Serw 
	uart_puts("; pX = "); 
	uart_putint(n, 10); 
	uart_puts("; pY = "); 
	uart_putint(n+15, 10); 
	uart_putc('\r'); 
	uart_putc('\n');

	n++;
}
}

Jeśli masz takie dziwne objawy działania, to albo masz jakieś efekty uboczne z kodu którego nie pokazałeś albo może problem tkwi w samym terminalu? Spróbuj starego dobrego Hyperterminala z Windows. Programu Br@a'y kiedyś używałem w pewnym szczegółnym przypadku (większy ekran niż typowe 25x80) ale HT nigdy mnie nie zawiódł. Poniżej zrzut z ekranu, "magia kolorów" z powodu przekształcenia do gifa:

HT1.thumb.GIF.8c9253e501bff46b61bb165c91b798ac.GIF

Link do komentarza
Share on other sites

Tak, nadaje w przerwaniach (szczegóły w pierwszym poście usart.c i usart.h).

Przetestowałem kilka terminali mianowicie UART Term, Realterm i wcześniejszą wersję Br@a'y wciąż to samo, zastanawiam się czy nie jest to jakiś problem sprzętowy ale sprawdziłem to na 2 komputerach pierwszy z Win7 Pro x64 a drugi z Win XP x86 Pro.

W samym programie nie mam nic niezwykłego mianowicie pomiar z pięciu czujników ADC z uśrednianiem wartości plus funkcja do ustawiania serw za pomocą Pwm Timera 1. Wszystko działa bez zarzutu poza tym UARTem 😕

Spróbuje jeszcze wymienić FT232RL może to coś da...

Link do komentarza
Share on other sites

Sprawdziłem trop marek1707 wyrzuciłem z programu wszystko poza wysyłaniem danych i przerwaniem od Timera0 i okazało się ze transmisja jest ok. Załączam całość dotychczasowego pliku main.c możę ktoś podpowie mi gdzie mam błąd 😕

Plik main.c

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include "adc.h"
#include "usart.h"
#include "config.h"

volatile char Sensor_Value[5];
volatile int pX = 1500, pY = 1500;

int main(void)
{
LIVE_OUT;
LINE_OUT = 0xFF;
LINE_PORT = 0xFF;

PWM_Init();
ADC_init();
Timer0_Init();
Timer0();
		Timer0_Temp = 0;


		//Wyslanie wartosci ADC do PC
		uart_puts("FT1 = ");
		uart_putint(Sensor_Value[0], 10);
		uart_puts("; FT2 = ");
		uart_putint(Sensor_Value[1], 10);
		uart_puts("; FT3 = ");
		uart_putint(Sensor_Value[2], 10);
		uart_puts("; FT4 = ");
		uart_putint(Sensor_Value[3], 10);
		uart_puts("; Vbat = ");
		uart_putint(Sensor_Value[4], 10);
		//Wyslanie wartosci pozycji Serw
		uart_puts("; pX = ");
		uart_putint(pX, 10);
		uart_puts("; pY = ");
		uart_putint(pY, 10);
		uart_puts("\r\n");


		Timer0(void)
{
TCCR0 &= ~(1<<CS02);
TCNT0 = 0;
}

ISR(TIMER0_COMP_vect)
{
Timer0_Temp++;
}

Plik config.h

#ifndef config__h
#define config__h

#define MODUL(x) 		(((x)>=0) ? (x) : -1*(x))
#define ZWROT(x)		(((x)>=0) ? 1 : -1)
#define GRANICA			1
#define KROK			1
#define Servo_X			OCR1A
#define Servo_Y			OCR1B

#define LIVE_OUT		DDRD |= (1<<PD7)
#define LIVE_TOGGLE		PORTD ^= (1<<PD7)
#define LINE_OUT		DDRC
#define LINE_PORT		PORTC

volatile int Timer0_Temp=0;
volatile int Last_Position[2]; 				

void PWM_Init(void);
void Timer0_Init(void);
void Timer0_Stop(void);
void Timer0_Start(void);
void Set_Servos(void);

#endif
Link do komentarza
Share on other sites

Po wejściu w przerwanie nie blokujesz innych przerwań, a później ich nie "startujesz", może to jest przyczyną 🙂

Niestety to nie to. Spróbowałem choć miałem do tego sceptyczne podejście ale przy tych anomaliach które występują pomyślałem ze wszystko jest możliwe 😋

Edit:

Posiedziałem jeszcze dzisiaj troszkę nad tym kodem i zaobserwowałem ze występuję to samo przy wysyłaniu jednego długiego stringa. Chyba skopałem coś w usart.c

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.