News:

Let's find out together what makes a PIC Tick!

Main Menu

Multi BIT symbols

Started by SeanG_65, Apr 02, 2023, 01:00 AM

Previous topic - Next topic

SeanG_65

I vaguely remember reading that it was possible to do things like;

symbol NCO_CLKsrc = NCO1CLK.0to1

Which, logically, would assign BITs 0 & 1 of the NCO1CLK register of the 16F1507 I am using to the symbol NCO_CLKsrc as in VHDL which would be;

variable v_Choices : std_logic_vector(1 downto 0)

There are other bits in that register I don't want to change, and short of reading the register then setting the bits I want then writing that value to the register, I thought it could be done like I have done it in the first code line. Oddly, it seems to compile OK but I can't find any reference to it in the manual.

Is this doable, have I done it right, and if not, how SHOULD I have done it please?

tumbleweed

QuoteOddly, it seems to compile OK
It does, but it's not doing what you want.

If you take a look at the asm output that results in:
symbol NCO_CLKsrc = NCO1CLK.0to1

; ALIAS VARIABLES
#define NCO_CLKsrc NCO1CLK,0

so it's only referencing a single bit (the first number given in the ".0to1")

AFAIK, there's no way to do multiple bits... you have to read, mask/set, and update the byte.


RGV250

Hi,
I cannot make out exactly what you want to do with your example but could you not just do a macro that when called sets the bits or possibly a proceedure.

Bob

SeanG_65

I want to change TWO BITS of the NCO register in a PIC as I read that changing other bits can cause problems.

RGV250

Hi,
If you dont need to change it on the fly while the program is running if it were me I would just put this in my init code.
NCO1CLK.0 = 1
NCO1CLK.1 = 1

Bob

top204

That casting type has never been part of the compiler, and its principle is a bit more involved that it looks, and sometimes rather wasteful of code space. Altering multiple bits within a variable or SFR requires masking with ANDing with 0, to clear the bits first, then ORing in the new value of the bits. But if the bits are further up the assignment variable or SFR, the value to write to them needs shifting left into the correct position before the ORing takes place.

The example below shows a method that can be used:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Demonstrate masking to alter more than one bit within a variable or SFR
'
' Written for the Positron8 compiler by Les Johnson.
'
    Device = 18F25K20                               ' Tell the compiler what device to compile for
    Declare Xtal = 16                               ' Tell the compiler what frequency the device will be operating at (in MHz)
'
' Setup USART1
'
    Declare Hserial1_Baud = 9600                    ' Set the Baud rate to 9600
    Declare HRSOut1_Pin = PORTC.6                   ' Set the TX pin

    Dim MySFR1 As Byte
   
    Symbol cMaskForBits01 = %11111100               ' Create a mask for bits 0 and 1
    Symbol cMaskForBits234 = %11100011              ' Create a mask for bits 2, 3 and 4
   
'-------------------------------------------------------------------------
' Use masking to alter more than one bit within a variable or SFR
' Input     : pValue holds the value to place into the bits
' Output    : MySFR1 will have its bits 0 and 1 changed
' Notes     : The principle works with ANDs and ORs...
'           : First, the two bits are cleared by ANDing with a mask that represents the bits required to alter.
'           : Then the new bits are ORed into the cleared bits, to place the required value.
'           : This works as it is if the bits are the LSB types, because pValue can be loaded directly into the assignment.
'
$define MySFR1_LoadBits01(pValue)    '
    MySFR1 = MySFR1 & cMaskForBits01 '
    MySFR1 = MySFR1 | pValue
 
