News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

Assigning a variable to a bit position

Started by david, Dec 29, 2022, 10:18 AM

Previous topic - Next topic

david

Hi,
I'm dabbling with a simple FSK project and need to step through the 8 bits in a byte value (letter) and thought I could do something like this.

For bitnum=0 To 7
    If letter.bitnum=0 Then t=1500  'low bit
    If letter.bitnum=1 Then t=1000  'high bit
    GPIO.0=1
    DelayUS t
    GPIO.0=0
    DelayUS t
 Next bitnum
    Return

The compiler doesn't accept the byte.bitnum format so I used the following which works fine.

For bitnum=0 To 7
    If letter.0=0 Then t=1500  'low bit
    If letter.0=1 Then t=1000  'high bit
    GPIO.0=1
    DelayUS t
    GPIO.0=0
    DelayUS t
    letter=letter>>1
  Next bitnum
    Return

Am I missing something here and is there a better method to do this?  I know the second approach needs to be changed to a Do..Until rather than a For...Next loop.

Cheers,
David

Amateurtje


david

Thank you!
I've only recently upgraded to Positron and this is a new command to me. (I can't check if it was in Proton now) That will do nicely.
Much appreciated.

David

david

OK.  Using the two GetBit commands and deleting the "letter=letter>>1" just added 25 more program words.  Seems a big overhead so I may stick with the more concise approach.

David

Pepe

It could also be like this
For bitnum=0 To 7
    t=1500
    If letter.0 <>0 Then t=1000  'high bit
    GPIO.0=1
    DelayUS t
    GPIO.0=0
    DelayUS t
    letter=letter>>1
  Next bitnum
    Return

david

Indeed.  That's two less program words than I started with.
Thanks,

David

trastikata

Quote from: david on Dec 29, 2022, 11:56 AMIndeed.  That's two less program words than I started with.
Thanks,

David

This will further save you some code space and RAM:

Dim bitnum As Byte
Dim letter As Byte

For bitnum = 0 To 7
   GPIO.0 = 1
   GoSub BitCheck
   GPIO.0 = 0
   GoSub BitCheck
   letter = letter >> 1
Next

BitCheck:
   If letter.0 = 0 Then
      DelayUS 1500
   Else
      DelayUS 1000
   EndIf
Return

keytapper

In economic term, without the GoSub will save programming two words  ;D
Ignorance comes with a cost

trastikata

Quote from: keytapper on Dec 29, 2022, 04:16 PMIn economic term, without the GoSub will save programming two words  ;D

Can you give an example in the OP context?

david

Quote from: trastikata on Dec 29, 2022, 01:25 PM
Quote from: david on Dec 29, 2022, 11:56 AMIndeed.  That's two less program words than I started with.
Thanks,

David

This will further save you some code space and RAM:

Dim bitnum As Byte
Dim letter As Byte

For bitnum = 0 To 7
   GPIO.0 = 1
   GoSub BitCheck
   GPIO.0 = 0
   GoSub BitCheck
   letter = letter >> 1
Next

BitCheck:
   If letter.0 = 0 Then
      DelayUS 1500
   Else
      DelayUS 1000
   EndIf
Return

Many thanks for the reply.  I've dropped your suggestion in to my code and it actually uses 1 more program word than before (Pepe's suggestion).  Now I should say I'm not pushed for code space but I did think the overhead of the GetBit command was too much.
I'm happy with what I have and I'm sure there are more worthy causes for the experts to engage with.  My thanks to you all for your input.

Cheers,
David

trastikata

HEllo david,

out of curiosity what device do you use?

Stephen Moss

Quote from: david on Dec 29, 2022, 10:18 AMThe compiler doesn't accept the byte.bitnum format so I used the following which works fine.

For bitnum=0 To 7
    If letter.0=0 Then t=1500  'low bit
    If letter.0=1 Then t=1000  'high bit
    GPIO.0=1
    DelayUS t
    GPIO.0=0
    DelayUS t
    letter=letter>>1
  Next bitnum
    Return
Maybe I am missing something here but if you are stepping through the Bits of a Byte sized Letter, therefore should not what appears to be a shift to the next letter (Letter=letter>>1) be performed after the For=Next loop once all the bits of the current letter have been processed?

Second, to me the code appears to be altering the length of the High/Low time of the bit, but only for one cycle which is not what my understanding of FSK is. For it to be FSK don't you need to do that for more than one cycle otherwise it is not generating a frequency?

