PIC assembly - swapping #define

Thread Starter

redtree

Joined Feb 3, 2014
14
I'm using a PIC16F1933 for some simple switch detection stuff.
I have a #define statement to map the PIC pins to a text name used within the assembly program.
I'd like to be able to change that definition within the program.

eg. the name SW_1 detects an input from PORTA,0 normally, but if some other condition is met, I'd like SW_1 to detect an input from PORTB,1 and still use the name SW_1

The goal is to reference the NAME within the body of the program while changing SOURCE depending on other conditions.

Is this (easily) possible? ... how?

Thanks
 

Papabravo

Joined Feb 24, 2006
21,159
You can use conditional compilation.

#ifdef __SYMBOL1__
#define SW_1 PORTA,0
#endif

#ifdef __SYMBOL2__
#define SW_1 PORTC,2
#endif

Now someplace else you have to define either __SYMBOL1__ or __SYMBOL2__ and one of the above will be included in the resulting build.

I generally enclose such symbols in double underscores to prevent confusion with actual symbols that I might use. Checkout #else, #elsif, and #ifndef

You compiler may implement additional idioms like #if defined(x) or #if !defined(x) for these functions. Read the compiler/assemble manual carefully.
 

JohnInTX

Joined Jun 26, 2012
4,787
One way is to use a flag, test it and jump to the routine with hard-coded IO.

Other, more general ways are:
Implement indirect reads of the port with FSR and a mask set up to extract the particular bit i.e.
Rich (BB code):
movf INDF0,W
andwf MaskInRam,W
btfsc STATUS,Z
goto bit_is_1
If the IO is related to a system mode, you can use the mode indicator as an index into a table of IO references, either Port address/bit mask for the FSR above or a list of goto the btfsc test on hard-coded ports.

You also could read everything using movf PORT,W movwf someRAM for all ports then test bits in the byte array according to your current state.

Have fun!

EDIT: PapaBravo's method works for build time. I thought you meant at run-time?
 

takao21203

Joined Apr 28, 2012
3,702
I'm using a PIC16F1933 for some simple switch detection stuff.
I have a #define statement to map the PIC pins to a text name used within the assembly program.
I'd like to be able to change that definition within the program.

eg. the name SW_1 detects an input from PORTA,0 normally, but if some other condition is met, I'd like SW_1 to detect an input from PORTB,1 and still use the name SW_1

The goal is to reference the NAME within the body of the program while changing SOURCE depending on other conditions.

Is this (easily) possible? ... how?

Thanks
defines are processed at assembly time. what you want is possible with the help of patching tables.

You need to set/reset/toggle bits in a byte with a numerical index, and there is no instruction for that on 16F. It can be done however with table reads.

It is possible in assembler but if you use table reads a lot, it will produce a lot of source code, in C it is just a few lines.
 

Papabravo

Joined Feb 24, 2006
21,159
I understood the question to be related to using the same source code on multiple platforms without ANY execution time penalty for figuring out which platform you are on. Conditional assembly/compilation is just the ticket for making a single source file build for either platform A or platform B depending on a symbol which might be defined in the IDE rather than in ANY source file.
 

Thread Starter

redtree

Joined Feb 3, 2014
14
Thanks PapaBravo!
I think your first idea is what I want to do.
The others ideas are beyond my limited comprehension abilities.

But, can I use the "ifdef" condition as an input parameter of the circuit?
To further explain: I have a multi-positional BCD encoded rotary switch which I want to use as my "condition"

If position "0" ; use Port A,0
if position "1" ; use Port A, 3
if position "2" ; use Port b, 3
....

Thanks much!
 

JohnInTX

Joined Jun 26, 2012
4,787
Thanks PapaBravo!
I think your first idea is what I want to do.
The others ideas are beyond my limited comprehension abilities.

But, can I use the "ifdef" condition as an input parameter of the circuit?
To further explain: I have a multi-positional BCD encoded rotary switch which I want to use as my "condition"

If position "0" ; use Port A,0
if position "1" ; use Port A, 3
if position "2" ; use Port b, 3
....

Thanks much!
PB's method is perfectly suited for changes that occur during 'build-time' i.e. when you actually compile the code. The result hard-codes the port access to the definitions in place when the code was built. After that, no changes. Any remapping of the ports would entail changing the #defines, re-assembling and re-programming the chip with the new hard-coded port values. Using #defines as PB illustrates is an excellent way to maintain different IO maps for different PC board revisions etc. But its not suitable for changes that need to happen at run-time i.e. as a result of a change in a system input.

