Práce s registrem UCSRC AT MEGA32

Ahoj.

Píšu v code vision program, který načte z EEPROM defaultní nastavení seriové komunikace (rychlost, parita apod…) a vypíše tento stav na terminál přes RS-232 (např. na putty).
Kód (jen ty podstatné věci):

//hlavičkové soubory
#include <MEGA32.h>
#include <mega32_bits.h>


//tady jsou uložené řetězce, které se vypíšou na terminál - žádná, sudá, lichá parita
volatile char *PARITY] = {"None", "Even", "Odd"};


//***********************************************************
//funkce, která by měla z UCSRC registru zjistit, jaká je nastavená parita a vrátit návratovou hodnotu:
//0 = žádná parita (None) - tj. UPM1[0] UPM0[0]
//1 = sudá parita (Even) - tj. UPM1[1] UPM0[0]
//2 = lichá parita (Odd) - tj. UPM1[1] UPM0[1]
unsigned char Parity_Set()
{
unsigned char PARITY_INDICATOR = 0;
unsigned char UCSRC_COPY = 0;

//zde provadim maskovani, abych ziskal pouze 4. a 5. bit (UPM0 a 1) registru UCSRC, podle kterých je nastavena parita
UCSRC_COPY = (UCSRC & 0b00110000);

if (UCSRC_COPY == 0x00){
    PARITY_INDICATOR = 0;     // parita NONE
    };
if (UCSRC_COPY == 0x20){
    PARITY_INDICATOR = 1;     // parita suda (Even)
    };
if (UCSRC_COPY == 0x30){
    PARITY_INDICATOR = 2;     // parita licha (Odd)
    };
 
//návratová hodnota, která se dosazuje za proměnnou "index" ve funkci "settings"   
return PARITY_INDICATOR;
}


//***********************************************************
//tato funkce vypíše na terminál pomocí řetězce, jaká je nastavená parita. K tomu si na pomoc volá funkci Parity_Set(), která zjistí jaká parita je přesně nastavena a pak to předá funkci usart_send(), který provede zápis na USART (terminál)
void settings()
{
unsigned char index = 0;
...
//do proměnné index uložím návratovou hodnotu z funkce Parity_Set. Tato hodnota může nabývat hodnoty 0, 1 nebo 2 a slouží jako index pole *PARITY pro výpis správaného řetězce z pole na USART.
index = Parity_Set();
usart_send(PARITY[index]);
return;
}


//***********************************************************
//funkce main
void main()
{
//načtení parametrů sériové komunikace z EEPROM (argumenty funkce EEPROM_read jsou jednotlivé adresy EEPROM)
UBRRH = EEPROM_read(0x000);
UBRRL = EEPROM_read(0x001);
UCSRB = EEPROM_read(0x002);
UCSRC = EEPROM_read(0x003);
...
//volám funkci pro nastavení (a zatím jen výpis) parametrů sér. komunikace
settings();
return;
}

A v čem je teda problém?
Když jsem do EEPROM nastavil na adresu 0x003 hodnotu 0xB6, tak má MCU s PC komunikovat ve formátu 8+1, lichá parita - což skutečně komunikuje.
Problém je v tom, že vždy vypíše řetězec “None”, tedy se to tváří že proměnná index byla nastavena do nuly na základě funkce “Parity_Set()”.

Když jsem tento kus kódu zkoušel ve Visual Studiu, tak to fungovalo dobře a pokud jsem měl nastaveno v UCSRC hodnotu 0xB6, tak vypsal řetězec “Odd”. Tam jsem měl ale tu hodnotu do UCSRC přiřazenou “natvrdo” přímo v programu. Zase na MCU mám také jistotu, že se do UCSRC dostala správná hodnota, protože když pozměním obsah v EEPROM, tak se podle toho opravdu mění parametry komunikace.

Příjde mi to, jako by funkce “Parity_Set()” neuměla číst registr UCSRC. Ten je ale definovaný v hlavičkovém souboru <MEGA32.h> a mám za to, že se chová jako globální proměnná a je možné k němu přistupovat odkudkoliv z programu, nebo žiju v bludu ?

Děkuji za nápady

Registr UCSRC sdílí stejnou adresu s UBRRH.
Při čtení UCSRC dostaneš hodnotu UBRRH.

Pro získání hodnoty UCSRC je nutné ho číst podruhé v bezprostředně následujícím hodinovém cyklu.

x = UCSRC; // čte ubrrh x = UCSRC; // čte ucsrc

Případné přerušení musí být během čtení vypnuté.
Viz datašít “Accessing UBRRH/ UCSRC Registers”.

