PIC assembly - swapping #define

Discussion in 'Embedded Systems and Microcontrollers' started by redtree, Feb 3, 2014.

  1. redtree

    Thread Starter New Member

    Feb 3, 2014
    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?

  2. Papabravo


    Feb 24, 2006
    You can use conditional compilation.

    #ifdef __SYMBOL1__
    #define SW_1 PORTA,0

    #ifdef __SYMBOL2__
    #define SW_1 PORTC,2

    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.
  3. JohnInTX


    Jun 26, 2012
    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.
    Code ( (Unknown Language)):
    1. movf INDF0,W
    2. andwf MaskInRam,W
    3. btfsc STATUS,Z
    4. 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?
  4. takao21203

    AAC Fanatic!

    Apr 28, 2012
    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.
  5. Papabravo


    Feb 24, 2006
    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.
  6. redtree

    Thread Starter New Member

    Feb 3, 2014
    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!
  7. JohnInTX


    Jun 26, 2012
    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: Feb 3, 2014
    Papabravo likes this.
  8. Papabravo


    Feb 24, 2006
    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.
  9. redtree

    Thread Starter New Member

    Feb 3, 2014
    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!
  10. JohnInTX


    Jun 26, 2012
    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: Feb 4, 2014
  11. redtree

    Thread Starter New Member

    Feb 3, 2014
    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.

  12. JohnInTX


    Jun 26, 2012
    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?
  13. redtree

    Thread Starter New Member

    Feb 3, 2014
    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.
  14. JohnInTX


    Jun 26, 2012
    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.

    Code ( (Unknown Language)):
    1. ;******************* IO REMAPPING FOR ENHANCED MIDRANGE  *****************
    2.     if 0
    3.     Built in MPLABX with MPASMX
    5.     This is a simple IO remapping scheme using a table to link logical
    6.     IO (the IO function) with physical IO (the actual bit handlers).
    8.     Each IO map is referenced as a 'mode' that is selected by a simulated
    9.     BCD switch.
    11.     The mappable IO is grouped together as an array of vectors to handling
    12.     routines.  The logical IO is always at the same index into the group, the
    13.     physical IO is determined by which handler routine that index points to.
    15.     Modes are implemented by replicating the group of logical/physical IO
    16.     relations, one for each mode.  Modes 0-2 are shown here.  Adding modes
    17.     that swap the IO in different ways is as simple as reordering the vectors
    18.     in a group and appending the group to the table.
    20.     Some liberties have been taken to simplify the demo here e.g. the initIO is
    21.     incomplete, the table offset has NOT been bounds checked etc. but the
    22.     general idea is workable.
    24.     Note that the logical IO works on a true/false basis.  The examples show
    25.     how to do inverted IO i.e. 0 turns on an LED (makes it TRUE).
    27.     Build this for MPSIM and step through watching the ports etc work.
    29.     This has NOT been extensively debugged. Banksels are as required only.
    30.     Certain things like table entries could be simplified using macros.
    31.     Not all of the physical IO is implemented but enough to give the idea.
    32.     endif
    34. #include p16F1933.inc
    36.     ERRORLEVEL -302     ; suppress bank warnings
    38. SimulatedBCDinput equ 1 ; Set to mode 0,1, or 2 to select the IO group
    40.     ;********************** RAM BANK 0  *********************
    41.     cblock 20h
    42.     endc
    44.     ;********************** COMMON RAM  *********************
    45.     cblock 70h
    46.     reserved:   1       ; for RealICE et al
    47.     ComFlags:   1       ; common bank flags, bit parameter passing etc
    48.     IO_ModeBase:  1     ; Mappable IO: base offset into jump table for current IO mode
    49.                         ; determined by reading BCD switch
    50.     endc
    51. #define WRparambit ComFlags,0   ; output bit value for mappable outputs
    53. ;******************* IO SETUP  **********************
    54. ; Same directions for all modes.  Could be added to table
    55. ; to configure different directions for different modes.
    57. PORTBinit  equ 00001111B      ; Ch 0-3 (RB0-RB3) inputs
    58. TRISBinit  equ 00001111B
    59. PORTCinit  equ 00000000B      ; Ch 4-7 (RC0-RC3) output0
    60. TRISCinit  equ 00000000B
    62.     ;****************** PROGRAM STARTS  **************************
    64.     ORG 0
    65.     nop
    67.     call    initIO          ; configure IO
    69.     ;------------------- MAIN LOOP  --------------------------
    70. playLoop:
    71.     call    setoutput_0       ; Output0 = true
    73.     call    input_0q          ; input0 true?
    74.     skpc
    75.     bra     in0false          ; nope
    77. in0true:
    78.     call    clearoutput_3     ; output 3 follows input0
    79.     bra     outKdone
    81. in0false:
    82.     call    setoutput_3
    83.     ;*
    84. outKdone:
    85.     call    clearoutput_0      ; output0 = false
    86.     nop
    87.     nop
    89.     goto    playLoop
    91. ;******************* INIT IO  ******************************
    92. ;; VERY cursory init IO..
    93. initIO:
    94.     banksel ANSELB
    95.     clrf    ANSELB      ; make ports digital
    97.     banksel WPUB        ; weak pullups off
    98.     clrf    WPUB
    100.     banksel PORTC       ; quickie init for MPSIM
    101.     movlw   low PORTCinit
    102.     movwf   LATC
    103.     movlw   low PORTBinit
    104.     movwf   LATB
    106.     banksel TRISB
    107.     movlw   low TRISBinit
    108.     movwf   TRISB
    109.     movlw   low TRISCinit
    110.     movwf   TRISC
    112. ; ADD IO init for the BCD switch then call this to find out the channel
    113.     call    readBCDsw
    114.     return
    117. ;******************** READ BCD SWITCH  **************************
    118. ; Simulates reading/debouncing/qualifying BCD switch.  Expectes output
    119. ; is offset into IO maptable corresponding to first IO channel in the mode
    120. ; For an 8 entry table:
    121. ; i.e. mode 0->offset=0, mode 1-> offset=8, mode 2-> offset=16 etc..
    122. ; SimulatedBCDinput is set above 0-2
    123. readBCDsw:
    124.     banksel PORTA               ; select 'inputs'
    125.     movlw   SimulatedBCDinput * _sizeofIOmode
    126.     movwf   IO_ModeBase
    127.     return
    129. ;******************  INPUT ROUTINEs  **********************
    130. ; the main code calls these to sample inputs.  Each loads the
    131. ;  offset of its logical channel as the offset from the base
    132. ;  value set by the BCD mode switch.
    133. ; rets C iff logical input 0 is true
    134. ; These change banks!
    137. input_0q:
    138.     movlw   in0ofs              ; W= channel offset into current mode
    139.     call    _procModeChanIO     ; vector to mapped handler through table
    140.     return
    142. ;********************** OUTPUT ROUTINES  *********************
    143. ; the main code calls these to flip output bits using the table to
    144. ; map logical outputs (output_0 etc) to output channels using the table
    145. ; to vector to the appropriate handler.
    147. clearoutput_0:
    148.     bcf     WRparambit          ; parameter = 0
    149.     movlw   out0ofs
    150.     call    _procModeChanIO
    151.     return
    153. clearoutput_3:
    154.     bcf     WRparambit          ; parameter = 0
    155.     movlw   out3ofs
    156.     call    _procModeChanIO
    157.     return
    159. setoutput_0:
    160.     bsf     WRparambit          ; parameter = 1
    161.     movlw   out0ofs
    162.     call    _procModeChanIO
    163.     return
    165. setoutput_3:
    166.     bsf     WRparambit          ; parameter = 1
    167.     movlw   out3ofs
    168.     call    _procModeChanIO
    169.     return
    171. ;****************** HARDWARE I/O HANDLERS  **********************
    172. ; Each physical channel is handled here.  The table connects physical
    173. ; channels with logical channels
    175. RDchannel_A:
    176.     banksel PORTB
    177.     clrc
    178.     btfsc   PORTB,0
    179.     setc
    180.     return
    182. RDchannel_B:
    183.     banksel PORTB
    184.     clrc
    185.     btfsc   PORTB,1
    186.     setc
    187.     return
    189. RDchannel_C:
    190.     banksel PORTB
    191.     clrc
    192.     btfsc   PORTB,2
    193.     setc
    194.     return
    196. RDchannel_D:
    197.     banksel PORTB
    198.     clrc
    199.     btfss   PORTB,3     ; this one reads inverted 0 in = TRUE, 1=FALSE
    200.     setc
    201.     return
    203. RDchannel_NULL:
    204.     banksel PORTB       ; bucket for unused inputs in the selected mode
    205.     clrc                ; always returns FALSE
    206.     return
    208.     ;------------------ WRITE HANDLERS  -----------------------
    209.     ; Each of these handles one physical channel. Logical channels
    210.     ; are mapped to individual physical channels by the table
    211.     ; WRparambit is set by the logical IO routine to make the port bit
    212.     ; TRUE/FALSE.
    213. WRchannel_J:
    214.     banksel LATC
    215.     btfss   WRparambit
    216.     bcf     LATC,0
    217.     btfsc   WRparambit
    218.     bsf     LATC,0
    219.     return
    221. WRchannel_K:
    222.     banksel LATC
    223.     btfss   WRparambit
    224.     bcf     LATC,1
    225.     btfsc   WRparambit
    226.     bsf     LATC,1
    227.     return
    228. WRchannel_L:
    229.     banksel LATC
    230.     btfss   WRparambit
    231.     bcf     LATC,2
    232.     btfsc   WRparambit
    233.     bsf     LATC,2
    234.     return
    236. WRchannel_M:                ; this one has an inverted output
    237.     banksel LATC
    238.     btfss   WRparambit      ; Parambit: 0 = FALSE, 1=TRUE
    239.     bsf     LATC,3          ; FALSE outputs a 1
    240.     btfsc   WRparambit
    241.     bcf     LATC,3          ; TRUE outpurs a 0
    242.     return
    244. WRchannel_NULL:
    245.     banksel LATC            ; bucket for unused output in the selected mode
    246.     return
    249. ;================================================
    250. ;------------------ IO MAPPING TABLE  -----------
    251. ;================================================
    252. ; Example:
    253. ; Channels 0-3 are always inputs, 4-7 outputs.
    254. ; Offset of each logical IO to handler in selected mode
    255. in0ofs  equ 0
    256. in1ofs  equ 1
    257. in2ofs  equ 2
    258. in3ofs  equ 3
    260. out0ofs equ 4
    261. out1ofs equ 5
    262. out2ofs equ 6
    263. out3ofs equ 7
    266. ; Process mode channel IO: Combines the mode indicator 0,8,16 etc
    267. ; with the IO offset 0-7 to get the offset into the table of the
    268. ; logical IO. At that location is a goto 'the physical IO handler'
    269. ; Logical IO is mapped to physical IO by shuffling the vectors to
    270. ; the handlers.
    272. _procModeChanIO:
    273.     addwf   IO_ModeBase,W       ; combine mode and channel offset
    274.     brw                         ; jump to _IOmode0 + mode + channel offset
    275.     ;----------------- MODE 0  -----------------
    276. _IOmode0:
    277.     goto RDchannel_A     ; In 0
    278.     goto RDchannel_B     ; In 1
    279.     goto RDchannel_C     ; In 2
    280.     goto RDchannel_D     ; In 3
    282.     goto WRchannel_J     ; Out 1
    283.     goto WRchannel_K     ; Out 2
    284.     goto WRchannel_L     ; Out 3
    285.     goto WRchannel_M     ; Out 4
    286. _sizeofIOmode   equ $ - _IOmode0
    288.     ;----------------- MODE 1  -----------------
    289. _IOmode1:
    290.     goto RDchannel_D     ; In 0
    291.     goto RDchannel_C     ; In 1
    292.     goto RDchannel_B     ; In 2
    293.     goto RDchannel_A     ; In 3
    295.     goto WRchannel_M     ; Out 1
    296.     goto WRchannel_L     ; Out 2
    297.     goto WRchannel_K     ; Out 3
    298.     goto WRchannel_J     ; Out 4
    300.     ;----------------- MODE 2  -----------------
    301. _IOmode2:
    302.     goto RDchannel_A     ; In 0
    303.     goto RDchannel_C     ; In 1
    304.     goto RDchannel_B     ; In 2
    305.     goto RDchannel_NULL  ; In 3 - None on this one
    307.     goto WRchannel_J     ; Out 1
    308.     goto WRchannel_L     ; Out 2
    309.     goto WRchannel_K     ; Out 3
    310.     goto WRchannel_NULL  ; Out 4 - None on this one
    312. _Nmodes equ ($-_IOmode0) / _sizeofIOmode
    314.     if (SimulatedBCDinput > (_Nmodes-1))
    315.         ERROR "IOswap.asm: SimulatedBCDinput exceeds modes in table!"
    316.     endif
    318.     END
  15. redtree

    Thread Starter New Member

    Feb 3, 2014
    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?)
  16. Papabravo


    Feb 24, 2006
    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 likes this.
  17. MaxHeadRoom


    Jul 18, 2013
    Funny.. I took that as sort of self-deprecating?:confused:
    Papabravo and JohnInTX like this.
  18. JohnInTX


    Jun 26, 2012
    No. Sorry for any misunderstanding.
    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: Feb 5, 2014
  19. redtree

    Thread Starter New Member

    Feb 3, 2014
    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!
    Papabravo likes this.
  20. JohnInTX


    Jun 26, 2012
    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!
    Last edited: Feb 5, 2014