Changes that read the encoder switch and remap the ports on the fly are run-time changes. You can't (easily) re-define the usual IO tests (btfsc PORT,BIT etc) because they are burned into ROM. You CAN do it by either pointing at the port using indirect addressing (through the FSR) and a stored mask -OR- having multiple instances of hardcoded btfsc-type port access and jumping to the appropriate ones based on your switch settings.

The PIC16F1933 has decent facilities to do all of this. If indeed you need run-time changes its not too difficult to do.
 
Last edited:

Papabravo

Joined Feb 24, 2006
21,159
PB's method is perfectly suited for changes that occur during 'build-time' i.e. when you actually compile the code. The result hard-codes the port access to the definitions in place when the code was built. After that, no changes. Any remapping of the ports would entail changing the #defines, re-assembling and re-programming the chip with the new hard-coded port values. Using #defines as PB illustrates is an excellent way to maintain different IO maps for different PC board revisions etc. But its not suitable for changes that need to happen at run-time i.e. as a result of a change in a system input.

Changes that read the encoder switch and remap the ports on the fly are run-time changes. You can't (easily) re-define the usual IO tests (btfsc PORT,BIT etc) because they are burned into ROM. You CAN do it by either pointing at the port using indirect addressing (through the FSR) and a stored mask -OR- having multiple instances of hardcoded btfsc-type port access and jumping to the appropriate ones based on your switch settings.

The PIC16F1933 has decent facilities to do all of this. If indeed you need run-time changes its not too difficult to do.
What he said is spot on. The #define, also known as compile time macro text substitution, only works at the time a program is compiled or assembled. It cannot predict the future and as such cannot respond to conditions which occur at run time. You can of course write code that evaluates run time conditions and responds accordingly but you pay a cost penalty in execution time, or program memory space or data memory space. All of these cost penalties you gladly pay to get the functionality required by your application.
 

Thread Starter

redtree

Joined Feb 3, 2014
14
OK, thank you.
Being an extreme newbie, I'm not sure how to accomplish this.
So, I'd like to re-use the MAIN code, just change the inputs to it.
I'm kind of familiar with the FSR and how to basically use it.
My question is how to use it with different inputs.
I was originally using the #define to set "input_A" to Port A,0 and then using "input_A" within the body of the program.
How do I do this in assembly?

Thank You for your patience!
 

JohnInTX

Joined Jun 26, 2012
4,787
OK, so ports are shuffled at run-time. A few questions first:

How many different values can the BCD switch have? You mentioned 0-2, are there more?

How many port,bit IO lines are being relocated for each position? Just one? a few? Remapping the whole chip?

Does the shuffling of the port,bit lines involve changing their direction?

How are you representing the position of the BCD switch after its read? I'm guessing its a single byte with a code 0->something, yes?

How often does the BCD code change? i.e. once, as it would be if you are using the switch to decode PCB revisions, etc. or many times as it would be for a user-selected mode etc.?

There may be more questions but this will get us started. Posting a schematic and any relevant code would be appropriate at this point.

EDIT: The reason for all the questions is that there are a couple ways of doing it, the best one depends on how what you need from the port remapping.
 
Last edited:

Thread Starter

redtree

Joined Feb 3, 2014
14
OK, I hope I get all the questions answered.
The switch has 9 different positions available. Presently, only 3-4 are targeted for use, but may fill the rest eventually.
The present configuration plans for re-configuring about 8 port pins, but the swaps are all within the same group; just re-configure those dedicated 8 pins. The inputs remain IN and the outputs remain OUT.
The switch is wired with its 4 bits into the lower half of Port A.
The intent is to build a single PCBA for multiple uses by re-selecting the switch position.

I don't have the schematic with me, but I'll try and post that later.

Thanks!
 

JohnInTX

Joined Jun 26, 2012
4,787
The intent is to build a single PCBA for multiple uses by re-selecting the switch position.
So on RESET, the code examines the BCD switch once, determines what the IO map should be, and the IO map stays the same after that?
 

Thread Starter

redtree

Joined Feb 3, 2014
14
that would probably work.
My original intent was to continually update the BCD status and respond to chagnes, but I don't think its necessary.
 

JohnInTX

Joined Jun 26, 2012
4,787
I noodled around and came up with this. After thinking about it, I didn't like the FSR approach - too cumbersome. This one uses a table of vectors to handlers that maps logical IO (e.g. MotorON) to physical IO port and bit.