PS
Jestli můžu malou poznámku k pojmenování:
parity_set()
‘set’ se používá pro nastavení, pro čtení se používá ‘get’.

Velké písmena se používají pro jména konstant a maker.
Pro proměnné, funkce atd. se používají malé písmena.

Děkuji. Už to pracuje jak má. Vysvětluje to i proč mi zápis funguje (tam dodržuji instrukce datasheetu, že na MSB (URSEL), že musí být 1 pro zápis do UCSRC). S tím čtením jsem to ale nejspíš přehlídl :slight_smile:

A díky za tu poznámku o jménech funkcí - ještě si pořád hledám nějaký “styl” programování a toto mi určitě pomůže zpřehlednit kód.

Ještě jednou díky.

Ještě mě napadá jeden problém na který jsem narazil. V datasheetu, jestli jsem to pochopil správně, tak mohu pracovat s jednotlivými bity u registrů s adresami 0x00 (TWBR) až 0x1F (EEARH). Tj. napříkald

//UCSRA je na adrese 0x0B
#define     U2X     UCSRA.1  

if (U2X == 0){
...
}

ale například u registru UCSRC, který je na adrese 0x20 už musím použít maskování, abych se dostal k informaci o hodnotě jednotlivých bitů ?
Není mi totiž úplně jasné, proč je v hlavičkovém souboru <mega32_bits.h> tento zápis:

/* UCSRC - USART Control and Status Register C */
#define    UCPOL           0       // Clock Polarity
#define    UCSZ0           1       // Character Size
#define    UCSZ1           2       // Character Size
#define    USBS            3       // Stop Bit Select
#define    UPM0            4       // Parity Mode Bit 0
#define    UPM1            5       // Parity Mode Bit 1
#define    UMSEL           6       // USART Mode Select
#define    URSEL           7       // Register Select

Pokud ve svém kódu používám názvy jako UPM0, UPM1, tak překladač chybu nehlásí. Mohu tedy používat takto definované názvy pro práci s bity napřímo a nebo musím zůstat u maskování ?

Děkuji za případné objasnění

#define USBS 4 // Stop Bit Select

není nic jiného, než pojmenované číslo, maskovat tedy musíš vždycky.

CodeVision neznám, ale co jsem tak vyčetl z různých fór, tak pokud funguje

UCSRC.4

tak úplně stejně by mělo fungovat

UCSRC.USBS

Pokud potřebuješ třeba reagovat na USBS=1, tak musíš tedy psát buď

if (UCSRC.USBS==1)

nebo

if ((UCSRC&(1<<USBS))==(1<<USBS))

To první nevím, jestli funguje; to druhý funguje určitě.

To se týká jenom asembleru.
V codevision zacházíš se všemi registry stejně.
Např.

if (UCSRC.USBS==1)

jak už bylo napsáno výše.

Zápisem

#define   USBS 3  //  UsartStopBitSelect

pojmenujeme 3. bit v registru UCSRC.
Zápis doslovně znamená, že každý výskyt výrazu USBS v kódu bude nahrazen číslem 3.

V oblasti IO registrů jsou pojmenovány nejen registry, ale i jejich jednotlivé bity.
S bity se často pracuje a je snadnější zapamatovat si tuto zkratku, než číslo bitu.
Jména registrů a bitů najdeš přehledně v datašítu v tabulce “Register Summary”.

#define U2X UCSRA.1 if (U2X == 0){
Tak ne.
U2X už bylo definováno a ty se snažíš definovat ho znovu.
Dostaneš varování warning: “U2X” redefined.
A je to zbytečné komplikování. Stačí

if (UCSRA.U2X == 0)

AB
Už je mi to jasnější. Díky

Ahoj,

Tak jak mi předtím načítání UCSRC fungovalo správně, tak teď opět nefunguje (a to dělám stejným způsobem) :frowning:

volatile unsigned char UCSRC_BUFFER = 0
...

void main ()
{
UCSRC = 0xB6;      // URSEL je 1 => zápis do UCSRC

UCSRC_BUFFER = UCSRC;
UCSRC_BUFFER = UCSRC;
...
return;
}

a v registru UCSRC_BUFFER mám místo hodnoty 0xB6 hodnotu 0x00, tj. stále čtu UBRRH.
Už fakt nevím jak to opravit. Máte někdo nějaký napad.
Díky

EDIT:
Jinak zápis do UCSRC podle všeho proběhne v pořádku, protože B6 znamená 8+1, lichá parita a tak to skutečně i komunikuje. Defaultni natavení UCSRC je 8+1 bez parity.

Možná kompiler nedal obě čtení těsně za sebou.
Zkus to v asm.