I wrote this just as a quick FSK test a few years ago...
Device = 18F452

Declare Reminders Off
@ CONFIG_REQ = 0 ; Override Compiler's configuration settings
Asm-
__Config Config1H, 0x22 ;OSC_HS & OSCS_OFF
__Config Config2L, 0x0D ;PWRT_OFF & BOR_OFF & BORV_20
__Config Config2H, 0x0E ;WDT_OFF & WDTPS_128
__Config Config3H, 0x01 ;CCP2MUX_ON
__Config Config4L, 0x85 ;STVR_ON & LVP_ON & DEBUG_OFF
__Config Config5L, 0x0F ;CP0_OFF & CP1_OFF & CP2_OFF & CP3_OFF
__Config Config5H, 0xC0 ;CPB_OFF & CPD_OFF
__Config Config6L, 0x0F ;WRT0_OFF & WRT1_OFF & WRT2_OFF & WRT3_OFF
__Config Config6H, 0xE0 ;WRTC_OFF & WRTB_OFF & WRTD_OFF
__Config Config7L, 0x0F ;EBTR0_OFF & EBTR1_OFF & EBTR2_OFF & EBTR3_OFF
__Config Config7H, 0x40 ;EBTRB_OFF
Endasm-
Declare Reminders On

;**** End of Fuse Configurator Settings ****
;-------------------------------------------------------------------------------
Xtal = 20

'Set Tris Registers
TRISA = $00
TRISB = $00
TRISC = $00
TRISD = $00
TRISE = $00

'Set port pin default output states
PORTA = $00
PORTB = $00
PORTC = $00
PORTD = $00
PORTE = $00

'Setp LCD Display
'LCD_Type = Alpha
'LCD_DTPin = PORTD.4
'LCD_ENPin = PORTE.1
'LCD_RSPin = PORTE.0
'LCD_Interface = 4

'Setup Variable & Constants
Dim Half_Period As Word 'Variable holding half period of FSK frequency
Dim Cycles As Byte      'Variable holding number of cycle displayed
Dim MaxCycles As Byte   'Variable holding Maximum Nuber of Cycles to be displayed
Dim Mask As Word        'Variable holding Mask for checking Test data
Dim A As Byte           'Variable for holding result of Test data logically ANDed with the Mask

Symbol Info = %11001010 'Constant holding dummy test data

Start:                  'Start of Main program
Mask = $0001            'Initial Mask Value


While Mask < $0100      'Test all 8 bits of data
A = Info & Mask         'Mask of data bit to be tested
If A = 1 Then
Half_Period = 250 : MaxCycles = 10   'Set Frequency, 250 = 2KHz (values in uS). MaxCycles set to keep bit length equal.
Else
Half_Period = 500 : MaxCycles = 5    'Set Frequency, 500 = 1 KHz(values in uS). MaxCycles Set To keep Bit length equal.
EndIf

GoSub FSK               'Jump to the bit that does the work
Mask = Mask * 2         'Alter Mask to test the next bit of data
Wend               

DelayUS 10000           'Wait 10mS - to seperate data bytes
GoTo Start              'Repeat endlessly

'Output signal on PortC.1 (5 cycles per bit)
FSK:
For Cycles = 1 To MaxCycles
High PORTC.1
DelayUS Half_Period
Low PORTC.1
DelayUS Half_Period
Next Cycles
Return

GoTo Start              'Back stop just in case return form subroutine fails
End                     'Back stop if it all goes really pear shaped

If you run it using the ISIS PIC18_ALDC_VHB, set the scope timebase to 5mS and the trigger to Single shot you can see it is clearly generating two different frequecy squarewaves (set by the Half_period value) for the duration of each bit (set by the Max_Cycles value) in the test byte.

keytapper

#12
Quote from: trastikata on Dec 29, 2022, 04:25 PMCan you give an example in the OP context?
My challenge  ;D 
Dim PP0 As Word System              ' addressing directly the bytes used for the
                                    ' DelayUS command, no bytes to move around
Dim Example as Byte

Main:

Example = 0b01001100
SendFSK(Example)
End