The table has a complete map for each 'mode' i.e. BCD switch. The mode in the demo is set at build-time by setting SimulatedBCDinput but all you have to do is read and qualify the BCD and stuff the value into IO_ModeBase to select the set of IO vectors to remap the IO. Note that the value is the mode number *8 IO entries per mode.

You can build this in MPLABX and step it with MPSIM and watch where it goes. Add stuff to playloop as desired (add additional logical IO to suit your fancy).

I didn't fully init the PIC or test everything but hopefully after stepping it a bit, you'll have something to start from.

Have fun.

Rich (BB code):
;******************* IO REMAPPING FOR ENHANCED MIDRANGE  *****************
    if 0
    Built in MPLABX with MPASMX

    This is a simple IO remapping scheme using a table to link logical
    IO (the IO function) with physical IO (the actual bit handlers).

    Each IO map is referenced as a 'mode' that is selected by a simulated
    BCD switch.

    The mappable IO is grouped together as an array of vectors to handling
    routines.  The logical IO is always at the same index into the group, the
    physical IO is determined by which handler routine that index points to.

    Modes are implemented by replicating the group of logical/physical IO
    relations, one for each mode.  Modes 0-2 are shown here.  Adding modes
    that swap the IO in different ways is as simple as reordering the vectors
    in a group and appending the group to the table.

    Some liberties have been taken to simplify the demo here e.g. the initIO is
    incomplete, the table offset has NOT been bounds checked etc. but the
    general idea is workable.

    Note that the logical IO works on a true/false basis.  The examples show
    how to do inverted IO i.e. 0 turns on an LED (makes it TRUE).

    Build this for MPSIM and step through watching the ports etc work.

    This has NOT been extensively debugged. Banksels are as required only.
    Certain things like table entries could be simplified using macros.
    Not all of the physical IO is implemented but enough to give the idea.
    endif

#include p16F1933.inc

    ERRORLEVEL -302     ; suppress bank warnings

SimulatedBCDinput equ 1 ; Set to mode 0,1, or 2 to select the IO group

    ;********************** RAM BANK 0  *********************
    cblock 20h
    endc

    ;********************** COMMON RAM  *********************
    cblock 70h
    reserved:   1       ; for RealICE et al
    ComFlags:   1       ; common bank flags, bit parameter passing etc
    IO_ModeBase:  1     ; Mappable IO: base offset into jump table for current IO mode
                        ; determined by reading BCD switch
    endc
#define WRparambit ComFlags,0   ; output bit value for mappable outputs

;******************* IO SETUP  **********************
; Same directions for all modes.  Could be added to table
; to configure different directions for different modes.

PORTBinit  equ 00001111B      ; Ch 0-3 (RB0-RB3) inputs
TRISBinit  equ 00001111B
PORTCinit  equ 00000000B      ; Ch 4-7 (RC0-RC3) output0
TRISCinit  equ 00000000B

    ;****************** PROGRAM STARTS  **************************

    ORG 0
    nop

    call    initIO          ; configure IO

    ;------------------- MAIN LOOP  --------------------------
playLoop:
    call    setoutput_0       ; Output0 = true

    call    input_0q          ; input0 true?
    skpc
    bra     in0false          ; nope

in0true:
    call    clearoutput_3     ; output 3 follows input0
    bra     outKdone

in0false:
    call    setoutput_3
    ;*
outKdone:
    call    clearoutput_0      ; output0 = false
    nop
    nop
    
    goto    playLoop

;******************* INIT IO  ******************************
;; VERY cursory init IO..
initIO:
    banksel ANSELB
    clrf    ANSELB      ; make ports digital

    banksel WPUB        ; weak pullups off
    clrf    WPUB

    banksel PORTC       ; quickie init for MPSIM
    movlw   low PORTCinit
    movwf   LATC
    movlw   low PORTBinit
    movwf   LATB

    banksel TRISB
    movlw   low TRISBinit
    movwf   TRISB
    movlw   low TRISCinit
    movwf   TRISC

; ADD IO init for the BCD switch then call this to find out the channel
    call    readBCDsw
    return


;******************** READ BCD SWITCH  **************************
; Simulates reading/debouncing/qualifying BCD switch.  Expectes output
; is offset into IO maptable corresponding to first IO channel in the mode
; For an 8 entry table:
; i.e. mode 0->offset=0, mode 1-> offset=8, mode 2-> offset=16 etc..
; SimulatedBCDinput is set above 0-2
readBCDsw:
    banksel PORTA               ; select 'inputs'
    movlw   SimulatedBCDinput * _sizeofIOmode
    movwf   IO_ModeBase
    return