#asm push r16 cli in r16, UCSRC in r16, UCSRC sei sts {UCSRC_BUFFER}, r16 pop r16 #endasm

To mi bohužel nejde. Píše to: Error: …\test32.asm(3302): syntax error, unexpected $undefined

a ukazuje to na řádek: sts {UCSRC_BUFFER}, r16 v souboru .asm

Nemám s asemblerem pro AVR moc zkušeností, takže vlastně ani nevím co ta chyba přesně znamená ?

Zapomeň co jem psal.
Vyhoď zbytečné slovo “volatile” a mělo by to chodit.

Už jsem problém vyřešil.
je to takhle

[code]
volatile unsigned char UCSRC_BUFFER = 0;

unsigned char Ucsrc_read()
{
unsigned char i = 0;
i = UCSRC;
i = UCSRC;
return i;
}

void main()
{

UCSRC_BUFFER = Ucsrc_read();

return;
}[/code]

Tedy dělám úplně to samé co předtím, ale ve zlváštní funkci Ucsrc_read. Předtím jsem to dělal ve funkci main() a tam to nešlo. Proč ? To je mi velkou záhadou…

Teď už to šlape - parametry sériové komunikace měním přes terminál putty v MCU, výpis aktuálního nastavení už běží korektně a “záchrané” tlačítko, které nastaví defaultní stav komunikace (9600Bd, 8N1) funguje výborně.

Jenom jsem narazil na problém, že pokud změním počet datových bitů na 5 nebo 6, tak to vypisuje na terminál nesmysly (samo sebou, že v putty to také změním - už jsem to zkoušel měnit i v nastavení windows).
Rovněž jsem zkoušel i jiné COM porty (mám v PC ještě přídavnou PCI desku s LPT a COM porty navíc) - a pořád stejné. Prostě na 5 nebo 6 datových bitů komunikace nejde správně, zatímco na 7 i 8 funguje správně při všech možných paritách a přenosových rychlostech. Nemáte někdo zkušenosti ?

P.S.
AB - myslel jsem si, že slovo volatile je nutné, pokud chci mít proměnnou dostupnou pro všechny funkce celého programu (taková “globální” proměnná) - nebo to funguje jinak ? (resp. mě to takhle přesně funguje)

Je to kvůli kvalifikátoru ‘volatile’.
Záhada se vyjasní když se podíváš na přeložený soubor .lst.
Bez volatile čte UCSRC dvakrát po sobě jak má (obr1).
S volatile vloží po prvním čtení instrukci STS. (obr2).
Ty jsi to obešel tak, že čteš ne přímo do UCSRC_BUFFER, ale do nevolatilní proměnné ‘i’.
Zdá se mi jednodušší škrtnout ‘volatile’ než vytvářet další funkci.

Uvědom si, že 5-bitové číslo má max. hodnotu 2^5 = 32.
Takže můžeš posílat jenom ascii znaky 0 - 31. (všechny jsou netisknutelné)
S šesti bity jsou to znaky 0 - 63. Můžeš se podívat do ascii tabulky jaké znaky se dají poslat.

Funguje to opravdu jinak.
Proměnnou která má být dostupná všude deklarujeme jako globální, to jest její deklarace je umístěná mimo funkce .
Proměnnou která má být dostupná jen v jedné funkci deklarujeme jako lokální, její deklarace je uvnitř funkce.

např.

[code]unsigned char var1; // globální proměnná, platí všude

int func(void)
{
int x; // lokální proměnná, platí jen ve funkci func()
x = 10;
return x
}

main()
{
[/code]

S kvalifikátorem ‘volatile’ deklarujeme proměnné, které mohou být změněny jinde než v hlavním programu (např. v obsluze přerušení).
Takže
**Jako volatile deklarujeme globální proměnné, které jsou použité v hlavním programu i v přerušení.
**

Snad by bylo dobré přečíst nějakou knihu o C, třeba Herouta.
obr1.png
obr2.png

AB: díky.

K tomu volatile jsem skutečně přišel kdysi přes přerušení a s tím to fungovalo a ačkoliv mi to vrtalo hlavou, zatím jsem to neřešil (že všude používám volatile) až do teď.
A stím 5 a 6 bitovým formátem jsem si to představoval trochu jinak - že vytváří bloky o délce 5 bitů, kdy bajt je automaticky rozdělen do dvou bloků a příjmací systém to opět automaticky sestaví. To s těmi prvními 32 znaky ASCII jsem se nikde nedočetl. Jen prosté sdělení, že “podporuje 5 bit formát”.

Inu nejlepší škola je začít problémy řešit prakticky a v průběhu realizace pak odhalovat jednotlivé detaily.
P.S. Na toho Herouta se podívám :slight_smile: