Atmega32 UART

Diskuze a poradna o programátorech a programování různých obvodů

Moderátor: Moderátoři

Zpráva
Autor
Uživatelský avatar
jim_h
Příspěvky: 10
Registrován: 14 lis 2016, 00:00

Atmega32 UART

#1 Příspěvek od jim_h »

Ahoj, trošku si začínám hrát s Atmegou32. Už jsem prošel pár tutoriálů, ale teď jsem se trochu zasekl u komunikace pomoci UART. Konkrétně jsem zkoušel jeden program ze stránek https://newbiehack.com/UART-OneWay-ChipToChip.aspx. Zdroják přiložím níže. Problém je, že z nějakého důvodu mám velké zpoždění cca 1s při odesílání dat. Daný program nedělá níc jiného, že po zmáčknutí tlačítka, pošle binární číslo po UARTU do druhého čipu a ten po přijmutí rozsvítí nebo zhasne LED. Problém je, že se mi změna na ledce projeví asi cca po 1s, čož mi přijde jako velké zpoždění. Lehce jsem měřil signál osciloskopem, a vím, že je to zpoždění už na TX čipu. Oba čipy jsou Atgmega32 s vnitřním 1MHz oscilátorem. Nevíte náhodou někdo v čem by mohl být problém? Projížděl jsem datasheet a nevím co by mohlo být blbě. Navíc na těch stránkách, z kterých čerpám autor žádné zpoždění nemá.
Uživatelský avatar
jim_h
Příspěvky: 10
Registrován: 14 lis 2016, 00:00

#2 Příspěvek od jim_h »

zde je kod pro TX čip
#include <avr/io.h>


int main(void)
{
DDRB |= (1 << PINB1);
PORTB &= ~(1<<PINB0);

DDRB &= ~(1 <<PINB0);
PORTB |= (1 << PINB0);

int Pressed = 0;
int Pressed_Confidence_Level = 0;
int Released_Confidence_Level = 0;

int UBRR_Value = 25;
UBRRH = (unsigned char)(UBRR_Value >> 8 );
UBRRL = (unsigned char)UBRR_Value;

UCSRB = (1 << RXEN) | (1 << TXEN);

UCSRC |= (1 << USBS);
UCSRC |= (3 << UCSZ0);



while (1)
{
if (bit_is_clear(PINB,0))
{
Pressed_Confidence_Level ++;
Released_Confidence_Level = 0;
if (Pressed_Confidence_Level > 500)
{
if (Pressed ==0)
{
Pressed =1;
PORTB ^= (1 << PINB1);

while (! (UCSRA & (1 << UDRE)));
UDR = 0b11110000;
}
Pressed_Confidence_Level = 0;

}
}

else
{
Released_Confidence_Level++;
Pressed_Confidence_Level = 0;
if (Released_Confidence_Level > 500)
{
Pressed = 0;
Released_Confidence_Level = 0;
}
}
}
}
Uživatelský avatar
JirkaRCK
Příspěvky: 248
Registrován: 25 dub 2010, 00:00
Bydliště: Olomouc/Brno
Kontaktovat uživatele:

#3 Příspěvek od JirkaRCK »

Ahoj,

přijde mi, že je to tlačítko řešeno příliš komplikovaně. Proč to pro základní test ve while smyčce neuděláš prostě následovně:

while 1{
Když je tlačítko stisknuto{
Zapsat byte do TX registru //Tím se hned i zahájí odesílání
delay cca 300ms //Provizorní ošetření zákmitu - aby se stisk nepřečetl 2x
while byte je odeslán; //Čekání na odeslaný packet
}
}

Dále bych doporučil i na TX stranu připojit na některý z volných pinů LEDku, nechat ji po resetu zhaslou, a postupně řádek od řádku zkusit posouvat příkaz, co ji rozsvítí. Až uvidíš, že nereaguje hned jak má, budeš přesně vědět, na jakém řádku to zůstává viset :-)

Delay můžeš udělat například funkcí _delay_ms() z knihovny util/delay.h
http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__util__delay_1gad22e7a36b80e2f917324dc43a425e9d3.html

Teď nemám moc času se tvým kódem zabývat a omlouvám se za užití pseudojazyka, ale pokud na osciloskopu chodí packety se správným bitratem a už odcházejí zpožděně, měl by být UART nakonfigurovaný správně. UART na atmeze je velmi jednoduchá věc. Ještě možná zkontroluj, jestli máš správně nastavený směr a pull-up pinu s tlačítkem.
Uživatelský avatar
jim_h
Příspěvky: 10
Registrován: 14 lis 2016, 00:00

