[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [avr-gcc-list] What Happened to the sbi() and cbi() Macros????
From: |
Ned Konz |
Subject: |
Re: [avr-gcc-list] What Happened to the sbi() and cbi() Macros???? |
Date: |
Mon, 31 Jan 2005 15:33:59 -0800 |
User-agent: |
KMail/1.7.2 |
On Monday 31 January 2005 9:09 am, E. Weddington wrote:
> Which way did Ned describe? Is it with a bit field?
Yes, at least in part. I also like inline functions to do higher-level things,
but I didn't talk about those on the list.
> If so, there are several portability issues with using bitfields.
The portability issues aren't relevant here, because my headers are
machine-generated from the text from the Atmel databooks (copy table from
PDF, paste into text file, generate headers directly) and so moving to
another compiler only requires re-generating the headers. Anyway, I intend to
stay with GCC. And it's likely that other compilers will also pack bits more
or less the same way for the AVR target, and I could generate the headers
with conditionals for bitfield ordering, if there were any demand from a user
of another free compiler whose authors hadn't for some reason created
headers.
> Also, if you use a bit field you cannot operate on non-continguous bits
simultaneously such as:
> PORTA |= (_BV(0) | _BV(6));
That's quite right, and that's why my headers also allow for this idiom.
For each SFR, there is a union declared, containing:
- both byte- or word- wise (just as the current avr-libc headers)
- bit-wise
- multi-bit fields where possible
So for, say the ATmega16's SFIOR register, I have:
/* SFIOR ($50) (page 55,86,133,199,219) */
typedef union SFIOR_t {
uint8_t asByte;
struct {
uint8_t bPSR10 :1;
uint8_t bPSR2 :1;
uint8_t bPUD :1;
uint8_t bACME :1;
uint8_t bReserved4 :1;
uint8_t bADTS0 :1;
uint8_t bADTS1 :1;
uint8_t bADTS2 :1;
} b;
struct {
uint8_t :1; /* PSR10 */
uint8_t :1; /* PSR2 */
uint8_t :1; /* PUD */
uint8_t :1; /* ACME */
uint8_t :1; /* Reserved4 */
uint8_t fADTS :3;
} f;
} SFIOR_t;
#define SFIOR_sfr (*(volatile SFIOR_t *) (0x50))
#define SFIOR SFIOR_sfr.asByte /* entire register */
#define PSR10 SFIOR_sfr.b.bPSR10
#define PSR2 SFIOR_sfr.b.bPSR2
#define PUD SFIOR_sfr.b.bPUD
#define ACME SFIOR_sfr.b.bACME
#define ADTS0 SFIOR_sfr.b.bADTS0
#define ADTS1 SFIOR_sfr.b.bADTS1
#define ADTS2 SFIOR_sfr.b.bADTS2
#define ADTS SFIOR_sfr.f.fADTS
Now, with this I can still go:
SFIOR |= 0xE1;
to set both ADTS and PSR10 at once.
Or I can do this:
SFIOR_sfr = (SFIOR_t) { .b.bPUD = 1, .b.bPSR10 = 1 };
96: 85 e0 ldi r24, 0x05 ; 5
98: 80 bf out 0x30, r24 ; 48
Note that combining .f.fADTS and .b.bPSR10 would not work right, though:
SFIOR_sfr = (SFIOR_t) { .f.fADTS = 7, .b.bPSR10 = 1 };
9a: 81 e0 ldi r24, 0x01 ; 1
9c: 80 bf out 0x30, r24 ; 48
Naturally, since SFIOR is declared as volatile, successive assignments won't
be combined:
ADTS = 7;
b2: 80 b7 in r24, 0x30 ; 48
b4: 80 6e ori r24, 0xE0 ; 224
b6: 80 bf out 0x30, r24 ; 48
PSR10 = 1;
b8: 80 b7 in r24, 0x30 ; 48
ba: 81 60 ori r24, 0x01 ; 1
bc: 80 bf out 0x30, r24 ; 48
Perhaps a hybrid scheme like this that also adds bit *numbers* for a BIT()
macro would be useful:
#define bitPSR10 0
#define bitPSR2 1
#define bitPUD 2
etc. (the names could probably be chosen better).
and then, given appropriate macros like:
#define bit(bitNum) (1<<(bitNum))
#define bit_set(reg, bits) reg |= (uint8_t)(bits);
#define bit_clear(reg, bits) reg &= ~(uint8_t)(bits);
we could do things the way we're used to if necessary:
bit_set(SFIOR, bit(bitPSR10) | bit(bitPSR2) | bit(bitPUD));
be: 80 b7 in r24, 0x30 ; 48
c0: 87 60 ori r24, 0x07 ; 7
c2: 80 bf out 0x30, r24 ; 48
--
Ned Konz
http://bike-nomad.com