Menu

#67 PIC14-PIC16 bitwise manipulation of registers and variables

closed
None
5
2011-11-05
2009-11-13
z-control
No

Hi
I am comming from CCS and GNUAVR, and I am missing some background about how to manipulate bit-level registers or variables.

Typically, the pic has the ASM functions such as BCF, BSF, BTFSC, BTFSS.

I currently only see a solution to read a full byte (register or variable) to set or clear or test a bit, for set or clear write it back to the right place.
Also I would like to not only set or clear a given bit, ie RA1 or RA2, but make this variable.
Ie, I want to have a routine that sets or clears a bit 1,2,3,4... in a raw.

In GNUAVR, I know the _BV function, in CCS, you find BIT_SET(reg+var/reg,bit), BIT_CLEAR(reg+var,bit), BIT_TEST(reg+var,bit).

is this a missing function in SDCC ?

Thanks for any help.

Discussion

  • Raphael Neider

    Raphael Neider - 2009-11-13
    • status: open --> pending
     
  • Raphael Neider

    Raphael Neider - 2009-11-13

    Hi,

    you can use

    #define BIT_SET(VAR, BIT) VAR |= (1 << (BIT))
    #define BIT_CLR(VAR, BIT) VAR &= ~(1 << (BIT))
    #define BIT_TEST(VAR, BIT) ((VAR) & (1 << (BIT)))

    SDCC will automatically replace single-bit accesses with the bit-manipulation instructions offered by the target architecture.

    I do not know what 'reg+var' is supposed to mean, but the above should get you started.

    Best regards

    Raphael

     
  • z-control

    z-control - 2009-11-13

    thank you for the input, I must make something wrong as I tried your code couple of days ago. I just checked again and copied the 2 options I had out of an interrupt routine:
    ...
    #define BIT_SET(VAR, BIT) VAR |= (1 << (BIT))
    #define BIT_CLR(VAR, BIT) VAR &= ~(1 << (BIT))
    #define BIT_TST(VAR, BIT) ((VAR) & (1 << (BIT)))
    ....
    if(++raw==8)raw=0;
    tmp=0xff;
    tmp=tmp ^ (1 << raw); // enable led raw (active low)
    PORTB=tmp;
    //the above code works, each bit of PORTB gets 0 in a raw.
    // but with your macro it would be simpler but that does not work !
    // is it only limited to variables and not to registers ?
    PORTB=0xff;
    BIT_CLR(PORTB,raw);
    // should do the same, but does not work
    // I am using a 16f887

    P.S. with VAR/REG I ment applicable to variables and PIC registers within SDCC.

     
  • z-control

    z-control - 2009-11-13
    • status: pending --> open
     
  • Raphael Neider

    Raphael Neider - 2009-11-13

    What does "does not work" mean?

    If all the PORTB-pins are 0, you might not have set them up as digital pins. Try
    ANSEL=ANSELH=0;
    before modifying the register (ANSELH is the important one for PORTB, see datasheet):
    If you assign tmp to PORTB, the hardware overwrites all bits and you are fine.
    If you clear individual bits, the hardware executes read-modify-write and -- unfortunately -- all pins read as 0 when configured as analog inputs (the power-on default, ANSEL=ANSELH=0xff).

    BTW: If you want to specify the bit to modify at runtime, the hardware cannot use BCF/BSF/BTF/... because these require a literal (compile-time constant) bit index. The compiler will generate a loop for (1 << (BIT)) and modify the PORT via IORWF or ANDWF instead.

     
  • z-control

    z-control - 2010-01-27

    Compiler efficiency or missing optimizer flag?

    Hi I discovered that the compiler is handling the bit code in different ways, is this a mistake from my side of using them or a bug/enhancement in the compiler optimizer ?

    example using the following definitions:
    #define BIT_SET(VAR, BIT) VAR |= (1 << (BIT))
    #define BIT_CLR(VAR, BIT) VAR &= ~(1 << (BIT))
    #define BIT_TEST(VAR, BIT) ((VAR) & (1 << (BIT)))

    // fonctions specifics a notre example
    #define test_bPousAck BIT_TEST(PousAck,0)
    #define set_bAck BIT_SET(PousAck,0)
    #define clear_bAck BIT_CLR(PousAck,0)
    #define test_bPous BIT_TEST(PORTC,2)

    char test_poussoir(void)
    {
    // on a pressé

    if(!test_bPousAck) {if( test_bPous==0) {set_bAck; return 1;}}

    // si non, toujours pressé on ne fait rien
    // état pas pressé
    else
    if(test_bPous) clear_bAck;
    // si non on ne fait rien
    return 0;
    }
    generates:
    _test_poussoir ;Function start
    ; 2 exit points
    ; .line 74; "test_bouton.c" if(!test_bPousAck) {if( test_bPous==0) {set_bAck; return 1;}}
    BTFSC _PousAck,0
    GOTO _00147_DS_
    BANKSEL _GPIO
    BTFSC _GPIO,2
    GOTO _00148_DS_
    BSF _PousAck,0
    MOVLW 0x01
    GOTO _00149_DS_
    _00147_DS_
    ; .line 79; "test_bouton.c" if(test_bPous) clear_bAck;
    BANKSEL _GPIO
    BTFSS _GPIO,2
    GOTO _00148_DS_
    BCF _PousAck,0
    _00148_DS_
    ; .line 81; "test_bouton.c" return 0;
    MOVLW 0x00
    _00149_DS_
    RETURN

    which is ok/perfect, but I only found this after many tests, I would prefere to write

    char test_poussoir(void)
    {
    // on a pressé

    if((!test_bPousAck)&( test_bPous==0)) {set_bAck; return 1;}

    // si non, toujours pressé on ne fait rien
    // état pas pressé
    else
    if(test_bPous) clear_bAck;
    // si non on ne fait rien
    return 0;
    }

    but this code which in my mind is equivalent generates much more code

    ;; Starting pCode block
    _test_poussoir ;Function start
    ; 2 exit points
    ; .line 74; "test_bouton.c" if((!test_bPousAck)&( test_bPous==0)) {set_bAck; return 1;}
    MOVLW 0x01
    ANDWF _PousAck,W
    MOVWF r0x1001
    MOVF r0x1001,W
    MOVLW 0x00
    BTFSC STATUS,2
    MOVLW 0x01
    MOVWF r0x1002
    MOVLW 0x04
    BANKSEL _GPIO
    ANDWF _GPIO,W
    MOVWF r0x1001
    MOVF r0x1001,W
    MOVLW 0x00
    BTFSC STATUS,2
    MOVLW 0x01
    ;;1 MOVWF r0x1003
    ANDWF r0x1002,F
    MOVF r0x1002,W
    BTFSC STATUS,2
    GOTO _00145_DS_
    BSF _PousAck,0
    MOVLW 0x01
    GOTO _00147_DS_
    _00145_DS_
    ; .line 79; "test_bouton.c" if(test_bPous) clear_bAck;
    BANKSEL _GPIO
    BTFSS _GPIO,2
    GOTO _00146_DS_
    BCF _PousAck,0
    _00146_DS_
    ; .line 81; "test_bouton.c" return 0;
    MOVLW 0x00
    _00147_DS_
    RETURN
    ; exit point of _test_poussoir

    ignoring the bit maniplulation.
    Is there any trick, or am I doing any mistake using SDCC ?

    Thanks for any advice
    Rolf

     
  • Raphael Neider

    Raphael Neider - 2010-01-27

    if ((!test_bPousAck)&( test_bPous==0)) {set_bAck; return 1;}

    You use bitwise AND '&', which is (in this case) pretty inefficient. Using logical AND '&&' yields much better results for me:

    _test_poussoir ;Function start
    ; 2 exit points
    ; .line 20; "bit-cond.c" if ((!test_bPousAck) && (test_bPous == 0)) {
    BANKSEL _PousAck
    BTFSC _PousAck,0
    GOTO _00108_DS_
    BANKSEL _PORTC
    BTFSC _PORTC,2
    GOTO _00108_DS_
    ; .line 21; "bit-cond.c" set_bAck; return 1;
    BANKSEL _PousAck
    BSF _PousAck,0
    MOVLW 0x01
    GOTO _00111_DS_
    _00108_DS_
    ; .line 27; "bit-cond.c" if (test_bPous)
    BANKSEL _PORTC
    BTFSS _PORTC,2
    GOTO _00109_DS_
    ; .line 28; "bit-cond.c" clear_bAck;
    BANKSEL _PousAck
    BCF _PousAck,0
    _00109_DS_
    ; .line 31; "bit-cond.c" return 0;
    MOVLW 0x00
    _00111_DS_
    RETURN

    Best regards

    Raphael

     
  • Maarten Brock

    Maarten Brock - 2011-11-05

    Looks like a closed discussion.

     
  • Maarten Brock

    Maarten Brock - 2011-11-05
    • assigned_to: nobody --> tecodev
    • status: open --> closed
     

Log in to post a comment.