Proc SendFSK(letter as Byte)
    Dim BitDelay As PP0.Word        ' alias for the two system bytes
    Dim bitnum As Byte              ' Number of bits to be sent. Byte sized
    bitnum = 8                      ' Initial count
    Repeat                          ' Fast mode to repeat a sequence, better
                                    ' than FOR/NEXT
        GPIO.0 = 1
        If letter.0 = 0 Then
            BitDelay = 1500         ' Setting for the long bit
        Else
            BitDelay = 1000         ' Setting for the short bit
        EndIf
        DelayUS BitDelay            ' Wait that much of time
        GPIO.0 = 0                  ' Output goes low
        letter = letter >> 1        ' Shift left the bit for the next round
        Dec bitnum                  ' how many bits to count
    Until bitnum > 0                ' Until the all the byte is sent
EndProc
So the procedure here serves for OP to insert in his code and then call the SendFSK with the byte to be transmitted. Here I just made a variable called Example :D
Ignorance comes with a cost

trastikata

Nice coding but you are missing the second delay, after GPIO.0 = 0.

If you add that second delay then you get 2 extra words. Thus again  ;):

QuoteIn economic term, without the GoSub will save programming two words


david

Quote from: trastikata on Dec 29, 2022, 11:01 PMHEllo david,

out of curiosity what device do you use?

Hi,
It's a 12F675 - old and very modest.

Cheers,
David

Pepe

I think it should be that way.


un ciclo, de lo contrario no está generando una frecuencia?

Escribí esto solo como una prueba rápida de FSK hace unos años...
CódigoSeleccione  Expandir
Device = 18F452

Declare Reminders Off
@ CONFIG_REQ = 0 ; Override Compiler's configuration settings
Asm-
__Config Config1H, 0x22 ;OSC_HS & OSCS_OFF
__Config Config2L, 0x0D ;PWRT_OFF & BOR_OFF & BORV_20
__Config Config2H, 0x0E ;WDT_OFF & WDTPS_128
__Config Config3H, 0x01 ;CCP2MUX_ON
__Config Config4L, 0x85 ;STVR_ON & LVP_ON & DEBUG_OFF
__Config Config5L, 0x0F ;CP0_OFF & CP1_OFF & CP2_OFF & CP3_OFF
__Config Config5H, 0xC0 ;CPB_OFF & CPD_OFF
__Config Config6L, 0x0F ;WRT0_OFF & WRT1_OFF & WRT2_OFF & WRT3_OFF
__Config Config6H, 0xE0 ;WRTC_OFF & WRTB_OFF & WRTD_OFF
__Config Config7L, 0x0F ;EBTR0_OFF & EBTR1_OFF & EBTR2_OFF & EBTR3_OFF
__Config Config7H, 0x40 ;EBTRB_OFF
Endasm-
Declare Reminders On

;**** End of Fuse Configurator Settings ****
;-------------------------------------------------------------------------------
Xtal = 20

'Set Tris Registers
TRISA = $00
TRISB = $00
TRISC = $00
TRISD = $00
TRISE = $00

'Set port pin default output states
PORTA = $00
PORTB = $00
PORTC = $00
PORTD = $00
PORTE = $00

'Setp LCD Display
'LCD_Type = Alpha
'LCD_DTPin = PORTD.4
'LCD_ENPin = PORTE.1
'LCD_RSPin = PORTE.0
'LCD_Interface = 4

'Setup Variable & Constants
Dim Half_Period As Word 'Variable holding half period of FSK frequency
Dim Cycles As Byte      'Variable holding number of cycle displayed
Dim MaxCycles As Byte  'Variable holding Maximum Nuber of Cycles to be displayed
Dim Mask As Word        'Variable holding Mask for checking Test data
Dim A As Byte          'Variable for holding result of Test data logically ANDed with the Mask

Symbol Info = %11001010 'Constant holding dummy test data

Start:                  'Start of Main program
Mask = $0001            'Initial Mask Value


While Mask < $0100      'Test all 8 bits of data
A = Info & Mask        'Mask of data bit to be tested
If A <> 0 Then
Half_Period = 500 : MaxCycles = 5    'Set Frequency, 500 = 1 KHz(values in uS). MaxCycles Set To keep Bit length equal.
Else
Half_Period = 250 : MaxCycles = 10  'Set Frequency, 250 = 2KHz (values in uS). MaxCycles set to keep bit length equal.
EndIf

GoSub FSK              'Jump to the bit that does the work
Mask = Mask << 1        'Alter Mask to test the next bit of data
Wend             

DelayUS 10000          'Wait 10mS - to seperate data bytes
GoTo Start              'Repeat endlessly