#4 Příspěvek od jim_h »

Díky za odpověď. Ještě jsem si s tím trošku hrál a zjistil jsem, že bude nakonec chyba v RX čip. Měl jsem k dispozici jenom takovej pseudo osciloskop z ebay, kterej má s větší časovou základnou poměrně pomalou odezvu. Při rychlejší časové základně tam vidím ty pulzy hned. Přidal jsem si taky ledku, která se rozsvítí hned jak se data zapíšou do UDR a tam žádné zpoždění není.

Jinak k tomu tlačítku, možná je to složité, ale zase mi to přijde elegantnější, že nemusím nikde čekat, ale jak říkám teprve začínám, takže si rád nechám poradit.

Níže ještě přidávám kod pro RX čip, jelikož to teďka vypadá, že bude chyba spíše tam. Kdyby měl někdo nápad budu vděčný.

{
DDRB |=(1 <<PINB0);

int UBRR_Value = 25;
UBRRH = (unsigned char)(UBRR_Value >> 8);
UBRRL = (unsigned char)UBRR_Value;

UCSRB = (1 << RXEN) | (1 << TXEN);

UCSRC |= (1 << USBS);
UCSRC |= (3 << UCSZ0);

unsigned char receiveData;

while (1)
{
while (! (UCSRA & (1 << RXC)));
receiveData = UDR;

if (receiveData == 0b11110000) PORTB ^= (1 << PINB0);
}

}
Uživatelský avatar
jim_h
Příspěvky: 10
Registrován: 14 lis 2016, 00:00

#5 Příspěvek od jim_h »

z nějakého důvodu RX čipu strašně dlouho trvá než se vyprázdní RX buffer, takže je to zaseklé na řádce

while ( !(UCSRA & (1 << RXC)));

Přidal jsem si tam ledku která se bude přepínat při každém průchodu kodu. Přidal jsem ji právě před while ( !(UCSRA & (1 << RXC))); , ale je taky zpožděná. Když ten řádek s while zakomentuji tak bliká jak má.
Uživatelský avatar
jim_h
Příspěvky: 10
Registrován: 14 lis 2016, 00:00

#6 Příspěvek od jim_h »

Tak jsem si s tím ještě trošku hrál a zjistil jsem, že to zpoždění je závislé na velikosti framu. Nejhorší asi 1s zpoždění je při konfiguraci 1start bit, 8bit data, 2 stop bity. U ubývajícím počtem bitů se zpoždění zmenšuje a při konfiguraci 1start bit, 5bitů data, 1 stop bit je zpoždění minimální nebo ho okem nevidím. Zkoušel jsem měnit i boud rate, ale to je bez efektu. Přijde mi divné, že by na tak jednoduchém programu bylo omezení v rychlosti čipu, který běží na 1MHz. Vzdálenost mezi čipy je cca 20cm. Původní autor používá stejné uspořádání a jde mu to, takže si nemyslím, že bych narážel na meze čipu. Nemá někdo nápad, čím by to mohlo být?
Uživatelský avatar
mtajovsky
Příspěvky: 3728
Registrován: 19 zář 2007, 00:00
Bydliště: Praha

#7 Příspěvek od mtajovsky »

Co to znamená "než se vyprázdní RX buffer"? RX buffer se sám nevyprázdní. Po přijetí dat se nahodí RXC a bude nahozen do té doby, než se data přečtou. Asi jste chtěl říci - než se buffer naplní přijatými daty. Měl byste zkontrolovat registr UCSRA na výskyt chyb při přenosu. V interrupt rutině by se to dělalo nějak takhle:

status = UCSRA; // read data and status
rchar = UDR;

// -------------- check RX status -----------------------

if((status & (_BV(FE) | _BV(DOR) | _BV(PE))) != (u08)0)
{
. . . obsluha chyby
}

. . . jinak zpracování dat

Pokud se RXC nenahazuje, pravděpodobně nepřicházejí na přijímač USARTu korektní rámce.
Uživatelský avatar
JirkaRCK
Příspěvky: 248
Registrován: 25 dub 2010, 00:00
Bydliště: Olomouc/Brno
Kontaktovat uživatele:

#8 Příspěvek od JirkaRCK »

Zajímavé... Abys vyloučil všechny duchy, doporučuji zkontrolovat makefile/místo, kde nastavuješ pojistky.