'-------------------------------------------------------------------------
' Use masking and shifting to alter more than one bit within a variable or SFR
' Input     : pValue holds the value to place into the bits
' Output    : MySFR1 will have its bits 2, 3, and 4 changed
' Notes     : The principle works with ANDs and ORs...
'           : First... The three bits are cleared by ANDing with a mask that represents the bits required to alter.
'           : Second... The value is shifted left so it fits into the bits position.
'           : Then the new bits are ORed into the cleared bits, to place the required value.
'           : The shift left is a must, otherwise, pValue would simply be placed into the LSBs of the assignment.
'
$define MySFR1_LoadBits234(pValue)    '
    MySFR1 = MySFR1 & cMaskForBits234 '
    WREG = pValue << 2                '
    MySFR1 = MySFR1 | WREG
          
'-------------------------------------------------------------------------
' The main program starts here
'
Main:
    MySFR1 = %10000000                          ' Load MySFR1 with a value, to make sure it remains in place
    HRSOutLn "Raw    : ", Bin8 MySFR1           ' Transmit its binary value to a serial terminal
   
    MySFR1_LoadBits01(1)                        ' Alter bits 0 and 1 of MySFR1 only
    HRSOutLn "Mask01 : ", Bin8 MySFR1           ' Transmit its binary value to a serial terminal
   
    MySFR1_LoadBits234(5)                       ' Alter bits 2, 3, and 4 of MySFR1 only   
    HRSOutLn "Mask234: ", Bin8 MySFR1           ' Transmit its binary value to a serial terminal

With the demo program above, the serial terminal will contain the texts:

Raw    : 10000000
Mask01 : 10000001
Mask234: 10010101


Where Raw is the initial value placed in "MySFR1", to make sure it is not altered after the bit maskings.
Mask01 is the value after a value of 1 (0b00000001) is written to bits 0 and 1 only of "MySFR1".
Mask234 is the value after a value of 5 (0b00000101) is written to bits 2, 3 and 4 only of "MySFR1".

Notice the mask constants for the bits required? : cMaskForBits01  and cMaskForBits234. These hold the positions of the bits to be written, so they can be cleared first.

The same type of mechanism can be used to read multiple bits only from a variable or SFR using a mask constant value and AND and shifts right.

The ANSI C language had an addition made to it many, many years ago for "Unions" that would handle bit operations using masking and shifts, because the original C language did not handle bit types. But as usual, the code produced can be rather large for such a straightforward mechanism on an 8-bit microcontroller, but it is sometimes handy to use. I sometimes create the above type preprocessor meta-macros for prescaler values within Timer SFRs.

I was pondering over adding a Union type to the compilers, but it would take a very large amount of time to do so for every variable type and iteration of the variables within the compilers, and brand new additions for "ALL" SFRs, within "ALL" .PPI files. i.e. Adding the Masking and Shifting constant values for every single and combination of bits for every SFR within every device! And for such a small benefit, it was not worth it when it can be accomplished with the preprocessor when required.

top204

#6
I do use the above masking mechanism sometimes, by my personal preference for altering multiple bits within a variable or SFR is to use a pre-processor meta-macro and its comparisons to do the task, because they are easier to create and can be copied and pasted and used multiple times for different bits and SFRs with such small changes made to them. For example:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Demonstrate a preprocessor meta-macro mechanism to alter more than one bit within a variable or SFR
'
' Written for the Positron8 compiler by Les Johnson.
'
    Device = 18F25K20                               ' Tell the compiler what device to compile for
    Declare Xtal = 16                               ' Tell the compiler what frequency the device will be operating at (in MHz)
'
' Setup USART1
'
    Declare Hserial1_Baud = 9600                    ' Set the Baud rate to 9600
    Declare HRSOut1_Pin = PORTC.6                   ' Set the TX pin

    Dim MySFR1 As Byte                              ' Create a variable to be be altered bit-wise
       
