este jedna dlazdicska...
Jan Waclawek
konfera na efton.sk
Neděle Leden 8 01:15:06 CET 2017
>je tu nejak ticho tak pridam blbu otazku...
>
>
>majme porty na procesore Atmega. Chcel by som niektore piny nadratovat
>do boolean premennej, tak aby som po precitani dostal 0/1. Takisto by
>som chcel zase niektore ine boolean premenne chcel nadratovat na
>vystupne piny. Ako sa to v C spravne robi?
Nijako.
_Bool v C nie je typ, ktory by sa dal namapovat na bit. Presnejsie, v C
nemoze existovat samostatny objekt (a tym ani typ, ktory by ten objekt
popisoval), ktory by nepozostaval z jedneho alebo viacerych bytov (C99,
6.2.6.1#2).
V C vsak existuje prostriedok na manipulaciu jednotlivych bitov, bitove
polia (bitfields, resp. v norme sa to pise so spojovnikom bit-fields) v
strukturach (struct). Pouzitie je trocha ine ako so samostatnou premennou,
t.j. nemozes zapisat nieco ako PORTC_1 = 1; a musis zapisat nieco ako
PORTC.b1 = 1; - ale tipujem, ze to by Ti az tak velmi nevadilo.
Zacnime vsak s tymi portami (resp. vseobecnejsie k SFR) u AVR. Tie
samozrejme su (mozu) byt bitovo manipulovatelne, rovnako ako u '51. V
povodnom AVR tak ako ho A&V vymysleli, je pre SFR vyhradena oblast 64
byte, kde samotne SFR su adresovane 0x00-0x3F - to su adresy P pouzite v
instrukciach pre SFR. Su dve instrukcie pre nastavenie/nulovanie SFR bitu
- SBI P,b a CBI P,b (Set resp. Clear Bit in I/O) - a dve instrukcie na
testovanie bitu a obskocenie nasledujucej instrukcie - SBIS P,b a SBIC P,b
(Skip if Bit in I/O Set resp. Cleared). Toto sa prenieslo aj do
programovania v C s prekladacom avr-gcc - avr-gcc spociatku nevedel
zohladnit tuto specialitu, a tak sa porty efektivne nastavovali pomocou
makier sbi()/cbi() generujucich inline assembler - toto spominam preto,
lebo pozostatky tych makier sa daju najst v starsich programoch ako aj v
dokumentacii k avr-libc, a pre spatnu kompatibilitu su stale obsiahnute v
<compat/deprecated.h>
http://www.nongnu.org/avr-libc/user-manual/group__deprecated__items.html .
Sucasne vsak je cela SFR oblast namapovana aj v "unifikovanej" pamati,
ktora u klasickych AVR obsahuje na adresach 0x00 az 0x1F registre
procesora (uzitocnost tohoto je na velmi dlhu diskusiu a tyka sa vlastne
len tych najmensich a najstarsich Tiny), na adresach 0x20 az 0x5F klasicke
bitovo adresovatelne SFR registre ako boli popisane hore (takze kazdy taky
register ma vlastne dve adresy, jednu v priestore SFR (od adresy 0x00) a
druhu v unifikovanej pamati (posunutu o 0x20). Unifikovana pamat znamena,
ze k vsetkym prostriedkom sa da pristupovat jednou sadou instrukcii v
jednom adresnom priestore. Unifikovana pamat je prave jedna z vlastnosti
AVR ktora bola zamerne pritomna uz od pociatku prave kvoli pouzitiu vo
vyssich jazykoch (na cele s C), kedze tie maju notoricky problem s
rozmanitostou pamati (prave preto vravi pan kolega Andel, ze C pre '51 je
Keil - C pre '51 musi totiz obsahovat rozsirenia pre viacero roznych
pamatovych oblasti, ktorych ma '51 tak zhruba 5, inak je takmer
nepouzitelne a urcite nie efektivne pouzitelne).
Klasicky bola v AVR nad SFR oblastou umiestnena SRAM (t.j. od adresy 0x60).
FLASH nebola v unifikovanej pamati (to prinasa dalsiu sadu problemov s
konstantnymi premennymi umiestnenymi vo FLASH, toto sa vo svete avr-gcc
prejavovalo skupinou makier a funkcii v <pgmspace.h> a s nimi spojenymi
zvlastnostami, od urciteho casu sa to prejavuje skupinou pomenovanych
pamatovych priestorov typu _flash) a ani EEPROM (tam sa to vsak obvykle
ani necaka a pristupuje sa k nej dost prirodzene zvlastnymi funkciami).
Toto sa zmenilo v xMEGA kde vznikla skutocne unifikovana pamat zahrnajuca
vsetky prostriedky, avsak za cenu strankovania, kedze prirodzeny adresny
priestor AVR je dost pochopitelne 16-bitovy (ta potreba vsak u FLASH >64kB
tak ci tak existuje).
Vratme sa vsak k SFR. Pochopitelne, v urcitom bode prestalo 64 byte pre
vsetky SFR stacit, a v novsich, vacsich modeloch boli dodatocne SFR
umiestnene uz len do unifikovanej pamate, nad oblast povodnych SFR t.j. od
adresy 0x60 (a SRAM bola posunuta nad nove SFR, na adresu 0x100 a neskor
aj vyssie). Tieto nove SFR uz teda neboli bitovo manipulovatelne, pre
unifikovanu pamat take instrukcie nie su. (Mimochodom, aj v '51 je len
pomerne mala cast SFR priestoru bitovo manipulovatelna, vsakze). Pre ne
teda zmena bitu znamena precitat cely SFR do registra, zmodifikovat ten
jeden bit, a zapisat naspat.
Pisatelom v C/avr-gcc teda vznikli dve moznosti: bud si pamatat, pre ktore
SFR sa da pouzit cbi()/sbi() a pre ktore nie, alebo prosto pouzivat pre
vsetky SFR pristup ako k normalnej pamati (to je to PORTx |= 1 << 5 apod.)
aj za cenu, ze to pre tie klasicke SFR nie je optimalne. Medzicasom sa
vsak nasiel aj dobrodinec ktory do avr-gcc doplnil optimalizaciu, ktora
skuma, ci bitovo-manipulovana adresa je v oblasti klasickych SFR, a ak
ano, tak pouzije cbi/sbi, co umoznilo standardizovat pristup k SFR ako k
pamati a zachovat optimalitu. Tym padom sa stali makra cbi()/sbi()
zastarale (deprecated) a oficialne doporuceny pristup je popisany tu:
http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html
Ono to PORTB |= _BV(PB1); nie je omnoho zrozumitelnejsie ci citatelnejsie
ako PORTB |= (1 << PB1), takze si to mnohi aj tak spestruju svojimi
makrami. My vo firme napriklad mame taky postup, ze si na zaciatku
projektu porobime makra typu
#define SetLED1() PORTB |= (1 << 3)
#define ClrLED1() PORTB &= ~(1 << 3)
#define GetButton1() ((PINB & (1 << 5)) == 0)
a podobne - je to mozno trocha tukania, ale potom zvysok programu je
relativne dobre citatelny.
Ale aby som sa po tejto obrovskej okluke vratil uplne na zaciatok, je mozne
urobit nieco taketo (http://bit.ly/2i5mMXr ):
#include <stdint.h>
#include <avr/io.h>
typedef union __attribute__((packed)) {
uint8_t all;
struct __attribute__((packed)) { uint8_t nibH : 4, nibL : 4; };
struct __attribute__((packed)) { _Bool lsb: 1; uint8_t : 6; _Bool msb: 1;
};
struct __attribute__((packed)) { _Bool b0: 1, b1: 1, b2: 1, b3: 1, b4: 1,
b5: 1, b6: 1, b7: 1; };
} bit8;
#define SFR2BITFIELD8(sfr) (*(volatile bit8 *)(&(sfr)))
#define pinC SFR2BITFIELD8(PINC)
#define portC SFR2BITFIELD8(PORTC)
#define pinF SFR2BITFIELD8(PINF)
#define portF SFR2BITFIELD8(PORTF)
int main(void) {
if (PINC & (1 << 4)) {
PORTC |= (1 << 5);
}
if (pinC.b4) {
portC.b5 = 1;
}
if (PINF & (1 << 4)) {
PORTF |= (1 << 5);
}
if (pinF.b4) {
portF.b5 = 1;
}
portC.b5 = !pinC.b4;
portC.b5 = ~pinC.b4;
}
z coho relevantna cast sa prelozi ako
int main(void) {
if (PINC & (1 << 4)) {
be: 9c 99 sbic 0x13, 4 ; 19
PORTC |= (1 << 5);
c0: ad 9a sbi 0x15, 5 ; 21
}
if (pinC.b4) {
c2: 9c 99 sbic 0x13, 4 ; 19
portC.b5 = 1;
c4: ad 9a sbi 0x15, 5 ; 21
}
if (PINF & (1 << 4)) {
c6: 04 9b sbis 0x00, 4 ; 0
c8: 05 c0 rjmp .+10 ; 0xd4 <main+0x16>
PORTF |= (1 << 5);
ca: 80 91 62 00 lds r24, 0x0062
ce: 80 62 ori r24, 0x20 ; 32
d0: 80 93 62 00 sts 0x0062, r24
}
if (pinF.b4) {
d4: 04 9b sbis 0x00, 4 ; 0
d6: 05 c0 rjmp .+10 ; 0xe2 <main+0x24>
portF.b5 = 1;
d8: 80 91 62 00 lds r24, 0x0062
dc: 80 62 ori r24, 0x20 ; 32
de: 80 93 62 00 sts 0x0062, r24
}
portC.b5 = !pinC.b4;
e2: 83 b3 in r24, 0x13 ; 19
e4: 82 95 swap r24
e6: 8f 70 andi r24, 0x0F ; 15
e8: 80 95 com r24
ea: 81 70 andi r24, 0x01 ; 1
ec: 82 95 swap r24
ee: 88 0f add r24, r24
f0: 80 7e andi r24, 0xE0 ; 224
f2: 95 b3 in r25, 0x15 ; 21
f4: 9f 7d andi r25, 0xDF ; 223
f6: 98 2b or r25, r24
f8: 95 bb out 0x15, r25 ; 21
portC.b5 = ~pinC.b4;
fa: 83 b3 in r24, 0x13 ; 19
fc: ad 9a sbi 0x15, 5 ; 21
(domaca uloha: vysvetlit preklad posledneho riadku)
wek
Další informace o konferenci Hw-list