;******************  INPUT ROUTINEs  **********************
; the main code calls these to sample inputs.  Each loads the
;  offset of its logical channel as the offset from the base
;  value set by the BCD mode switch.
; rets C iff logical input 0 is true
; These change banks!


input_0q:
    movlw   in0ofs              ; W= channel offset into current mode
    call    _procModeChanIO     ; vector to mapped handler through table
    return

;********************** OUTPUT ROUTINES  *********************
; the main code calls these to flip output bits using the table to
; map logical outputs (output_0 etc) to output channels using the table
; to vector to the appropriate handler.

clearoutput_0:
    bcf     WRparambit          ; parameter = 0
    movlw   out0ofs
    call    _procModeChanIO
    return

clearoutput_3:
    bcf     WRparambit          ; parameter = 0
    movlw   out3ofs
    call    _procModeChanIO
    return

setoutput_0:
    bsf     WRparambit          ; parameter = 1
    movlw   out0ofs
    call    _procModeChanIO
    return

setoutput_3:
    bsf     WRparambit          ; parameter = 1
    movlw   out3ofs
    call    _procModeChanIO
    return

;****************** HARDWARE I/O HANDLERS  **********************
; Each physical channel is handled here.  The table connects physical
; channels with logical channels

RDchannel_A:
    banksel PORTB
    clrc
    btfsc   PORTB,0
    setc
    return

RDchannel_B:
    banksel PORTB
    clrc
    btfsc   PORTB,1
    setc
    return

RDchannel_C:
    banksel PORTB
    clrc
    btfsc   PORTB,2
    setc
    return

RDchannel_D:
    banksel PORTB
    clrc
    btfss   PORTB,3     ; this one reads inverted 0 in = TRUE, 1=FALSE
    setc
    return

RDchannel_NULL:
    banksel PORTB       ; bucket for unused inputs in the selected mode
    clrc                ; always returns FALSE
    return

    ;------------------ WRITE HANDLERS  -----------------------
    ; Each of these handles one physical channel. Logical channels
    ; are mapped to individual physical channels by the table
    ; WRparambit is set by the logical IO routine to make the port bit
    ; TRUE/FALSE.
WRchannel_J:
    banksel LATC
    btfss   WRparambit
    bcf     LATC,0
    btfsc   WRparambit
    bsf     LATC,0
    return

WRchannel_K:
    banksel LATC
    btfss   WRparambit
    bcf     LATC,1
    btfsc   WRparambit
    bsf     LATC,1
    return
WRchannel_L:
    banksel LATC
    btfss   WRparambit
    bcf     LATC,2
    btfsc   WRparambit
    bsf     LATC,2
    return

WRchannel_M:                ; this one has an inverted output
    banksel LATC
    btfss   WRparambit      ; Parambit: 0 = FALSE, 1=TRUE
    bsf     LATC,3          ; FALSE outputs a 1
    btfsc   WRparambit
    bcf     LATC,3          ; TRUE outpurs a 0
    return

WRchannel_NULL:
    banksel LATC            ; bucket for unused output in the selected mode
    return


;================================================
;------------------ IO MAPPING TABLE  -----------
;================================================
; Example:
; Channels 0-3 are always inputs, 4-7 outputs.
; Offset of each logical IO to handler in selected mode
in0ofs  equ 0
in1ofs  equ 1
in2ofs  equ 2
in3ofs  equ 3

out0ofs equ 4
out1ofs equ 5
out2ofs equ 6
out3ofs equ 7


; Process mode channel IO: Combines the mode indicator 0,8,16 etc
; with the IO offset 0-7 to get the offset into the table of the
; logical IO. At that location is a goto 'the physical IO handler'
; Logical IO is mapped to physical IO by shuffling the vectors to
; the handlers.

_procModeChanIO:
    addwf   IO_ModeBase,W       ; combine mode and channel offset
    brw                         ; jump to _IOmode0 + mode + channel offset
    ;----------------- MODE 0  -----------------
_IOmode0:
    goto RDchannel_A     ; In 0
    goto RDchannel_B     ; In 1
    goto RDchannel_C     ; In 2
    goto RDchannel_D     ; In 3

    goto WRchannel_J     ; Out 1
    goto WRchannel_K     ; Out 2
    goto WRchannel_L     ; Out 3
    goto WRchannel_M     ; Out 4
