Menu

Setting pin values using an array

Help
2018-10-16
2018-10-16
  • Jim Gregory

    Jim Gregory - 2018-10-16

    I have a subroutine that turns a port pin on or off based on a numerical index:

    Sub TurnOnOff(index, level)
        ' level is ON or OFF
        Select Case i
            Case 1
                SetWith( portA.2, level)
            Case 2
                SetWith( portA.5, level)
            Case 3
                SetWith( portC.0, level)
            Case 4
                SetWith( portC.3, level)
            Case 5
                SetWith( portC.6, level)
            Case 6
                SetWith( portB.0, level)
            Case 7
                SetWith( portB.3, level)
            Case 8
                SetWith( portB.3, level)
        End Select
    End Sub
    

    It would seem like using an array would be a natural way to handle this, but the following doesn't work:

    device_ports = portA.2, portA.5, portC.0, portC.3, portC.6, portB.0, portB.3, portB.3 
    
    Sub TurnOnOff(index, level)
        ' level is ON or OFF
        SetWith(device_ports(index), level)
    End Sub
    

    Is there another way this could be done? I'm using Version: 0.98.02 2018-05-16 (Linux), FWIW.

    -Jim

     

    Last edit: Jim Gregory 2018-10-16
  • Chris Roper

    Chris Roper - 2018-10-16

    Ports are not Variables, they are Physical registers within the target device, therefore, you cannot form an array of Ports.

    So the short answer is that you can not do what you want to do.

    Now for the Long answer............

    If you look at the Datasheet for the PIC16 family some interesting patterns emerge that may help:

    1. Each Port is actually an Array of Bits that we normally address in the form of PortX.BitX for example PortB.4 is device Pin RB4

    2. The Port Registers are 8 bit and consecutively maped in the Memory Space of the Device.

    So if you are ready we can delve into The Dark Art of Bit Manipulation to find a possible solution.

    Disclaimer:
    What follows is not a good technique in the wrong hands. This sort of coding should only be used with care and a full understanding of the Device Memory Map and all of its Registers. In addition you can forget about Portability if you use Direct Address Level access to the Device.
    Your mileage may vary, batteries not included.

    That said, it should port across most PIC16 devices.

    Now let's get down to the fun......

    GCBASIC has a useful function that allows us to determine the address of a object, for example:

    Dim MyVar as Byte
    Dim VarAddress as Word
    
     VarAddress = @MyVar
    

    It also has PEEK and POKE Commands that allow access to anywhere in the Memory Map.

    So from point 2. above we can use @PortA to infer the address of all of the I/O Ports of the device then use Peek and Poke to access them.

    So rather than creating an Array we will use the existing memory map as if it were an Array.

    The first thing we need to determine is how to Index the Array, keeping in mind that it is an array of bits. Having obtained a base point we can take it as the Base of the Array and we need our own function to access it.

    Effectively it is a two dimensional Array composed of (Port, Bit) and could be addressed as:

    Sub TurnOnOff(MyPort, MyBit,  Level)
    

    But your initial request was to access tha array in the form of:

    Sub TurnOnOff(index, level)
    

    So I will treat it as such. I think the Best solution is to treat PortA as zero, so Porta.0 or in RA0 would be index 0x00 and RA7 would be Index 0x07. We could then say that PortB was indexed as 0x10 for RB0 and 0x17 for RB7.

    Notice that I am indexing in Hexadecimal. This is essential when working at register or Direct memory access level as all registers and memory locations are addressed in Hex and are 8 or 16 bit wide. In this instance Octal would be the better choice but as far as I am aware GCBASIC has no Octal Numerical Type.

    The Solution:

    This is what i came up with for the fist pass:

        Sub TurnOnOff(in Index as word, in Level)
        ' Index is the Port/Bit Number (PortA.4 = 0x04, PortB.1 = 0x11 etc.)
        ' level is ON or OFF
            Dim BitX as Byte
            BitX = Index AND 0x0F
            Index = FnLSR(Index, 4)
            Index += @PORTA
            if Level then
              Poke Index, Peek(Index) OR FnLSL(1, BitX)
            else
              Poke Index, Peek(Index) AND NOT FnLSL(1, BitX)
            End if
        End Sub
    

    Expanding that into quick example Program to Flash an LED would now be:

        ;Chip Settings
        #chip 16f628a
    
        Dir PortB Out
    
        Dim MyIndex as Byte
    
        MyIndex = 0x14  ' PortB Bit 4 ie RB4
    
        Do Forever
            TurnOnOff(MyIndex, ON)
            Wait 250 ms
            TurnOnOff(MyIndex, OFF)
            Wait 250 ms
        Loop
    
    end
    

    Taking the Example one step further we can bring it closer to your original request by defining an Array of Hexadecimal values to suit your intended ports and use that as an index into the TurnOnOff() subroutine.

    I am also using slightly more obscure names for the final code as names like Index are common and could cause conflicts in a function/sub that may be used by others.

    As I don't have access to your hardware and I do not know your intended application, I used a 16F877a in Real PIC Simulator to create a chase sequence on 8 LED's as per your example ports:


    Not animated unfortunately but you get the idea :)

        ;Chip Settings
        #chip 16f877a
    
        #option Explicit
    
        Dim LedNum as Byte
        Dim device_ports(8)
        ' device_ports = portA.2, portA.5, portC.0, portC.3, portC.6, portB.0, portB.3, portB.4
        device_ports = 0x02, 0x05, 0x20, 0x23, 0x26, 0x10, 0x13, 0x14
    
        Dir PortA Out
        Dir PortB Out
        Dir PortC Out
    
        Do Forever
            LedNum += 1
            If LedNum > 8 then LedNum = 1
            TurnOnOff(device_ports(LedNum), ON)
            Wait 250 ms
            TurnOnOff(device_ports(LedNum), OFF)
            Wait 250 ms
        Loop
    
    end
    
        Sub TurnOnOff(in PortNum as word, in LevelOut)
        ' PortNum is the Port/Bit Number (PortA.4 = 0x04, PortB.1 = 0x11 etc.)
        ' level is ON or OFF
            Dim BitX as Byte              ' Working Variable
            BitX = PortNum AND 0x0F       ' Mask off the Bit Number
            PortNum = FnLSR(PortNum, 4)   ' Drop lower 4 bits (bit Number)
            PortNum += @PORTA             ' Adjust to Port address
            if LevelOut then
              Poke PortNum, Peek(PortNum) OR FnLSL(1, BitX)       ' Set PortX.Bit ON
            else
              Poke PortNum, Peek(PortNum) AND NOT FnLSL(1, BitX)  ' Set PortX.Bit OFF
            End if
        End Sub
    

    I hope that helps.

    Cheers
    Chris

     

    Last edit: Chris Roper 2018-10-16
  • stan cartwright

    stan cartwright - 2018-10-16

    I'm not sure what the original request was about but your solution reminds me of picaxe basic.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.