'Output signal on PortC.1 (5 cycles per bit)
FSK:
For Cycles = 1 To MaxCycles
High PORTC.1
DelayUS Half_Period
Low PORTC.1
DelayUS Half_Period
Next Cycles
Return

GoTo Start              'Back stop just in case return form subroutine fails
End                    'Back stop if it all goes really pear shaped


top204

#16
A better, and more efficient, method of detecting an LSB (Least Significant Bit) or MSB (Most Significant Bit) is to rotate the variable into the Carry flag with either the Ror or Rol commands, then use the Carry flag as the comparison whether the bit was set or clear. Also, a backward For-Next loop creates very efficient assembler code. For example, the procedure below shows a method of using both:

'-----------------------------------------------------------------------
' Send a character
' Input     : pChar holds the character to send
' Output    : None
' Notes     : None
'
Proc Send_Char(pChar As Byte) 
    Dim bBitnum As Byte             ' Holds the bit loop index
    Dim wTime As Word               ' Holds the time for the delay

    For bBitnum = 7 To 0 Step -1    ' Create a loop for the 8-bits
        wTime = 1000                ' Default to a delay of 1000 uS
        Ror pChar                   ' Rotate pChar right, into the Carry flag      
        If STATUSbits_C = 0 Then    ' Is the Carry flag clear?
            wTime = 1500            ' Yes. So set the delay to 1500 uS
        EndIf
        PinSet GPIO.0               ' Set the pin as high
        DelayUS wTime               ' Wait for a length of time
        PinClear GPIO.0             ' Pull the pin low
        DelayUS wTime               ' Wait for a length of time
    Next                            ' Close the loop
EndProc

Notice it is using the Ror command because the data is being send LSB (Least Significant Bit) first, then comparing what has been right rotated into the Carry flag (STATUSbits_C). If the carry flag holds 1, then bit-0 of the variable that was rotated (pChar) was holding 1, and vice-versa.

The above procedure is an alteration of your method of transmitting a character and it is not a tested FSK procedure, because I will need to study up on that type of method. To me, it looks more like Pulse Coded Modulation, but I could be wrong.

david

Hello Stephen,
Yes both questions are fair comment.
My choice of name for "letter" was not good and the term letter=letter>>1 is actually stepping through the bits of a single character.  I even caught myself out with this so I will change the term to "charbits" unless you can think of something more suitable.

The FSK explanation is a bit more long winded.  I've used little RF modules before but they were 868MHz, FM using FSK.  No need for Manchester encoding - just throw serial data at them and they were good for 100m+.  The modules I have now are OOK or ASK and need a very balanced data stream for the data slicer to work well. I tried Manchester encoding the data but perhaps I was missing something as the start and stop bits produced some asymmetry which limited range. That's about when I thought I would dabble with my own protocol to emulate the FM modules I had previously used.
One advantage of FM is that it has a constant carrier level without modulation.  In my experiment I used a 4mS on/4mS off modulation as a background signal.   For data I use a single cycle of 1mS/1mS for a high bit and 1.5mS/1.5mS for a low bit.  A start bit is 6mS/6mS.  This gives a fully balanced stream and so far the range/sensitivity seems good but I may play with the times.
So yes it's not bursts of two tones as in classic FSK but it does use two different frequencies for high and low bits even if only a single cycle of each.
I know continuous transmission may not be allowed on some bands in some countries and I can stop the background pulses and instead use a short burst of them as a preamble before the start bit.
On the receive side I use Tmr1 to measure each transition, rejecting anything less or more than ~6mS to give the sync then decode the next 8 bits as a character.  I'm sure this has all been done before, probably by HAMs and for other early comms applications.

Cheers,

keytapper

#18
Quote from: trastikata on Dec 30, 2022, 10:16 AMNice coding but you are missing the second delay, after GPIO.0 = 0.
Oops I misunderstood how one bit is sent. Now I read more carefully and I presume that a low bit is made of two levels but shorter than the high bit.

@top204
Ror or Rol may save the extra code to clear the carry bit each shift used with >> or <<.
My concern was how the delay variable is moved twice to carry out the delay.  ;)
The FOR/NEXT loop has a bit more overhead to a variable that has only the purpose to count how many bit are shifted out. This what I learnt here.
But surely better not risking black magic and let the compiler make the correct compiling.
Ignorance comes with a cost