'-------------------------------------------------------------------------
' Use bit manipulation to alter more than one bit within a variable or SFR
' Input     : pValue holds the constant value to place into the bits
' Output    : MySFR1 will have its bits 0 and 1 changed
' Notes     : None
'
$define MySFR1_LoadBits01(pValue) '
    $if pValue = 0                '
        MySFR1.0 = 0              '
        MySFR1.1 = 0              '
    $elseif pValue = 1            '
        MySFR1.0 = 1              '
        MySFR1.1 = 0              '
    $elseif pValue= 2             '
        MySFR1.0 = 0              '
        MySFR1.1 = 1              '
    $elseif pValue= 3             '
        MySFR1.0 = 1              '
        MySFR1.1 = 1              '
    $else                         '
        $error "MySFR1_LoadBits01 can only handle a constant value from 0 to 3" '
    $endif
   
'-------------------------------------------------------------------------
' Use bit manipulation to alter more than one bit within a variable or SFR
' Input     : pValue holds the constant value to place into the bits
' Output    : MySFR1 will have its bits 2, 3, and 4 changed
' Notes     : None
'
$define MySFR1_LoadBits234(pValue) '
    $if pValue = 0                 '
        MySFR1.2 = 0               '
        MySFR1.3 = 0               '
        MySFR1.4 = 0               '
    $elseif pValue = 1             '
        MySFR1.2 = 1               '
        MySFR1.3 = 0               '
        MySFR1.4 = 0               '
    $elseif pValue= 2              '
        MySFR1.2 = 0               '
        MySFR1.3 = 1               '
        MySFR1.4 = 0               '
    $elseif pValue= 3              '
        MySFR1.2 = 1               '
        MySFR1.3 = 1               '
        MySFR1.4 = 0               '
    $elseif pValue= 4              '
        MySFR1.2 = 0               '
        MySFR1.3 = 0               '
        MySFR1.4 = 1               '
    $elseif pValue= 5              '
        MySFR1.2 = 1               '
        MySFR1.3 = 0               '
        MySFR1.4 = 1               '
    $elseif pValue= 6              '
        MySFR1.2 = 0               '
        MySFR1.3 = 1               '
        MySFR1.4 = 1               '
    $elseif pValue= 7              '
        MySFR1.2 = 1               '
        MySFR1.3 = 1               '
        MySFR1.4 = 1               '
    $else                          '
        $error "MySFR1_LoadBits234 can only handle a constant value from 0 to 7" '
    $endif         
          
'-------------------------------------------------------------------------
' The main program starts here
'
Main:
    MySFR1 = %10000000                          ' Load MySFR1 with a value, to make sure it remains in place
    HRSOutLn "Raw    : ", Bin8 MySFR1           ' Transmit its binary value to a serial terminal
   
    MySFR1_LoadBits01(2)                        ' Alter bits 0 and 1 of MySFR1 only
    HRSOutLn "Mask01 : ", Bin8 MySFR1           ' Transmit its binary value to a serial terminal
   
    MySFR1_LoadBits234(5)                       ' Alter bits 2, 3, and 4 of MySFR1 only   
    HRSOutLn "Mask234: ", Bin8 MySFR1           ' Transmit its binary value to a serial terminal

I know the meta-macros look large and bulky in the program's listing, but the above program's operation is quite efficient and very easy to alter for other SFRs and bits within an SFR, and when the meta-macros are placed in an Include file, they do not bulk up the main program's listing at all.

For example, the MySFR1_LoadBits01 meta-macro will produce the assembler code:

bcf MySFR1,0,0
bsf MySFR1,1,0


And the MySFR1_LoadBits234 meta-macro will produce the assembler code:

bsf MySFR1,2,0
bcf MySFR1,3,0
bsf MySFR1,4,0


Not forgetting if the SFR or variable is in a banked RAM area, the above meta-macro machanisms will alter the RAM bank once only and remember it when altering the other bits of the variable or SFR, instead of the multiple RAM bank changes that may be required when masking. Even when using the WREG SFR as a temporary workspace on some of the newer devices!

And if an incorrect constant value is passed to them, they will produce an error message.


Pepe

i think i should say