_sizeofIOmode   equ $ - _IOmode0

    ;----------------- MODE 1  -----------------
_IOmode1:
    goto RDchannel_D     ; In 0
    goto RDchannel_C     ; In 1
    goto RDchannel_B     ; In 2
    goto RDchannel_A     ; In 3

    goto WRchannel_M     ; Out 1
    goto WRchannel_L     ; Out 2
    goto WRchannel_K     ; Out 3
    goto WRchannel_J     ; Out 4
 
    ;----------------- MODE 2  -----------------
_IOmode2:
    goto RDchannel_A     ; In 0
    goto RDchannel_C     ; In 1
    goto RDchannel_B     ; In 2
    goto RDchannel_NULL  ; In 3 - None on this one

    goto WRchannel_J     ; Out 1
    goto WRchannel_L     ; Out 2
    goto WRchannel_K     ; Out 3
    goto WRchannel_NULL  ; Out 4 - None on this one

_Nmodes equ ($-_IOmode0) / _sizeofIOmode

    if (SimulatedBCDinput > (_Nmodes-1))
        ERROR "IOswap.asm: SimulatedBCDinput exceeds modes in table!"
    endif
   
    END
 

Thread Starter

redtree

Joined Feb 3, 2014
14
Thank you very much, I'll take a look and see if I can decipher enough of it to make it work.

How DO YOU beat the stupid out of sand? (I guess I'm sand?)
 

Papabravo

Joined Feb 24, 2006
21,159
Thank you very much, I'll take a look and see if I can decipher enough of it to make it work.

How DO YOU beat the stupid out of sand? (I guess I'm sand?)
Sand contains the element Silicon which is the primary component used to fabricate electronic parts like transistors, FETs, and integrated circuits such as microcontrollers.

A microcontroller is pretty stupid without a program in it. Putting a program into a microcontroller, or a fusemap into a PAL or FPGA is equivalent to "beating the stupid out of sand".

BTW most of us take a very dim view of the use of pejorative terms with respect to ANY member. It will get you banned for life if you're not careful. So don't assume that such comments automatically refer to people.

I've been doing it since 1962 so I guess I've passed the half century mark.
 

JohnInTX

Joined Jun 26, 2012
4,787
How DO YOU beat the stupid out of sand? (I guess I'm sand?)
No. Sorry for any misunderstanding.
Sand contains the element Silicon which is the primary component used to fabricate electronic parts like transistors, FETs, and integrated circuits such as microcontrollers.

A microcontroller is pretty stupid without a program in it. Putting a program into a microcontroller, or a fusemap into a PAL or FPGA is equivalent to "beating the stupid out of sand".
Exactly, PB. I kind of meant it as an inside joke. Its a signature so shouldn't be taken personally by anyone but maybe I should change it.

I can be a bit blunt sometimes but would never intentionally disparage anyone's knowledge or intentions. Like the other members, my sole motivation is to repay what I've learned here and from many other mentors over the years by sharing hard-earned knowledge and experience.

EDIT: Thanks Max. That's how its intended.

I'd rather talk about the absolutely fabulous :rolleyes: code snippet I created for redtree.
 
Last edited:

Thread Starter

redtree

Joined Feb 3, 2014
14
Sorry to have created a firestorm.
Personally, I meant it as self-deprecating, being new at this whole programming thing.

I'd rather talk about the "awesome code" as well, but alas, I'm still trying to get it imported (and built) correctly!

Thanks Users!
 

JohnInTX

Joined Jun 26, 2012
4,787
Sorry to have created a firestorm.
Personally, I meant it as self-deprecating, being new at this whole programming thing.

I'd rather talk about the "awesome code" as well, but alas, I'm still trying to get it imported (and built) correctly!

Thanks Users!
No worries.
This .zip is the .asm in native MPLABX format if that would help. It should snap right into a new project and build using MPASMX defaults. An easy way to import it might be to open a new project with a blank .asm file, File->open this one, select everything with CTRL-A and copy/paste into the new project's .asm file. Then MPLABX will know where everything is.

It checks out in MPLAB 8.x too. Its intended to run standalone so play with it as and then you can incorporate any useful stuff into your own code.

Post with any questions.
EDIT: Extra credit for finding the morning-sickness issue in the port init that I just found. Geeze

Have fun!
 

Attachments

Last edited:
Top