Dále zkus v makefilu úplně vypnout optimalizace. U AVR mi optimalizování už několikrát udělalo dost velké peklo, když narazilo na pro kompilátor zdánlivě nesmyslný cyklus/if, který ale měl své opodstatnění. Může to tak být právě v případě whilu čekajícího na byte.

Pro jistotu doporučuji také nespoléhat se na to, že se při povolení UARTu nastaví i směr GPIO, ale přes DDRx registr na začátku kódu nastavovat ručně TX jako výstupní a RX jako vstupní - možná to není třeba, ale já to tak vždycky raději dělal.

Při zápisu do registru UCSRC bych možná zkusil hodnotu vždy orovat hodnotou 10000000b (bit URSEL registru UCSRC). Nepamatuji si, že bych to takto dělal při konfiguraci UARTu na atmeze8, ale u 32ky píší v datasheetu "This bit selects between accessing the UCSRC or the UBRRH Register. It is read as one when
reading UCSRC. The URSEL must be one when writing the UCSRC."

Jinak mi na tvé konfiguraci nepřipadne nic zásadně chybného. Zkoušel jsem si ji dosadit do popisu v datasheetu a měla by symbolizovat 8 bitů dat, 1 stop bit, bez parity, baud rate 2403.
Uživatelský avatar
mtajovsky
Příspěvky: 3728
Registrován: 19 zář 2007, 00:00
Bydliště: Praha

#9 Příspěvek od mtajovsky »

Inicializaci UARTu na ATmeag32 je třeba provést takto (pro 38400 bps a přerušení):

Kód: Vybrat vše

#define BAUDRATE    ((u16)(F_CPU/(16L * 38400L) - 1))         // 38400 bps on given F_CPU clock

. . .
    usartInit(BAUDRATE);
. . .

/** Function initializes the USART HW */
void usartInit(u16 baud_rate)
{
    UBRRH = (u08)(baud_rate>>8);
    UBRRL = (u08)baud_rate;                                // set the baud rate

    UCSRB = _BV(RXEN) | _BV(TXEN) |
            _BV(TXCIE)|_BV(RXCIE);                         // Enable receiver and transmitter and interrupts
    UCSRC = _BV(URSEL) | (0<<USBS) | (3<<UCSZ0);           // Set frame format: 8data, 1stop bit
}


Bez nastavení bitu URSEL se bude přepisovat UBRRH (sdílí společnou adresu), čímž se zmastí přenosová rychlost. Také si nejsem jist, že vnitřní hodiny jsou dostatečně přesné pro správné časování UARTu.
Uživatelský avatar
jim_h
Příspěvky: 10
Registrován: 14 lis 2016, 00:00

#10 Příspěvek od jim_h »

Chlapy díky moc za rady, měly jste pravdu, že bodrel dělal nenastavení URSEL bitu v registru UCRC. Sice jsem tuhle větu v datasheetu četl a i zkoušel, ale asi jak už jsem zkoušel měnit všechno možné dohromady (počet bitů, rychlosti, počet stopbitů), tak jsem někde dělal chybu. Teď už to jede pěkně. Asi je tam problém, jak jste psali, že je URSEL sdílenej s jiným registrem. Jsem překvapenej, že jsem skoro nikde na internetu neviděl, že by někdo tento bit nastavoval, ale třeba je to jenom doména atmegy32. Ještě jednou díky za radu.
Uživatelský avatar
jim_h
Příspěvky: 10
Registrován: 14 lis 2016, 00:00

#11 Příspěvek od jim_h »

Jeste bych mel dotaz, jestli nekdo nema zkusenost s posilanim vice bytu nebo promennych. Jde jenom o zkusebni program, ale chtel bych pres UART poslat z jednoho cipu obsah tri promennych. Kazda promenna muze obsahovat cislo 0-255, takze 8 bitu. A me moc neni jasne jak to poslat, abych to na druhe strane prijmul do spravne promenne. Bohuzel vyuzivam celych 8 bitu, takze to nemuzu nejak identifikovat. Navi nekdo alespon teoreticky, jak se to dela?
Uživatelský avatar
Victronix
Příspěvky: 978
Registrován: 23 lis 2007, 00:00
Bydliště: Vážany

#12 Příspěvek od Victronix »

Tomu se říká "protokol".
Buď můžeš zkusit nějaký obecný, třeba modbus, ale s tím se budeš zabývat dlouho a zbytečně, pokud jej dále nevyužiješ.
Nebo si vytvoříš svůj. Třeba:
Pošleš nejdříve identifikátor např: 01h
Na druhé straně budeš vědět, že budou následovat data první proměnné.
Pak pošleš hodnotu.
A tak dál.
Uživatelský avatar
jim_h
Příspěvky: 10
Registrován: 14 lis 2016, 00:00