$elseif pValue= 3            '
        MySFR1.0 = 1              '
        MySFR1.1 = 1              '
    $else                         '
        $error "MySFR1_LoadBits01 can only handle a constant value from 0 to 3" '
    $endif

top204

#8
Whoops.... Well spotted Pepe, and thanks.

Now changed to:

$elseif pValue = 3

instead of the original double comparison of:

$elseif pValue = 2

But it shows how easy it is to change. :-)

SeanG_65

BRILLIANT!!

I used this method MANY years ago when programming Z80 in assembler, but I do so little coding I often forget how to do things, and despite searching for code examples, I have little luck. You guys rock. THANKS!

RGV250

Hi Les,
Is it possible to use a port directly in place of MySFR1, I tried and it did not work as expected, the only way I could get it to work is to load MySFR1 with the port data before calling it. I assume this is because it is using the modified data at every operation in the routine. I did wonder if I could specify the port in the parameters to tidy the code up.

    PORTB.4 = 1
    MySFR1 = PORTB
    PortB_RowBits(RowCount)                         ' Alter bits 0 - 3 of Port B

Bob

RGV250

Hi,
This half works, is it possible to have a return value as I will need to use it for PortC later.

$define PortB_RowBits (Port,pValue)  '
    MySFR1 = PortNr                    '
    MySFR1 = MySFR1 & cMaskForBits0123  '
    MySFR1 = MySFR1 | pValue            '
    PORTB = MySFR1

top204

#12
When using a Port, you will need to use a phantom variable to hold the Port's value, because the Port can be Input or Output and the Read Before Modify anomaly can creeep into it if the Port is read when it is set as an Output. Also, the compiler always writes directly to a Port's LAT SFR to eliminate the RBM anomaly on devices that have easy access to the LAT SFRs, and this is not always passed on immediately to the PORT SFR by the microcontroller.

The Phantom byte is always loaded with what will be represented on the port, then transferred to the Port itself as a single byte transfer.

If using a standard 18F device, I usually use either PRODL or PRODH SFRs for temporary, short term, variables because they are normally only used for multiplications or commands that require multiplications carried out in them. Also, in the standard 18F devices, they are RAM bankless, so operate a lot more efficiently.

RGV250

Hi Les,
It looks like I was overthinking the return variable.
This works as expected now, are you saying that I could replace MySFR1 with PRODL to save a byte?
$define PortBits_0123 (Port,pValue)   '
    MySFR1 = Port                     '
    MySFR1 = MySFR1 & cMaskForBits0123  '
    MySFR1 = MySFR1 | pValue            '
    Port = MySFR1

'implementation   
    PortBits_0123(PORTB,RowCount)                         ' Alter bits 0 - 3 of Port B
    PortBits_0123(PORTC,ColCount)                         ' Alter bits 0 - 3 of Port C       

top204

#14
Glad you got it working, but be careful that some pins set as inputs are not effected when reading the port's value, because they may also be transferred into the final port value. Reading and changing, then writing to a port does work in a controlled environment, but in the "real word", I always find it better to have a global phantom variable that holds the port's state. Then no external influences will change its value, only the program itself can change it, then transfer the global phantom variable to the port when required.

With 18F devices, the PRODL SFR will work nicely in that position:

$define PortBits_0123(pPort, pValue) '
    PRODL = pPort                    '
    PRODL = PRODL & %11110000        '
    PRODL = PRODL | pValue           '
    pPort = PRODL

And produce the very clean assembler code:

    movff PORTB,PRODL
    movlw 240
    andwf PRODL,F,0
    movlw 5
    iorwf PRODL,F,0
    movff PRODL,PORTB

It should also be possible to replace the PRODL SFR with the WREG SFR in your meta-macro, because the compiler recognises when WREG is used in very small expressions and changes the assembler code to suit where it can be changed. This produces even tigher and faster code. But always check the assembler code to make sure WREG is not being over-written as a temporary workhorse variable, because that is actually what it is in the microcontroller. i.e. Working Register.