EEVblog Electronics Community Forum
Electronics => Microcontrollers => Topic started by: TrickyNekro on March 09, 2018, 06:11:04 pm
-
Hello ladies and gents,
So the question I have is a bit more complex than it might seem.
The usual "nice" way to write to an AVR register bitwise is this:
Register |= (1<<Register_bit); // To Set the bit
Register &= ~(1<<Register_bit); // To Clean the bit
That makes for a really nice code when you try to set or clear multiple bits are you can see what´s going on.
If I remember correctly the Register_bit(s) are also defined as positions for AVRs.
In PICs not so much though. You have two definitions, one I can understand the other I do have a problem with understanding.
For example:
extern volatile __bit STRA @ (((unsigned) &PSTR1CON)*8 ) + 0;
#define STRA_bit BANKMASK(PSTR1CON), 0
I get that the STRA "definition" is the absolute memory position of this bit. But I really don´t get the STRA_bit definition with the comma separated value.
I suppose you can not really use something like this with the XC8 compiler:
PSTR1CON |= (1<<STRA/ *or STRA_bit for the matter */ );
Am I wrong or am I missing something, I really do want to learn!
Thanks in advance, Lefteris
-
I use the macros you posted at the beginning in all my XC8 projects.
Although it is trickier because you have different definitions for writing whole registers and single bits (with the suffix "bits" to signify a structure for that register), but ultimately you can just use the macros.
I'm not sure where you got those two lines from, but I'm guessing its either from the example projects or generated files. Like others have commented here, I find the generated code to be bloated, but it does what its meant to do - quick and dirty testing, to be rewritten later on.
For you to use the "PSTR1CON |= (1<<STRA/ *or STRA_bit for the matter */ ):" part, then STRA or STRA_bit would have to be defined as a single digit indicating the bit in the register. I don't think microchip has these in the include files (rather they are structures).
Ultimately, there is multiple ways of doing this, and it all depends on how much you want to rely on the built in libraries (we all want to).
-
Yeah,
You can of course use the PSTR1CON.STRA_bit = 1; for example
but it gets messy, plus you can not access multiple bits at a time forcing you into more CPU cycles, unless the compiler does something.
Now that microchip and atmel merged let´s hope they invest some time to port GCC for the 8 bit micros.
That´s something to expect the next years (plus a combined programming degubbing tool... ahem ahem...)
Cheers,
Lefteris
-
Well that would be
PSTR1CONbits.STRA_bit = 1;
Also, you can set/clear multiple bits in a register, or rather set multiple bits, or clear them:
PSTR1CON |= (1<<bit1) | (1<<bit2) | (1<<bit3);
PSTR1CON &= ~((1<<bit1) | (1<<bit2) | (1<<bit3)); // note the extra parenthesis
But this would also require definitions of bit1, bit2, bit3 etc..
Admittedly very few people just write a value to the whole register as this is unhelpful when reading code. And yes, setting the bits one by one explicitly isn't particularly efficient - but often registers only have to be set once at start up. Doing it one by one also means you can see exactly how each register is configured.
I'm unsure why you wish to port macros when you can just copy/paste them into your header file. I wasn't aware the above macros were included in GCC I've always just copied them over from a template:
// Bit macro definitions
#define BIT(x) (1 << (x))
#define SETBITS(x,y) ((x) |= (y))
#define CLEARBITS(x,y) ((x) &= (~(y)))
#define SETBIT(x,y) SETBITS((x), (BIT((y))))
#define CLEARBIT(x,y) CLEARBITS((x), (BIT((y))))
#define BITSET(x,y) ((x) & (BIT(y)))
#define BITCLEAR(x,y) !BITSET((x), (y))
But you're right in that it would be helpful, although may not be compatible with the structure/unions that microchip uses (as in, adds confusion) - although I could be wrong here... I haven't written much code for PIC s in a while and mostly just use the MCC to sort out register settings!
It would be nice if they made the free compilers less bloaty, the MCC poo, replaced the PICkit 3 with something less frustrating (PICkit 4 is out apparently) but we shall see :)
-
Quote from: Buriedcode on Yesterday at 23:28:12 (https://www.eevblog.com/forum/index.php?topic=105639.msg1448236#msg1448236)
Also, you can set/clear multiple bits in a register, or rather set multiple bits, or clear them:
Code:
[Select] (https://www.eevblog.com/forum/javascript:void(0);)PSTR1CON |= (1<<bit1) | (1<<bit2) | (1<<bit3);
PSTR1CON &= ~((1<<bit1) | (1<<bit2) | (1<<bit3)); // note the extra parenthesis
Well yeah and the names of bit1, bit2, etc? That´s my point you see... In AVR GCC you have this, with the PIC not so much.
Quote from: Buriedcode on Yesterday at 23:28:12 (https://www.eevblog.com/forum/index.php?topic=105639.msg1448236#msg1448236)And yes, setting the bits one by one explicitly isn't particularly efficient - but often registers only have to be set once at start up. Doing it one by one also means you can see exactly how each register is configured.
Well sometimes you can write to the complete register and it will do its thing. There was one time though, I can´t remember with which peripheral it was, using AVR, that the bits in the same register had
to be configured one at a time. And that was not clear at all in the datasheet, if even mentioned, but it´s been some time I got to see my older code to tell exactly where the problem was.
Quote from: Buriedcode on Yesterday at 23:28:12 (https://www.eevblog.com/forum/index.php?topic=105639.msg1448236#msg1448236)It would be nice if they made the free compilers less bloaty
Oh that´s the funny part really... XC8 is not based on an open source compiler but rather the old Hi-Tech C compiler.
And yet the AVR compiler manages to be cleaner on a lot of stuff, which is based on GCC.
BTW just for the laughs, as much as I praise the AVR GCC, the PIC peripherals although convoluted are very flexible. TWI on AVRs gave me f*****g cancer.
I was trying to code a real time system and had to use pointers to structures and I can´t even remember what else all cause they couldn´t include a transmission ready bit on the HW level.
Cancer, simply cancer and these are not things you would thing of on the designing phase. But I digress! Now I get the cancer from the PWM set up in PICs.
You spend more time designing how your functions want to be rather than implementing them... pff...
PS: Important Important! I´ve been doing some digging in the compiler files, so of course there is a "rather cancerous" way to do it the AVR way, of course few must know of this.
For my example it would be:Code: [Select] (https://www.eevblog.com/forum/javascript:void(0);)PSTR1CON |= (1<<_PSTR1CON_STRA_POSN) | (1<<_PSTR1CON_STRB_POSN) | (1<<_PSTR1CON_STRC_POSN);
Cancer but it is exactly the same as in AVRs and this is well hidden! I had to look in the microcontroller header file, that´s a 46k line header file, although repetitive you can miss what you are looking for!
Cheers,
Lefteris
-
XC8 does provide suitable #defines for the bit positions etc. for that style of register bit manipulation across all the 8 bit PIC ranges, but it is NOT at all well documented. I haven't looked in the latest manual to see if they are mentioned but I believe you still have to dig in the headers.
e.g. here's the pic16f84.h OPTION register definitions:
// Register: OPTION_REG
extern volatile unsigned char OPTION_REG @ 0x081;
#ifndef _LIB_BUILD
asm("OPTION_REG equ 081h");
#endif
// bitfield definitions
typedef union {
struct {
unsigned PS :3;
unsigned PSA :1;
unsigned T0SE :1;
unsigned T0CS :1;
unsigned INTEDG :1;
unsigned nRBPU :1;
};
struct {
unsigned PS0 :1;
unsigned PS1 :1;
unsigned PS2 :1;
};
} OPTION_REGbits_t;
extern volatile OPTION_REGbits_t OPTION_REGbits @ 0x081;
// bitfield macros
#define _OPTION_REG_PS_POSN 0x0
#define _OPTION_REG_PS_POSITION 0x0
#define _OPTION_REG_PS_SIZE 0x3
#define _OPTION_REG_PS_LENGTH 0x3
#define _OPTION_REG_PS_MASK 0x7
#define _OPTION_REG_PSA_POSN 0x3
#define _OPTION_REG_PSA_POSITION 0x3
#define _OPTION_REG_PSA_SIZE 0x1
#define _OPTION_REG_PSA_LENGTH 0x1
#define _OPTION_REG_PSA_MASK 0x8
#define _OPTION_REG_T0SE_POSN 0x4
#define _OPTION_REG_T0SE_POSITION 0x4
#define _OPTION_REG_T0SE_SIZE 0x1
#define _OPTION_REG_T0SE_LENGTH 0x1
#define _OPTION_REG_T0SE_MASK 0x10
#define _OPTION_REG_T0CS_POSN 0x5
#define _OPTION_REG_T0CS_POSITION 0x5
#define _OPTION_REG_T0CS_SIZE 0x1
#define _OPTION_REG_T0CS_LENGTH 0x1
#define _OPTION_REG_T0CS_MASK 0x20
#define _OPTION_REG_INTEDG_POSN 0x6
#define _OPTION_REG_INTEDG_POSITION 0x6
#define _OPTION_REG_INTEDG_SIZE 0x1
#define _OPTION_REG_INTEDG_LENGTH 0x1
#define _OPTION_REG_INTEDG_MASK 0x40
#define _OPTION_REG_nRBPU_POSN 0x7
#define _OPTION_REG_nRBPU_POSITION 0x7
#define _OPTION_REG_nRBPU_SIZE 0x1
#define _OPTION_REG_nRBPU_LENGTH 0x1
#define _OPTION_REG_nRBPU_MASK 0x80
#define _OPTION_REG_PS0_POSN 0x0
#define _OPTION_REG_PS0_POSITION 0x0
#define _OPTION_REG_PS0_SIZE 0x1
#define _OPTION_REG_PS0_LENGTH 0x1
#define _OPTION_REG_PS0_MASK 0x1
#define _OPTION_REG_PS1_POSN 0x1
#define _OPTION_REG_PS1_POSITION 0x1
#define _OPTION_REG_PS1_SIZE 0x1
#define _OPTION_REG_PS1_LENGTH 0x1
#define _OPTION_REG_PS1_MASK 0x2
#define _OPTION_REG_PS2_POSN 0x2
#define _OPTION_REG_PS2_POSITION 0x2
#define _OPTION_REG_PS2_SIZE 0x1
#define _OPTION_REG_PS2_LENGTH 0x1
#define _OPTION_REG_PS2_MASK 0x4
Note the presence of OPTION_REG_bits.PS which allows the whole prescaler ratio to be set or read in a single assignment. Other SFRs that have meaningful multi-bit fields smaller than the SFR size have similar multi-bit fields defined - see your PIC's device specific header for details.
The #defines you need are the _SFR_BIT_POSN, which gives the position of the bit (or of bit 0 of a multi-bit field), _SFR_BIT_SIZE, which gives the width of the field, and _SFR_BIT_MASK, which gives a '1's mask for it. ..._POSITION and ..._LENGTH are verbose aliases for ..._POSN and ..._SIZE, so ignore them!
The ..._MASK ones are most useful for 'AVR style' SFR bit manipulation. e.g your latest code in your P.S. becomes:
PSTR1CON |= _PSTR1CON_STRA_MASK | _PSTR1CON_STRB_MASK | _PSTR1CON_STRC_MASK;
which is far less fugly. Obviously to clear those bits you'd simply AND with the complement of the combined masks:
PSTR1CON &= ~(_PSTR1CON_STRA_MASK | _PSTR1CON_STRB_MASK | _PSTR1CON_STRC_MASK);
Also see the thread bitset bitclear C @microchip.com (http://www.microchip.com/forums/FindPost/518913), for assigning values to multiple bits simultaniously without causing any glitches (important on ports or if one of the bits in the SFR triggers a peripheral action) using XOR.
There are also the legacy bit variables, of the same type as you originally found, and ASPIC assembler named bit support. For clarity, I've pruned out SFR bits unrelated to OPTION_REG:
/*
* Bit Definitions
* */
#define _DEPRECATED __attribute__((__deprecated__))
#ifndef BANKMASK
#define BANKMASK(addr) ((addr)&07Fh)
#endif
extern volatile __bit INTEDG @ (((unsigned) &OPTION_REG)*8) + 6;
#define INTEDG_bit BANKMASK(OPTION_REG), 6
extern volatile __bit PS0 @ (((unsigned) &OPTION_REG)*8) + 0;
#define PS0_bit BANKMASK(OPTION_REG), 0
extern volatile __bit PS1 @ (((unsigned) &OPTION_REG)*8) + 1;
#define PS1_bit BANKMASK(OPTION_REG), 1
extern volatile __bit PS2 @ (((unsigned) &OPTION_REG)*8) + 2;
#define PS2_bit BANKMASK(OPTION_REG), 2
extern volatile __bit PSA @ (((unsigned) &OPTION_REG)*8) + 3;
#define PSA_bit BANKMASK(OPTION_REG), 3
extern volatile __bit T0CS @ (((unsigned) &OPTION_REG)*8) + 5;
#define T0CS_bit BANKMASK(OPTION_REG), 5
extern volatile __bit T0SE @ (((unsigned) &OPTION_REG)*8) + 4;
#define T0SE_bit BANKMASK(OPTION_REG), 4
extern volatile __bit nRBPU @ (((unsigned) &OPTION_REG)*8) + 7;
#define nRBPU_bit BANKMASK(OPTION_REG), 7
It should be noted that the legacy SFR single bit variables are *NOT* implemented on many new devices and should be avoided if you want your code to be reasonably future-proof. Even when they are implemented, some may be missing due to bit name conflicts. The xxxx_bit defines are provided for inline assembler programming only, as they are virtually useless in C.
-
It should be noted that the legacy SFR single bit variables are *NOT* implemented on many new devices and should be avoided if you want your code to be reasonably future-proof. Even when they are implemented, some may be missing due to bit name conflicts. The xxxx_bit defines are provided for inline assembler programming only, as they are virtually useless in C.
I have noticed these occasionally in source code from projects published on the web, often a mix of just using the single-bit variable, along with the "xxxxbits.xxx" structure, and even the aforementioned macros (our over-used example PSTR1CON |= (1<<bit1) | (1<<bit2) | (1<<bit3);) .... all in the same file.
That has bugged/irritated me no-end, and I'm not exactly a neat programmer. My guess is, many things are just copied/pasted from project to project, which is fine (I do it far too often) but can bite you in the ass later on when it points out something is undefined. I'll also have to admit I was rather confused when I peeked at the include files for XC8, with so many definitions for the same register - but I guess that can account for different preferences.
I've also used MikroC a fair bit, and that seems to make much more sense - GCC style - with the downsides that it isn't free, and that you end up using their libraries - which are very handy, work fine but..there is no readable source. I could whine about XC8, but frankly it does the job. Somewhat verbosely, but I'll get used to it - eventually.
To the OP, microchips own application libraries come with a boat-load of example projects, that all seem to be very consistent with conventions. These would be a good place to see what the preferred (as in, fully supported across all devices) macros are.