#13 Příspěvek od jim_h »

Díky za odpověď, s tím identifikátorem mi to napadlo, ale problém je, že každá proměnná využije všech 8bitů (jak jsem psal přenáším číslo mezi 0 a 255), takže už mi na něj nezbývá místo.
Uživatelský avatar
Atlan
Příspěvky: 4514
Registrován: 10 kvě 2004, 00:00
Bydliště: Košice

#14 Příspěvek od Atlan »

Posles prvy bajt, na primacej strane zistis ze je to identifikator, dakze dalsie 3 bajty budu data ktore ta zaujimaju tie si ulozis.
Pouzi hotovy protokol bude to lepsie pre nas.
Uživatelský avatar
JirkaRCK
Příspěvky: 248
Registrován: 25 dub 2010, 00:00
Bydliště: Olomouc/Brno
Kontaktovat uživatele:

#15 Příspěvek od JirkaRCK »

Když pracuji s UARTem, RS232kou atd, tak jsem většinou neúsporný a kvůli kompatibilitě s terminály na PC a snadnému debugu posílám namísto binární komunikace textové řetězce. (Tzn. pro laika např. číslo 255 nepošlu jako byte 0xFF, ale jako tři ASCII znaky "2", "5", "5"). S řetězci lze následovně pracovat za pomocí běžných céčkových funkcí pro práci s řetězci. Lze je parsovat, převádět textovou symboliku čísla zpět na inteeger atd. Pokud bych chtěl přenést tři proměnné A,B,C, může packet vypadat třeba následovně: $AAA,BBB,CCC\0. V přijímacím mikrokontroléru přijatými znaky postupně plním pole. Vím, že $ je začátek nové zprávy, takže mám pole vyprázdnit a plnit ho opět od začátku. Naopak vím, že zvolený ukončovací znak, například EOS nebo CR značí konec zprávy a po jeho přijetí mám to mezi ním a dolarem zpracovat - umazat čárky a převést z textu zpět na trojici integeerů, které přiřadím proměnným. Podobně vypadá například to, co leze po sériové lince z GPS přijímačů (u nich se tomu říká NMEA věty).

Druhou možností je binární komunikace (neposíláš srozumitelný text, ale přímo obsah proměnných). Výhodou je kratší program, menší zátěž kontroléru a efektivnější přenos linky (bytovou proměnnou pošlu jako byte či dva, ne jako tři v případě ASCII řetězců). Nevýhodou je horší debug - data nejsou na terminálu v PC srozumitelná.

Když už se bavíme o binární komunikaci, tak osmibitová binární čísla v čisté podobě, nebo jak to nazvat, prostě byte dat = byte na uartu, nemám moc rád, protože když bych zvolil hlavičku zprávy např. 0xAA, nikde nemám zaručeno, že tuto hodnotu za běhu programu nenabude přenášená proměnná a nespletu si data s hlavičkou. Když už bych komunikoval binárně, přenášel bych jeden byte proměnné dvěma byty na uartu (4 významnější ("vyšší") byty pak můžeš použít pro označení, o co jde, 4 "nižší" pro přenos poloviny informace.)

Binární komunikace přenášející jeden byte (například číslo 0-255) dvěma byty by mohla vypadat následovně:

Číslo 0xAB rozdělím na dvě - na číslo 0x0A pomocí cisloA=proměnná>>4 a 0x0B pomocí cislo b=proměnná&0x0F.

Následně provedu binární OR s hlavičkou. Pro první půlku proměnné třebas 0xC0, pro druhou 0xD0.

Tím získám čísla 0xCA a 0XDB. Tato čísla pošlu po UARTu.

Na druhé straně - přijímací - vím, že 0xCA přenáší významnější čtveřici bitů proměnné cislo a tyto bity jsou 0xA0. Dále vím, že 0xDB přenáší méně významnější čtveřici a tyto bity jsou 0x0B. Když s hodnotami provedu OR, získám původní číslo 0xAB :-)

Pokud budu chtít přenášet víc různých proměnných, tak stejně, jak jsem si tuto označil C a D, mohu jinou označit třeba 1 a 2...snad je to alespoň trochu srozumitelné. Jestli najdu chvíli, pokusím se to napsat jako kód.
Odpovědět

Zpět na „Programování PIC, ATMEL, EEPROM a dalších obvodů“